blob: 3a3f65f3150e26d6c02d79164cdf2810137edad2 [file] [log] [blame]
// 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