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);