Round dimensions down when decoding subsampled YUV with odd dimensions
diff --git a/CHANGELOG.md b/CHANGELOG.md index b48ce47..1ea5718 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ ### Changed - avifenc - Removed accidental double-delete of avifImage when failing to read a y4m file input +- Round dimensions down when decoding subsampled YUV with odd dimensions ## [0.5.2] - 2019-11-23 ### Changed
diff --git a/include/avif/internal.h b/include/avif/internal.h index f4d39ed..fe2edf5 100644 --- a/include/avif/internal.h +++ b/include/avif/internal.h
@@ -12,6 +12,7 @@ // Yes, clamp macros are nasty. Do not use them. #define AVIF_CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) +#define AVIF_MIN(a, b) (((a) < (b)) ? (a) : (b)) // Used by stream related things. #define CHECK(A) \
diff --git a/src/reformat.c b/src/reformat.c index bf71940..0278fdf 100644 --- a/src/reformat.c +++ b/src/reformat.c
@@ -190,12 +190,14 @@ const float kr = state->kr; const float kg = state->kg; const float kb = state->kb; + const uint32_t maxUVI = (image->width > 1) ? (image->width >> state->formatInfo.chromaShiftX) - 1 : 0; + const uint32_t maxUVJ = (image->height > 1) ? (image->height >> state->formatInfo.chromaShiftY) - 1 : 0; float maxChannel = (float)((1 << image->depth) - 1); uint8_t ** rgbPlanes = image->rgbPlanes; uint32_t * rgbRowBytes = image->rgbRowBytes; for (uint32_t j = 0; j < image->height; ++j) { - const int uvJ = j >> state->formatInfo.chromaShiftY; + const uint32_t uvJ = AVIF_MIN(j >> state->formatInfo.chromaShiftY, maxUVJ); uint16_t * ptrY = (uint16_t *)&image->yuvPlanes[AVIF_CHAN_Y][(j * image->yuvRowBytes[AVIF_CHAN_Y])]; uint16_t * ptrU = (uint16_t *)&image->yuvPlanes[AVIF_CHAN_U][(uvJ * image->yuvRowBytes[AVIF_CHAN_U])]; uint16_t * ptrV = (uint16_t *)&image->yuvPlanes[AVIF_CHAN_V][(uvJ * image->yuvRowBytes[AVIF_CHAN_V])]; @@ -205,7 +207,7 @@ for (uint32_t i = 0; i < image->width; ++i) { // Unpack YUV into unorm - const int uvI = i >> state->formatInfo.chromaShiftX; + uint32_t uvI = AVIF_MIN(i >> state->formatInfo.chromaShiftX, maxUVI); uint32_t unormY = ptrY[i]; uint32_t unormU = ptrU[uvI]; uint32_t unormV = ptrV[uvI]; @@ -286,12 +288,14 @@ const float kr = state->kr; const float kg = state->kg; const float kb = state->kb; + const uint32_t maxUVI = (image->width > 1) ? (image->width >> state->formatInfo.chromaShiftX) - 1 : 0; + const uint32_t maxUVJ = (image->height > 1) ? (image->height >> state->formatInfo.chromaShiftY) - 1 : 0; float maxChannel = (float)((1 << image->depth) - 1); uint8_t ** rgbPlanes = image->rgbPlanes; uint32_t * rgbRowBytes = image->rgbRowBytes; for (uint32_t j = 0; j < image->height; ++j) { - int uvJ = j >> state->formatInfo.chromaShiftY; + const uint32_t uvJ = AVIF_MIN(j >> state->formatInfo.chromaShiftY, maxUVJ); uint8_t * ptrY = &image->yuvPlanes[AVIF_CHAN_Y][(j * image->yuvRowBytes[AVIF_CHAN_Y])]; uint8_t * ptrU = &image->yuvPlanes[AVIF_CHAN_U][(uvJ * image->yuvRowBytes[AVIF_CHAN_U])]; uint8_t * ptrV = &image->yuvPlanes[AVIF_CHAN_V][(uvJ * image->yuvRowBytes[AVIF_CHAN_V])]; @@ -301,7 +305,7 @@ for (uint32_t i = 0; i < image->width; ++i) { // Unpack YUV into unorm - const int uvI = i >> state->formatInfo.chromaShiftX; + uint32_t uvI = AVIF_MIN(i >> state->formatInfo.chromaShiftX, maxUVI); uint32_t unormY = ptrY[i]; uint32_t unormU = ptrU[uvI]; uint32_t unormV = ptrV[uvI];