summaryrefslogtreecommitdiffstatshomepage
path: root/src/frontend/mame/ui/icorender.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/frontend/mame/ui/icorender.cpp')
-rw-r--r--src/frontend/mame/ui/icorender.cpp508
1 files changed, 24 insertions, 484 deletions
diff --git a/src/frontend/mame/ui/icorender.cpp b/src/frontend/mame/ui/icorender.cpp
index bea85f9dd60..5bda0836838 100644
--- a/src/frontend/mame/ui/icorender.cpp
+++ b/src/frontend/mame/ui/icorender.cpp
@@ -19,6 +19,7 @@
#include "emu.h"
#include "icorender.h"
+#include "util/msdib.h"
#include "util/png.h"
#include <algorithm>
@@ -26,11 +27,10 @@
#include <cstdint>
#include <cstring>
-// need to set LOG_OUTPUT_STREAM because there's no logerror outside devices
-#define LOG_OUTPUT_STREAM std::cerr
+// need to set LOG_OUTPUT_FUNC or LOG_OUTPUT_STREAM because there's no logerror outside devices
+#define LOG_OUTPUT_FUNC osd_printf_verbose
#define LOG_GENERAL (1U << 0)
-#define LOG_DIB (1U << 1)
//#define VERBOSE (LOG_GENERAL | LOG_DIB)
@@ -41,16 +41,6 @@ namespace ui {
namespace {
-// DIB compression schemes
-enum : uint32_t
-{
- DIB_COMP_NONE = 0,
- DIB_COMP_RLE8 = 1,
- DIB_COMP_RLE4 = 2,
- DIB_COMP_BITFIELDS = 3
-};
-
-
// ICO file header
struct icon_dir_t
{
@@ -83,67 +73,6 @@ struct icon_dir_entry_t
uint32_t offset; // offset to image data from start of file
};
-// old-style DIB header
-struct bitmap_core_header_t
-{
- uint32_t size; // size of the header (12, 16 or 64)
- int16_t width; // width of bitmap in pixels
- int16_t height; // height of the image in pixels
- uint16_t planes; // number of colour planes (must be 1)
- uint16_t bpp; // bits per pixel
-};
-
-// new-style DIB header
-struct bitmap_info_header_t
-{
- uint32_t size; // size of the header
- int32_t width; // width of bitmap in pixels
- int32_t height; // height of bitmap in pixels
- uint16_t planes; // number of colour planes (must be 1)
- uint16_t bpp; // bits per pixel
- uint32_t comp; // compression method
- uint32_t rawsize; // size of bitmap data after decompression or 0 if uncompressed
- int32_t hres; // horizontal resolution in pixels/metre
- int32_t vres; // horizontal resolution in pixels/metre
- uint32_t colors; // number of colours or 0 for 1 << bpp
- uint32_t important; // number of important colours or 0 if all important
- uint32_t red; // red field mask - must be contiguous
- uint32_t green; // green field mask - must be contiguous
- uint32_t blue; // blue field mask - must be contiguous
- uint32_t alpha; // alpha field mask - must be contiguous
-};
-
-
-bool dib_parse_mask(uint32_t mask, unsigned &shift, unsigned &bits)
-{
- shift = count_leading_zeros(mask);
- mask <<= shift;
- bits = count_leading_ones(mask);
- mask <<= shift;
- shift = 32 - shift - bits;
- return !mask;
-}
-
-
-void dib_truncate_channel(unsigned &shift, unsigned &bits)
-{
- if (8U < bits)
- {
- unsigned const excess(bits - 8);
- shift += excess;
- bits -= excess;
- }
-}
-
-
-uint8_t dib_splat_sample(uint8_t val, unsigned bits)
-{
- assert(8U >= bits);
- for (val <<= (8U - bits); bits && (8U > bits); bits <<= 1)
- val |= val >> bits;
- return val;
-}
-
bool load_ico_png(util::core_file &fp, icon_dir_entry_t const &dir, bitmap_argb32 &bitmap)
{
@@ -151,10 +80,10 @@ bool load_ico_png(util::core_file &fp, icon_dir_entry_t const &dir, bitmap_argb3
if (9U >= dir.size)
return false;
fp.seek(dir.offset, SEEK_SET);
- png_error const err(png_read_bitmap(fp, bitmap));
+ util::png_error const err(util::png_read_bitmap(fp, bitmap));
switch (err)
{
- case PNGERR_NONE:
+ case util::png_error::NONE:
// found valid PNG image
assert(bitmap.valid());
if ((dir.get_width() == bitmap.width()) && ((dir.get_height() == bitmap.height())))
@@ -172,7 +101,7 @@ bool load_ico_png(util::core_file &fp, icon_dir_entry_t const &dir, bitmap_argb3
}
return true;
- case PNGERR_BAD_SIGNATURE:
+ case util::png_error::BAD_SIGNATURE:
// doesn't look like PNG data - just fall back to DIB without the file header
return false;
@@ -190,426 +119,37 @@ bool load_ico_png(util::core_file &fp, icon_dir_entry_t const &dir, bitmap_argb3
bool load_ico_dib(util::core_file &fp, icon_dir_entry_t const &dir, bitmap_argb32 &bitmap)
{
- // check that these things haven't been padded somehow
- static_assert(sizeof(bitmap_core_header_t) == 12U, "compiler has applied padding to bitmap_core_header_t");
- static_assert(sizeof(bitmap_info_header_t) == 56U, "compiler has applied padding to bitmap_info_header_t");
-
- // ensure the header fits in the space for the image data
- union { bitmap_core_header_t core; bitmap_info_header_t info; } header;
- assert(&header.core.size == &header.info.size);
- if (sizeof(header.core) > dir.size)
- return false;
- std::memset(&header, 0, sizeof(header));
fp.seek(dir.offset, SEEK_SET);
- if (fp.read(&header.core.size, sizeof(header.core.size)) != sizeof(header.core.size))
- {
- LOG(
- "Error reading DIB header size from ICO file at offset %u (directory size %u)\n",
- dir.offset,
- dir.size);
- return false;
- }
- header.core.size = little_endianize_int32(header.core.size);
- if (dir.size < header.core.size)
- {
- LOG(
- "ICO file image data at %u (%u bytes) is too small for DIB header (%u bytes)\n",
- dir.offset,
- dir.size,
- header.core.size);
- return false;
- }
-
- // identify and read the header - convert OS/2 headers to Windows 3 format
- unsigned palette_bytes(4U);
- switch (header.core.size)
+ util::msdib_error const err(util::msdib_read_bitmap_data(fp, bitmap, dir.size, dir.get_height()));
+ switch (err)
{
- case 16U:
- case 64U:
- // extended OS/2 bitmap header with support for compression
- LOG(
- "ICO image data at %u (%u bytes) uses unsupported OS/2 DIB header (size %u)\n",
- dir.offset,
- dir.size,
- header.core.size);
- return false;
-
- case 12U:
- // introduced in OS/2 and Windows 2.0
+ case util::msdib_error::NONE:
+ // found valid DIB image
+ assert(bitmap.valid());
+ if ((dir.get_width() == bitmap.width()) && ((dir.get_height() == bitmap.height())))
{
- palette_bytes = 3U;
- uint32_t const header_read(std::min<uint32_t>(header.core.size, sizeof(header.core)) - sizeof(header.core.size));
- if (fp.read(&header.core.width, header_read) != header_read)
- {
- LOG("Error reading DIB core header from ICO file image data at %u (%u bytes)\n", dir.offset, dir.size);
- return false;
- }
- fp.seek(header.core.size - sizeof(header.core.size) - header_read, SEEK_CUR);
- header.core.width = little_endianize_int16(header.core.width);
- header.core.height = little_endianize_int16(header.core.height);
- header.core.planes = little_endianize_int16(header.core.planes);
- header.core.bpp = little_endianize_int16(header.core.bpp);
- LOGMASKED(
- LOG_DIB,
- "Read DIB core header from ICO file image data at %u: %d*%d, %u planes, %u bpp\n",
- dir.offset,
- header.core.width,
- header.core.height,
- header.core.planes,
- header.core.bpp);
-
- // this works because the core header only aliases the width/height of the info header
- header.info.bpp = header.core.bpp;
- header.info.planes = header.core.planes;
- header.info.height = header.core.height;
- header.info.width = header.core.width;
- header.info.size = 40U;
+ LOG("Loaded %d*%d pixel DIB image from ICO file\n", bitmap.width(), bitmap.height());
}
- break;
-
- default:
- // the next version will be longer
- if (124U >= header.core.size)
+ else
{
LOG(
- "ICO image data at %u (%u bytes) uses unsupported DIB header format (size %u)\n",
- dir.offset,
- dir.size,
- header.core.size);
- return false;
- }
- // fall through
- case 40U:
- case 52U:
- case 56U:
- case 108U:
- case 124U:
- // the Windows 3 bitmap header with optional extensions
- {
- palette_bytes = 4U;
- uint32_t const header_read(std::min<uint32_t>(header.info.size, sizeof(header.info)) - sizeof(header.info.size));
- if (fp.read(&header.info.width, header_read) != header_read)
- {
- LOG("Error reading DIB info header from ICO file image data at %u (%u bytes)\n", dir.offset, dir.size);
- return false;
- }
- fp.seek(header.info.size - sizeof(header.info.size) - header_read, SEEK_CUR);
- header.info.width = little_endianize_int32(header.info.width);
- header.info.height = little_endianize_int32(header.info.height);
- header.info.planes = little_endianize_int16(header.info.planes);
- header.info.bpp = little_endianize_int16(header.info.bpp);
- header.info.comp = little_endianize_int32(header.info.comp);
- header.info.rawsize = little_endianize_int32(header.info.rawsize);
- header.info.hres = little_endianize_int32(header.info.hres);
- header.info.vres = little_endianize_int32(header.info.vres);
- header.info.colors = little_endianize_int32(header.info.colors);
- header.info.important = little_endianize_int32(header.info.important);
- header.info.red = little_endianize_int32(header.info.red);
- header.info.green = little_endianize_int32(header.info.green);
- header.info.blue = little_endianize_int32(header.info.blue);
- header.info.alpha = little_endianize_int32(header.info.alpha);
- LOGMASKED(
- LOG_DIB,
- "Read DIB info header from ICO file image data at %u: %d*%d (%d*%d ppm), %u planes, %u bpp %u/%s%u colors\n",
- dir.offset,
- header.info.width,
- header.info.height,
- header.info.hres,
- header.info.vres,
- header.info.planes,
- header.info.bpp,
- header.info.important,
- header.info.colors ? "" : "2^",
- header.info.colors ? header.info.colors : header.info.bpp);
- }
- break;
- }
-
- // check for unsupported planes/bit depth
- if ((1U != header.info.planes) || !header.info.bpp || (32U < header.info.bpp) || ((8U < header.info.bpp) ? (header.info.bpp % 8) : (8 % header.info.bpp)))
- {
- LOG(
- "ICO file DIB image data at %u uses unsupported planes/bits per pixel %u*%u\n",
- dir.offset,
- header.info.planes,
- header.info.bpp);
- return false;
- }
-
- // check dimensions
- if ((0 >= header.info.width) || (0 == header.info.height))
- {
- LOG(
- "ICO file DIB image data at %u has invalid dimensions %u*%u\n",
- dir.offset,
- header.info.width,
- header.info.height);
- return false;
- }
- bool const top_down(0 > header.info.height);
- if (top_down)
- header.info.height = -header.info.height;
- bool have_and_mask((2 * dir.get_height()) == header.info.height);
- if (!have_and_mask && (dir.get_height() != header.info.height))
- {
- osd_printf_verbose(
- "ICO file DIB image data at %lu height %ld doesn't match directory height %u with or without AND mask\n",
- (unsigned long)dir.offset,
- (long)header.info.height,
- dir.get_height());
- return false;
- }
- if (have_and_mask)
- header.info.height >>= 1;
-
- // ensure compression scheme is supported
- bool indexed(true), no_palette(false);
- switch (header.info.comp)
- {
- case DIB_COMP_NONE:
- // uncompressed - direct colour with implied bitfields if more than eight bits/pixel
- indexed = 8U >= header.info.bpp;
- if (indexed)
- {
- if ((1U << header.info.bpp) < header.info.colors)
- {
- osd_printf_verbose(
- "ICO file DIB image data at %lu has oversized palette with %lu entries for %u bits per pixel\n",
- (unsigned long)dir.offset,
- (unsigned long)header.info.colors,
- (unsigned)header.info.bpp);
- }
- }
- if (!indexed)
- {
- no_palette = true;
- switch(header.info.bpp)
- {
- case 16U:
- header.info.red = 0x00007c00;
- header.info.green = 0x000003e0;
- header.info.blue = 0x0000001f;
- header.info.alpha = 0x00000000;
- break;
- case 24U:
- case 32U:
- header.info.red = 0x00ff0000;
- header.info.green = 0x0000ff00;
- header.info.blue = 0x000000ff;
- header.info.alpha = 0x00000000;
- break;
- }
- }
- break;
-
- case DIB_COMP_BITFIELDS:
- // uncompressed direct colour with explicitly-specified bitfields
- indexed = false;
- if (offsetof(bitmap_info_header_t, alpha) > header.info.size)
- {
- osd_printf_verbose(
- "ICO file DIB image data at %lu specifies bit masks but is too small (size %lu)\n",
- (unsigned long)dir.offset,
- (unsigned long)header.info.size);
- return false;
+ "Loaded %d*%d pixel DIB image from ICO file (directory indicated %u*%u)\n",
+ bitmap.width(),
+ bitmap.height(),
+ dir.get_width(),
+ dir.get_height());
}
- break;
+ return true;
default:
- LOG("ICO file DIB image data at %u uses unsupported compression scheme %u\n", header.info.comp);
- return false;
- }
-
- // we can now calculate the size of the palette and row data
- size_t const palette_entries(
- indexed
- ? ((1U == header.info.bpp) ? 2U : header.info.colors ? header.info.colors : (1U << header.info.bpp))
- : (no_palette ? 0U : header.info.colors));
- size_t const palette_size(palette_bytes * palette_entries);
- size_t const row_bytes(((31 + (header.info.width * header.info.bpp)) >> 5) << 2);
- size_t const mask_row_bytes(((31 + header.info.width) >> 5) << 2);
- size_t const required_size(
- header.info.size +
- palette_size +
- ((row_bytes + (have_and_mask ? mask_row_bytes : 0U)) * header.info.height));
- if (required_size > dir.size)
- {
+ // invalid DIB data or I/O error
LOG(
- "ICO file image data at %u (%u bytes) smaller than calculated DIB data size (%u bytes)\n",
+ "Error %u reading DIB image data from ICO file at offset %u (directory size %u)\n",
+ unsigned(err),
dir.offset,
- dir.size,
- required_size);
+ dir.size);
return false;
}
-
- // load the palette for indexed colour formats or the shifts for direct colour formats
- unsigned red_shift(0), green_shift(0), blue_shift(0), alpha_shift(0);
- unsigned red_bits(0), green_bits(0), blue_bits(0), alpha_bits(0);
- std::unique_ptr<rgb_t []> palette;
- if (indexed)
- {
- // read palette and convert
- std::unique_ptr<uint8_t []> palette_data(new uint8_t [palette_size]);
- if (fp.read(palette_data.get(), palette_size) != palette_size)
- {
- LOG("Error reading palette from ICO file DIB image data at %u (%u bytes)\n", dir.offset, dir.size);
- return false;
- }
- size_t const palette_usable(std::min<size_t>(palette_entries, size_t(1) << header.info.bpp));
- palette.reset(new rgb_t [palette_usable]);
- uint8_t const *ptr(palette_data.get());
- for (size_t i = 0; palette_usable > i; ++i, ptr += palette_bytes)
- palette[i] = rgb_t(ptr[2], ptr[1], ptr[0]);
- }
- else
- {
- // skip over the palette if necessary
- if (palette_entries)
- fp.seek(palette_bytes * palette_entries, SEEK_CUR);
-
- // convert masks to shifts
- bool const masks_contiguous(
- dib_parse_mask(header.info.red, red_shift, red_bits) &&
- dib_parse_mask(header.info.green, green_shift, green_bits) &&
- dib_parse_mask(header.info.blue, blue_shift, blue_bits) &&
- dib_parse_mask(header.info.alpha, alpha_shift, alpha_bits));
- if (!masks_contiguous)
- {
- osd_printf_verbose(
- "ICO file DIB image data at %lu specifies non-contiguous channel masks 0x%lx | 0x%lx | 0x%lx | 0x%lx\n",
- (unsigned long)dir.offset,
- (unsigned long)header.info.red,
- (unsigned long)header.info.green,
- (unsigned long)header.info.blue,
- (unsigned long)header.info.alpha);
- }
- if ((32U != header.info.bpp) && ((header.info.red | header.info.green | header.info.blue | header.info.alpha) >> header.info.bpp))
- {
- LOG(
- "ICO file DIB image data at %lu specifies channel masks 0x%x | 0x%x | 0x%x | 0x%x that exceed %u bits per pixel\n",
- dir.offset,
- header.info.red,
- header.info.green,
- header.info.blue,
- header.info.alpha,
- header.info.bpp);
- return false;
- }
- LOGMASKED(
- LOG_DIB,
- "DIB from ICO file image data at %1$u using channels: R((x >> %3$u) & 0x%4$0*2$x) G((x >> %5$u) & 0x%6$0*2$x) B((x >> %7$u) & 0x%8$0*2$x) A((x >> %9$u) & 0x%10$0*2$x)\n",
- dir.offset,
- (header.info.bpp + 3) >> 2,
- red_shift,
- (uint32_t(1) << red_bits) - 1,
- green_shift,
- (uint32_t(1) << green_bits) - 1,
- blue_shift,
- (uint32_t(1) << blue_bits) - 1,
- alpha_shift,
- (uint32_t(1) << alpha_bits) - 1);
-
- // the MAME bitmap only supports 8 bits/sample maximum
- dib_truncate_channel(red_shift, red_bits);
- dib_truncate_channel(green_shift, green_bits);
- dib_truncate_channel(blue_shift, blue_bits);
- dib_truncate_channel(alpha_shift, alpha_bits);
- }
-
- // allocate the bitmap and process row data
- std::unique_ptr<uint8_t []> row_data(new uint8_t [row_bytes]);
- bitmap.allocate(header.info.width, header.info.height);
- int const y_inc(top_down ? 1 : -1);
- for (int32_t i = 0, y = top_down ? 0 : (header.info.height - 1); header.info.height > i; ++i, y += y_inc)
- {
- if (fp.read(row_data.get(), row_bytes) != row_bytes)
- {
- LOG("Error reading DIB row %d data from ICO image data at %u\n", i, dir.offset);
- return false;
- }
- uint8_t *src(row_data.get());
- uint32_t *dest(&bitmap.pix(y));
- unsigned shift(0U);
- for (int32_t x = 0; header.info.width > x; ++x, ++dest)
- {
- // extract or compose a pixel
- uint32_t pix(0U);
- if (8U >= header.info.bpp)
- {
- assert(8U > shift);
- pix = *src >> (8U - header.info.bpp);
- *src <<= header.info.bpp;
- shift += header.info.bpp;
- if (8U <= shift)
- {
- shift = 0U;
- ++src;
- }
- }
- else for (shift = 0; header.info.bpp > shift; shift += 8U, ++src)
- {
- pix |= uint32_t(*src) << shift;
- }
-
- // convert to RGB
- if (indexed)
- {
- if (palette_entries > pix)
- {
- *dest = palette[pix];
- }
- else
- {
- *dest = rgb_t::transparent();
- osd_printf_verbose(
- "ICO file DIB image data at %lu has out-of-range color %lu at (%ld, %ld) with %lu palette entries\n",
- (unsigned long)dir.offset,
- (unsigned long)pix,
- (long)x,
- (long)y,
- (unsigned long)palette_entries);
- }
- }
- else
- {
- uint8_t r(dib_splat_sample((pix >> red_shift) & ((uint32_t(1) << red_bits) - 1), red_bits));
- uint8_t g(dib_splat_sample((pix >> green_shift) & ((uint32_t(1) << green_bits) - 1), green_bits));
- uint8_t b(dib_splat_sample((pix >> blue_shift) & ((uint32_t(1) << blue_bits) - 1), blue_bits));
- uint8_t a(dib_splat_sample((pix >> alpha_shift) & ((uint32_t(1) << alpha_bits) - 1), alpha_bits));
- *dest = rgb_t(alpha_bits ? a : 255, r, g, b);
- }
- }
- }
-
- // process the AND mask if present
- if (have_and_mask)
- {
- for (int32_t i = 0, y = top_down ? 0 : (header.info.height - 1); header.info.height > i; ++i, y += y_inc)
- {
- if (fp.read(row_data.get(), mask_row_bytes) != mask_row_bytes)
- {
- LOG("Error reading DIB mask row %d data from ICO image data at %u\n", i, dir.offset);
- return false;
- }
- uint8_t *src(row_data.get());
- uint32_t *dest(&bitmap.pix(y));
- unsigned shift(0U);
- for (int32_t x = 0; header.info.width > x; ++x, ++dest)
- {
- assert(8U > shift);
- rgb_t pix(*dest);
- *dest = pix.set_a(BIT(*src, 7U - shift) ? 0U : pix.a());
- if (8U <= ++shift)
- {
- shift = 0U;
- ++src;
- }
- }
- }
- }
-
- // we're done!
- return true;
}