| // 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 true if both images have the same features and close pixel values. | |
| bool AreImagesSimilar(const avifImage& image1, const avifImage& image2, | |
| double min_psnr = 30, bool ignore_alpha = false); | |
| // 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. | |
| // The gainmap, if any, is ignored. | |
| 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 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_ |