| // Copyright 2022 Google LLC. All rights reserved. |
| // SPDX-License-Identifier: BSD-2-Clause |
| |
| #include "aviftest_helpers.h" |
| #include "avif/avif.h" |
| |
| #include <algorithm> |
| #include <cassert> |
| |
| namespace libavif |
| { |
| namespace testutil |
| { |
| namespace |
| { |
| |
| constexpr int AVIF_CHAN_A = AVIF_CHAN_V + 1; |
| constexpr int AVIF_CHANS[] = { AVIF_CHAN_Y, AVIF_CHAN_U, AVIF_CHAN_V, AVIF_CHAN_A }; |
| |
| } // namespace |
| |
| //------------------------------------------------------------------------------ |
| |
| avifImagePtr createImage(int width, int height, int depth, avifPixelFormat yuvFormat, avifPlanesFlags planes, avifRange yuvRange) |
| { |
| avifImagePtr image(avifImageCreate(width, height, depth, yuvFormat), avifImageDestroy); |
| if (!image) { |
| return { nullptr, nullptr }; |
| } |
| image->yuvRange = yuvRange; |
| avifImageAllocatePlanes(image.get(), planes); |
| return image; |
| } |
| |
| void fillImagePlain(avifImage * image, const uint32_t yuva[4]) |
| { |
| avifPixelFormatInfo info; |
| avifGetPixelFormatInfo(image->yuvFormat, &info); |
| |
| for (int c : AVIF_CHANS) { |
| uint8_t * row = (c == AVIF_CHAN_A) ? image->alphaPlane : image->yuvPlanes[c]; |
| if (!row) { |
| continue; |
| } |
| const uint32_t rowBytes = (c == AVIF_CHAN_A) ? image->alphaRowBytes : image->yuvRowBytes[c]; |
| const uint32_t planeWidth = |
| (c == AVIF_CHAN_Y || c == AVIF_CHAN_A) ? image->width : ((image->width + info.chromaShiftX) >> info.chromaShiftX); |
| const uint32_t planeHeight = |
| (c == AVIF_CHAN_Y || c == AVIF_CHAN_A) ? image->height : ((image->height + info.chromaShiftY) >> info.chromaShiftY); |
| for (uint32_t y = 0; y < planeHeight; ++y) { |
| if (avifImageUsesU16(image)) { |
| std::fill(reinterpret_cast<uint16_t *>(row), |
| reinterpret_cast<uint16_t *>(row) + planeWidth, |
| static_cast<uint16_t>(yuva[c])); |
| } else { |
| std::fill(row, row + planeWidth, static_cast<uint8_t>(yuva[c])); |
| } |
| row += rowBytes; |
| } |
| } |
| } |
| |
| void fillImageGradient(avifImage * image) |
| { |
| avifPixelFormatInfo info; |
| avifGetPixelFormatInfo(image->yuvFormat, &info); |
| |
| for (int c : AVIF_CHANS) { |
| uint8_t * row = (c == AVIF_CHAN_A) ? image->alphaPlane : image->yuvPlanes[c]; |
| if (!row) { |
| continue; |
| } |
| const uint32_t rowBytes = (c == AVIF_CHAN_A) ? image->alphaRowBytes : image->yuvRowBytes[c]; |
| const uint32_t planeWidth = |
| (c == AVIF_CHAN_Y || c == AVIF_CHAN_A) ? image->width : ((image->width + info.chromaShiftX) >> info.chromaShiftX); |
| const uint32_t planeHeight = |
| (c == AVIF_CHAN_Y || c == AVIF_CHAN_A) ? image->height : ((image->height + info.chromaShiftY) >> info.chromaShiftY); |
| for (uint32_t y = 0; y < planeHeight; ++y) { |
| for (uint32_t x = 0; x < planeWidth; ++x) { |
| const uint32_t value = (x + y) * ((1u << image->depth) - 1u) / std::max(1u, planeWidth + planeHeight - 2); |
| if (avifImageUsesU16(image)) { |
| reinterpret_cast<uint16_t *>(row)[x] = static_cast<uint16_t>(value); |
| } else { |
| row[x] = static_cast<uint8_t>(value); |
| } |
| } |
| row += rowBytes; |
| } |
| } |
| } |
| |
| //------------------------------------------------------------------------------ |
| |
| // Returns true if image1 and image2 are identical. |
| bool areImagesEqual(const avifImage & image1, const avifImage & image2) |
| { |
| if (image1.width != image2.width || image1.height != image2.height || image1.depth != image2.depth || |
| image1.yuvFormat != image2.yuvFormat || image1.yuvRange != image2.yuvRange) { |
| return false; |
| } |
| assert(image1.width * image1.height > 0); |
| |
| avifPixelFormatInfo info; |
| avifGetPixelFormatInfo(image1.yuvFormat, &info); |
| |
| for (int c : AVIF_CHANS) { |
| uint8_t * row1 = (c == AVIF_CHAN_A) ? image1.alphaPlane : image1.yuvPlanes[c]; |
| uint8_t * row2 = (c == AVIF_CHAN_A) ? image2.alphaPlane : image2.yuvPlanes[c]; |
| if (!row1 != !row2) { |
| return false; |
| } |
| if (!row1) { |
| continue; |
| } |
| const uint32_t rowBytes1 = (c == AVIF_CHAN_A) ? image1.alphaRowBytes : image1.yuvRowBytes[c]; |
| const uint32_t rowBytes2 = (c == AVIF_CHAN_A) ? image2.alphaRowBytes : image2.yuvRowBytes[c]; |
| const uint32_t planeWidth = |
| (c == AVIF_CHAN_Y || c == AVIF_CHAN_A) ? image1.width : ((image1.width + info.chromaShiftX) >> info.chromaShiftX); |
| const uint32_t planeHeight = |
| (c == AVIF_CHAN_Y || c == AVIF_CHAN_A) ? image1.height : ((image1.height + info.chromaShiftY) >> info.chromaShiftY); |
| for (uint32_t y = 0; y < planeHeight; ++y) { |
| if (avifImageUsesU16(&image1)) { |
| if (!std::equal(reinterpret_cast<uint16_t *>(row1), |
| reinterpret_cast<uint16_t *>(row1) + planeWidth, |
| reinterpret_cast<uint16_t *>(row2))) { |
| return false; |
| } |
| } else { |
| if (!std::equal(row1, row1 + planeWidth, row2)) { |
| return false; |
| } |
| } |
| row1 += rowBytes1; |
| row2 += rowBytes2; |
| } |
| } |
| return true; |
| } |
| |
| //------------------------------------------------------------------------------ |
| |
| } // namespace testutil |
| } // namespace libavif |