From 44bc3ffa43cdd4cdd886d025d2aacd4da3d357da Mon Sep 17 00:00:00 2001 From: Vas Crabb Date: Sun, 13 Aug 2017 15:53:36 +1000 Subject: [PATCH] png: support 16-bit samples --- src/emu/rendutil.cpp | 16 ++++++------ src/lib/util/png.cpp | 58 +++++++++++++++++++++++++++----------------- 2 files changed, 44 insertions(+), 30 deletions(-) diff --git a/src/emu/rendutil.cpp b/src/emu/rendutil.cpp index 01691af4374..c8d120f616a 100644 --- a/src/emu/rendutil.cpp +++ b/src/emu/rendutil.cpp @@ -647,14 +647,6 @@ bool render_load_png(bitmap_argb32 &bitmap, emu_file &file, const char *dirname, if (result != PNGERR_NONE) return false; - // verify we can handle this PNG - if (png.bit_depth > 8) - { - osd_printf_error("%s: Unsupported bit depth %d (8 bit max)\n", filename, png.bit_depth); - png_free(&png); - return false; - } - // if less than 8 bits, upsample if (PNGERR_NONE != png_expand_buffer_8bit(&png)) { @@ -674,6 +666,14 @@ bool render_load_png(bitmap_argb32 &bitmap, emu_file &file, const char *dirname, } else if (png.width == bitmap.width() && png.height == bitmap.height()) { + // verify we can handle this PNG + if (png.bit_depth > 8) + { + osd_printf_error("%s: Unsupported bit depth %d (8 bit max)\n", filename, png.bit_depth); + png_free(&png); + return false; + } + // alpha case hasalpha = copy_png_alpha_to_bitmap(bitmap, &png); } diff --git a/src/lib/util/png.cpp b/src/lib/util/png.cpp index fbc2eca2180..d344c1b72e8 100644 --- a/src/lib/util/png.cpp +++ b/src/lib/util/png.cpp @@ -145,16 +145,20 @@ public: png_error copy_to_bitmap(bitmap_argb32 &bitmap, bool &hasalpha) const { // 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 if ((0 != pnginfo.interlace_method) && (1 != pnginfo.interlace_method)) return PNGERR_UNSUPPORTED_FORMAT; // unknown interlace method - if (8 != pnginfo.bit_depth) - return PNGERR_UNSUPPORTED_FORMAT; // only do 8bpp here - expand lower bit depth first + if ((3 == pnginfo.color_type) && (8 != pnginfo.bit_depth)) + return PNGERR_UNSUPPORTED_FORMAT; // indexed colour must be exactly 8bpp // everything looks sane, allocate the bitmap and deinterlace into it bitmap.allocate(pnginfo.width, pnginfo.height); std::uint8_t accumalpha(0xff); + uint32_t const bps(pnginfo.bit_depth >> 3); + uint32_t const bpp(bps * samples[pnginfo.color_type]); unsigned const pass_count(get_pass_count()); std::uint32_t pass_offset[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; for (unsigned pass = 0; pass_count > pass; ++pass) @@ -173,7 +177,7 @@ public: // handle 8bpp palettized case for (std::uint32_t y = 0; dimensions.second > y; ++y) { - for (std::uint32_t x = 0; dimensions.first > x; ++x, ++src) + for (std::uint32_t x = 0; dimensions.first > x; ++x, src += bpp) { // determine alpha and expand to 32bpp std::uint8_t const alpha((*src < pnginfo.num_trans) ? pnginfo.trans[*src] : 0xff); @@ -186,10 +190,11 @@ public: } else if (0 == pnginfo.color_type) { - // handle 8bpp grayscale non-alpha case + // handle grayscale non-alpha case + uint32_t const bpp(pnginfo.bit_depth >> 3); for (std::uint32_t y = 0; dimensions.second > y; ++y) { - for (std::uint32_t x = 0; dimensions.first > x; ++x, ++src) + for (std::uint32_t x = 0; dimensions.first > x; ++x, src += bpp) { rgb_t const pix(0xff, src[0], src[0], src[0]); bitmap.pix32((y << y_shift) + y_offs, (x << x_shift) + x_offs) = pix; @@ -198,38 +203,47 @@ public: } else if (4 == pnginfo.color_type) { - // handle 8bpp grayscale alpha case + // handle grayscale alpha case + uint32_t const i(0 * bps); + uint32_t const a(1 * bps); for (std::uint32_t y = 0; dimensions.second > y; ++y) { - for (std::uint32_t x = 0; dimensions.first > x; ++x, src += 2) + for (std::uint32_t x = 0; dimensions.first > x; ++x, src += bpp) { - accumalpha &= src[1]; - rgb_t const pix(src[1], src[0], src[0], src[0]); + 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; } } } else if (2 == pnginfo.color_type) { - // handle 32bpp non-alpha case + // handle RGB non-alpha case + uint32_t const r(0 * bps); + uint32_t const g(1 * bps); + uint32_t const b(2 * bps); for (std::uint32_t y = 0; dimensions.second > y; ++y) { - for (std::uint32_t x = 0; dimensions.first > x; ++x, src += 3) + for (std::uint32_t x = 0; dimensions.first > x; ++x, src += bpp) { - rgb_t const pix(0xff, src[0], src[1], src[2]); + rgb_t const pix(0xff, src[r], src[g], src[b]); bitmap.pix32((y << y_shift) + y_offs, (x << x_shift) + x_offs) = pix; } } } else { - // handle 32bpp alpha case + // handle RGB alpha case + uint32_t const r(0 * bps); + uint32_t const g(1 * bps); + uint32_t const b(2 * bps); + uint32_t const a(3 * bps); for (std::uint32_t y = 0; dimensions.second > y; ++y) { - for (std::uint32_t x = 0; dimensions.first > x; ++x, src += 4) + for (std::uint32_t x = 0; dimensions.first > x; ++x, src += bpp) { - accumalpha &= src[3]; - rgb_t const pix(src[3], src[0], src[1], src[2]); + 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; } } @@ -248,12 +262,12 @@ public: return PNGERR_NONE; // do some basic checks for unsupported images - if ((0 != pnginfo.color_type) && (3 != pnginfo.color_type)) - return PNGERR_UNSUPPORTED_FORMAT; // unknown colour sample format - if ((0 != pnginfo.interlace_method) && (1 != pnginfo.interlace_method)) - return PNGERR_UNSUPPORTED_FORMAT; // unknown interlace method if (!pnginfo.bit_depth || (8 % pnginfo.bit_depth)) return PNGERR_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 + if ((0 != pnginfo.interlace_method) && (1 != pnginfo.interlace_method)) + return PNGERR_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()); @@ -368,8 +382,8 @@ private: png_error process(std::list const &idata) { // do some basic checks for unsupported images - if ((ARRAY_LENGTH(samples) <= pnginfo.color_type) || !samples[pnginfo.color_type]) - return PNGERR_UNSUPPORTED_FORMAT; // unknown colour sample format + if (!pnginfo.bit_depth || (ARRAY_LENGTH(samples) <= pnginfo.color_type) || !samples[pnginfo.color_type]) + return PNGERR_UNSUPPORTED_FORMAT; // unknown colour format if ((0 != pnginfo.interlace_method) && (1 != pnginfo.interlace_method)) return PNGERR_UNSUPPORTED_FORMAT; // unknown interlace method