blob: 4adfb59a18af837f670e9c82db7217a67fdda751 [file] [log] [blame]
// Copyright 2022 Google LLC
// SPDX-License-Identifier: BSD-2-Clause
#ifndef LIBAVIF_TESTS_AVIFTEST_HELPERS_H_
#define LIBAVIF_TESTS_AVIFTEST_HELPERS_H_
#include <array>
#include <cstdint>
#include <limits>
#include <memory>
#include <string>
#include <vector>
#include "avif/avif.h"
#include "avif/avif_cxx.h"
//------------------------------------------------------------------------------
// Duplicated from internal.h
// Used for debugging. Define AVIF_BREAK_ON_ERROR to catch the earliest failure
// during encoding or decoding.
#if defined(AVIF_BREAK_ON_ERROR)
static inline void avifBreakOnError() {
// Same mechanism as OpenCV's error() function, or replace by a breakpoint.
int* p = NULL;
*p = 0;
}
#else
#define avifBreakOnError()
#endif
// Used by stream related things.
#define AVIF_CHECK(A) \
do { \
if (!(A)) { \
avifBreakOnError(); \
return AVIF_FALSE; \
} \
} while (0)
// Used instead of CHECK if needing to return a specific error on failure,
// instead of AVIF_FALSE
#define AVIF_CHECKERR(A, ERR) \
do { \
if (!(A)) { \
avifBreakOnError(); \
return ERR; \
} \
} while (0)
// Forward any error to the caller now or continue execution.
#define AVIF_CHECKRES(A) \
do { \
const avifResult result__ = (A); \
if (result__ != AVIF_RESULT_OK) { \
avifBreakOnError(); \
return result__; \
} \
} while (0)
//------------------------------------------------------------------------------
namespace avif {
namespace testutil {
//------------------------------------------------------------------------------
// ICC color profiles are not checked by libavif so the content does not matter.
// This is a truncated widespread ICC color profile.
static const std::array<uint8_t, 24> kSampleIcc = {
0x00, 0x00, 0x02, 0x0c, 0x6c, 0x63, 0x6d, 0x73, 0x02, 0x10, 0x00, 0x00,
0x6d, 0x6e, 0x74, 0x72, 0x52, 0x47, 0x42, 0x20, 0x58, 0x59, 0x5a, 0x20};
// Exif bytes are partially checked by libavif.
// This is a truncated widespread Exif metadata chunk.
static const std::array<uint8_t, 24> kSampleExif = {
0xff, 0x1, 0x45, 0x78, 0x69, 0x76, 0x32, 0xff, 0xe1, 0x12, 0x5a, 0x45,
0x78, 0x69, 0x66, 0x0, 0x0, 0x49, 0x49, 0x2a, 0x0, 0x8, 0x0, 0x0};
// XMP bytes are not checked by libavif so the content does not matter.
// This is a truncated widespread XMP metadata chunk.
static const std::array<uint8_t, 24> kSampleXmp = {
0x3c, 0x3f, 0x78, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x62, 0x65,
0x67, 0x69, 0x6e, 0x3d, 0x22, 0xef, 0xbb, 0xbf, 0x22, 0x20, 0x69, 0x64};
//------------------------------------------------------------------------------
// Memory management
class AvifRwData : public avifRWData {
public:
AvifRwData() : avifRWData{nullptr, 0} {}
AvifRwData(const AvifRwData&) = delete;
AvifRwData(AvifRwData&& other);
~AvifRwData() { avifRWDataFree(this); }
};
class AvifRgbImage : public avifRGBImage {
public:
AvifRgbImage(const avifImage* yuv, int rgbDepth, avifRGBFormat rgbFormat);
~AvifRgbImage() { avifRGBImageFreePixels(this); }
};
//------------------------------------------------------------------------------
// Samples and images
// Contains the sample position of each channel for a given avifRGBFormat.
// The alpha sample position is set to 0 for layouts having no alpha channel.
struct RgbChannelOffsets {
uint8_t r, g, b, a;
};
RgbChannelOffsets GetRgbChannelOffsets(avifRGBFormat format);
// Creates an image. Returns null in case of memory failure.
ImagePtr CreateImage(int width, int height, int depth,
avifPixelFormat yuv_format, avifPlanesFlags planes,
avifRange yuv_range = AVIF_RANGE_FULL);
// Set all pixels of each plane of an image.
void FillImagePlain(avifImage* image, const uint32_t yuva[4]);
// 'offset' is a value to spatially offset the gradient, useful to create
// distinct images.
void FillImageGradient(avifImage* image, int offset = 0);
void FillImageChannel(avifRGBImage* image, uint32_t channel_offset,
uint32_t value);
// Returns true if both arrays are empty or have the same length and bytes.
// data1 may be null only when data1_length is 0.
// data2 may be null only when data2_length is 0.
bool AreByteSequencesEqual(const uint8_t data1[], size_t data1_length,
const uint8_t data2[], size_t data2_length);
bool AreByteSequencesEqual(const avifRWData& data1, const avifRWData& data2);
// Returns true if both images have the same features, pixel values and
// metadata. If ignore_alpha is true, the alpha channel is not taken into
// account in the comparison.
bool AreImagesEqual(const avifImage& image1, const avifImage& image2,
bool ignore_alpha = false);
// Returns true if both images have the same features and pixel values.
bool AreImagesEqual(const avifRGBImage& image1, const avifRGBImage& image2);
// Returns the Peak Signal-to-Noise Ratio of image1 compared to image2.
// A value of 99dB means all samples are exactly the same.
// A negative value means that the input images cannot be compared.
double GetPsnr(const avifImage& image1, const avifImage& image2,
bool ignore_alpha = false);
// Merges the given image grid cells into a single image.
avifResult MergeGrid(int grid_cols, int grid_rows,
const std::vector<ImagePtr>& cells, avifImage* merged);
avifResult MergeGrid(int grid_cols, int grid_rows,
const std::vector<const avifImage*>& cells,
avifImage* merged);
//------------------------------------------------------------------------------
// Reads the file at file_path into bytes and returns them.
testutil::AvifRwData ReadFile(const std::string& file_path);
//------------------------------------------------------------------------------
// Shorter versions of libavif functions
// Reads the image named file_name located in directory at folder_path.
// Returns nullptr in case of error.
ImagePtr ReadImage(const char* folder_path, const char* file_name,
avifPixelFormat requested_format = AVIF_PIXEL_FORMAT_NONE,
int requested_depth = 0,
avifChromaDownsampling chroma_downsampling =
AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC,
avifBool ignore_icc = false, avifBool ignore_exif = false,
avifBool ignore_xmp = false,
avifBool allow_changing_cicp = true,
avifBool ignore_gain_map = false);
// Convenient wrapper around avifPNGWrite() for debugging purposes.
// Do not remove.
bool WriteImage(const avifImage* image, const char* file_path);
// Encodes the image with default parameters.
// Returns an empty payload in case of error.
AvifRwData Encode(const avifImage* image, int speed = AVIF_SPEED_DEFAULT,
int quality = AVIF_QUALITY_DEFAULT);
// Decodes the bytes to an image with default parameters.
// Returns nullptr in case of error.
ImagePtr Decode(const uint8_t* bytes, size_t num_bytes);
// Decodes the file to an image with default parameters.
// Returns nullptr in case of error.
ImagePtr DecodeFile(const std::string& path);
// Returns true if an AV1 encoder is available.
bool Av1EncoderAvailable();
// Returns true if an AV1 decoder is available.
bool Av1DecoderAvailable();
//------------------------------------------------------------------------------
// avifIO overlay
struct AvifIOLimitedReader {
static constexpr uint64_t kNoClamp = std::numeric_limits<uint64_t>::max();
avifIO io;
avifIO* underlyingIO;
uint64_t clamp;
};
avifIO* AvifIOCreateLimitedReader(avifIO* underlyingIO, uint64_t clamp);
//------------------------------------------------------------------------------
// Splits the input image into grid_cols*grid_rows views to be encoded as a
// grid. Returns an empty vector if the input image cannot be split that way.
std::vector<ImagePtr> ImageToGrid(const avifImage* image, uint32_t grid_cols,
uint32_t grid_rows);
// Converts a unique_ptr array to a raw pointer array as needed by libavif API.
std::vector<const avifImage*> UniquePtrToRawPtr(
const std::vector<ImagePtr>& unique_ptrs);
//------------------------------------------------------------------------------
} // namespace testutil
} // namespace avif
#endif // LIBAVIF_TESTS_AVIFTEST_HELPERS_H_