Add avifImageCopySamples() to internal.h Remove testutil::CopyImageSamples(). Use avifImageCopySamples() in read.c.
diff --git a/include/avif/internal.h b/include/avif/internal.h index 54cf85f..4eb5bf2 100644 --- a/include/avif/internal.h +++ b/include/avif/internal.h
@@ -89,6 +89,11 @@ // Copies all fields that do not need to be freed/allocated from srcImage to dstImage. void avifImageCopyNoAlloc(avifImage * dstImage, const avifImage * srcImage); +// Copies the samples from srcImage to dstImage. dstImage must be allocated. +// srcImage and dstImage must have the same width, height, and depth. +// If the AVIF_PLANES_YUV bit is set in planes, then srcImage and dstImage must have the same yuvFormat and yuvRange. +void avifImageCopySamples(avifImage * dstImage, const avifImage * srcImage, avifPlanesFlags planes); + typedef struct avifAlphaParams { uint32_t width;
diff --git a/src/avif.c b/src/avif.c index 087c827..8fa2b36 100644 --- a/src/avif.c +++ b/src/avif.c
@@ -182,6 +182,44 @@ dstImage->imir = srcImage->imir; } +void avifImageCopySamples(avifImage * dstImage, const avifImage * srcImage, avifPlanesFlags planes) +{ + assert(srcImage->depth == dstImage->depth); + if (planes & AVIF_PLANES_YUV) { + assert((srcImage->yuvFormat == dstImage->yuvFormat) && (srcImage->yuvRange == dstImage->yuvRange)); + } + const size_t bytesPerPixel = avifImageUsesU16(srcImage) ? 2 : 1; + + const avifBool skipColor = !(planes & AVIF_PLANES_YUV); + const avifBool skipAlpha = !(planes & AVIF_PLANES_A); + for (int c = AVIF_CHAN_Y; c <= AVIF_CHAN_A; ++c) { + const avifBool alpha = c == AVIF_CHAN_A; + if ((skipColor && !alpha) || (skipAlpha && alpha)) { + continue; + } + + const uint32_t planeWidth = avifImagePlaneWidth(srcImage, c); + const uint32_t planeHeight = avifImagePlaneHeight(srcImage, c); + const uint8_t * srcRow = avifImagePlane(srcImage, c); + uint8_t * dstRow = avifImagePlane(dstImage, c); + const uint32_t srcRowBytes = avifImagePlaneRowBytes(srcImage, c); + const uint32_t dstRowBytes = avifImagePlaneRowBytes(dstImage, c); + assert(!srcRow == !dstRow); + if (!srcRow) { + continue; + } + assert(planeWidth == avifImagePlaneWidth(dstImage, c)); + assert(planeHeight == avifImagePlaneHeight(dstImage, c)); + + const size_t planeWidthBytes = planeWidth * bytesPerPixel; + for (uint32_t y = 0; y < planeHeight; ++y) { + memcpy(dstRow, srcRow, planeWidthBytes); + srcRow += srcRowBytes; + dstRow += dstRowBytes; + } + } +} + avifResult avifImageCopy(avifImage * dstImage, const avifImage * srcImage, avifPlanesFlags planes) { avifImageFreePlanes(dstImage, AVIF_PLANES_ALL); @@ -208,22 +246,7 @@ return allocationResult; } } - for (int plane = AVIF_CHAN_Y; plane <= AVIF_CHAN_A; ++plane) { - uint8_t * dstRow = avifImagePlane(dstImage, plane); - if (!dstRow) { - continue; - } - const uint8_t * srcRow = avifImagePlane(srcImage, plane); - uint32_t srcRowBytes = avifImagePlaneRowBytes(srcImage, plane); - uint32_t dstRowBytes = avifImagePlaneRowBytes(dstImage, plane); - uint32_t planeWidthBytes = avifImagePlaneWidth(dstImage, plane) << (dstImage->depth > 8); - uint32_t planeHeight = avifImagePlaneHeight(dstImage, plane); - for (uint32_t j = 0; j < planeHeight; ++j) { - memcpy(dstRow, srcRow, planeWidthBytes); - srcRow += srcRowBytes; - dstRow += dstRowBytes; - } - } + avifImageCopySamples(dstImage, srcImage, planes); return AVIF_RESULT_OK; }
diff --git a/src/read.c b/src/read.c index 14f9041..d115e23 100644 --- a/src/read.c +++ b/src/read.c
@@ -1375,11 +1375,14 @@ avifGetPixelFormatInfo(firstTile->image->yuvFormat, &formatInfo); unsigned int tileIndex = oldDecodedTileCount; - size_t pixelBytes = avifImageUsesU16(dstImage) ? 2 : 1; unsigned int rowIndex = oldDecodedTileCount / grid->columns; unsigned int colIndex = oldDecodedTileCount % grid->columns; // Only the first iteration of the outer for loop uses this initial value of colIndex. // Subsequent iterations of the outer for loop initializes colIndex to 0. + avifImage srcView; + avifImageSetDefaults(&srcView); + avifImage dstView; + avifImageSetDefaults(&dstView); for (; rowIndex < grid->rows; ++rowIndex, colIndex = 0) { for (; colIndex < grid->columns; ++colIndex, ++tileIndex) { if (tileIndex >= decodedTileCount) { @@ -1388,60 +1391,22 @@ } avifTile * tile = &data->tiles.tile[firstTileIndex + tileIndex]; - unsigned int widthToCopy = firstTile->image->width; - unsigned int maxX = firstTile->image->width * (colIndex + 1); - if (maxX > grid->outputWidth) { - widthToCopy -= maxX - grid->outputWidth; + avifCropRect dstViewRect = { + firstTile->image->width * colIndex, firstTile->image->height * rowIndex, firstTile->image->width, firstTile->image->height + }; + if (dstViewRect.x + dstViewRect.width > grid->outputWidth) { + dstViewRect.width = grid->outputWidth - dstViewRect.x; } - - unsigned int heightToCopy = firstTile->image->height; - unsigned int maxY = firstTile->image->height * (rowIndex + 1); - if (maxY > grid->outputHeight) { - heightToCopy -= maxY - grid->outputHeight; + if (dstViewRect.y + dstViewRect.height > grid->outputHeight) { + dstViewRect.height = grid->outputHeight - dstViewRect.y; } - - // Y and A channels - size_t yaColOffset = (size_t)colIndex * firstTile->image->width; - size_t yaRowOffset = (size_t)rowIndex * firstTile->image->height; - size_t yaRowBytes = widthToCopy * pixelBytes; - - if (alpha) { - // A - for (unsigned int j = 0; j < heightToCopy; ++j) { - const uint8_t * src = &tile->image->alphaPlane[j * tile->image->alphaRowBytes]; - uint8_t * dst = &dstImage->alphaPlane[(yaColOffset * pixelBytes) + ((yaRowOffset + j) * dstImage->alphaRowBytes)]; - memcpy(dst, src, yaRowBytes); - } - } else { - // Y - for (unsigned int j = 0; j < heightToCopy; ++j) { - const uint8_t * src = &tile->image->yuvPlanes[AVIF_CHAN_Y][j * tile->image->yuvRowBytes[AVIF_CHAN_Y]]; - uint8_t * dst = - &dstImage->yuvPlanes[AVIF_CHAN_Y][(yaColOffset * pixelBytes) + ((yaRowOffset + j) * dstImage->yuvRowBytes[AVIF_CHAN_Y])]; - memcpy(dst, src, yaRowBytes); - } - - if (!firstTileUVPresent) { - continue; - } - - // UV - heightToCopy >>= formatInfo.chromaShiftY; - size_t uvColOffset = yaColOffset >> formatInfo.chromaShiftX; - size_t uvRowOffset = yaRowOffset >> formatInfo.chromaShiftY; - size_t uvRowBytes = yaRowBytes >> formatInfo.chromaShiftX; - for (unsigned int j = 0; j < heightToCopy; ++j) { - const uint8_t * srcU = &tile->image->yuvPlanes[AVIF_CHAN_U][j * tile->image->yuvRowBytes[AVIF_CHAN_U]]; - uint8_t * dstU = - &dstImage->yuvPlanes[AVIF_CHAN_U][(uvColOffset * pixelBytes) + ((uvRowOffset + j) * dstImage->yuvRowBytes[AVIF_CHAN_U])]; - memcpy(dstU, srcU, uvRowBytes); - - const uint8_t * srcV = &tile->image->yuvPlanes[AVIF_CHAN_V][j * tile->image->yuvRowBytes[AVIF_CHAN_V]]; - uint8_t * dstV = - &dstImage->yuvPlanes[AVIF_CHAN_V][(uvColOffset * pixelBytes) + ((uvRowOffset + j) * dstImage->yuvRowBytes[AVIF_CHAN_V])]; - memcpy(dstV, srcV, uvRowBytes); - } + const avifCropRect srcViewRect = { 0, 0, dstViewRect.width, dstViewRect.height }; + if ((avifImageSetViewRect(&dstView, dstImage, &dstViewRect) != AVIF_RESULT_OK) || + (avifImageSetViewRect(&srcView, tile->image, &srcViewRect) != AVIF_RESULT_OK)) { + assert(AVIF_FALSE); + return AVIF_FALSE; } + avifImageCopySamples(&dstView, &srcView, alpha ? AVIF_PLANES_A : AVIF_PLANES_YUV); } }
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4cdd8f3..57639d7 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt
@@ -91,7 +91,7 @@ add_test(NAME avifcllitest COMMAND avifcllitest) add_executable(avifgridapitest gtest/avifgridapitest.cc) - target_link_libraries(avifgridapitest aviftest_helpers ${GTEST_BOTH_LIBRARIES}) + target_link_libraries(avifgridapitest avif_internal aviftest_helpers ${GTEST_BOTH_LIBRARIES}) target_include_directories(avifgridapitest PRIVATE ${GTEST_INCLUDE_DIRS}) add_test(NAME avifgridapitest COMMAND avifgridapitest)
diff --git a/tests/gtest/avifgridapitest.cc b/tests/gtest/avifgridapitest.cc index 6c606d9..96fe7fc 100644 --- a/tests/gtest/avifgridapitest.cc +++ b/tests/gtest/avifgridapitest.cc
@@ -4,6 +4,7 @@ #include <vector> #include "avif/avif.h" +#include "avif/internal.h" #include "aviftest_helpers.h" #include "gtest/gtest.h" @@ -94,7 +95,7 @@ if (result != AVIF_RESULT_OK) { return result; } - testutil::CopyImageSamples(**it, view.get()); + avifImageCopySamples(/*dstImage=*/view.get(), it->get(), AVIF_PLANES_ALL); assert(!view->imageOwnsYUVPlanes); ++it; rect.x += rect.width;
diff --git a/tests/gtest/aviftest_helpers.cc b/tests/gtest/aviftest_helpers.cc index ed05d2a..283a681 100644 --- a/tests/gtest/aviftest_helpers.cc +++ b/tests/gtest/aviftest_helpers.cc
@@ -220,37 +220,6 @@ return true; } -void CopyImageSamples(const avifImage& from, avifImage* to) { - assert(from.width == to->width); - assert(from.height == to->height); - assert(from.depth == to->depth); - assert(from.yuvFormat == to->yuvFormat); - assert(from.yuvRange == to->yuvRange); - - for (avifChannelIndex c : - {AVIF_CHAN_Y, AVIF_CHAN_U, AVIF_CHAN_V, AVIF_CHAN_A}) { - const uint8_t* from_row = avifImagePlane(&from, c); - uint8_t* to_row = avifImagePlane(to, c); - assert(!from_row == !to_row); - const uint32_t from_row_bytes = avifImagePlaneRowBytes(&from, c); - const uint32_t to_row_bytes = avifImagePlaneRowBytes(to, c); - const uint32_t plane_width = avifImagePlaneWidth(&from, c); - // 0 for A if no alpha and 0 for UV if 4:0:0. - const uint32_t plane_height = avifImagePlaneHeight(&from, c); - for (uint32_t y = 0; y < plane_height; ++y) { - if (avifImageUsesU16(&from)) { - std::copy(reinterpret_cast<const uint16_t*>(from_row), - reinterpret_cast<const uint16_t*>(from_row) + plane_width, - reinterpret_cast<uint16_t*>(to_row)); - } else { - std::copy(from_row, from_row + plane_width, to_row); - } - from_row += from_row_bytes; - to_row += to_row_bytes; - } - } -} - //------------------------------------------------------------------------------ AvifImagePtr ReadImage(const char* folder_path, const char* file_name,
diff --git a/tests/gtest/aviftest_helpers.h b/tests/gtest/aviftest_helpers.h index 420d1d2..b2674e7 100644 --- a/tests/gtest/aviftest_helpers.h +++ b/tests/gtest/aviftest_helpers.h
@@ -50,10 +50,6 @@ avifPixelFormat yuv_format, avifPlanesFlags planes, avifRange yuv_range = AVIF_RANGE_FULL); -// Copy the pixel values from an image to another. They must share the same -// features (dimensions, depth etc.). -void CopyImageSamples(const avifImage& from, avifImage* to); - // Set all pixels of each plane of an image. void FillImagePlain(avifImage* image, const uint32_t yuva[4]); void FillImageGradient(avifImage* image);