summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/rendutil.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/rendutil.cpp')
-rw-r--r--src/emu/rendutil.cpp673
1 files changed, 404 insertions, 269 deletions
diff --git a/src/emu/rendutil.cpp b/src/emu/rendutil.cpp
index ba8e177f93e..5d09061ab70 100644
--- a/src/emu/rendutil.cpp
+++ b/src/emu/rendutil.cpp
@@ -2,27 +2,144 @@
// copyright-holders:Aaron Giles
/***************************************************************************
- rendutil.c
+ rendutil.cpp
Core rendering utilities.
***************************************************************************/
#include "emu.h"
-#include "render.h"
#include "rendutil.h"
+
+#include "msdib.h"
#include "png.h"
#include "jpeglib.h"
+#include "jerror.h"
+
+#include <csetjmp>
+#include <cstdlib>
+#include <tuple>
+
+
+namespace {
+
+struct jpeg_corefile_source : public jpeg_source_mgr
+{
+ static void source(j_decompress_ptr cinfo, util::random_read &file) noexcept;
+
+private:
+ static constexpr unsigned INPUT_BUF_SIZE = 4096;
+
+ static void do_init(j_decompress_ptr cinfo) noexcept
+ {
+ jpeg_corefile_source &src = *static_cast<jpeg_corefile_source *>(cinfo->src);
+ src.start_of_file = true;
+ }
+
+ static boolean do_fill(j_decompress_ptr cinfo) noexcept
+ {
+ jpeg_corefile_source &src = *static_cast<jpeg_corefile_source *>(cinfo->src);
+
+ auto [err, nbytes] = read(*src.infile, src.buffer, INPUT_BUF_SIZE); // TODO: check error return
+
+ if (0 >= nbytes)
+ {
+ if (src.start_of_file)
+ ERREXIT(cinfo, JERR_INPUT_EMPTY);
+ WARNMS(cinfo, JWRN_JPEG_EOF);
+ src.buffer[0] = JOCTET(0xff);
+ src.buffer[1] = JOCTET(JPEG_EOI);
+ nbytes = 2;
+ }
+
+ src.next_input_byte = src.buffer;
+ src.bytes_in_buffer = nbytes;
+ src.start_of_file = false;
+
+ return TRUE;
+ }
+
+ static void do_skip(j_decompress_ptr cinfo, long num_bytes) noexcept
+ {
+ jpeg_corefile_source &src = *static_cast<jpeg_corefile_source *>(cinfo->src);
+
+ if (0 < num_bytes)
+ {
+ while (long(src.bytes_in_buffer) < num_bytes)
+ {
+ num_bytes -= long(src.bytes_in_buffer);
+ std::ignore = (*src.fill_input_buffer)(cinfo);
+ }
+ src.next_input_byte += size_t(num_bytes);
+ src.bytes_in_buffer -= size_t(num_bytes);
+ }
+ }
+
+ static void do_term(j_decompress_ptr cinfo) noexcept
+ {
+ }
+
+ util::random_read *infile;
+ JOCTET *buffer;
+ bool start_of_file;
+};
+
+void jpeg_corefile_source::source(j_decompress_ptr cinfo, util::random_read &file) noexcept
+{
+ jpeg_corefile_source *src;
+ if (!cinfo->src)
+ {
+ src = reinterpret_cast<jpeg_corefile_source *>(
+ (*cinfo->mem->alloc_small)(
+ reinterpret_cast<j_common_ptr>(cinfo),
+ JPOOL_PERMANENT,
+ sizeof(jpeg_corefile_source)));
+ cinfo->src = src;
+ src->buffer = reinterpret_cast<JOCTET *>(
+ (*cinfo->mem->alloc_small)(
+ reinterpret_cast<j_common_ptr>(cinfo),
+ JPOOL_PERMANENT,
+ INPUT_BUF_SIZE * sizeof(JOCTET)));
+ }
+ else
+ {
+ src = static_cast<jpeg_corefile_source *>(cinfo->src);
+ }
+
+ src->init_source = &jpeg_corefile_source::do_init;
+ src->fill_input_buffer = &jpeg_corefile_source::do_fill;
+ src->skip_input_data = &jpeg_corefile_source::do_skip;
+ src->resync_to_restart = jpeg_resync_to_restart;
+ src->term_source = &jpeg_corefile_source::do_term;
+ src->infile = &file;
+ src->bytes_in_buffer = 0;
+ src->next_input_byte = nullptr;
+}
+
+
+struct jpeg_setjmp_error_mgr : public jpeg_error_mgr
+{
+ jpeg_setjmp_error_mgr() noexcept
+ {
+ jpeg_std_error(this);
+ error_exit = [] (j_common_ptr cinfo) { std::longjmp(static_cast<jpeg_setjmp_error_mgr *>(cinfo->err)->m_jump_buffer, 1); };
+ }
+
+ std::jmp_buf m_jump_buffer;
+};
+
+} // anonymous namespace
+
/***************************************************************************
FUNCTION PROTOTYPES
***************************************************************************/
/* utilities */
-static void resample_argb_bitmap_average(u32 *dest, u32 drowpixels, u32 dwidth, u32 dheight, const u32 *source, u32 srowpixels, u32 swidth, u32 sheight, const render_color &color, u32 dx, u32 dy);
-static void resample_argb_bitmap_bilinear(u32 *dest, u32 drowpixels, u32 dwidth, u32 dheight, const u32 *source, u32 srowpixels, u32 swidth, u32 sheight, const render_color &color, u32 dx, u32 dy);
-static bool copy_png_alpha_to_bitmap(bitmap_argb32 &bitmap, const png_info &png);
+static void resample_argb_bitmap_average(u32 *dest, u32 drowpixels, u32 dwidth, u32 dheight, const u32 *source, u32 srowpixels, u32 swidth, u32 sheight, const render_color &color, u32 dx, u32 dy) noexcept;
+static void resample_argb_bitmap_bilinear(u32 *dest, u32 drowpixels, u32 dwidth, u32 dheight, const u32 *source, u32 srowpixels, u32 swidth, u32 sheight, const render_color &color, u32 dx, u32 dy) noexcept;
+static bool copy_png_alpha_to_bitmap(bitmap_argb32 &bitmap, const util::png_info &png) noexcept;
@@ -35,15 +152,15 @@ static bool copy_png_alpha_to_bitmap(bitmap_argb32 &bitmap, const png_info &png)
quality resampling of a texture
-------------------------------------------------*/
-void render_resample_argb_bitmap_hq(bitmap_argb32 &dest, bitmap_argb32 &source, const render_color &color, bool force)
+void render_resample_argb_bitmap_hq(bitmap_argb32 &dest, bitmap_argb32 &source, const render_color &color, bool force) noexcept
{
if (dest.width() == 0 || dest.height() == 0)
return;
- /* adjust the source base */
- const u32 *sbase = &source.pix32(0);
+ // adjust the source base
+ const u32 *sbase = &source.pix(0);
- /* determine the steppings */
+ // determine the steppings
u32 swidth = source.width();
u32 sheight = source.height();
u32 dwidth = dest.width();
@@ -51,7 +168,7 @@ void render_resample_argb_bitmap_hq(bitmap_argb32 &dest, bitmap_argb32 &source,
u32 dx = (swidth << 12) / dwidth;
u32 dy = (sheight << 12) / dheight;
- /* if the source is higher res than the target, use full averaging */
+ // if the source is higher res than the target, use full averaging
if (dx > 0x1000 || dy > 0x1000 || force)
resample_argb_bitmap_average(&dest.pix(0), dest.rowpixels(), dwidth, dheight, sbase, source.rowpixels(), swidth, sheight, color, dx, dy);
else
@@ -65,62 +182,58 @@ void render_resample_argb_bitmap_hq(bitmap_argb32 &dest, bitmap_argb32 &source,
all contributing pixels
-------------------------------------------------*/
-static void resample_argb_bitmap_average(u32 *dest, u32 drowpixels, u32 dwidth, u32 dheight, const u32 *source, u32 srowpixels, u32 swidth, u32 sheight, const render_color &color, u32 dx, u32 dy)
+static void resample_argb_bitmap_average(u32 *dest, u32 drowpixels, u32 dwidth, u32 dheight, const u32 *source, u32 srowpixels, u32 swidth, u32 sheight, const render_color &color, u32 dx, u32 dy) noexcept
{
- u64 sumscale = u64(dx) * u64(dy);
- u32 r, g, b, a;
- u32 x, y;
-
- /* precompute premultiplied R/G/B/A factors */
- r = color.r * color.a * 256.0f;
- g = color.g * color.a * 256.0f;
- b = color.b * color.a * 256.0f;
- a = color.a * 256.0f;
-
- /* loop over the target vertically */
- for (y = 0; y < dheight; y++)
+ u64 const sumscale = u64(dx) * u64(dy);
+
+ // precompute premultiplied R/G/B/A factors
+ u32 const r = color.r * color.a * 256.0f;
+ u32 const g = color.g * color.a * 256.0f;
+ u32 const b = color.b * color.a * 256.0f;
+ u32 const a = color.a * 256.0f;
+
+ // loop over the target vertically
+ for (u32 y = 0; y < dheight; y++)
{
- u32 starty = y * dy;
+ u32 const starty = y * dy;
- /* loop over the target horizontally */
- for (x = 0; x < dwidth; x++)
+ // loop over the target horizontally
+ for (u32 x = 0; x < dwidth; x++)
{
u64 sumr = 0, sumg = 0, sumb = 0, suma = 0;
- u32 startx = x * dx;
- u32 xchunk, ychunk;
- u32 curx, cury;
+ u32 const startx = x * dx;
u32 yremaining = dy;
+ u32 ychunk;
- /* accumulate all source pixels that contribute to this pixel */
- for (cury = starty; yremaining; cury += ychunk)
+ // accumulate all source pixels that contribute to this pixel
+ for (u32 cury = starty; yremaining; cury += ychunk)
{
u32 xremaining = dx;
+ u32 xchunk;
- /* determine the Y contribution, clamping to the amount remaining */
+ // determine the Y contribution, clamping to the amount remaining
ychunk = 0x1000 - (cury & 0xfff);
if (ychunk > yremaining)
ychunk = yremaining;
yremaining -= ychunk;
- /* loop over all source pixels in the X direction */
- for (curx = startx; xremaining; curx += xchunk)
+ // loop over all source pixels in the X direction
+ for (u32 curx = startx; xremaining; curx += xchunk)
{
- u32 factor;
-
- /* determine the X contribution, clamping to the amount remaining */
+ // determine the X contribution, clamping to the amount remaining
xchunk = 0x1000 - (curx & 0xfff);
if (xchunk > xremaining)
xchunk = xremaining;
xremaining -= xchunk;
- /* total contribution = x * y */
- factor = xchunk * ychunk;
+ // total contribution = x * y
+ u32 const factor = xchunk * ychunk;
- /* fetch the source pixel */
- rgb_t pix = source[(cury >> 12) * srowpixels + (curx >> 12)];
+ // fetch the source pixel
+ rgb_t const pix = source[(cury >> 12) * srowpixels + (curx >> 12)];
- /* accumulate the RGBA values */
+ // accumulate the RGBA values
sumr += factor * pix.r();
sumg += factor * pix.g();
sumb += factor * pix.b();
@@ -128,23 +241,23 @@ static void resample_argb_bitmap_average(u32 *dest, u32 drowpixels, u32 dwidth,
}
}
- /* apply scaling */
+ // apply scaling
suma = (suma / sumscale) * a / 256;
sumr = (sumr / sumscale) * r / 256;
sumg = (sumg / sumscale) * g / 256;
sumb = (sumb / sumscale) * b / 256;
- /* if we're translucent, add in the destination pixel contribution */
+ // if we're translucent, add in the destination pixel contribution
if (a < 256)
{
- rgb_t dpix = dest[y * drowpixels + x];
+ rgb_t const dpix = dest[y * drowpixels + x];
suma += dpix.a() * (256 - a);
sumr += dpix.r() * (256 - a);
sumg += dpix.g() * (256 - a);
sumb += dpix.b() * (256 - a);
}
- /* store the target pixel, dividing the RGBA values by the overall scale factor */
+ // store the target pixel, dividing the RGBA values by the overall scale factor
dest[y * drowpixels + x] = rgb_t(suma, sumr, sumg, sumb);
}
}
@@ -156,44 +269,37 @@ static void resample_argb_bitmap_average(u32 *dest, u32 drowpixels, u32 dwidth,
sampling via a bilinear filter
-------------------------------------------------*/
-static void resample_argb_bitmap_bilinear(u32 *dest, u32 drowpixels, u32 dwidth, u32 dheight, const u32 *source, u32 srowpixels, u32 swidth, u32 sheight, const render_color &color, u32 dx, u32 dy)
+static void resample_argb_bitmap_bilinear(u32 *dest, u32 drowpixels, u32 dwidth, u32 dheight, const u32 *source, u32 srowpixels, u32 swidth, u32 sheight, const render_color &color, u32 dx, u32 dy) noexcept
{
- u32 maxx = swidth << 12, maxy = sheight << 12;
- u32 r, g, b, a;
- u32 x, y;
-
- /* precompute premultiplied R/G/B/A factors */
- r = color.r * color.a * 256.0f;
- g = color.g * color.a * 256.0f;
- b = color.b * color.a * 256.0f;
- a = color.a * 256.0f;
-
- /* loop over the target vertically */
- for (y = 0; y < dheight; y++)
+ u32 const maxx = swidth << 12, maxy = sheight << 12;
+
+ // precompute premultiplied R/G/B/A factors
+ u32 const r = color.r * color.a * 256.0f;
+ u32 const g = color.g * color.a * 256.0f;
+ u32 const b = color.b * color.a * 256.0f;
+ u32 const a = color.a * 256.0f;
+
+ // loop over the target vertically
+ for (u32 y = 0; y < dheight; y++)
{
- u32 starty = y * dy;
+ u32 const starty = y * dy;
- /* loop over the target horizontally */
- for (x = 0; x < dwidth; x++)
+ // loop over the target horizontally
+ for (u32 x = 0; x < dwidth; x++)
{
- u32 startx = x * dx;
- rgb_t pix0, pix1, pix2, pix3;
- u32 sumr, sumg, sumb, suma;
- u32 nextx, nexty;
- u32 curx, cury;
- u32 factor;
+ u32 const startx = x * dx;
- /* adjust start to the center; note that this math will tend to produce */
- /* negative results on the first pixel, which is why we clamp below */
- curx = startx + dx / 2 - 0x800;
- cury = starty + dy / 2 - 0x800;
+ // adjust start to the center; note that this math will tend to produce
+ // negative results on the first pixel, which is why we clamp below
+ u32 curx = startx + dx / 2 - 0x800;
+ u32 cury = starty + dy / 2 - 0x800;
- /* compute the neighboring pixel */
- nextx = curx + 0x1000;
- nexty = cury + 0x1000;
+ // compute the neighboring pixel
+ u32 const nextx = curx + 0x1000;
+ u32 const nexty = cury + 0x1000;
- /* fetch the four relevant pixels */
- pix0 = pix1 = pix2 = pix3 = 0;
+ // fetch the four relevant pixels
+ rgb_t pix0 = 0, pix1 = 0, pix2 = 0, pix3 = 0;
if (s32(cury) >= 0 && cury < maxy && s32(curx) >= 0 && curx < maxx)
pix0 = source[(cury >> 12) * srowpixels + (curx >> 12)];
if (s32(cury) >= 0 && cury < maxy && s32(nextx) >= 0 && nextx < maxx)
@@ -203,173 +309,176 @@ static void resample_argb_bitmap_bilinear(u32 *dest, u32 drowpixels, u32 dwidth,
if (s32(nexty) >= 0 && nexty < maxy && s32(nextx) >= 0 && nextx < maxx)
pix3 = source[(nexty >> 12) * srowpixels + (nextx >> 12)];
- /* compute the x/y scaling factors */
+ // compute the x/y scaling factors
curx &= 0xfff;
cury &= 0xfff;
- /* contributions from pixel 0 (top,left) */
+ u32 factor;
+ u32 sumr, sumg, sumb, suma;
+
+ // contributions from pixel 0 (top,left)
factor = (0x1000 - curx) * (0x1000 - cury);
sumr = factor * pix0.r();
sumg = factor * pix0.g();
sumb = factor * pix0.b();
suma = factor * pix0.a();
- /* contributions from pixel 1 (top,right) */
+ // contributions from pixel 1 (top,right)
factor = curx * (0x1000 - cury);
sumr += factor * pix1.r();
sumg += factor * pix1.g();
sumb += factor * pix1.b();
suma += factor * pix1.a();
- /* contributions from pixel 2 (bottom,left) */
+ // contributions from pixel 2 (bottom,left)
factor = (0x1000 - curx) * cury;
sumr += factor * pix2.r();
sumg += factor * pix2.g();
sumb += factor * pix2.b();
suma += factor * pix2.a();
- /* contributions from pixel 3 (bottom,right) */
+ // contributions from pixel 3 (bottom,right)
factor = curx * cury;
sumr += factor * pix3.r();
sumg += factor * pix3.g();
sumb += factor * pix3.b();
suma += factor * pix3.a();
- /* apply scaling */
+ // apply scaling
suma = (suma >> 24) * a / 256;
sumr = (sumr >> 24) * r / 256;
sumg = (sumg >> 24) * g / 256;
sumb = (sumb >> 24) * b / 256;
- /* if we're translucent, add in the destination pixel contribution */
+ // if we're translucent, add in the destination pixel contribution
if (a < 256)
{
- rgb_t dpix = dest[y * drowpixels + x];
+ rgb_t const dpix = dest[y * drowpixels + x];
suma += dpix.a() * (256 - a);
sumr += dpix.r() * (256 - a);
sumg += dpix.g() * (256 - a);
sumb += dpix.b() * (256 - a);
}
- /* store the target pixel, dividing the RGBA values by the overall scale factor */
+ // store the target pixel, dividing the RGBA values by the overall scale factor
dest[y * drowpixels + x] = rgb_t(suma, sumr, sumg, sumb);
}
}
}
-/*-------------------------------------------------
- render_clip_line - clip a line to a rectangle
--------------------------------------------------*/
+//-------------------------------------------------
+// render_clip_line - clip a line to a rectangle
+//-------------------------------------------------
-bool render_clip_line(render_bounds *bounds, const render_bounds *clip)
+bool render_clip_line(render_bounds &bounds, const render_bounds &clip)
{
- /* loop until we get a final result */
- while (1)
+ // loop until we get a final result
+ while (true)
{
u8 code0 = 0, code1 = 0;
u8 thiscode;
float x, y;
- /* compute Cohen Sutherland bits for first coordinate */
- if (bounds->y0 > clip->y1)
+ // compute Cohen Sutherland bits for first coordinate
+ if (bounds.y0 > clip.y1)
code0 |= 1;
- if (bounds->y0 < clip->y0)
+ if (bounds.y0 < clip.y0)
code0 |= 2;
- if (bounds->x0 > clip->x1)
+ if (bounds.x0 > clip.x1)
code0 |= 4;
- if (bounds->x0 < clip->x0)
+ if (bounds.x0 < clip.x0)
code0 |= 8;
- /* compute Cohen Sutherland bits for second coordinate */
- if (bounds->y1 > clip->y1)
+ // compute Cohen Sutherland bits for second coordinate
+ if (bounds.y1 > clip.y1)
code1 |= 1;
- if (bounds->y1 < clip->y0)
+ if (bounds.y1 < clip.y0)
code1 |= 2;
- if (bounds->x1 > clip->x1)
+ if (bounds.x1 > clip.x1)
code1 |= 4;
- if (bounds->x1 < clip->x0)
+ if (bounds.x1 < clip.x0)
code1 |= 8;
- /* trivial accept: just return false */
+ // trivial accept: just return false
if ((code0 | code1) == 0)
return false;
- /* trivial reject: just return true */
+ // trivial reject: just return true
if ((code0 & code1) != 0)
return true;
- /* fix one of the OOB cases */
+ // fix one of the OOB cases
thiscode = code0 ? code0 : code1;
- /* off the bottom */
+ // off the bottom
if (thiscode & 1)
{
- x = bounds->x0 + (bounds->x1 - bounds->x0) * (clip->y1 - bounds->y0) / (bounds->y1 - bounds->y0);
- y = clip->y1;
+ x = bounds.x0 + (bounds.x1 - bounds.x0) * (clip.y1 - bounds.y0) / (bounds.y1 - bounds.y0);
+ y = clip.y1;
}
- /* off the top */
+ // off the top
else if (thiscode & 2)
{
- x = bounds->x0 + (bounds->x1 - bounds->x0) * (clip->y0 - bounds->y0) / (bounds->y1 - bounds->y0);
- y = clip->y0;
+ x = bounds.x0 + (bounds.x1 - bounds.x0) * (clip.y0 - bounds.y0) / (bounds.y1 - bounds.y0);
+ y = clip.y0;
}
- /* off the right */
+ // off the right
else if (thiscode & 4)
{
- y = bounds->y0 + (bounds->y1 - bounds->y0) * (clip->x1 - bounds->x0) / (bounds->x1 - bounds->x0);
- x = clip->x1;
+ y = bounds.y0 + (bounds.y1 - bounds.y0) * (clip.x1 - bounds.x0) / (bounds.x1 - bounds.x0);
+ x = clip.x1;
}
- /* off the left */
+ // off the left
else
{
- y = bounds->y0 + (bounds->y1 - bounds->y0) * (clip->x0 - bounds->x0) / (bounds->x1 - bounds->x0);
- x = clip->x0;
+ y = bounds.y0 + (bounds.y1 - bounds.y0) * (clip.x0 - bounds.x0) / (bounds.x1 - bounds.x0);
+ x = clip.x0;
}
- /* fix the appropriate coordinate */
+ // fix the appropriate coordinate
if (thiscode == code0)
{
- bounds->x0 = x;
- bounds->y0 = y;
+ bounds.x0 = x;
+ bounds.y0 = y;
}
else
{
- bounds->x1 = x;
- bounds->y1 = y;
+ bounds.x1 = x;
+ bounds.y1 = y;
}
}
}
-/*-------------------------------------------------
- render_clip_quad - clip a quad to a rectangle
--------------------------------------------------*/
+//-------------------------------------------------
+// render_clip_quad - clip a quad to a rectangle
+//-------------------------------------------------
-bool render_clip_quad(render_bounds *bounds, const render_bounds *clip, render_quad_texuv *texcoords)
+bool render_clip_quad(render_bounds &bounds, const render_bounds &clip, render_quad_texuv *texcoords)
{
- /* ensure our assumptions about the bounds are correct */
- assert(bounds->x0 <= bounds->x1);
- assert(bounds->y0 <= bounds->y1);
+ // ensure our assumptions about the bounds are correct
+ assert(bounds.x0 <= bounds.x1);
+ assert(bounds.y0 <= bounds.y1);
- /* trivial reject */
- if (bounds->y1 < clip->y0)
+ // trivial reject
+ if (bounds.y1 < clip.y0)
return true;
- if (bounds->y0 > clip->y1)
+ if (bounds.y0 > clip.y1)
return true;
- if (bounds->x1 < clip->x0)
+ if (bounds.x1 < clip.x0)
return true;
- if (bounds->x0 > clip->x1)
+ if (bounds.x0 > clip.x1)
return true;
- /* clip top (x0,y0)-(x1,y1) */
- if (bounds->y0 < clip->y0)
+ // clip top (x0,y0)-(x1,y1)
+ if (bounds.y0 < clip.y0)
{
- float frac = (clip->y0 - bounds->y0) / (bounds->y1 - bounds->y0);
- bounds->y0 = clip->y0;
+ float frac = (clip.y0 - bounds.y0) / (bounds.y1 - bounds.y0);
+ bounds.y0 = clip.y0;
if (texcoords != nullptr)
{
texcoords->tl.u += (texcoords->bl.u - texcoords->tl.u) * frac;
@@ -379,11 +488,11 @@ bool render_clip_quad(render_bounds *bounds, const render_bounds *clip, render_q
}
}
- /* clip bottom (x3,y3)-(x2,y2) */
- if (bounds->y1 > clip->y1)
+ // clip bottom (x3,y3)-(x2,y2)
+ if (bounds.y1 > clip.y1)
{
- float frac = (bounds->y1 - clip->y1) / (bounds->y1 - bounds->y0);
- bounds->y1 = clip->y1;
+ float frac = (bounds.y1 - clip.y1) / (bounds.y1 - bounds.y0);
+ bounds.y1 = clip.y1;
if (texcoords != nullptr)
{
texcoords->bl.u -= (texcoords->bl.u - texcoords->tl.u) * frac;
@@ -393,11 +502,11 @@ bool render_clip_quad(render_bounds *bounds, const render_bounds *clip, render_q
}
}
- /* clip left (x0,y0)-(x3,y3) */
- if (bounds->x0 < clip->x0)
+ // clip left (x0,y0)-(x3,y3)
+ if (bounds.x0 < clip.x0)
{
- float frac = (clip->x0 - bounds->x0) / (bounds->x1 - bounds->x0);
- bounds->x0 = clip->x0;
+ float frac = (clip.x0 - bounds.x0) / (bounds.x1 - bounds.x0);
+ bounds.x0 = clip.x0;
if (texcoords != nullptr)
{
texcoords->tl.u += (texcoords->tr.u - texcoords->tl.u) * frac;
@@ -407,11 +516,11 @@ bool render_clip_quad(render_bounds *bounds, const render_bounds *clip, render_q
}
}
- /* clip right (x1,y1)-(x2,y2) */
- if (bounds->x1 > clip->x1)
+ // clip right (x1,y1)-(x2,y2)
+ if (bounds.x1 > clip.x1)
{
- float frac = (bounds->x1 - clip->x1) / (bounds->x1 - bounds->x0);
- bounds->x1 = clip->x1;
+ float frac = (bounds.x1 - clip.x1) / (bounds.x1 - bounds.x0);
+ bounds.x1 = clip.x1;
if (texcoords != nullptr)
{
texcoords->tr.u -= (texcoords->tr.u - texcoords->tl.u) * frac;
@@ -424,14 +533,14 @@ bool render_clip_quad(render_bounds *bounds, const render_bounds *clip, render_q
}
-/*-------------------------------------------------
- render_line_to_quad - convert a line and a
- width to four points
--------------------------------------------------*/
+//-------------------------------------------------
+// render_line_to_quad - convert a line and a
+// width to four points
+//-----------------------------------------------
-void render_line_to_quad(const render_bounds *bounds, float width, float length_extension, render_bounds *bounds0, render_bounds *bounds1)
+std::pair<render_bounds, render_bounds> render_line_to_quad(const render_bounds &bounds, float width, float length_extension)
{
- render_bounds modbounds = *bounds;
+ render_bounds modbounds = bounds;
/*
High-level logic -- due to math optimizations, this info is lost below.
@@ -478,18 +587,18 @@ void render_line_to_quad(const render_bounds *bounds, float width, float length_
D.y = p1.y - 0.5 * w * u.x
*/
- /* we only care about the half-width */
+ // we only care about the half-width
float half_width = width * 0.5f;
- /* compute a vector from point 0 to point 1 */
+ // compute a vector from point 0 to point 1
float unitx = modbounds.x1 - modbounds.x0;
float unity = modbounds.y1 - modbounds.y0;
- /* points just use a +1/+1 unit vector; this gives a nice diamond pattern */
+ // points just use a +1/+1 unit vector; this gives a nice diamond pattern
if (unitx == 0 && unity == 0)
{
- /* length of a unit vector (1,1) */
- float unit_length = 0.70710678f;
+ // length of a unit vector (1,1)
+ constexpr float unit_length = 0.70710678f;
unitx = unity = unit_length * half_width;
modbounds.x0 -= unitx;
@@ -498,12 +607,12 @@ void render_line_to_quad(const render_bounds *bounds, float width, float length_
modbounds.y1 += unity;
}
- /* lines need to be divided by their length */
+ // lines need to be divided by their length
else
{
float length = sqrtf(unitx * unitx + unity * unity);
- /* extend line length */
+ // extend line length
if (length_extension > 0.0f)
{
float half_length_extension = length_extension *0.5f;
@@ -517,106 +626,127 @@ void render_line_to_quad(const render_bounds *bounds, float width, float length_
modbounds.y1 += directiony * half_length_extension;
}
- /* prescale unitx and unity by the half-width */
+ // prescale unitx and unity by the half-width
float invlength = half_width / length;
unitx *= invlength;
unity *= invlength;
}
- /* rotate the unit vector by 90 degrees and add to point 0 */
- bounds0->x0 = modbounds.x0 - unity;
- bounds0->y0 = modbounds.y0 + unitx;
+ // rotate the unit vector by 90 and -90 degrees and add to points 0 and 1
+ return std::make_pair(
+ render_bounds{ modbounds.x0 - unity, modbounds.y0 + unitx, modbounds.x0 + unity, modbounds.y0 - unitx },
+ render_bounds{ modbounds.x1 - unity, modbounds.y1 + unitx, modbounds.x1 + unity, modbounds.y1 - unitx });
+}
- /* rotate the unit vector by -90 degrees and add to point 0 */
- bounds0->x1 = modbounds.x0 + unity;
- bounds0->y1 = modbounds.y0 - unitx;
- /* rotate the unit vector by 90 degrees and add to point 1 */
- bounds1->x0 = modbounds.x1 - unity;
- bounds1->y0 = modbounds.y1 + unitx;
+/*-------------------------------------------------
+ render_load_msdib - load a Microsoft DIB file
+ into a bitmap
+-------------------------------------------------*/
+
+void render_load_msdib(bitmap_argb32 &bitmap, util::random_read &file) noexcept
+{
+ // deallocate previous bitmap
+ bitmap.reset();
- /* rotate the unit vector by -90 degrees and add to point 1 */
- bounds1->x1 = modbounds.x1 + unity;
- bounds1->y1 = modbounds.y1 - unitx;
+ // read the DIB data
+ util::msdib_error const result = util::msdib_read_bitmap(file, bitmap);
+ if (result != util::msdib_error::NONE)
+ {
+ osd_printf_error("Error reading Microsoft DIB file\n");
+ bitmap.reset();
+ }
}
/*-------------------------------------------------
- render_load_jpeg - load a JPG file into a
+ render_load_jpeg - load a JPEG file into a
bitmap
-------------------------------------------------*/
-void render_load_jpeg(bitmap_argb32 &bitmap, emu_file &file, const char *dirname, const char *filename)
+void render_load_jpeg(bitmap_argb32 &bitmap, util::random_read &file) noexcept
{
// deallocate previous bitmap
bitmap.reset();
- // define file's full name
- std::string fname;
-
- if (dirname == nullptr)
- fname = filename;
- else
- fname.assign(dirname).append(PATH_SEPARATOR).append(filename);
-
- if (file.open(fname.c_str()) != osd_file::error::NONE)
- return;
-
- // define standard JPEG structures
+ // set up context for error handling
jpeg_decompress_struct cinfo;
- jpeg_error_mgr jerr;
- cinfo.err = jpeg_std_error(&jerr);
- jpeg_create_decompress(&cinfo);
-
- // allocates a buffer for the image
- u32 jpg_size = file.size();
- std::unique_ptr<unsigned char[]> jpg_buffer = std::make_unique<unsigned char[]>(jpg_size);
+ jpeg_setjmp_error_mgr jerr;
+ cinfo.err = &jerr;
+ JSAMPARRAY buffer = nullptr;
+ int w, h, s, row_stride, j, i;
+ if (setjmp(jerr.m_jump_buffer)) // setjmp must be used in control expression
+ {
+ char msg[1024];
+ (cinfo.err->format_message)(reinterpret_cast<j_common_ptr>(&cinfo), msg);
+ osd_printf_error("JPEG error reading data from file: %s\n", msg);
+ bitmap.reset();
+ goto cleanup; // use goto to ensure longjmp can't cross an initialisation
+ }
- // read data from the file and set them in the buffer
- file.read(jpg_buffer.get(), jpg_size);
- jpeg_mem_src(&cinfo, jpg_buffer.get(), jpg_size);
+ // create a JPEG source for the file
+ jpeg_create_decompress(&cinfo);
+ cinfo.mem->max_memory_to_use = 128 * 1024 * 1024;
+ jpeg_corefile_source::source(&cinfo, file);
// read JPEG header and start decompression
jpeg_read_header(&cinfo, TRUE);
jpeg_start_decompress(&cinfo);
// allocates the destination bitmap
- int w = cinfo.output_width;
- int h = cinfo.output_height;
- int s = cinfo.output_components;
+ w = cinfo.output_width;
+ h = cinfo.output_height;
+ s = cinfo.output_components;
bitmap.allocate(w, h);
// allocates a buffer to receive the information and copy them into the bitmap
- int row_stride = cinfo.output_width * cinfo.output_components;
- JSAMPARRAY buffer = (JSAMPARRAY)malloc(sizeof(JSAMPROW));
- buffer[0] = (JSAMPROW)malloc(sizeof(JSAMPLE) * row_stride);
+ row_stride = cinfo.output_width * cinfo.output_components;
+ buffer = reinterpret_cast<JSAMPARRAY>(std::malloc(sizeof(JSAMPROW)));
+ if (buffer)
+ buffer[0] = reinterpret_cast<JSAMPROW>(std::malloc(sizeof(JSAMPLE) * row_stride));
- while ( cinfo.output_scanline < cinfo.output_height )
+ if (bitmap.valid() && buffer && buffer[0])
{
- int j = cinfo.output_scanline;
- jpeg_read_scanlines(&cinfo, buffer, 1);
+ while (cinfo.output_scanline < cinfo.output_height)
+ {
+ j = cinfo.output_scanline;
+ jpeg_read_scanlines(&cinfo, buffer, 1);
- if (s == 1)
- for (int i = 0; i < w; ++i)
- bitmap.pix32(j, i) = rgb_t(0xFF, buffer[0][i], buffer[0][i], buffer[0][i]);
+ if (s == 1)
+ {
+ for (i = 0; i < w; ++i)
+ bitmap.pix(j, i) = rgb_t(0xff, buffer[0][i], buffer[0][i], buffer[0][i]);
- else if (s == 3)
- for (int i = 0; i < w; ++i)
- bitmap.pix32(j, i) = rgb_t(0xFF, buffer[0][i * s], buffer[0][i * s + 1], buffer[0][i * s + 2]);
- else
- {
- osd_printf_error("Cannot read JPEG data from %s file.\n", fname.c_str());
- bitmap.reset();
- break;
+ }
+ else if (s == 3)
+ {
+ for (i = 0; i < w; ++i)
+ bitmap.pix(j, i) = rgb_t(0xff, buffer[0][i * s], buffer[0][i * s + 1], buffer[0][i * s + 2]);
+ }
+ else
+ {
+ osd_printf_error("Cannot read JPEG data from file.\n");
+ bitmap.reset();
+ break;
+ }
}
}
+ else
+ {
+ osd_printf_error("Error allocating memory for JPEG image.\n");
+ bitmap.reset();
+ }
- // finish decompression and frees the memory
+ // finish decompression and free the memory
jpeg_finish_decompress(&cinfo);
+cleanup:
jpeg_destroy_decompress(&cinfo);
- file.close();
- free(buffer[0]);
- free(buffer);
+ if (buffer)
+ {
+ if (buffer[0])
+ std::free(buffer[0]);
+ std::free(buffer);
+ }
}
@@ -625,36 +755,25 @@ void render_load_jpeg(bitmap_argb32 &bitmap, emu_file &file, const char *dirname
bitmap
-------------------------------------------------*/
-bool render_load_png(bitmap_argb32 &bitmap, emu_file &file, const char *dirname, const char *filename, bool load_as_alpha_to_existing)
+bool render_load_png(bitmap_argb32 &bitmap, util::random_read &file, bool load_as_alpha_to_existing) noexcept
{
// deallocate if we're not overlaying alpha
if (!load_as_alpha_to_existing)
bitmap.reset();
- // open the file
- std::string fname;
- if (dirname)
- fname.assign(dirname).append(PATH_SEPARATOR).append(filename);
- else
- fname.assign(filename);
- osd_file::error const filerr = file.open(fname.c_str());
- if (filerr != osd_file::error::NONE)
- return false;
-
// read the PNG data
- png_info png;
- png_error const result = png.read_file(file);
- file.close();
- if (result != PNGERR_NONE)
+ util::png_info png;
+ std::error_condition const result = png.read_file(file);
+ if (result)
{
- osd_printf_error("%s: Error reading PNG file\n", filename);
+ osd_printf_error("Error reading PNG file\n");
return false;
}
// if less than 8 bits, upsample
- if (PNGERR_NONE != png.expand_buffer_8bit())
+ if (png.expand_buffer_8bit())
{
- osd_printf_error("%s: Error upsampling PNG bitmap\n", filename);
+ osd_printf_error("Error upsampling PNG bitmap\n");
return false;
}
@@ -662,9 +781,9 @@ bool render_load_png(bitmap_argb32 &bitmap, emu_file &file, const char *dirname,
if (!load_as_alpha_to_existing)
{
// non-alpha case
- if (PNGERR_NONE != png.copy_to_bitmap(bitmap, hasalpha))
+ if (png.copy_to_bitmap(bitmap, hasalpha))
{
- osd_printf_error("%s: Error copying PNG bitmap to MAME bitmap\n", filename);
+ osd_printf_error("Error copying PNG bitmap to MAME bitmap\n");
return false;
}
}
@@ -673,7 +792,7 @@ bool render_load_png(bitmap_argb32 &bitmap, emu_file &file, const char *dirname,
// 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);
+ osd_printf_error("Unsupported bit depth %d (8 bit max)\n", png.bit_depth);
return false;
}
@@ -691,7 +810,7 @@ bool render_load_png(bitmap_argb32 &bitmap, emu_file &file, const char *dirname,
to the alpha channel of a bitmap
-------------------------------------------------*/
-static bool copy_png_alpha_to_bitmap(bitmap_argb32 &bitmap, const png_info &png)
+static bool copy_png_alpha_to_bitmap(bitmap_argb32 &bitmap, const util::png_info &png) noexcept
{
// FIXME: this function is basically copy/pasted from the PNG code in util, and should be unified with it
u8 accumalpha = 0xff;
@@ -725,7 +844,7 @@ static bool copy_png_alpha_to_bitmap(bitmap_argb32 &bitmap, const png_info &png)
{
for (u32 x = 0; width > x; ++x, ++src)
{
- bitmap_argb32::pixel_t &dest(png.interlace_method ? bitmap.pix32(y_trans(y), x_trans(x)) : bitmap.pix32(y, x));
+ bitmap_argb32::pixel_t &dest(png.interlace_method ? bitmap.pix(y_trans(y), x_trans(x)) : bitmap.pix(y, x));
rgb_t const pixel(dest);
u8 const alpha(rgb_t(png.palette[*src * 3], png.palette[*src * 3 + 1], png.palette[*src * 3 + 2]).brightness());
accumalpha &= alpha;
@@ -740,7 +859,7 @@ static bool copy_png_alpha_to_bitmap(bitmap_argb32 &bitmap, const png_info &png)
{
for (u32 x = 0; width > x; ++x, ++src)
{
- bitmap_argb32::pixel_t &dest(png.interlace_method ? bitmap.pix32(y_trans(y), x_trans(x)) : bitmap.pix32(y, x));
+ bitmap_argb32::pixel_t &dest(png.interlace_method ? bitmap.pix(y_trans(y), x_trans(x)) : bitmap.pix(y, x));
rgb_t const pixel(dest);
accumalpha &= *src;
dest = rgb_t(*src, pixel.r(), pixel.g(), pixel.b());
@@ -754,7 +873,7 @@ static bool copy_png_alpha_to_bitmap(bitmap_argb32 &bitmap, const png_info &png)
{
for (u32 x = 0; width > x; ++x, src += 2)
{
- bitmap_argb32::pixel_t &dest(png.interlace_method ? bitmap.pix32(y_trans(y), x_trans(x)) : bitmap.pix32(y, x));
+ bitmap_argb32::pixel_t &dest(png.interlace_method ? bitmap.pix(y_trans(y), x_trans(x)) : bitmap.pix(y, x));
rgb_t const pixel(dest);
accumalpha &= *src;
dest = rgb_t(*src, pixel.r(), pixel.g(), pixel.b());
@@ -768,7 +887,7 @@ static bool copy_png_alpha_to_bitmap(bitmap_argb32 &bitmap, const png_info &png)
{
for (u32 x = 0; width > x; ++x, src += 3)
{
- bitmap_argb32::pixel_t &dest(png.interlace_method ? bitmap.pix32(y_trans(y), x_trans(x)) : bitmap.pix32(y, x));
+ bitmap_argb32::pixel_t &dest(png.interlace_method ? bitmap.pix(y_trans(y), x_trans(x)) : bitmap.pix(y, x));
rgb_t const pixel(dest);
u8 const alpha(rgb_t(src[0], src[1], src[2]).brightness());
accumalpha &= alpha;
@@ -783,7 +902,7 @@ static bool copy_png_alpha_to_bitmap(bitmap_argb32 &bitmap, const png_info &png)
{
for (u32 x = 0; width > x; ++x, src += 4)
{
- bitmap_argb32::pixel_t &dest(png.interlace_method ? bitmap.pix32(y_trans(y), x_trans(x)) : bitmap.pix32(y, x));
+ bitmap_argb32::pixel_t &dest(png.interlace_method ? bitmap.pix(y_trans(y), x_trans(x)) : bitmap.pix(y, x));
rgb_t const pixel(dest);
u8 const alpha(rgb_t(src[0], src[1], src[2]).brightness());
accumalpha &= alpha;
@@ -802,29 +921,45 @@ static bool copy_png_alpha_to_bitmap(bitmap_argb32 &bitmap, const png_info &png)
render_detect_image - detect image format
-------------------------------------------------*/
-ru_imgformat render_detect_image(emu_file &file, const char *dirname, const char *filename)
+ru_imgformat render_detect_image(util::random_read &file) noexcept
{
- // open the file
- std::string fname;
- if (dirname)
- fname.assign(dirname).append(PATH_SEPARATOR).append(filename);
- else
- fname.assign(filename);
- osd_file::error const filerr = file.open(fname.c_str());
- if (filerr != osd_file::error::NONE)
- return RENDUTIL_IMGFORMAT_ERROR;
-
// PNG: check for valid header
- png_error const result = png_info::verify_header(file);
- if (result == PNGERR_NONE)
{
- file.close();
- return RENDUTIL_IMGFORMAT_PNG;
+ std::error_condition const png = util::png_info::verify_header(file);
+ file.seek(0, SEEK_SET); // TODO: check error return
+ if (!png)
+ return RENDUTIL_IMGFORMAT_PNG;
+ }
+
+ // JPEG: attempt to read header with libjpeg
+ {
+ jpeg_decompress_struct cinfo;
+ jpeg_setjmp_error_mgr jerr;
+ cinfo.err = &jerr;
+ if (setjmp(jerr.m_jump_buffer)) // setjmp must be used in control expression
+ goto notjpeg; // use goto to ensure longjmp can't cross an initialisation
+
+ jpeg_create_decompress(&cinfo);
+ cinfo.mem->max_memory_to_use = 128 * 1024 * 1024;
+ jpeg_corefile_source::source(&cinfo, file);
+ jpeg_read_header(&cinfo, TRUE);
+ jpeg_destroy_decompress(&cinfo);
+ file.seek(0, SEEK_SET); // TODO: check error return
+ return RENDUTIL_IMGFORMAT_JPEG;
+
+ notjpeg:
+ jpeg_destroy_decompress(&cinfo);
+ file.seek(0, SEEK_SET);
}
- file.seek(0, SEEK_SET);
- // TODO: add more when needed
+ // Microsoft DIB: check for valid header
+ {
+ util::msdib_error const msdib = util::msdib_verify_header(file);
+ file.seek(0, SEEK_SET);
+ if (util::msdib_error::NONE == msdib)
+ return RENDUTIL_IMGFORMAT_MSDIB;
+ }
- file.close();
+ // TODO: add more as necessary
return RENDUTIL_IMGFORMAT_UNKNOWN;
}