/* * pngxio.c - libpng extension: I/O state query. * * Copyright (C) 2003-2017 Cosmin Truta. * This software is distributed under the same licensing and warranty terms * as libpng. * * NOTE: * The functionality provided in this module has "graduated", and is now * part of libpng. The original code, retrofitted as a back-port, has * limitations: it is thread-unsafe, and it only allows one png_ptr object * for reading and one for writing. * * CAUTION: * libpng-1.4.5 is the earliest version whose I/O state implementation * can be used reliably. */ #include "pngxutil.h" #define PNGX_INTERNAL #include "pngxpriv.h" #if PNG_LIBPNG_VER < 10405 static png_structp _pngxio_read_ptr; static png_structp _pngxio_write_ptr; static png_rw_ptr _pngxio_read_fn; static png_rw_ptr _pngxio_write_fn; static int _pngxio_read_io_state; static int _pngxio_write_io_state; static png_byte _pngxio_read_crt_chunk_hdr[9]; static png_byte _pngxio_write_crt_chunk_hdr[9]; static unsigned int _pngxio_read_crt_chunk_hdr_len; static unsigned int _pngxio_write_crt_chunk_hdr_len; static png_uint_32 _pngxio_read_crt_len; static png_uint_32 _pngxio_write_crt_len; static const char *_pngxio_errmsg_invalid_argument = "[PNGXIO internal] invalid argument"; /* Update io_state and call the user-supplied read/write functions. */ void /* PRIVATE */ pngxio_read_write(png_structp png_ptr, png_bytep data, size_t length) { png_rw_ptr io_data_fn; int *io_state_ptr; int io_state_op; png_byte *crt_chunk_hdr; unsigned int *crt_chunk_hdr_len_ptr; png_uint_32 *crt_len_ptr; if (png_ptr == _pngxio_read_ptr) { io_data_fn = _pngxio_read_fn; io_state_ptr = &_pngxio_read_io_state; io_state_op = PNGX_IO_READING; crt_chunk_hdr = _pngxio_read_crt_chunk_hdr; crt_chunk_hdr_len_ptr = &_pngxio_read_crt_chunk_hdr_len; crt_len_ptr = &_pngxio_read_crt_len; } else if (png_ptr == _pngxio_write_ptr) { io_data_fn = _pngxio_write_fn; io_state_ptr = &_pngxio_write_io_state; io_state_op = PNGX_IO_WRITING; crt_chunk_hdr = _pngxio_write_crt_chunk_hdr; crt_chunk_hdr_len_ptr = &_pngxio_write_crt_chunk_hdr_len; crt_len_ptr = &_pngxio_write_crt_len; } else { png_error(png_ptr, _pngxio_errmsg_invalid_argument); /* NOTREACHED */ return; } switch (*io_state_ptr & PNGX_IO_MASK_LOC) { case PNGX_IO_SIGNATURE: /* libpng must serialize the signature in a single I/O session. */ PNGX_ASSERT(length <= 8); io_data_fn(png_ptr, data, length); *io_state_ptr = io_state_op | PNGX_IO_CHUNK_HDR; *crt_chunk_hdr_len_ptr = 0; return; case PNGX_IO_CHUNK_HDR: /* libpng must serialize the chunk header in a single I/O session. * (This was done in libpng-1.2.30, but regressed in libpng-1.4.0, * so we cannot rely on it here.) */ PNGX_ASSERT(length == 4 || length == 8); PNGX_ASSERT(length + *crt_chunk_hdr_len_ptr <= 8); if (io_state_op == PNGX_IO_READING) { if (*crt_chunk_hdr_len_ptr == 0) io_data_fn(png_ptr, crt_chunk_hdr, 8); memcpy(data, crt_chunk_hdr + *crt_chunk_hdr_len_ptr, length); *crt_chunk_hdr_len_ptr += length; if (*crt_chunk_hdr_len_ptr < 8) return; *crt_len_ptr = png_get_uint_32(crt_chunk_hdr); /* memcpy(png_ptr->chunk_name, crt_chunk_hdr + 4, 4); */ } else /* io_state_op == PNGX_IO_WRITING */ { memcpy(crt_chunk_hdr + *crt_chunk_hdr_len_ptr, data, length); *crt_chunk_hdr_len_ptr += length; if (*crt_chunk_hdr_len_ptr < 8) return; *crt_len_ptr = png_get_uint_32(crt_chunk_hdr); /* memcpy(png_ptr->chunk_name, crt_chunk_hdr + 4, 4); */ io_data_fn(png_ptr, crt_chunk_hdr, 8); } *crt_chunk_hdr_len_ptr = 0; *io_state_ptr = io_state_op | PNGX_IO_CHUNK_DATA; return; case PNGX_IO_CHUNK_DATA: /* libpng may serialize the chunk data in multiple I/O sessions. */ if (length == 0) return; if (*crt_len_ptr > 0) { PNGX_ASSERT(length <= *crt_len_ptr); io_data_fn(png_ptr, data, length); *crt_len_ptr -= length; return; } *io_state_ptr = io_state_op | PNGX_IO_CHUNK_CRC; /* FALLTHROUGH */ case PNGX_IO_CHUNK_CRC: /* libpng must serialize the chunk CRC in a single I/O session. */ PNGX_ASSERT(length == 4); io_data_fn(png_ptr, data, 4); *io_state_ptr = io_state_op | PNGX_IO_CHUNK_HDR; return; } } /* Get png_ptr->io_state. */ png_uint_32 PNGAPI pngx_get_io_state(png_structp png_ptr) { png_uint_32 io_state; if (png_ptr == _pngxio_read_ptr) io_state = _pngxio_read_io_state; else if (png_ptr == _pngxio_write_ptr) io_state = _pngxio_write_io_state; else { png_error(png_ptr, _pngxio_errmsg_invalid_argument); /* NOTREACHED */ io_state = PNGX_IO_NONE; } return io_state; } /* Get png_ptr->chunk_name. */ png_bytep PNGAPI pngx_get_io_chunk_name(png_structp png_ptr) { png_bytep chunk_name; if (png_ptr == _pngxio_read_ptr) chunk_name = _pngxio_read_crt_chunk_hdr + 4; else if (png_ptr == _pngxio_write_ptr) chunk_name = _pngxio_write_crt_chunk_hdr + 4; else { png_error(png_ptr, _pngxio_errmsg_invalid_argument); /* NOTREACHED */ chunk_name = NULL; } return chunk_name; } /* Wrap png_set_read_fn. */ void PNGAPI pngx_set_read_fn(png_structp png_ptr, png_voidp io_ptr, png_rw_ptr read_data_fn) { _pngxio_read_ptr = png_ptr; _pngxio_write_ptr = NULL; _pngxio_read_fn = read_data_fn; png_set_read_fn(png_ptr, io_ptr, pngxio_read_write); _pngxio_read_io_state = PNGX_IO_READING | PNGX_IO_SIGNATURE; } /* Wrap png_set_write_fn. */ void PNGAPI pngx_set_write_fn(png_structp png_ptr, png_voidp io_ptr, png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn) { _pngxio_write_ptr = png_ptr; _pngxio_read_ptr = NULL; _pngxio_write_fn = write_data_fn; png_set_write_fn(png_ptr, io_ptr, pngxio_read_write, output_flush_fn); _pngxio_write_io_state = PNGX_IO_WRITING | PNGX_IO_SIGNATURE; } /* Wrap png_write_sig. */ void PNGAPI pngx_write_sig(png_structp png_ptr) { #if PNG_LIBPNG_VER >= 10400 png_write_sig(png_ptr); #else /* png_write_sig is not exported from the earlier libpng versions. */ static png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10}; pngxio_read_write(png_ptr, png_signature, 8); /* TODO: Take png_ptr->sig_bytes into account. */ #endif } #endif /* PNG_LIBPNG_VER < 10405 */