/* SPDX-License-Identifier: (BSD-2-Clause AND libpng-2.0) */
#define SPNG_STATIC
#define SPNG_USE_MINIZ

#ifndef SPNG_H
#define SPNG_H

#ifdef __cplusplus
extern "C" {
#endif

#if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(SPNG_STATIC)
    #if defined(SPNG__BUILD)
        #define SPNG_API __declspec(dllexport)
    #else
        #define SPNG_API __declspec(dllimport)
    #endif
#else
    #define SPNG_API
#endif

#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include "miniz.h"

#define SPNG_VERSION_MAJOR 0
#define SPNG_VERSION_MINOR 6
#define SPNG_VERSION_PATCH 3

enum spng_errno
{
    SPNG_IO_ERROR = -2,
    SPNG_IO_EOF = -1,
    SPNG_OK = 0,
    SPNG_EINVAL,
    SPNG_EMEM,
    SPNG_EOVERFLOW,
    SPNG_ESIGNATURE,
    SPNG_EWIDTH,
    SPNG_EHEIGHT,
    SPNG_EUSER_WIDTH,
    SPNG_EUSER_HEIGHT,
    SPNG_EBIT_DEPTH,
    SPNG_ECOLOR_TYPE,
    SPNG_ECOMPRESSION_METHOD,
    SPNG_EFILTER_METHOD,
    SPNG_EINTERLACE_METHOD,
    SPNG_EIHDR_SIZE,
    SPNG_ENOIHDR,
    SPNG_ECHUNK_POS,
    SPNG_ECHUNK_SIZE,
    SPNG_ECHUNK_CRC,
    SPNG_ECHUNK_TYPE,
    SPNG_ECHUNK_UNKNOWN_CRITICAL,
    SPNG_EDUP_PLTE,
    SPNG_EDUP_CHRM,
    SPNG_EDUP_GAMA,
    SPNG_EDUP_ICCP,
    SPNG_EDUP_SBIT,
    SPNG_EDUP_SRGB,
    SPNG_EDUP_BKGD,
    SPNG_EDUP_HIST,
    SPNG_EDUP_TRNS,
    SPNG_EDUP_PHYS,
    SPNG_EDUP_TIME,
    SPNG_EDUP_OFFS,
    SPNG_EDUP_EXIF,
    SPNG_ECHRM,
    SPNG_EPLTE_IDX,
    SPNG_ETRNS_COLOR_TYPE,
    SPNG_ETRNS_NO_PLTE,
    SPNG_EGAMA,
    SPNG_EICCP_NAME,
    SPNG_EICCP_COMPRESSION_METHOD,
    SPNG_ESBIT,
    SPNG_ESRGB,
    SPNG_ETEXT,
    SPNG_ETEXT_KEYWORD,
    SPNG_EZTXT,
    SPNG_EZTXT_COMPRESSION_METHOD,
    SPNG_EITXT,
    SPNG_EITXT_COMPRESSION_FLAG,
    SPNG_EITXT_COMPRESSION_METHOD,
    SPNG_EITXT_LANG_TAG,
    SPNG_EITXT_TRANSLATED_KEY,
    SPNG_EBKGD_NO_PLTE,
    SPNG_EBKGD_PLTE_IDX,
    SPNG_EHIST_NO_PLTE,
    SPNG_EPHYS,
    SPNG_ESPLT_NAME,
    SPNG_ESPLT_DUP_NAME,
    SPNG_ESPLT_DEPTH,
    SPNG_ETIME,
    SPNG_EOFFS,
    SPNG_EEXIF,
    SPNG_EIDAT_TOO_SHORT,
    SPNG_EIDAT_STREAM,
    SPNG_EZLIB,
    SPNG_EFILTER,
    SPNG_EBUFSIZ,
    SPNG_EIO,
    SPNG_EOF,
    SPNG_EBUF_SET,
    SPNG_EBADSTATE,
    SPNG_EFMT,
    SPNG_EFLAGS,
    SPNG_ECHUNKAVAIL,
    SPNG_ENCODE_ONLY,
    SPNG_EOI,
    SPNG_ENOPLTE,
    SPNG_ECHUNK_LIMITS,
    SPNG_EZLIB_INIT,
    SPNG_ECHUNK_STDLEN,
    SPNG_EINTERNAL,
};

enum spng_text_type
{
    SPNG_TEXT = 1,
    SPNG_ZTXT = 2,
    SPNG_ITXT = 3
};

enum spng_color_type
{
    SPNG_COLOR_TYPE_GRAYSCALE = 0,
    SPNG_COLOR_TYPE_TRUECOLOR = 2,
    SPNG_COLOR_TYPE_INDEXED = 3,
    SPNG_COLOR_TYPE_GRAYSCALE_ALPHA = 4,
    SPNG_COLOR_TYPE_TRUECOLOR_ALPHA = 6
};

enum spng_filter
{
    SPNG_FILTER_NONE = 0,
    SPNG_FILTER_SUB = 1,
    SPNG_FILTER_UP = 2,
    SPNG_FILTER_AVERAGE = 3,
    SPNG_FILTER_PAETH = 4
};

enum spng_interlace_method
{
    SPNG_INTERLACE_NONE = 0,
    SPNG_INTERLACE_ADAM7 = 1
};

/* Channels are always in byte-order */
enum spng_format
{
    SPNG_FMT_RGBA8 = 1,
    SPNG_FMT_RGBA16 = 2,
    SPNG_FMT_RGB8 = 4,

    /* Partially implemented, see documentation */
    SPNG_FMT_GA8 = 16,
    SPNG_FMT_GA16 = 32,
    SPNG_FMT_G8 = 64,

    /* No conversion or scaling */
    SPNG_FMT_PNG = 256, /* host-endian */
    SPNG_FMT_RAW = 512  /* big-endian */
};

enum spng_ctx_flags
{
    SPNG_CTX_IGNORE_ADLER32 = 1 /* Ignore checksum in DEFLATE streams */
};

enum spng_decode_flags
{
    SPNG_DECODE_USE_TRNS = 1, /* Deprecated */
    SPNG_DECODE_USE_GAMA = 2, /* Deprecated */
    SPNG_DECODE_USE_SBIT = 8, /* Undocumented */

    SPNG_DECODE_TRNS = 1, /* Apply transparency */
    SPNG_DECODE_GAMMA = 2, /* Apply gamma correction */
    SPNG_DECODE_PROGRESSIVE = 256 /* Initialize for progressive reads */
};

enum spng_crc_action
{
    /* Default for critical chunks */
    SPNG_CRC_ERROR = 0,

    /* Discard chunk, invalid for critical chunks.
       Since v0.6.2: default for ancillary chunks */
    SPNG_CRC_DISCARD = 1,

    /* Ignore and don't calculate checksum.
       Since v0.6.2: also ignores checksums in DEFLATE streams */
    SPNG_CRC_USE = 2
};

struct spng_ihdr
{
    uint32_t width;
    uint32_t height;
    uint8_t bit_depth;
    uint8_t color_type;
    uint8_t compression_method;
    uint8_t filter_method;
    uint8_t interlace_method;
};

struct spng_plte_entry
{
    uint8_t red;
    uint8_t green;
    uint8_t blue;

    uint8_t alpha; /* Reserved for internal use */
};

struct spng_plte
{
    uint32_t n_entries;
    struct spng_plte_entry entries[256];
};

struct spng_trns
{
    uint16_t gray;

    uint16_t red;
    uint16_t green;
    uint16_t blue;

    uint32_t n_type3_entries;
    uint8_t type3_alpha[256];
};

struct spng_chrm_int
{
    uint32_t white_point_x;
    uint32_t white_point_y;
    uint32_t red_x;
    uint32_t red_y;
    uint32_t green_x;
    uint32_t green_y;
    uint32_t blue_x;
    uint32_t blue_y;
};

struct spng_chrm
{
    double white_point_x;
    double white_point_y;
    double red_x;
    double red_y;
    double green_x;
    double green_y;
    double blue_x;
    double blue_y;
};

struct spng_iccp
{
    char profile_name[80];
    size_t profile_len;
    char *profile;
};

struct spng_sbit
{
    uint8_t grayscale_bits;
    uint8_t red_bits;
    uint8_t green_bits;
    uint8_t blue_bits;
    uint8_t alpha_bits;
};

struct spng_text
{
    char keyword[80];
    int type;

    size_t length;
    char *text;

    uint8_t compression_flag; /* iTXt only */
    uint8_t compression_method; /* iTXt, ztXt only */
    char *language_tag; /* iTXt only */
    char *translated_keyword; /* iTXt only */
};

struct spng_bkgd
{
    uint16_t gray; /* Only for gray/gray alpha */
    uint16_t red;
    uint16_t green;
    uint16_t blue;
    uint16_t plte_index; /* Only for indexed color */
};

struct spng_hist
{
    uint16_t frequency[256];
};

struct spng_phys
{
    uint32_t ppu_x, ppu_y;
    uint8_t unit_specifier;
};

struct spng_splt_entry
{
    uint16_t red;
    uint16_t green;
    uint16_t blue;
    uint16_t alpha;
    uint16_t frequency;
};

struct spng_splt
{
    char name[80];
    uint8_t sample_depth;
    uint32_t n_entries;
    struct spng_splt_entry *entries;
};

struct spng_time
{
    uint16_t year;
    uint8_t month;
    uint8_t day;
    uint8_t hour;
    uint8_t minute;
    uint8_t second;
};

struct spng_offs
{
    int32_t x, y;
    uint8_t unit_specifier;
};

struct spng_exif
{
    size_t length;
    char *data;
};

struct spng_chunk
{
    size_t offset;
    uint32_t length;
    uint8_t type[4];
    uint32_t crc;
};

typedef void* spng_malloc_fn(size_t size);
typedef void* spng_realloc_fn(void* ptr, size_t size);
typedef void* spng_calloc_fn(size_t count, size_t size);
typedef void spng_free_fn(void* ptr);

struct spng_alloc
{
    spng_malloc_fn *malloc_fn;
    spng_realloc_fn *realloc_fn;
    spng_calloc_fn *calloc_fn;
    spng_free_fn *free_fn;
};

struct spng_row_info
{
    uint32_t scanline_idx;
    uint32_t row_num; /* deinterlaced row index */
    int pass;
    uint8_t filter;
};

typedef struct spng_ctx spng_ctx;

struct spng_subimage
{
	uint32_t width;
	uint32_t height;
	size_t out_width; /* byte width based on output format */
	size_t scanline_width;
};

struct spng_plte_entry16
{
	uint16_t red;
	uint16_t green;
	uint16_t blue;
	uint16_t alpha;
};

struct spng_chunk_bitfield
{
	unsigned ihdr : 1;
	unsigned plte : 1;
	unsigned chrm : 1;
	unsigned iccp : 1;
	unsigned gama : 1;
	unsigned sbit : 1;
	unsigned srgb : 1;
	unsigned text : 1;
	unsigned bkgd : 1;
	unsigned hist : 1;
	unsigned trns : 1;
	unsigned phys : 1;
	unsigned splt : 1;
	unsigned time : 1;
	unsigned offs : 1;
	unsigned exif : 1;
};

struct decode_flags
{
	unsigned apply_trns : 1;
	unsigned apply_gamma : 1;
	unsigned use_sbit : 1;
	unsigned indexed : 1;
	unsigned do_scaling : 1;
	unsigned interlaced : 1;
	unsigned same_layout : 1;
	unsigned zerocopy : 1;
	unsigned unpack : 1;
};

typedef int spng_read_fn(spng_ctx* ctx, void* user, void* dest, size_t length);
typedef void spng__undo(spng_ctx* ctx);

struct spng_ctx
{
	size_t data_size;
	size_t bytes_read;
	unsigned char* stream_buf;
	const unsigned char* data;

	/* User-defined pointers for streaming */
	spng_read_fn* read_fn;
	void* read_user_ptr;

	/* Used for buffer reads */
	const unsigned char* png_buf; /* base pointer for the buffer */
	size_t bytes_left;
	size_t last_read_size;

	/* These are updated by read_header()/read_chunk_bytes() */
	struct spng_chunk current_chunk;
	uint32_t cur_chunk_bytes_left;
	uint32_t cur_actual_crc;

	struct spng_alloc alloc;

	int flags; /* context flags */
	int fmt;

	unsigned state : 4;
	unsigned streaming : 1;

	unsigned encode_only : 1;
	unsigned strict : 1;
	unsigned discard : 1;
	unsigned skip_crc : 1;
	unsigned prev_was_idat : 1;

	spng__undo* undo;

	/* input file contains this chunk */
	struct spng_chunk_bitfield file;

	/* chunk was stored with spng_set_*() */
	struct spng_chunk_bitfield user;

	/* chunk was stored by reading or with spng_set_*() */
	struct spng_chunk_bitfield stored;

	/* used to reset the above in case of an error */
	struct spng_chunk_bitfield prev_stored;

	struct spng_chunk first_idat, last_idat;

	uint32_t max_width, max_height;

	size_t max_chunk_size;
	size_t chunk_cache_limit;
	size_t chunk_cache_usage;

	int crc_action_critical;
	int crc_action_ancillary;

	struct spng_ihdr ihdr;

	struct spng_plte plte;

	struct spng_chrm_int chrm_int;
	struct spng_iccp iccp;

	uint32_t gama;

	struct spng_sbit sbit;

	uint8_t srgb_rendering_intent;

	uint32_t n_text;
	struct spng_text2* text_list;

	struct spng_bkgd bkgd;
	struct spng_hist hist;
	struct spng_trns trns;
	struct spng_phys phys;

	uint32_t n_splt;
	struct spng_splt* splt_list;

	struct spng_time time;
	struct spng_offs offs;
	struct spng_exif exif;

	struct spng_subimage subimage[7];

	z_stream zstream;
	unsigned char* scanline_buf, * prev_scanline_buf, * row_buf;
	unsigned char* scanline, * prev_scanline, * row;

	size_t total_out_size;
	size_t out_width; /* total_out_size / ihdr.height */

	unsigned channels;
	unsigned bytes_per_pixel; /* input PNG */
	unsigned pixel_size; /* output format */
	int widest_pass;
	int last_pass; /* last non-empty pass */

	uint16_t* gamma_lut; /* points to either _lut8 or _lut16 */
	uint16_t* gamma_lut16;
	uint16_t gamma_lut8[256];
	unsigned char trns_px[8];
	struct spng_plte_entry16 decode_plte[256];
	struct spng_sbit decode_sb;
	struct decode_flags decode_flags;
	struct spng_row_info row_info;
};

SPNG_API spng_ctx *spng_ctx_new(int flags);
SPNG_API spng_ctx *spng_ctx_new2(struct spng_alloc *alloc, int flags);
SPNG_API void spng_ctx_free(spng_ctx *ctx);

SPNG_API int spng_set_png_buffer(spng_ctx *ctx, const void *buf, size_t size);
SPNG_API int spng_set_png_stream(spng_ctx *ctx, spng_read_fn *read_fn, void *user);
SPNG_API int spng_set_png_file(spng_ctx *ctx, FILE *file);

SPNG_API int spng_set_image_limits(spng_ctx *ctx, uint32_t width, uint32_t height);
SPNG_API int spng_get_image_limits(spng_ctx *ctx, uint32_t *width, uint32_t *height);

SPNG_API int spng_set_chunk_limits(spng_ctx *ctx, size_t chunk_size, size_t cache_size);
SPNG_API int spng_get_chunk_limits(spng_ctx *ctx, size_t *chunk_size, size_t *cache_size);

SPNG_API int spng_set_crc_action(spng_ctx *ctx, int critical, int ancillary);

SPNG_API int spng_decoded_image_size(spng_ctx *ctx, int fmt, size_t *len);

/* Decode */
SPNG_API int spng_decode_image(spng_ctx *ctx, void *out, size_t len, int fmt, int flags);

/* Progressive decode */
SPNG_API int spng_decode_scanline(spng_ctx *ctx, void *out, size_t len);
SPNG_API int spng_decode_row(spng_ctx *ctx, void *out, size_t len);

SPNG_API int spng_get_row_info(spng_ctx *ctx, struct spng_row_info *row_info);

SPNG_API int spng_get_ihdr(spng_ctx *ctx, struct spng_ihdr *ihdr);
SPNG_API int spng_get_plte(spng_ctx *ctx, struct spng_plte *plte);
SPNG_API int spng_get_trns(spng_ctx *ctx, struct spng_trns *trns);
SPNG_API int spng_get_chrm(spng_ctx *ctx, struct spng_chrm *chrm);
SPNG_API int spng_get_chrm_int(spng_ctx *ctx, struct spng_chrm_int *chrm_int);
SPNG_API int spng_get_gama(spng_ctx *ctx, double *gamma);
SPNG_API int spng_get_iccp(spng_ctx *ctx, struct spng_iccp *iccp);
SPNG_API int spng_get_sbit(spng_ctx *ctx, struct spng_sbit *sbit);
SPNG_API int spng_get_srgb(spng_ctx *ctx, uint8_t *rendering_intent);
SPNG_API int spng_get_text(spng_ctx *ctx, struct spng_text *text, uint32_t *n_text);
SPNG_API int spng_get_bkgd(spng_ctx *ctx, struct spng_bkgd *bkgd);
SPNG_API int spng_get_hist(spng_ctx *ctx, struct spng_hist *hist);
SPNG_API int spng_get_phys(spng_ctx *ctx, struct spng_phys *phys);
SPNG_API int spng_get_splt(spng_ctx *ctx, struct spng_splt *splt, uint32_t *n_splt);
SPNG_API int spng_get_time(spng_ctx *ctx, struct spng_time *time);

/* Official extensions */
SPNG_API int spng_get_offs(spng_ctx *ctx, struct spng_offs *offs);
SPNG_API int spng_get_exif(spng_ctx *ctx, struct spng_exif *exif);


SPNG_API int spng_set_ihdr(spng_ctx *ctx, struct spng_ihdr *ihdr);
SPNG_API int spng_set_plte(spng_ctx *ctx, struct spng_plte *plte);
SPNG_API int spng_set_trns(spng_ctx *ctx, struct spng_trns *trns);
SPNG_API int spng_set_chrm(spng_ctx *ctx, struct spng_chrm *chrm);
SPNG_API int spng_set_chrm_int(spng_ctx *ctx, struct spng_chrm_int *chrm_int);
SPNG_API int spng_set_gama(spng_ctx *ctx, double gamma);
SPNG_API int spng_set_iccp(spng_ctx *ctx, struct spng_iccp *iccp);
SPNG_API int spng_set_sbit(spng_ctx *ctx, struct spng_sbit *sbit);
SPNG_API int spng_set_srgb(spng_ctx *ctx, uint8_t rendering_intent);
SPNG_API int spng_set_text(spng_ctx *ctx, struct spng_text *text, uint32_t n_text);
SPNG_API int spng_set_bkgd(spng_ctx *ctx, struct spng_bkgd *bkgd);
SPNG_API int spng_set_hist(spng_ctx *ctx, struct spng_hist *hist);
SPNG_API int spng_set_phys(spng_ctx *ctx, struct spng_phys *phys);
SPNG_API int spng_set_splt(spng_ctx *ctx, struct spng_splt *splt, uint32_t n_splt);
SPNG_API int spng_set_time(spng_ctx *ctx, struct spng_time *time);

/* Official extensions */
SPNG_API int spng_set_offs(spng_ctx *ctx, struct spng_offs *offs);
SPNG_API int spng_set_exif(spng_ctx *ctx, struct spng_exif *exif);

SPNG_API const char *spng_strerror(int err);
SPNG_API const char *spng_version_string(void);

#ifdef __cplusplus
}
#endif

#endif /* SPNG_H */
