diff options
Diffstat (limited to 'src/lib/util/png.cpp')
-rw-r--r-- | src/lib/util/png.cpp | 710 |
1 files changed, 381 insertions, 329 deletions
diff --git a/src/lib/util/png.cpp b/src/lib/util/png.cpp index 6b13f4f9593..ce28a866604 100644 --- a/src/lib/util/png.cpp +++ b/src/lib/util/png.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles, Vas Crabb /********************************************************************* - png.c + png.cpp PNG reading functions. @@ -10,40 +10,24 @@ #include "png.h" +#include "ioprocs.h" #include "unicode.h" +#include "osdcomm.h" + #include <zlib.h> #include <algorithm> #include <cassert> -#include <cstdint> -#include <cstring> +#include <cerrno> #include <cmath> +#include <cstdlib> +#include <cstring> #include <new> - -#include <math.h> -#include <stdlib.h> - - - -/*************************************************************************** - GLOBAL VARIABLES -***************************************************************************/ - -static const int samples[] = { 1, 0, 3, 1, 2, 0, 4 }; - - - -/*************************************************************************** - INLINE FUNCTIONS -***************************************************************************/ - -static inline int compute_rowbytes(const png_info &pnginfo) -{ - return (pnginfo.width * samples[pnginfo.color_type] * pnginfo.bit_depth + 7) / 8; -} +#include <tuple> +namespace util { /*************************************************************************** GENERAL FUNCTIONS @@ -54,7 +38,7 @@ static inline int compute_rowbytes(const png_info &pnginfo) pnginfo structure -------------------------------------------------*/ -void png_info::free_data() +void png_info::free_data() noexcept { textlist.clear(); palette.reset(); @@ -66,6 +50,12 @@ void png_info::free_data() namespace { +/*************************************************************************** + GLOBAL VARIABLES +***************************************************************************/ + +constexpr int samples[] = { 1, 0, 3, 1, 2, 0, 4 }; + constexpr std::uint8_t PNG_SIGNATURE[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a }; #define MNG_Signature "\x8A\x4D\x4E\x47\x0D\x0A\x1A\x0A" @@ -101,13 +91,51 @@ constexpr std::uint8_t PNG_PF_Average = 3; constexpr std::uint8_t PNG_PF_Paeth = 4; -inline uint8_t fetch_8bit(uint8_t const *v) { return *v; } -inline uint16_t fetch_16bit(uint8_t const *v) { return big_endianize_int16(*reinterpret_cast<uint16_t const *>(v)); } -inline uint32_t fetch_32bit(uint8_t const *v) { return big_endianize_int32(*reinterpret_cast<uint32_t const *>(v)); } +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +inline int compute_rowbytes(const png_info &pnginfo) noexcept { return (pnginfo.width * samples[pnginfo.color_type] * pnginfo.bit_depth + 7) / 8; } + +inline uint8_t fetch_8bit(uint8_t const *v) noexcept { return *v; } +inline uint16_t fetch_16bit(uint8_t const *v) noexcept { return big_endianize_int16(*reinterpret_cast<uint16_t const *>(v)); } +inline uint32_t fetch_32bit(uint8_t const *v) noexcept { return big_endianize_int32(*reinterpret_cast<uint32_t const *>(v)); } + +inline void put_8bit(uint8_t *v, uint8_t data) noexcept { *v = data; } +inline void put_16bit(uint8_t *v, uint16_t data) noexcept { *reinterpret_cast<uint16_t *>(v) = big_endianize_int16(data); } +inline void put_32bit(uint8_t *v, uint32_t data) noexcept { *reinterpret_cast<uint32_t *>(v) = big_endianize_int32(data); } + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +class png_category_impl : public std::error_category +{ +public: + virtual char const *name() const noexcept override { return "png"; } + + virtual std::string message(int condition) const override + { + using namespace std::literals; + static std::string_view const s_messages[] = { + "No error"sv, + "Unknown filter algorithm"sv, + "Bad file signature"sv, + "Decompression error"sv, + "Image file truncated"sv, + "Image file corrupt"sv, + "Unknown chunk type"sv, + "Compression error"sv, + "Unsupported image format"sv }; + if ((0 <= condition) && (std::size(s_messages) > condition)) + return std::string(s_messages[condition]); + else + return "Unknown error"s; + } +}; -inline void put_8bit(uint8_t *v, uint8_t data) { *v = data; } -inline void put_16bit(uint8_t *v, uint16_t data) { *reinterpret_cast<uint16_t *>(v) = big_endianize_int16(data); } -inline void put_32bit(uint8_t *v, uint32_t data) { *reinterpret_cast<uint32_t *>(v) = big_endianize_int32(data); } +png_category_impl const f_png_category_instance; class png_private @@ -122,21 +150,21 @@ private: struct image_data_chunk { - image_data_chunk(std::uint32_t l, std::unique_ptr<std::uint8_t []> &&d) : length(l), data(std::move(d)) { } + image_data_chunk(std::uint32_t l, std::unique_ptr<std::uint8_t []> &&d) noexcept : length(l), data(std::move(d)) { } std::uint32_t length; std::unique_ptr<std::uint8_t []> data; }; - png_error process(std::list<image_data_chunk> const &idata) + std::error_condition process(std::list<image_data_chunk> const &idata) noexcept { // do some basic checks for unsupported images - if (!pnginfo.bit_depth || (ARRAY_LENGTH(samples) <= pnginfo.color_type) || !samples[pnginfo.color_type]) - return PNGERR_UNSUPPORTED_FORMAT; // unknown colour format + if (!pnginfo.bit_depth || (std::size(samples) <= pnginfo.color_type) || !samples[pnginfo.color_type]) + return png_error::UNSUPPORTED_FORMAT; // unknown colour format if ((0 != pnginfo.interlace_method) && (1 != pnginfo.interlace_method)) - return PNGERR_UNSUPPORTED_FORMAT; // unknown interlace method + return png_error::UNSUPPORTED_FORMAT; // unknown interlace method if ((3 == pnginfo.color_type) && (!pnginfo.num_palette || !pnginfo.palette)) - return PNGERR_FILE_CORRUPT; // indexed colour with no palette + return png_error::FILE_CORRUPT; // indexed colour with no palette // calculate the offset for each pass of the interlace unsigned const pass_count(get_pass_count()); @@ -145,14 +173,15 @@ private: pass_offset[pass + 1] = pass_offset[pass] + get_pass_bytes(pass); // allocate memory for the filtered image - try { pnginfo.image.reset(new std::uint8_t [pass_offset[pass_count]]); } - catch (std::bad_alloc const &) { return PNGERR_OUT_OF_MEMORY; } + pnginfo.image.reset(new (std::nothrow) std::uint8_t [pass_offset[pass_count]]); + if (!pnginfo.image) + return std::errc::not_enough_memory; // decompress image data - png_error error = PNGERR_NONE; + std::error_condition error; error = decompress(idata, pass_offset[pass_count]); std::uint32_t const bpp(get_bytes_per_pixel()); - for (unsigned pass = 0; (pass_count > pass) && (PNGERR_NONE == error); ++pass) + for (unsigned pass = 0; (pass_count > pass) && !error; ++pass) { // compute some basic parameters std::pair<std::uint32_t, std::uint32_t> const dimensions(get_pass_dimensions(pass)); @@ -161,7 +190,7 @@ private: // we de-filter in place, stripping the filter bytes off the rows uint8_t *dst(&pnginfo.image[pass_offset[pass]]); uint8_t const *src(dst); - for (std::uint32_t y = 0; (dimensions.second > y) && (PNGERR_NONE == error); ++y) + for (std::uint32_t y = 0; (dimensions.second > y) && !error; ++y) { // first byte of each row is the filter type uint8_t const filter(*src++); @@ -172,17 +201,17 @@ private: } // if we errored, free the image data - if (error != PNGERR_NONE) + if (error) pnginfo.image.reset(); return error; } - png_error decompress(std::list<image_data_chunk> const &idata, std::uint32_t expected) + std::error_condition decompress(std::list<image_data_chunk> const &idata, std::uint32_t expected) noexcept { // only deflate is permitted if (0 != pnginfo.compression_method) - return PNGERR_DECOMPRESS_ERROR; + return png_error::DECOMPRESS_ERROR; // allocate zlib stream z_stream stream; @@ -194,14 +223,18 @@ private: stream.avail_in = 0; stream.next_in = Z_NULL; zerr = inflateInit(&stream); - if (Z_OK != zerr) - return PNGERR_DECOMPRESS_ERROR; + if (Z_ERRNO == zerr) + return std::error_condition(errno, std::generic_category()); + else if (Z_MEM_ERROR == zerr) + return std::errc::not_enough_memory; + else if (Z_OK != zerr) + return png_error::DECOMPRESS_ERROR; // decompress IDAT blocks stream.next_out = pnginfo.image.get(); stream.avail_out = expected; stream.avail_in = 0; - std::list<image_data_chunk>::const_iterator it = idata.begin(); + auto it = idata.begin(); while ((idata.end() != it) && ((Z_OK == zerr) || (Z_BUF_ERROR == zerr)) && !stream.avail_in) { stream.avail_in = it->length; @@ -217,28 +250,28 @@ private: // it's all good if we got end-of-stream or we have with no data remaining if ((Z_OK == inflateEnd(&stream)) && ((Z_STREAM_END == zerr) || ((Z_OK == zerr) && (idata.end() == it) && !stream.avail_in))) - return PNGERR_NONE; + return std::error_condition(); else - return PNGERR_DECOMPRESS_ERROR; + return png_error::DECOMPRESS_ERROR; // TODO: refactor this function for more fine-grained error reporting? } - png_error unfilter_row(std::uint8_t type, uint8_t const *src, uint8_t *dst, uint8_t const *dstprev, int bpp, std::uint32_t rowbytes) + std::error_condition unfilter_row(std::uint8_t type, uint8_t const *src, uint8_t *dst, uint8_t const *dstprev, int bpp, std::uint32_t rowbytes) noexcept { if (0 != pnginfo.filter_method) - return PNGERR_UNKNOWN_FILTER; + return png_error::UNKNOWN_FILTER; switch (type) { case PNG_PF_None: // no filter, just copy std::copy_n(src, rowbytes, dst); - return PNGERR_NONE; + return std::error_condition(); case PNG_PF_Sub: // SUB = previous pixel dst = std::copy_n(src, bpp, dst); src += bpp; for (std::uint32_t x = bpp; rowbytes > x; ++x, ++src, ++dst) *dst = *src + dst[-bpp]; - return PNGERR_NONE; + return std::error_condition(); case PNG_PF_Up: // UP = pixel above if (dstprev) @@ -250,7 +283,7 @@ private: { std::copy_n(src, rowbytes, dst); } - return PNGERR_NONE; + return std::error_condition(); case PNG_PF_Average: // AVERAGE = average of pixel above and previous pixel if (dstprev) @@ -267,7 +300,7 @@ private: for (std::uint32_t x = bpp; rowbytes > x; ++x, ++src, ++dst) *dst = *src + (dst[-bpp] >> 1); } - return PNGERR_NONE; + return std::error_condition(); case PNG_PF_Paeth: // PAETH = special filter for (std::uint32_t x = 0; rowbytes > x; ++x, ++src, ++dst) @@ -281,20 +314,20 @@ private: int32_t const dc(std::abs(prediction - pc)); *dst = ((da <= db) && (da <= dc)) ? (*src + pa) : (db <= dc) ? (*src + pb) : (*src + pc); } - return PNGERR_NONE; + return std::error_condition(); default: // unknown filter type - return PNGERR_UNKNOWN_FILTER; + return png_error::UNKNOWN_FILTER; } } - png_error process_chunk(std::list<image_data_chunk> &idata, std::unique_ptr<std::uint8_t []> &&data, uint32_t type, uint32_t length) + std::error_condition process_chunk(std::list<image_data_chunk> &idata, std::unique_ptr<std::uint8_t []> &&data, uint32_t type, uint32_t length) noexcept { switch (type) { case PNG_CN_IHDR: // image header if (13 > length) - return PNGERR_FILE_CORRUPT; + return png_error::FILE_CORRUPT; pnginfo.width = fetch_32bit(&data[0]); pnginfo.height = fetch_32bit(&data[4]); pnginfo.bit_depth = fetch_8bit(&data[8]); @@ -307,31 +340,31 @@ private: case PNG_CN_PLTE: // palette pnginfo.num_palette = length / 3; if ((length % 3) || ((3 == pnginfo.color_type) && ((1 << pnginfo.bit_depth) < pnginfo.num_palette))) - return PNGERR_FILE_CORRUPT; + return png_error::FILE_CORRUPT; pnginfo.palette = std::move(data); break; case PNG_CN_tRNS: // transparency information if (((0 == pnginfo.color_type) && (2 > length)) || ((2 == pnginfo.color_type) && (6 > length))) - return PNGERR_FILE_CORRUPT; + return png_error::FILE_CORRUPT; pnginfo.num_trans = length; pnginfo.trans = std::move(data); break; case PNG_CN_IDAT: // image data try { idata.emplace_back(length, std::move(data)); } - catch (std::bad_alloc const &) { return PNGERR_OUT_OF_MEMORY; } + catch (std::bad_alloc const &) { return std::errc::not_enough_memory; } break; case PNG_CN_gAMA: // gamma if (4 > length) - return PNGERR_FILE_CORRUPT; + return png_error::FILE_CORRUPT; pnginfo.source_gamma = fetch_32bit(data.get()) / 100000.0; break; case PNG_CN_pHYs: // physical information if (9 > length) - return PNGERR_FILE_CORRUPT; + return png_error::FILE_CORRUPT; pnginfo.xres = fetch_32bit(&data[0]); pnginfo.yres = fetch_32bit(&data[4]); pnginfo.resolution_unit = fetch_8bit(&data[8]); @@ -361,25 +394,25 @@ private: } catch (std::bad_alloc const &) { - return PNGERR_OUT_OF_MEMORY; + return std::errc::not_enough_memory; } break; /* anything else */ default: if ((type & 0x20000000) == 0) - return PNGERR_UNKNOWN_CHUNK; + return png_error::UNKNOWN_CHUNK; break; } - return PNGERR_NONE; + return std::error_condition(); } - unsigned get_pass_count() const + unsigned get_pass_count() const noexcept { return (1 == pnginfo.interlace_method) ? 7 : 1; } - std::pair<uint32_t, uint32_t> get_pass_dimensions(unsigned pass) const + std::pair<uint32_t, uint32_t> get_pass_dimensions(unsigned pass) const noexcept { if (0 == pnginfo.interlace_method) return std::make_pair(pnginfo.width, pnginfo.height); @@ -387,110 +420,130 @@ private: return std::make_pair((pnginfo.width + ADAM7_X_BIAS[pass]) >> ADAM7_X_SHIFT[pass], (pnginfo.height + ADAM7_Y_BIAS[pass]) >> ADAM7_Y_SHIFT[pass]); } - std::uint32_t get_pass_bytes(unsigned pass) const + std::uint32_t get_pass_bytes(unsigned pass) const noexcept { return get_pass_bytes(pass, pnginfo.bit_depth); } - std::uint32_t get_pass_bytes(unsigned pass, uint8_t bit_depth) const + std::uint32_t get_pass_bytes(unsigned pass, uint8_t bit_depth) const noexcept { std::pair<std::uint32_t, std::uint32_t> const dimensions(get_pass_dimensions(pass)); return (get_row_bytes(dimensions.first, bit_depth) + 1) * dimensions.second; } - std::uint32_t get_row_bytes(std::uint32_t width) const + std::uint32_t get_row_bytes(std::uint32_t width) const noexcept { return get_row_bytes(width, pnginfo.bit_depth); } - std::uint32_t get_row_bytes(std::uint32_t width, uint8_t bit_depth) const + std::uint32_t get_row_bytes(std::uint32_t width, uint8_t bit_depth) const noexcept { return ((width * samples[pnginfo.color_type] * bit_depth) + 7) >> 3; } - std::uint32_t get_bytes_per_pixel() const + std::uint32_t get_bytes_per_pixel() const noexcept { return ((samples[pnginfo.color_type] * pnginfo.bit_depth) + 7) >> 3; } - static png_error read_chunk(util::core_file &fp, std::unique_ptr<std::uint8_t []> &data, std::uint32_t &type, std::uint32_t &length) + static std::error_condition read_chunk(read_stream &fp, std::unique_ptr<std::uint8_t []> &data, std::uint32_t &type, std::uint32_t &length) noexcept { + std::error_condition err; + std::size_t actual; std::uint8_t tempbuff[4]; - /* fetch the length of this chunk */ - if (fp.read(tempbuff, 4) != 4) - return PNGERR_FILE_TRUNCATED; + // fetch the length of this chunk + std::tie(err, actual) = read(fp, tempbuff, 4); + if (err) + return err; + else if (4 != actual) + return png_error::FILE_TRUNCATED; length = fetch_32bit(tempbuff); - /* fetch the type of this chunk */ - if (fp.read(tempbuff, 4) != 4) - return PNGERR_FILE_TRUNCATED; + // fetch the type of this chunk + std::tie(err, actual) = read(fp, tempbuff, 4); + if (err) + return err; + else if (4 != actual) + return png_error::FILE_TRUNCATED; type = fetch_32bit(tempbuff); - /* stop when we hit an IEND chunk */ + // stop when we hit an IEND chunk if (type == PNG_CN_IEND) - return PNGERR_NONE; + return std::error_condition(); - /* start the CRC with the chunk type (but not the length) */ + // start the CRC with the chunk type (but not the length) std::uint32_t crc = crc32(0, tempbuff, 4); - /* read the chunk itself into an allocated memory buffer */ + // read the chunk itself into an allocated memory buffer if (length) { - /* allocate memory for this chunk */ - try { data.reset(new std::uint8_t [length]); } - catch (std::bad_alloc const &) { return PNGERR_OUT_OF_MEMORY; } - - /* read the data from the file */ - if (fp.read(data.get(), length) != length) + // allocate memory and read the data from the file + std::tie(err, data, actual) = read(fp, length); + if (err) { data.reset(); - return PNGERR_FILE_TRUNCATED; + return err; + } + else if (length != actual) + { + data.reset(); + return png_error::FILE_TRUNCATED; } - /* update the CRC */ + // update the CRC crc = crc32(crc, data.get(), length); } - /* read the CRC */ - if (fp.read(tempbuff, 4) != 4) + // read the CRC + std::tie(err, actual) = read(fp, tempbuff, 4); + if (err) { data.reset(); - return PNGERR_FILE_TRUNCATED; + return err; + } + else if (4 != actual) + { + data.reset(); + return png_error::FILE_TRUNCATED; } std::uint32_t const chunk_crc = fetch_32bit(tempbuff); - /* validate the CRC */ + // validate the CRC if (crc != chunk_crc) { data.reset(); - return PNGERR_FILE_CORRUPT; + return png_error::FILE_CORRUPT; } - return PNGERR_NONE; + return std::error_condition(); } png_info & pnginfo; public: - png_private(png_info &info) : pnginfo(info) + png_private(png_info &info) noexcept : pnginfo(info) { } - png_error copy_to_bitmap(bitmap_argb32 &bitmap, bool &hasalpha) const + std::error_condition copy_to_bitmap(bitmap_argb32 &bitmap, bool &hasalpha) const noexcept { // do some basic checks for unsupported images if ((8 > pnginfo.bit_depth) || (pnginfo.bit_depth % 8)) - return PNGERR_UNSUPPORTED_FORMAT; // only do multiples of 8bps here - expand lower bit depth first - if ((ARRAY_LENGTH(samples) <= pnginfo.color_type) || !samples[pnginfo.color_type]) - return PNGERR_UNSUPPORTED_FORMAT; // unknown colour sample format + return png_error::UNSUPPORTED_FORMAT; // only do multiples of 8bps here - expand lower bit depth first + if ((std::size(samples) <= pnginfo.color_type) || !samples[pnginfo.color_type]) + return png_error::UNSUPPORTED_FORMAT; // unknown colour sample format if ((0 != pnginfo.interlace_method) && (1 != pnginfo.interlace_method)) - return PNGERR_UNSUPPORTED_FORMAT; // unknown interlace method + return png_error::UNSUPPORTED_FORMAT; // unknown interlace method if ((3 == pnginfo.color_type) && (8 != pnginfo.bit_depth)) - return PNGERR_UNSUPPORTED_FORMAT; // indexed colour must be exactly 8bpp + return png_error::UNSUPPORTED_FORMAT; // indexed colour must be exactly 8bpp - // everything looks sane, allocate the bitmap and deinterlace into it + // try to allocate the bitmap bitmap.allocate(pnginfo.width, pnginfo.height); + if (!bitmap.valid()) + return std::errc::not_enough_memory; + + // everything looks sane, deinterlace into the bitmap std::uint8_t accumalpha(0xff); uint32_t const bps(pnginfo.bit_depth >> 3); uint32_t const bpp(bps * samples[pnginfo.color_type]); @@ -519,7 +572,7 @@ public: accumalpha &= alpha; std::uint16_t const paloffs(std::uint16_t(*src) * 3); rgb_t const pix(alpha, pnginfo.palette[paloffs], pnginfo.palette[paloffs + 1], pnginfo.palette[paloffs + 2]); - bitmap.pix32((y << y_shift) + y_offs, (x << x_shift) + x_offs) = pix; + bitmap.pix((y << y_shift) + y_offs, (x << x_shift) + x_offs) = pix; } } } @@ -537,7 +590,7 @@ public: std::uint8_t const a_val((pnginfo.trans && (transpen == i_val)) ? 0x00 : 0xff); i_val >>= samp_shift; accumalpha &= a_val; - bitmap.pix32((y << y_shift) + y_offs, (x << x_shift) + x_offs) = rgb_t(a_val, i_val, i_val, i_val); + bitmap.pix((y << y_shift) + y_offs, (x << x_shift) + x_offs) = rgb_t(a_val, i_val, i_val, i_val); } } } @@ -552,7 +605,7 @@ public: { accumalpha &= src[a]; rgb_t const pix(src[a], src[i], src[i], src[i]); - bitmap.pix32((y << y_shift) + y_offs, (x << x_shift) + x_offs) = pix; + bitmap.pix((y << y_shift) + y_offs, (x << x_shift) + x_offs) = pix; } } } @@ -578,7 +631,7 @@ public: g_val >>= samp_shift; b_val >>= samp_shift; accumalpha &= a_val; - bitmap.pix32((y << y_shift) + y_offs, (x << x_shift) + x_offs) = rgb_t(a_val, r_val, g_val, b_val); + bitmap.pix((y << y_shift) + y_offs, (x << x_shift) + x_offs) = rgb_t(a_val, r_val, g_val, b_val); } } } @@ -595,7 +648,7 @@ public: { accumalpha &= src[a]; rgb_t const pix(src[a], src[r], src[g], src[b]); - bitmap.pix32((y << y_shift) + y_offs, (x << x_shift) + x_offs) = pix; + bitmap.pix((y << y_shift) + y_offs, (x << x_shift) + x_offs) = pix; } } } @@ -603,22 +656,22 @@ public: // set hasalpha flag and return hasalpha = 0xffU != accumalpha; - return PNGERR_NONE; + return std::error_condition(); } - png_error expand_buffer_8bit() + std::error_condition expand_buffer_8bit() noexcept { // nothing to do if we're at 8 or greater already if (pnginfo.bit_depth >= 8) - return PNGERR_NONE; + return std::error_condition(); // do some basic checks for unsupported images if (!pnginfo.bit_depth || (8 % pnginfo.bit_depth)) - return PNGERR_UNSUPPORTED_FORMAT; // bit depth must be a factor of eight + return png_error::UNSUPPORTED_FORMAT; // bit depth must be a factor of eight if ((0 != pnginfo.color_type) && (3 != pnginfo.color_type)) - return PNGERR_UNSUPPORTED_FORMAT; // only upsample monochrome and indexed colour + return png_error::UNSUPPORTED_FORMAT; // only upsample monochrome and indexed colour if ((0 != pnginfo.interlace_method) && (1 != pnginfo.interlace_method)) - return PNGERR_UNSUPPORTED_FORMAT; // unknown interlace method + return png_error::UNSUPPORTED_FORMAT; // unknown interlace method // calculate the offset for each pass of the interlace on the input and output unsigned const pass_count(get_pass_count()); @@ -631,9 +684,9 @@ public: } // allocate a new buffer at 8-bit - std::unique_ptr<std::uint8_t []> outbuf; - try { outbuf.reset(new std::uint8_t [outp_offset[pass_count]]); } - catch (std::bad_alloc const &) { return PNGERR_OUT_OF_MEMORY; } + std::unique_ptr<std::uint8_t []> outbuf(new (std::nothrow) std::uint8_t [outp_offset[pass_count]]); + if (!outbuf) + return std::errc::not_enough_memory; // upsample bitmap std::uint8_t const bytesamples(8 / pnginfo.bit_depth); @@ -687,13 +740,13 @@ public: pnginfo.image = std::move(outbuf); pnginfo.bit_depth = 8; - return PNGERR_NONE; + return std::error_condition(); } - png_error read_file(util::core_file &fp) + std::error_condition read_file(read_stream &fp) noexcept { // initialize the data structures - png_error error = PNGERR_NONE; + std::error_condition error; pnginfo.reset(); std::list<image_data_chunk> idata; @@ -701,13 +754,13 @@ public: error = verify_header(fp); // loop until we hit an IEND chunk - while (PNGERR_NONE == error) + while (!error) { // read a chunk std::unique_ptr<std::uint8_t []> chunk_data; std::uint32_t chunk_type = 0, chunk_length; error = read_chunk(fp, chunk_data, chunk_type, chunk_length); - if (PNGERR_NONE == error) + if (!error) { if (chunk_type == PNG_CN_IEND) break; // stop when we hit an IEND chunk @@ -717,29 +770,32 @@ public: } // finish processing the image - if (PNGERR_NONE == error) + if (!error) error = process(idata); // if we have an error, free all the output data - if (error != PNGERR_NONE) + if (error) pnginfo.reset(); return error; } - static png_error verify_header(util::core_file &fp) + static std::error_condition verify_header(read_stream &fp) noexcept { - EQUIVALENT_ARRAY(PNG_SIGNATURE, std::uint8_t) signature; + std::uint8_t signature[sizeof(PNG_SIGNATURE)]; // read 8 bytes - if (fp.read(signature, sizeof(signature)) != sizeof(signature)) - return PNGERR_FILE_TRUNCATED; + auto const [err, actual] = read(fp, signature, sizeof(signature)); + if (err) + return err; + else if (sizeof(signature) != actual) + return png_error::FILE_TRUNCATED; // return an error if we don't match if (std::memcmp(signature, PNG_SIGNATURE, sizeof(PNG_SIGNATURE))) - return PNGERR_BAD_SIGNATURE; + return png_error::BAD_SIGNATURE; - return PNGERR_NONE; + return std::error_condition(); } }; @@ -760,7 +816,7 @@ constexpr unsigned png_private::ADAM7_Y_OFFS[7]; core stream -------------------------------------------------*/ -png_error png_info::verify_header(util::core_file &fp) +std::error_condition png_info::verify_header(read_stream &fp) noexcept { return png_private::verify_header(fp); } @@ -770,7 +826,7 @@ png_error png_info::verify_header(util::core_file &fp) read_file - read a PNG from a core stream -------------------------------------------------*/ -png_error png_info::read_file(util::core_file &fp) +std::error_condition png_info::read_file(read_stream &fp) noexcept { return png_private(*this).read_file(fp); } @@ -781,20 +837,20 @@ png_error png_info::read_file(util::core_file &fp) bitmap -------------------------------------------------*/ -png_error png_read_bitmap(util::core_file &fp, bitmap_argb32 &bitmap) +std::error_condition png_read_bitmap(read_stream &fp, bitmap_argb32 &bitmap) noexcept { - png_error result; + std::error_condition result; png_info pnginfo; png_private png(pnginfo); // read the PNG data result = png.read_file(fp); - if (PNGERR_NONE != result) + if (result) return result; // resample to 8bpp if necessary result = png.expand_buffer_8bit(); - if (PNGERR_NONE != result) + if (result) { pnginfo.free_data(); return result; @@ -811,7 +867,7 @@ png_error png_read_bitmap(util::core_file &fp, bitmap_argb32 &bitmap) bitmap -------------------------------------------------*/ -png_error png_info::copy_to_bitmap(bitmap_argb32 &bitmap, bool &hasalpha) +std::error_condition png_info::copy_to_bitmap(bitmap_argb32 &bitmap, bool &hasalpha) noexcept { return png_private(*this).copy_to_bitmap(bitmap, hasalpha); } @@ -822,7 +878,7 @@ png_error png_info::copy_to_bitmap(bitmap_argb32 &bitmap, bool &hasalpha) sub 8-bit to 8-bit -------------------------------------------------*/ -png_error png_info::expand_buffer_8bit() +std::error_condition png_info::expand_buffer_8bit() noexcept { return png_private(*this).expand_buffer_8bit(); } @@ -837,40 +893,38 @@ png_error png_info::expand_buffer_8bit() add_text - add a text entry to the png_info -------------------------------------------------*/ -png_error png_info::add_text(const char *keyword, const char *text) +std::error_condition png_info::add_text(std::string_view keyword, std::string_view text) noexcept { // apply rules to keyword char32_t prev(0); std::size_t cnt(0); - char const *const kwend(keyword + std::strlen(keyword)); - for (char const *ptr = keyword; kwend > ptr; ) + for (std::string_view kw = keyword; !kw.empty(); ) { char32_t ch; - int const len(uchar_from_utf8(&ch, ptr, kwend - ptr)); - if ((0 >= len) || (32 > ch) || (255 < ch) || ((126 < ch) && (161 > ch)) || (((32 == prev) || (keyword == ptr)) && (32 == ch))) - return PNGERR_UNSUPPORTED_FORMAT; + int const len(uchar_from_utf8(&ch, kw)); + if ((0 >= len) || (32 > ch) || (255 < ch) || ((126 < ch) && (161 > ch)) || (((32 == prev) || (0 == cnt)) && (32 == ch))) + return png_error::UNSUPPORTED_FORMAT; prev = ch; ++cnt; - ptr += len; + kw.remove_prefix(len); } if ((32 == prev) || (1 > cnt) || (79 < cnt)) - return PNGERR_UNSUPPORTED_FORMAT; + return png_error::UNSUPPORTED_FORMAT; // apply rules to text - char const *const textend(text + std::strlen(text)); - for (char const *ptr = text; textend > ptr; ) + for (std::string_view tx = text; !tx.empty(); ) { char32_t ch; - int const len(uchar_from_utf8(&ch, ptr, textend - ptr)); + int const len(uchar_from_utf8(&ch, tx)); if ((0 >= len) || (1 > ch) || (255 < ch)) - return PNGERR_UNSUPPORTED_FORMAT; - ptr += len; + return png_error::UNSUPPORTED_FORMAT; + tx.remove_prefix(len); } // allocate a new text element - try { textlist.emplace_back(std::piecewise_construct, std::forward_as_tuple(keyword, kwend), std::forward_as_tuple(text, textend)); } - catch (std::bad_alloc const &) { return PNGERR_OUT_OF_MEMORY; } - return PNGERR_NONE; + try { textlist.emplace_back(std::piecewise_construct, std::forward_as_tuple(keyword), std::forward_as_tuple(text)); } + catch (std::bad_alloc const &) { return std::errc::not_enough_memory; } + return std::error_condition(); } @@ -879,34 +933,38 @@ png_error png_info::add_text(const char *keyword, const char *text) the given file -------------------------------------------------*/ -static png_error write_chunk(util::core_file &fp, const uint8_t *data, uint32_t type, uint32_t length) +static std::error_condition write_chunk(write_stream &fp, const uint8_t *data, uint32_t type, uint32_t length) noexcept { - uint8_t tempbuff[8]; - uint32_t crc; + std::error_condition err; + std::uint8_t tempbuff[8]; + std::uint32_t crc; - /* stuff the length/type into the buffer */ + // stuff the length/type into the buffer put_32bit(tempbuff + 0, length); put_32bit(tempbuff + 4, type); crc = crc32(0, tempbuff + 4, 4); - /* write that data */ - if (fp.write(tempbuff, 8) != 8) - return PNGERR_FILE_ERROR; + // write that data + std::tie(err, std::ignore) = write(fp, tempbuff, 8); + if (err) + return err; - /* append the actual data */ + // append the actual data if (length > 0) { - if (fp.write(data, length) != length) - return PNGERR_FILE_ERROR; + std::tie(err, std::ignore) = write(fp, data, length); + if (err) + return err; crc = crc32(crc, data, length); } - /* write the CRC */ + // write the CRC put_32bit(tempbuff, crc); - if (fp.write(tempbuff, 4) != 4) - return PNGERR_FILE_ERROR; + std::tie(err, std::ignore) = write(fp, tempbuff, 4); + if (err) + return err; - return PNGERR_NONE; + return std::error_condition(); } @@ -915,33 +973,43 @@ static png_error write_chunk(util::core_file &fp, const uint8_t *data, uint32_t chunk to the given file by deflating it -------------------------------------------------*/ -static png_error write_deflated_chunk(util::core_file &fp, uint8_t *data, uint32_t type, uint32_t length) +static std::error_condition write_deflated_chunk(random_write &fp, uint8_t *data, uint32_t type, uint32_t length) noexcept { - uint64_t lengthpos = fp.tell(); - uint8_t tempbuff[8192]; - uint32_t zlength = 0; + std::error_condition err; + std::uint64_t lengthpos; + err = fp.tell(lengthpos); + if (err) + return err; + + std::uint8_t tempbuff[8192]; + std::uint32_t zlength = 0; z_stream stream; - uint32_t crc; + std::uint32_t crc; int zerr; - /* stuff the length/type into the buffer */ + // stuff the length/type into the buffer put_32bit(tempbuff + 0, length); put_32bit(tempbuff + 4, type); crc = crc32(0, tempbuff + 4, 4); - /* write that data */ - if (fp.write(tempbuff, 8) != 8) - return PNGERR_FILE_ERROR; + // write that data + std::tie(err, std::ignore) = write(fp, tempbuff, 8); + if (err) + return err; - /* initialize the stream */ + // initialize the stream memset(&stream, 0, sizeof(stream)); stream.next_in = data; stream.avail_in = length; zerr = deflateInit(&stream, Z_DEFAULT_COMPRESSION); - if (zerr != Z_OK) - return PNGERR_COMPRESS_ERROR; - - /* now loop until we run out of data */ + if (Z_ERRNO == zerr) + return std::error_condition(errno, std::generic_category()); + else if (Z_MEM_ERROR == zerr) + return std::errc::not_enough_memory; + else if (Z_OK != zerr) + return png_error::COMPRESS_ERROR; + + // now loop until we run out of data for ( ; ; ) { /* compress this chunk */ @@ -949,50 +1017,63 @@ static png_error write_deflated_chunk(util::core_file &fp, uint8_t *data, uint32 stream.avail_out = sizeof(tempbuff); zerr = deflate(&stream, Z_FINISH); - /* if there's data to write, do it */ + // if there's data to write, do it if (stream.avail_out < sizeof(tempbuff)) { int bytes = sizeof(tempbuff) - stream.avail_out; - if (fp.write(tempbuff, bytes) != bytes) + std::tie(err, std::ignore) = write(fp, tempbuff, bytes); + if (err) { deflateEnd(&stream); - return PNGERR_FILE_ERROR; + return err; } crc = crc32(crc, tempbuff, bytes); zlength += bytes; } - /* stop at the end of the stream */ + // stop at the end of the stream if (zerr == Z_STREAM_END) break; - /* other errors are fatal */ + // other errors are fatal if (zerr != Z_OK) { deflateEnd(&stream); - return PNGERR_COMPRESS_ERROR; + if (Z_ERRNO == zerr) + return std::error_condition(errno, std::generic_category()); + else if (Z_MEM_ERROR == zerr) + return std::errc::not_enough_memory; + else + return png_error::COMPRESS_ERROR; } } - /* clean up deflater(maus) */ + // clean up deflater zerr = deflateEnd(&stream); - if (zerr != Z_OK) - return PNGERR_COMPRESS_ERROR; - - /* write the CRC */ + if (Z_ERRNO == zerr) + return std::error_condition(errno, std::generic_category()); + else if (Z_MEM_ERROR == zerr) + return std::errc::not_enough_memory; + else if (Z_OK != zerr) + return png_error::COMPRESS_ERROR; + + // write the CRC put_32bit(tempbuff, crc); - if (fp.write(tempbuff, 4) != 4) - return PNGERR_FILE_ERROR; - - /* seek back and update the length */ - fp.seek(lengthpos, SEEK_SET); + std::tie(err, std::ignore) = write(fp, tempbuff, 4); + if (err) + return err; + + // seek back and update the length + err = fp.seek(lengthpos, SEEK_SET); + if (err) + return err; put_32bit(tempbuff + 0, zlength); - if (fp.write(tempbuff, 4) != 4) - return PNGERR_FILE_ERROR; + std::tie(err, std::ignore) = write(fp, tempbuff, 4); + if (err) + return err; - /* return to the end */ - fp.seek(lengthpos + 8 + zlength + 4, SEEK_SET); - return PNGERR_NONE; + // return to the end + return fp.seek(lengthpos + 8 + zlength + 4, SEEK_SET); } @@ -1001,26 +1082,24 @@ static png_error write_deflated_chunk(util::core_file &fp, uint8_t *data, uint32 bitmap to a palettized image -------------------------------------------------*/ -static png_error convert_bitmap_to_image_palette(png_info &pnginfo, const bitmap_t &bitmap, int palette_length, const rgb_t *palette) +static std::error_condition convert_bitmap_to_image_palette(png_info &pnginfo, bitmap_t const &bitmap, int palette_length, const rgb_t *palette) noexcept { - int rowbytes; - int x, y; - - /* set the common info */ + // set the common info pnginfo.width = bitmap.width(); pnginfo.height = bitmap.height(); pnginfo.bit_depth = 8; pnginfo.color_type = 3; pnginfo.num_palette = 256; - rowbytes = pnginfo.width; + int const rowbytes = pnginfo.width; - /* allocate memory for the palette */ - try { pnginfo.palette.reset(new std::uint8_t [3 * 256]); } - catch (std::bad_alloc const &) { return PNGERR_OUT_OF_MEMORY; } + // allocate memory for the palette + pnginfo.palette.reset(new (std::nothrow) std::uint8_t [3 * 256]); + if (!pnginfo.palette) + return std::errc::not_enough_memory; - /* build the palette */ + // build the palette std::fill_n(pnginfo.palette.get(), 3 * 256, 0); - for (x = 0; x < palette_length; x++) + for (int x = 0; x < palette_length; x++) { rgb_t color = palette[x]; pnginfo.palette[3 * x + 0] = color.r(); @@ -1028,30 +1107,27 @@ static png_error convert_bitmap_to_image_palette(png_info &pnginfo, const bitmap pnginfo.palette[3 * x + 2] = color.b(); } - /* allocate memory for the image */ - try - { - pnginfo.image.reset(new std::uint8_t [pnginfo.height * (rowbytes + 1)]); - } - catch (std::bad_alloc const &) + // allocate memory for the image + pnginfo.image.reset(new (std::nothrow) std::uint8_t [pnginfo.height * (rowbytes + 1)]); + if (!pnginfo.image) { pnginfo.palette.reset(); - return PNGERR_OUT_OF_MEMORY; + return std::errc::not_enough_memory; } - /* copy in the pixels, specifying a nullptr filter */ - for (y = 0; y < pnginfo.height; y++) + // copy in the pixels, specifying a nullptr filter + for (int y = 0; y < pnginfo.height; y++) { - uint16_t *src = reinterpret_cast<uint16_t *>(bitmap.raw_pixptr(y)); + uint16_t const *src = reinterpret_cast<uint16_t const *>(bitmap.raw_pixptr(y)); uint8_t *dst = &pnginfo.image[y * (rowbytes + 1)]; - /* store the filter byte, then copy the data */ + // store the filter byte, then copy the data *dst++ = 0; - for (x = 0; x < pnginfo.width; x++) + for (int x = 0; x < pnginfo.width; x++) *dst++ = *src++; } - return PNGERR_NONE; + return std::error_condition(); } @@ -1060,77 +1136,75 @@ static png_error convert_bitmap_to_image_palette(png_info &pnginfo, const bitmap bitmap to an RGB image -------------------------------------------------*/ -static png_error convert_bitmap_to_image_rgb(png_info &pnginfo, const bitmap_t &bitmap, int palette_length, const rgb_t *palette) +static std::error_condition convert_bitmap_to_image_rgb(png_info &pnginfo, bitmap_t const &bitmap, int palette_length, const rgb_t *palette) noexcept { - int alpha = (bitmap.format() == BITMAP_FORMAT_ARGB32); - int rowbytes; - int x, y; + bool const alpha = (bitmap.format() == BITMAP_FORMAT_ARGB32); - /* set the common info */ + // set the common info pnginfo.width = bitmap.width(); pnginfo.height = bitmap.height(); pnginfo.bit_depth = 8; pnginfo.color_type = alpha ? 6 : 2; - rowbytes = pnginfo.width * (alpha ? 4 : 3); + int const rowbytes = pnginfo.width * (alpha ? 4 : 3); - /* allocate memory for the image */ - try { pnginfo.image.reset(new std::uint8_t [pnginfo.height * (rowbytes + 1)]); } - catch (std::bad_alloc const &) { return PNGERR_OUT_OF_MEMORY; } + // allocate memory for the image + pnginfo.image.reset(new (std::nothrow) std::uint8_t [pnginfo.height * (rowbytes + 1)]); + if (!pnginfo.image) + return std::errc::not_enough_memory; - /* copy in the pixels, specifying a nullptr filter */ - for (y = 0; y < pnginfo.height; y++) + // copy in the pixels, specifying a nullptr filter + for (int y = 0; y < pnginfo.height; y++) { uint8_t *dst = &pnginfo.image[y * (rowbytes + 1)]; - /* store the filter byte, then copy the data */ + // store the filter byte, then copy the data *dst++ = 0; - /* 16bpp palettized format */ if (bitmap.format() == BITMAP_FORMAT_IND16) { - uint16_t *src16 = reinterpret_cast<uint16_t *>(bitmap.raw_pixptr(y)); - for (x = 0; x < pnginfo.width; x++) + // 16bpp palettized format + uint16_t const *src16 = reinterpret_cast<uint16_t const *>(bitmap.raw_pixptr(y)); + for (int x = 0; x < pnginfo.width; x++) { - rgb_t color = palette[*src16++]; + rgb_t const color = palette[*src16++]; *dst++ = color.r(); *dst++ = color.g(); *dst++ = color.b(); } } - - /* 32-bit RGB direct */ else if (bitmap.format() == BITMAP_FORMAT_RGB32) { - uint32_t *src32 = reinterpret_cast<uint32_t *>(bitmap.raw_pixptr(y)); - for (x = 0; x < pnginfo.width; x++) + // 32-bit RGB direct + uint32_t const *src32 = reinterpret_cast<uint32_t const *>(bitmap.raw_pixptr(y)); + for (int x = 0; x < pnginfo.width; x++) { - rgb_t raw = *src32++; + rgb_t const raw = *src32++; *dst++ = raw.r(); *dst++ = raw.g(); *dst++ = raw.b(); } } - - /* 32-bit ARGB direct */ else if (bitmap.format() == BITMAP_FORMAT_ARGB32) { - uint32_t *src32 = reinterpret_cast<uint32_t *>(bitmap.raw_pixptr(y)); - for (x = 0; x < pnginfo.width; x++) + // 32-bit ARGB direct + uint32_t const *src32 = reinterpret_cast<uint32_t const *>(bitmap.raw_pixptr(y)); + for (int x = 0; x < pnginfo.width; x++) { - rgb_t raw = *src32++; + rgb_t const raw = *src32++; *dst++ = raw.r(); *dst++ = raw.g(); *dst++ = raw.b(); *dst++ = raw.a(); } } - - /* unsupported format */ else - return PNGERR_UNSUPPORTED_FORMAT; + { + // unsupported format + return png_error::UNSUPPORTED_FORMAT; + } } - return PNGERR_NONE; + return std::error_condition(); } @@ -1139,17 +1213,17 @@ static png_error convert_bitmap_to_image_rgb(png_info &pnginfo, const bitmap_t & chunks to the given file -------------------------------------------------*/ -static png_error write_png_stream(util::core_file &fp, png_info &pnginfo, const bitmap_t &bitmap, int palette_length, const rgb_t *palette) +static std::error_condition write_png_stream(random_write &fp, png_info &pnginfo, const bitmap_t &bitmap, int palette_length, const rgb_t *palette) noexcept { uint8_t tempbuff[16]; - png_error error; + std::error_condition error; // create an unfiltered image in either palette or RGB form if (bitmap.format() == BITMAP_FORMAT_IND16 && palette_length <= 256) error = convert_bitmap_to_image_palette(pnginfo, bitmap, palette_length, palette); else error = convert_bitmap_to_image_rgb(pnginfo, bitmap, palette_length, palette); - if (error != PNGERR_NONE) + if (error) return error; // if we wanted to get clever and do filtering, we would do it here @@ -1163,18 +1237,18 @@ static png_error write_png_stream(util::core_file &fp, png_info &pnginfo, const put_8bit(tempbuff + 11, pnginfo.filter_method); put_8bit(tempbuff + 12, pnginfo.interlace_method); error = write_chunk(fp, tempbuff, PNG_CN_IHDR, 13); - if (error != PNGERR_NONE) + if (error) return error; // write the PLTE chunk if (pnginfo.num_palette > 0) error = write_chunk(fp, pnginfo.palette.get(), PNG_CN_PLTE, pnginfo.num_palette * 3); - if (error != PNGERR_NONE) + if (error) return error; - // write a single IDAT chunk */ + // write a single IDAT chunk error = write_deflated_chunk(fp, pnginfo.image.get(), PNG_CN_IDAT, pnginfo.height * (compute_rowbytes(pnginfo) + 1)); - if (error != PNGERR_NONE) + if (error) return error; // write TEXT chunks @@ -1182,38 +1256,36 @@ static png_error write_png_stream(util::core_file &fp, png_info &pnginfo, const for (png_info::png_text const &text : pnginfo.textlist) { try { textbuf.resize(text.first.length() + 1 + text.second.length()); } - catch (std::bad_alloc const &) { return PNGERR_OUT_OF_MEMORY; } + catch (std::bad_alloc const &) { return std::errc::not_enough_memory; } std::uint8_t *dst(&textbuf[0]); // convert keyword to ISO-8859-1 - char const *const kwend(text.first.c_str() + text.first.length()); - for (char const *src = text.first.c_str(); kwend > src; ++dst) + for (std::string_view src = text.first; !src.empty(); ++dst) { char32_t ch; - int const len(uchar_from_utf8(&ch, src, kwend - src)); + int const len(uchar_from_utf8(&ch, src)); if (0 >= len) break; *dst = std::uint8_t(ch); - src += len; + src.remove_prefix(len); } - // NUL separator between keword and text + // NUL separator between keyword and text *dst++ = 0; // convert text to ISO-8859-1 - char const *const textend(text.second.c_str() + text.second.length()); - for (char const *src = text.second.c_str(); textend > src; ++dst) + for (std::string_view src = text.second; !src.empty(); ++dst) { char32_t ch; - int const len(uchar_from_utf8(&ch, src, textend - src)); + int const len(uchar_from_utf8(&ch, src)); if (0 >= len) break; *dst = std::uint8_t(ch); - src += len; + src.remove_prefix(len); } error = write_chunk(fp, &textbuf[0], PNG_CN_tEXt, dst - &textbuf[0]); - if (error != PNGERR_NONE) + if (error) return error; } @@ -1222,18 +1294,19 @@ static png_error write_png_stream(util::core_file &fp, png_info &pnginfo, const } -png_error png_write_bitmap(util::core_file &fp, png_info *info, bitmap_t const &bitmap, int palette_length, const rgb_t *palette) +std::error_condition png_write_bitmap(random_write &fp, png_info *info, bitmap_t const &bitmap, int palette_length, const rgb_t *palette) noexcept { // use a dummy pnginfo if none passed to us png_info pnginfo; - if (info == nullptr) + if (!info) info = &pnginfo; // write the PNG signature - if (fp.write(PNG_SIGNATURE, sizeof(PNG_SIGNATURE)) != sizeof(PNG_SIGNATURE)) - return PNGERR_FILE_ERROR; + auto const [err, written] = write(fp, PNG_SIGNATURE, sizeof(PNG_SIGNATURE)); + if (err) + return err; - /* write the rest of the PNG data */ + // write the rest of the PNG data return write_png_stream(fp, *info, bitmap, palette_length, palette); } @@ -1245,25 +1318,13 @@ png_error png_write_bitmap(util::core_file &fp, png_info *info, bitmap_t const & ********************************************************************************/ -/** - * @fn png_error mng_capture_start(util::core_file &fp, bitmap_t &bitmap, double rate) - * - * @brief Mng capture start. - * - * @param [in,out] fp If non-null, the fp. - * @param [in,out] bitmap The bitmap. - * @param rate The rate. - * - * @return A png_error. - */ - -png_error mng_capture_start(util::core_file &fp, bitmap_t &bitmap, double rate) +std::error_condition mng_capture_start(random_write &fp, bitmap_t const &bitmap, unsigned rate) noexcept { - uint8_t mhdr[28]; - - if (fp.write(MNG_Signature, 8) != 8) - return PNGERR_FILE_ERROR; + auto const [err, written] = write(fp, MNG_Signature, 8); + if (err) + return err; + uint8_t mhdr[28]; memset(mhdr, 0, 28); put_32bit(mhdr + 0, bitmap.width()); put_32bit(mhdr + 4, bitmap.height()); @@ -1272,36 +1333,27 @@ png_error mng_capture_start(util::core_file &fp, bitmap_t &bitmap, double rate) return write_chunk(fp, mhdr, MNG_CN_MHDR, 28); } -/** - * @fn png_error mng_capture_frame(util::core_file &fp, png_info *info, bitmap_t &bitmap, int palette_length, const rgb_t *palette) - * - * @brief Mng capture frame. - * - * @param [in,out] fp If non-null, the fp. - * @param [in,out] info If non-null, the information. - * @param [in,out] bitmap The bitmap. - * @param palette_length Length of the palette. - * @param palette The palette. - * - * @return A png_error. - */ - -png_error mng_capture_frame(util::core_file &fp, png_info &info, bitmap_t const &bitmap, int palette_length, const rgb_t *palette) + +std::error_condition mng_capture_frame(random_write &fp, png_info &info, bitmap_t const &bitmap, int palette_length, rgb_t const *palette) noexcept { return write_png_stream(fp, info, bitmap, palette_length, palette); } -/** - * @fn png_error mng_capture_stop(util::core_file &fp) - * - * @brief Mng capture stop. - * - * @param [in,out] fp If non-null, the fp. - * - * @return A png_error. - */ - -png_error mng_capture_stop(util::core_file &fp) + +std::error_condition mng_capture_stop(random_write &fp) noexcept { return write_chunk(fp, nullptr, MNG_CN_MEND, 0); } + + +/*------------------------------------------------- + png_category - gets the PNG error category + instance +-------------------------------------------------*/ + +std::error_category const &png_category() noexcept +{ + return f_png_category_instance; +} + +} // namespace util |