// Copyright 2019 Joe Drago. All rights reserved.
// SPDX-License-Identifier: BSD-2-Clause

#ifndef LIBAVIF_APPS_SHARED_AVIFUTIL_H
#define LIBAVIF_APPS_SHARED_AVIFUTIL_H

#include "avif/avif.h"

#ifdef __cplusplus
extern "C" {
#endif

// The %z format specifier is not available in the old Windows CRT msvcrt,
// hence the %I format specifier must be used instead to print out `size_t`.
// The new Windows CRT UCRT, which is used by Visual Studio 2015 or later,
// supports the %z specifier properly.
//
// Additionally, with c99 set as the standard mingw-w64 toolchains built with
// the commit mentioned can patch format functions to support the %z specifier,
// even if it's using the old msvcrt, and this can be detected by
// the `__USE_MINGW_ANSI_STDIO` macro.
//
// Related mingw-w64 commit: bfd33f6c0ec5e652cc9911857dd1492ece8d8383

#if !defined(_UCRT) && (defined(__USE_MINGW_ANSI_STDIO) && __USE_MINGW_ANSI_STDIO == 0)
#define AVIF_FMT_ZU "Iu"
#else
#define AVIF_FMT_ZU "zu"
#endif

void avifImageDump(const avifImage * avif, uint32_t gridCols, uint32_t gridRows, avifProgressiveState progressiveState);
void avifContainerDump(const avifDecoder * decoder);
void avifPrintVersions(void);
void avifDumpDiagnostics(const avifDiagnostics * diag);
int avifQueryCPUCount(void); // Returns 1 if it cannot query or fails to query

typedef enum avifAppFileFormat
{
    AVIF_APP_FILE_FORMAT_UNKNOWN = 0,

    AVIF_APP_FILE_FORMAT_AVIF,
    AVIF_APP_FILE_FORMAT_JPEG,
    AVIF_APP_FILE_FORMAT_PNG,
    AVIF_APP_FILE_FORMAT_Y4M
} avifAppFileFormat;

char * avifFileFormatToString(avifAppFileFormat format);

// Guesses the format of a file by looking at the first bytes, or at the extension if the file
// can't be read or is empty.
avifAppFileFormat avifGuessFileFormat(const char * filename);
// Guesses the format of a buffer by looking at the first bytes.
avifAppFileFormat avifGuessBufferFileFormat(const uint8_t * data, size_t size);

// Returns the best cell size for a given horizontal or vertical dimension.
avifBool avifGetBestCellSize(const char * dimensionStr, uint32_t numPixels, uint32_t numCells, avifBool isSubsampled, uint32_t * cellSize);

// Splits an image into a grid of cells, including its gain map, if any.
// The returned cells must be destroyed with avifImageDestroy().
avifBool avifImageSplitGrid(const avifImage * gridSplitImage, uint32_t gridCols, uint32_t gridRows, avifImage ** gridCells);

// Performs a shallow copy of a rectangular area of an RGB image. 'dstImage' does not own the pixel data.
// Assumes that cropRect is a valid cropping rectangle for srcImage. This is true if it was obtained
// using avifCropRectFromCleanApertureBox().
void avifRGBImageSetViewRect(avifRGBImage * dstImage, const avifRGBImage * srcImage, const avifCropRect * cropRect);

// Rotates srcImage into dstImage. The two pointers must be different (does not rotate in place).
// Allocates the pixels of dstImage which must be freed by the caller.
avifResult avifRGBImageRotate(avifRGBImage * dstImage, const avifRGBImage * srcImage, const avifImageRotation * rotation);

// Mirrors the image in place.
avifResult avifRGBImageMirror(avifRGBImage * image, const avifImageMirror * mirror);

// Applies clap, irot and imir transforms (in this order) specified in the avif image to srcImage.
// The result is in dstView which does not own its pixels.
// srcImage may also have its pixel data modified. Assumes that srcImage DOES own its pixels.
// If an error is returned, only some of the transforms may have been applied.
avifResult avifApplyTransforms(avifRGBImage * dstView, avifRGBImage * srcImage, const avifImage * avif);

// This structure holds any timing data coming from source (typically non-AVIF) inputs being fed
// into avifenc. If either or both values are 0, the timing is "invalid" / sentinel and the values
// should be ignored. This structure is used to override the timing defaults in avifenc when the
// enduser doesn't provide timing on the commandline and the source content provides a framerate.
typedef struct avifAppSourceTiming
{
    uint64_t duration;  // duration in time units (based on the timescale below)
    uint64_t timescale; // timescale of the media (Hz)
} avifAppSourceTiming;

struct y4mFrameIterator;
// Reads an image from a file with the requested format and depth.
// If 'inputFormat' is AVIF_APP_FILE_FORMAT_UNKNOWN, the image format is guessed
// based on the filename or first few bytes.
// At most imageSizeLimit pixels will be read or an error returned.
// In case of a y4m file, sourceTiming and frameIter can be set.
// Returns the format of the file, or AVIF_APP_FILE_FORMAT_UNKNOWN in case of
// error.
// 'ignoreGainMap' is only relevant for jpeg files that have a gain map
// and only if AVIF_ENABLE_JPEG_GAIN_MAP_CONVERSION is ON
// (requires libxml2). Otherwise it has no effect.
// May set the image's colorPrimaries, transferCharacteristics and ICC fields
// based on color information found in the image (unless ignoreColorProfile is true).
avifAppFileFormat avifReadImage(const char * filename,
                                avifAppFileFormat inputFormat,
                                avifPixelFormat requestedFormat,
                                int requestedDepth,
                                avifChromaDownsampling chromaDownsampling,
                                avifBool ignoreColorProfile,
                                avifBool ignoreExif,
                                avifBool ignoreXMP,
                                avifBool ignoreGainMap,
                                uint32_t imageSizeLimit,
                                avifImage * image,
                                uint32_t * outDepth,
                                avifAppSourceTiming * sourceTiming,
                                struct y4mFrameIterator ** frameIter);

// Copies all the bytes from the file at filename to a newly allocated memory chunk.
avifBool avifReadEntireFile(const char * filename, avifRWData * raw);

// Removes a single trailing null character from the image->xmp, if there is exactly one.
void avifImageFixXMP(avifImage * image);

// Used by image decoders when the user doesn't explicitly choose a format with --yuv
// This must match the cited fallback for "--yuv auto" in avifenc.c's syntax() function.
#define AVIF_APP_DEFAULT_PIXEL_FORMAT AVIF_PIXEL_FORMAT_YUV444

#ifdef __cplusplus
} // extern "C"
#endif

#endif // ifndef LIBAVIF_APPS_SHARED_AVIFUTIL_H
