summaryrefslogtreecommitdiff
path: root/src/libs/graphics
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/graphics')
-rw-r--r--src/libs/graphics/Makeinfo12
-rw-r--r--src/libs/graphics/bbox.c133
-rw-r--r--src/libs/graphics/bbox.h46
-rw-r--r--src/libs/graphics/boxint.c183
-rw-r--r--src/libs/graphics/clipline.c241
-rw-r--r--src/libs/graphics/cmap.c663
-rw-r--r--src/libs/graphics/cmap.h77
-rw-r--r--src/libs/graphics/context.c404
-rw-r--r--src/libs/graphics/context.h147
-rw-r--r--src/libs/graphics/dcqueue.c670
-rw-r--r--src/libs/graphics/dcqueue.h55
-rw-r--r--src/libs/graphics/drawable.c501
-rw-r--r--src/libs/graphics/drawable.h88
-rw-r--r--src/libs/graphics/drawcmd.h202
-rw-r--r--src/libs/graphics/filegfx.c72
-rw-r--r--src/libs/graphics/font.c334
-rw-r--r--src/libs/graphics/font.h71
-rw-r--r--src/libs/graphics/frame.c266
-rw-r--r--src/libs/graphics/gfx_common.c196
-rw-r--r--src/libs/graphics/gfx_common.h112
-rw-r--r--src/libs/graphics/gfxintrn.h32
-rw-r--r--src/libs/graphics/gfxload.c597
-rw-r--r--src/libs/graphics/intersec.c415
-rw-r--r--src/libs/graphics/loaddisp.c65
-rw-r--r--src/libs/graphics/pixmap.c170
-rw-r--r--src/libs/graphics/prim.h80
-rw-r--r--src/libs/graphics/resgfx.c54
-rw-r--r--src/libs/graphics/sdl/2xscalers.c260
-rw-r--r--src/libs/graphics/sdl/2xscalers.h30
-rw-r--r--src/libs/graphics/sdl/2xscalers_3dnow.c102
-rw-r--r--src/libs/graphics/sdl/2xscalers_mmx.c136
-rw-r--r--src/libs/graphics/sdl/2xscalers_mmx.h56
-rw-r--r--src/libs/graphics/sdl/2xscalers_sse.c100
-rw-r--r--src/libs/graphics/sdl/Makeinfo9
-rw-r--r--src/libs/graphics/sdl/biadv2x.c532
-rw-r--r--src/libs/graphics/sdl/bilinear2x.c112
-rw-r--r--src/libs/graphics/sdl/canvas.c2176
-rw-r--r--src/libs/graphics/sdl/hq2x.c2888
-rw-r--r--src/libs/graphics/sdl/nearest2x.c207
-rw-r--r--src/libs/graphics/sdl/opengl.c575
-rw-r--r--src/libs/graphics/sdl/opengl.h89
-rw-r--r--src/libs/graphics/sdl/palette.c47
-rw-r--r--src/libs/graphics/sdl/palette.h57
-rw-r--r--src/libs/graphics/sdl/png2sdl.c300
-rw-r--r--src/libs/graphics/sdl/png2sdl.h24
-rw-r--r--src/libs/graphics/sdl/primitives.c633
-rw-r--r--src/libs/graphics/sdl/primitives.h62
-rw-r--r--src/libs/graphics/sdl/pure.c474
-rw-r--r--src/libs/graphics/sdl/pure.h29
-rw-r--r--src/libs/graphics/sdl/rotozoom.c1038
-rw-r--r--src/libs/graphics/sdl/rotozoom.h96
-rw-r--r--src/libs/graphics/sdl/scaleint.h433
-rw-r--r--src/libs/graphics/sdl/scalemmx.h793
-rw-r--r--src/libs/graphics/sdl/scalers.c289
-rw-r--r--src/libs/graphics/sdl/scalers.h27
-rw-r--r--src/libs/graphics/sdl/sdl1_common.c247
-rw-r--r--src/libs/graphics/sdl/sdl2_common.c222
-rw-r--r--src/libs/graphics/sdl/sdl2_pure.c465
-rw-r--r--src/libs/graphics/sdl/sdl_common.c308
-rw-r--r--src/libs/graphics/sdl/sdl_common.h63
-rw-r--r--src/libs/graphics/sdl/sdluio.c153
-rw-r--r--src/libs/graphics/sdl/sdluio.h39
-rw-r--r--src/libs/graphics/sdl/triscan2x.c155
-rw-r--r--src/libs/graphics/tfb_draw.c493
-rw-r--r--src/libs/graphics/tfb_draw.h199
-rw-r--r--src/libs/graphics/tfb_prim.c237
-rw-r--r--src/libs/graphics/tfb_prim.h30
-rw-r--r--src/libs/graphics/widgets.c941
-rw-r--r--src/libs/graphics/widgets.h222
69 files changed, 21234 insertions, 0 deletions
diff --git a/src/libs/graphics/Makeinfo b/src/libs/graphics/Makeinfo
new file mode 100644
index 0000000..1a9f7ee
--- /dev/null
+++ b/src/libs/graphics/Makeinfo
@@ -0,0 +1,12 @@
+if [ "$uqm_GFXMODULE" = "sdl" ]; then
+ uqm_SUBDIRS="sdl"
+fi
+
+uqm_CFILES="boxint.c clipline.c cmap.c context.c drawable.c filegfx.c
+ bbox.c dcqueue.c gfxload.c
+ font.c frame.c gfx_common.c intersec.c loaddisp.c
+ pixmap.c resgfx.c tfb_draw.c tfb_prim.c widgets.c"
+
+uqm_HFILES="bbox.h cmap.h context.h dcqueue.h drawable.h drawcmd.h font.h
+ gfx_common.h gfxintrn.h prim.h tfb_draw.h tfb_prim.h widgets.h"
+
diff --git a/src/libs/graphics/bbox.c b/src/libs/graphics/bbox.c
new file mode 100644
index 0000000..ce57d32
--- /dev/null
+++ b/src/libs/graphics/bbox.c
@@ -0,0 +1,133 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "port.h"
+#include "libs/graphics/bbox.h"
+
+TFB_BoundingBox TFB_BBox;
+int maxWidth;
+int maxHeight;
+
+void
+TFB_BBox_Init (int width, int height)
+{
+ maxWidth = width;
+ maxHeight = height;
+ TFB_BBox.clip.extent.width = width;
+ TFB_BBox.clip.extent.height = height;
+}
+
+void
+TFB_BBox_Reset (void)
+{
+ TFB_BBox.valid = 0;
+}
+
+void
+TFB_BBox_SetClipRect (const RECT *r)
+{
+ if (!r)
+ { /* No clipping -- full rect */
+ TFB_BBox.clip.corner.x = 0;
+ TFB_BBox.clip.corner.y = 0;
+ TFB_BBox.clip.extent.width = maxWidth;
+ TFB_BBox.clip.extent.height = maxHeight;
+ return;
+ }
+
+ TFB_BBox.clip = *r;
+
+ /* Make sure the cliprect is sane */
+ if (TFB_BBox.clip.corner.x < 0)
+ TFB_BBox.clip.corner.x = 0;
+
+ if (TFB_BBox.clip.corner.y < 0)
+ TFB_BBox.clip.corner.y = 0;
+
+ if (TFB_BBox.clip.corner.x + TFB_BBox.clip.extent.width > maxWidth)
+ TFB_BBox.clip.extent.width = maxWidth - TFB_BBox.clip.corner.x;
+
+ if (TFB_BBox.clip.corner.y + TFB_BBox.clip.extent.height > maxHeight)
+ TFB_BBox.clip.extent.height = maxHeight - TFB_BBox.clip.corner.y;
+}
+
+void
+TFB_BBox_RegisterPoint (int x, int y)
+{
+ int x1 = TFB_BBox.clip.corner.x;
+ int y1 = TFB_BBox.clip.corner.y;
+ int x2 = TFB_BBox.clip.corner.x + TFB_BBox.clip.extent.width - 1;
+ int y2 = TFB_BBox.clip.corner.y + TFB_BBox.clip.extent.height - 1;
+
+ /* Constrain coordinates */
+ if (x < x1) x = x1;
+ if (x >= x2) x = x2;
+ if (y < y1) y = y1;
+ if (y >= y2) y = y2;
+
+ /* Is this the first point? If so, set a pixel-region and return. */
+ if (!TFB_BBox.valid)
+ {
+ TFB_BBox.valid = 1;
+ TFB_BBox.region.corner.x = x;
+ TFB_BBox.region.corner.y = y;
+ TFB_BBox.region.extent.width = 1;
+ TFB_BBox.region.extent.height = 1;
+ return;
+ }
+
+ /* Otherwise expand the rectangle if necessary. */
+ x1 = TFB_BBox.region.corner.x;
+ y1 = TFB_BBox.region.corner.y;
+ x2 = TFB_BBox.region.corner.x + TFB_BBox.region.extent.width - 1;
+ y2 = TFB_BBox.region.corner.y + TFB_BBox.region.extent.height - 1;
+
+ if (x < x1) {
+ TFB_BBox.region.corner.x = x;
+ TFB_BBox.region.extent.width += x1 - x;
+ }
+ if (y < y1) {
+ TFB_BBox.region.corner.y = y;
+ TFB_BBox.region.extent.height += y1 - y;
+ }
+ if (x > x2) {
+ TFB_BBox.region.extent.width += x - x2;
+ }
+ if (y > y2) {
+ TFB_BBox.region.extent.height += y - y2;
+ }
+}
+
+void
+TFB_BBox_RegisterRect (const RECT *r)
+{
+ /* RECT will still register as a corner point of the cliprect even
+ * if it does not intersect with the cliprect at all. This is not
+ * a problem, as more is not less. */
+ TFB_BBox_RegisterPoint (r->corner.x, r->corner.y);
+ TFB_BBox_RegisterPoint (r->corner.x + r->extent.width - 1,
+ r->corner.y + r->extent.height - 1);
+}
+
+void
+TFB_BBox_RegisterCanvas (TFB_Canvas c, int x, int y)
+{
+ RECT r;
+ r.corner.x = x;
+ r.corner.y = y;
+ TFB_DrawCanvas_GetExtent (c, &r.extent);
+ TFB_BBox_RegisterRect (&r);
+}
diff --git a/src/libs/graphics/bbox.h b/src/libs/graphics/bbox.h
new file mode 100644
index 0000000..33d3000
--- /dev/null
+++ b/src/libs/graphics/bbox.h
@@ -0,0 +1,46 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef BBOX_H_INCL__
+#define BBOX_H_INCL__
+
+#include "libs/gfxlib.h"
+#include "libs/graphics/tfb_draw.h"
+
+/* Bounding Box operations. These operations are NOT synchronized.
+ * However, they should only be accessed by TFB_FlushGraphics and
+ * TFB_SwapBuffers, or the routines that they exclusively call -- all
+ * of which are only callable by the thread that is permitted to touch
+ * the screen. No explicit locks should therefore be required. */
+
+typedef struct {
+ int valid; // If zero, the next point registered becomes the region
+ RECT region; // The actual modified rectangle
+ RECT clip; // Points outside of this rectangle are pushed to
+ // the closest border point
+} TFB_BoundingBox;
+
+extern TFB_BoundingBox TFB_BBox;
+
+void TFB_BBox_RegisterPoint (int x, int y);
+void TFB_BBox_RegisterRect (const RECT *r);
+void TFB_BBox_RegisterCanvas (TFB_Canvas c, int x, int y);
+
+void TFB_BBox_Init (int width, int height);
+void TFB_BBox_Reset (void);
+void TFB_BBox_SetClipRect (const RECT *r);
+
+#endif /* BBOX_H_INCL__ */
diff --git a/src/libs/graphics/boxint.c b/src/libs/graphics/boxint.c
new file mode 100644
index 0000000..0500311
--- /dev/null
+++ b/src/libs/graphics/boxint.c
@@ -0,0 +1,183 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "gfxintrn.h"
+
+#undef MIN
+#define MIN(a, b) (((a) <= (b)) ? (a) : (b))
+#undef MAX
+#define MAX(a, b) (((a) >= (b)) ? (a) : (b))
+
+INTERSECT_CODE
+BoxIntersect (RECT *pr1, RECT *pr2, RECT *pinter)
+{
+ INTERSECT_CODE intersect_code;
+ COORD x1;
+ SIZE w1, w2, delta;
+
+ intersect_code = INTERSECT_NOCLIP;
+
+ x1 = pr1->corner.x - pr2->corner.x;
+
+ w1 = pr1->extent.width;
+ w2 = pr2->extent.width;
+ if ((delta = w2 - x1) <= w1)
+ {
+ if (delta != w1)
+ {
+ w1 = delta;
+ intersect_code &= ~INTERSECT_NOCLIP;
+ }
+ intersect_code |= INTERSECT_RIGHT;
+ }
+ if (x1 <= 0)
+ {
+ if (x1 < 0)
+ {
+ w1 += x1;
+ x1 = 0;
+ intersect_code &= ~INTERSECT_NOCLIP;
+ }
+ intersect_code |= INTERSECT_LEFT;
+ }
+
+ if (w1 > 0)
+ {
+#define h2 w2
+ COORD y1;
+ SIZE h1;
+
+ y1 = pr1->corner.y - pr2->corner.y;
+
+ h1 = pr1->extent.height;
+ h2 = pr2->extent.height;
+ if ((delta = h2 - y1) <= h1)
+ {
+ if (delta != h1)
+ {
+ h1 = delta;
+ intersect_code &= ~INTERSECT_NOCLIP;
+ }
+ intersect_code |= INTERSECT_BOTTOM;
+ }
+ if (y1 <= 0)
+ {
+ if (y1 < 0)
+ {
+ h1 += y1;
+ y1 = 0;
+ intersect_code &= ~INTERSECT_NOCLIP;
+ }
+ intersect_code |= INTERSECT_TOP;
+ }
+
+ if (h1 > 0)
+ {
+ pinter->corner.x = x1 + pr2->corner.x;
+ pinter->corner.y = y1 + pr2->corner.y;
+ pinter->extent.width = w1;
+ pinter->extent.height = h1;
+
+ return (intersect_code);
+ }
+#undef h2
+ }
+
+ return ((INTERSECT_CODE)0);
+}
+
+void
+BoxUnion (RECT *pr1, RECT *pr2, RECT *punion)
+{
+#if NEVER // Part of lower FIXME.
+ COORD x2, y2, w2, h2;
+#endif // NEVER
+
+ // Union is A AND B, put together, correct? Returns a bigger box that
+ // encompasses the two.
+ punion->corner.x = MIN(pr1->corner.x, pr2->corner.x);
+ punion->corner.y = MIN(pr1->corner.y, pr2->corner.y);
+
+ punion->extent.width = MAX(pr1->corner.x + pr1->extent.width,
+ pr2->corner.x + pr2->extent.width) - punion->corner.x;
+ punion->extent.height = MAX(pr1->corner.y + pr1->extent.height,
+ pr2->corner.y + pr2->extent.height) - punion->corner.y;
+
+
+#if NEVER // FIXME - I think this is broken, but keeping it around for reference
+ // FIXME - just in case.
+
+#if 1 /* alter based on 0 widths */
+
+ x2 =
+ (pr1->corner.x < pr2->corner.x)? pr1->corner.x : pr2->corner.x;
+
+ y2 =
+ (pr1->corner.y < pr2->corner.y)? pr1->corner.y : pr2->corner.y;
+
+ w2 = (
+ ((pr1->corner.x + pr1->extent.width) > (pr2->corner.x + pr2->extent.width))?
+ (pr1->corner.x + pr1->extent.width) : (pr2->corner.x + pr2->extent.width)
+ ) - punion->corner.x;
+
+ h2 = (
+ ((pr1->corner.y + pr1->extent.height) > (pr2->corner.y + pr2->extent.height))?
+ (pr1->corner.y + pr1->extent.height) : (pr2->corner.y + pr2->extent.height)
+ ) - punion->corner.y;
+#else
+ SIZE delta;
+ COORD x1, y1, w1, h1;
+
+ x1 = pr1->corner.x;
+ w1 = pr1->extent.width;
+ x2 = pr2->corner.x;
+ w2 = pr2->extent.width;
+ if ((delta = x1 - x2) >= 0)
+ w1 += delta;
+ else
+ {
+ w2 -= delta;
+ x2 += delta;
+ }
+
+ y1 = pr1->corner.y;
+ h1 = pr1->extent.height;
+ y2 = pr2->corner.y;
+ h2 = pr2->extent.height;
+ if ((delta = y1 - y2) >= 0)
+ h1 += delta;
+ else
+ {
+ h2 -= delta;
+ y2 += delta;
+ }
+
+ if ((delta = w1 - w2) > 0)
+ w2 += delta;
+ if ((delta = h1 - h2) > 0)
+ h2 += delta;
+#endif
+
+ punion->corner.x = x2;
+ punion->corner.y = y2;
+ punion->extent.width = w2;
+ punion->extent.height = h2;
+
+#endif // NEVER
+}
+
diff --git a/src/libs/graphics/clipline.c b/src/libs/graphics/clipline.c
new file mode 100644
index 0000000..ab2d7dd
--- /dev/null
+++ b/src/libs/graphics/clipline.c
@@ -0,0 +1,241 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "gfxintrn.h"
+
+INTERSECT_CODE
+_clip_line (const RECT *pClipRect, BRESENHAM_LINE *pLine)
+{
+ COORD p;
+ COORD x0, y0, xmin, ymin, xmax, ymax;
+ SIZE abs_delta_x, abs_delta_y;
+ INTERSECT_CODE intersect_code;
+
+ xmin = pClipRect->corner.x;
+ ymin = pClipRect->corner.y;
+ xmax = pClipRect->corner.x + pClipRect->extent.width - 1;
+ ymax = pClipRect->corner.y + pClipRect->extent.height - 1;
+ if (pLine->first.x <= pLine->second.x)
+ pLine->end_points_exchanged = FALSE;
+ else
+ {
+ p = pLine->first.x;
+ pLine->first.x = pLine->second.x;
+ pLine->second.x = p;
+
+ p = pLine->first.y;
+ pLine->first.y = pLine->second.y;
+ pLine->second.y = p;
+
+ pLine->end_points_exchanged = TRUE;
+ }
+
+ if (pLine->first.x > xmax || pLine->second.x < xmin ||
+ (pLine->first.y > ymax && pLine->second.y > ymax) ||
+ (pLine->first.y < ymin && pLine->second.y < ymin))
+ return ((INTERSECT_CODE)0);
+
+ intersect_code = INTERSECT_NOCLIP;
+ x0 = y0 = 0;
+ abs_delta_x = (pLine->second.x - pLine->first.x) << 1;
+ abs_delta_y = (pLine->second.y - pLine->first.y) << 1;
+ pLine->abs_delta_x = abs_delta_x;
+ pLine->abs_delta_y = abs_delta_y;
+ if (abs_delta_y == 0)
+ {
+ if (pLine->first.x < xmin)
+ {
+ pLine->first.x = xmin;
+ intersect_code |= INTERSECT_LEFT;
+ }
+ if (pLine->second.x > xmax)
+ {
+ pLine->second.x = xmax;
+ intersect_code |= INTERSECT_RIGHT;
+ }
+ }
+ else if (abs_delta_x == 0)
+ {
+ if (abs_delta_y < 0)
+ {
+ p = pLine->first.y;
+ pLine->first.y = pLine->second.y;
+ pLine->second.y = p;
+
+ pLine->abs_delta_y =
+ abs_delta_y = -abs_delta_y;
+ }
+
+ if (pLine->first.y < ymin)
+ {
+ pLine->first.y = ymin;
+ intersect_code |= INTERSECT_TOP;
+ }
+ if (pLine->second.y > ymax)
+ {
+ pLine->second.y = ymax;
+ intersect_code |= INTERSECT_BOTTOM;
+ }
+ }
+ else
+ {
+ COORD x1, y1;
+
+ p = pLine->first.x;
+ x1 = pLine->second.x - p;
+ xmin = xmin - p;
+ xmax = xmax - p;
+
+ p = pLine->first.y;
+ if (abs_delta_y > 0)
+ {
+ y1 = pLine->second.y - p;
+ ymin = ymin - p;
+ ymax = ymax - p;
+ }
+ else
+ {
+ y1 = p - pLine->second.y;
+ ymin = p - ymin;
+ ymax = p - ymax;
+
+ p = ymin;
+ ymin = ymax;
+ ymax = p;
+ abs_delta_y = -abs_delta_y;
+ }
+
+ if (abs_delta_x > abs_delta_y)
+ {
+ SIZE half_dx;
+
+ half_dx = abs_delta_x >> 1;
+ if (x0 < xmin)
+ {
+ if ((y0 = (COORD)(((long)abs_delta_y *
+ (x0 = xmin) + half_dx) / abs_delta_x)) > ymax)
+ return ((INTERSECT_CODE)0);
+ intersect_code |= INTERSECT_LEFT;
+ }
+ if (x1 > xmax)
+ {
+ if ((y1 = (COORD)(((long)abs_delta_y *
+ (x1 = xmax) + half_dx) / abs_delta_x)) < ymin)
+ return ((INTERSECT_CODE)0);
+ intersect_code |= INTERSECT_RIGHT;
+ }
+ if (y0 < ymin)
+ {
+ if ((x0 = (COORD)(((long)abs_delta_x *
+ (y0 = ymin) - half_dx + (abs_delta_y - 1)) /
+ abs_delta_y)) > xmax)
+ return ((INTERSECT_CODE)0);
+ intersect_code |= INTERSECT_TOP;
+ intersect_code &= ~INTERSECT_LEFT;
+ }
+ if (y1 > ymax)
+ {
+ if ((x1 = (COORD)(((long)abs_delta_x *
+ ((y1 = ymax) + 1) - half_dx + (abs_delta_y - 1)) /
+ abs_delta_y) - 1) < xmin)
+ return ((INTERSECT_CODE)0);
+ intersect_code |= INTERSECT_BOTTOM;
+ intersect_code &= ~INTERSECT_RIGHT;
+ }
+ }
+ else
+ {
+ SIZE half_dy;
+
+ half_dy = abs_delta_y >> 1;
+ if (y0 < ymin)
+ {
+ if ((x0 = (COORD)(((long)abs_delta_x *
+ (y0 = ymin) + half_dy) / abs_delta_y)) > xmax)
+ return ((INTERSECT_CODE)0);
+ intersect_code |= INTERSECT_TOP;
+ }
+ if (y1 > ymax)
+ {
+ if ((x1 = (COORD)(((long)abs_delta_x *
+ (y1 = ymax) + half_dy) / abs_delta_y)) < xmin)
+ return ((INTERSECT_CODE)0);
+ intersect_code |= INTERSECT_BOTTOM;
+ }
+ if (x0 < xmin)
+ {
+ if ((y0 = (COORD)(((long)abs_delta_y *
+ (x0 = xmin) - half_dy + (abs_delta_x - 1)) /
+ abs_delta_x)) > ymax)
+ return ((INTERSECT_CODE)0);
+ intersect_code |= INTERSECT_LEFT;
+ intersect_code &= ~INTERSECT_TOP;
+ }
+ if (x1 > xmax)
+ {
+ if ((y1 = (COORD)(((long)abs_delta_y *
+ ((x1 = xmax) + 1) - half_dy + (abs_delta_x - 1)) /
+ abs_delta_x) - 1) < ymin)
+ return ((INTERSECT_CODE)0);
+ intersect_code |= INTERSECT_RIGHT;
+ intersect_code &= ~INTERSECT_BOTTOM;
+ }
+ }
+
+ pLine->second.x = pLine->first.x + x1;
+ pLine->first.x += x0;
+ if (pLine->abs_delta_y > 0)
+ {
+ pLine->second.y = pLine->first.y + y1;
+ pLine->first.y += y0;
+ }
+ else
+ {
+ INTERSECT_CODE y_code;
+
+ pLine->second.y = pLine->first.y - y1;
+ pLine->first.y -= y0;
+
+ y_code = (INTERSECT_CODE)(intersect_code
+ & (INTERSECT_TOP | INTERSECT_BOTTOM));
+ if (y_code && y_code != (INTERSECT_TOP | INTERSECT_BOTTOM))
+ intersect_code ^= (INTERSECT_TOP | INTERSECT_BOTTOM);
+ }
+ }
+
+ if (!(intersect_code & INTERSECT_ALL_SIDES))
+ {
+ if (abs_delta_x > abs_delta_y)
+ pLine->error_term = -(SIZE)(abs_delta_x >> 1);
+ else
+ pLine->error_term = -(SIZE)(abs_delta_y >> 1);
+ }
+ else
+ {
+ intersect_code &= ~INTERSECT_NOCLIP;
+ if (abs_delta_x > abs_delta_y)
+ pLine->error_term = (SIZE)((x0 * (long)abs_delta_y) -
+ (y0 * (long)abs_delta_x)) - (abs_delta_x >> 1);
+ else
+ pLine->error_term = (SIZE)((y0 * (long)abs_delta_x) -
+ (x0 * (long)abs_delta_y)) - (abs_delta_y >> 1);
+ }
+
+ return (pLine->intersect_code = intersect_code);
+}
+
diff --git a/src/libs/graphics/cmap.c b/src/libs/graphics/cmap.c
new file mode 100644
index 0000000..53cd13f
--- /dev/null
+++ b/src/libs/graphics/cmap.c
@@ -0,0 +1,663 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "libs/graphics/cmap.h"
+#include "libs/threadlib.h"
+#include "libs/timelib.h"
+#include "libs/inplib.h"
+#include "libs/strlib.h"
+ // for GetStringAddress()
+#include "libs/log.h"
+#include <string.h>
+#include <stdlib.h>
+
+
+typedef struct xform_control
+{
+ int CMapIndex; // -1 means unused
+ COLORMAPPTR CMapPtr;
+ SIZE Ticks;
+ DWORD StartTime;
+ DWORD EndTime;
+ Color OldCMap[NUMBER_OF_PLUTVALS];
+} XFORM_CONTROL;
+
+#define MAX_XFORMS 16
+static struct
+{
+ XFORM_CONTROL TaskControl[MAX_XFORMS];
+ volatile int Highest;
+ // 'pending' is Highest >= 0
+ Mutex Lock;
+} XFormControl;
+
+static int fadeAmount = FADE_NORMAL_INTENSITY;
+static int fadeDelta;
+static TimeCount fadeStartTime;
+static sint32 fadeInterval;
+static Mutex fadeLock;
+
+#define SPARE_COLORMAPS 20
+
+// Colormaps are rapidly replaced in some parts of the game, so
+// it pays to have some spares on hand
+static TFB_ColorMap *poolhead;
+static int poolcount;
+
+static TFB_ColorMap * colormaps[MAX_COLORMAPS];
+static int mapcount;
+static Mutex maplock;
+
+
+static void release_colormap (TFB_ColorMap *map);
+static void delete_colormap (TFB_ColorMap *map);
+
+
+void
+InitColorMaps (void)
+{
+ int i;
+
+ // init colormaps
+ maplock = CreateMutex ("Colormaps Lock", SYNC_CLASS_TOPLEVEL | SYNC_CLASS_VIDEO);
+
+ // init xform control
+ XFormControl.Highest = -1;
+ XFormControl.Lock = CreateMutex ("Transform Lock", SYNC_CLASS_TOPLEVEL | SYNC_CLASS_VIDEO);
+ for (i = 0; i < MAX_XFORMS; ++i)
+ XFormControl.TaskControl[i].CMapIndex = -1;
+
+ fadeLock = CreateMutex ("Fade Lock", SYNC_CLASS_TOPLEVEL | SYNC_CLASS_VIDEO);
+}
+
+void
+UninitColorMaps (void)
+{
+ int i;
+ TFB_ColorMap *next;
+
+ for (i = 0; i < MAX_COLORMAPS; ++i)
+ {
+ TFB_ColorMap *map = colormaps[i];
+ if (!map)
+ continue;
+ release_colormap (map);
+ colormaps[i] = 0;
+ }
+
+ // free spares
+ for ( ; poolhead; poolhead = next, --poolcount)
+ {
+ next = poolhead->next;
+ delete_colormap (poolhead);
+ }
+
+ DestroyMutex (fadeLock);
+ // uninit xform control
+ DestroyMutex (XFormControl.Lock);
+
+ // uninit colormaps
+ DestroyMutex (maplock);
+}
+
+static inline TFB_ColorMap *
+alloc_colormap (void)
+ // returns an addrefed object
+{
+ TFB_ColorMap *map;
+
+ if (poolhead)
+ { // have some spares
+ map = poolhead;
+ poolhead = map->next;
+ --poolcount;
+ }
+ else
+ { // no spares, need a new one
+ map = HMalloc (sizeof (*map));
+ map->palette = AllocNativePalette ();
+ if (!map->palette)
+ {
+ HFree (map);
+ return NULL;
+ }
+ }
+ map->next = NULL;
+ map->index = -1;
+ map->refcount = 1;
+ map->version = 0;
+
+ return map;
+}
+
+static TFB_ColorMap *
+clone_colormap (TFB_ColorMap *from, int index)
+ // returns an addrefed object
+{
+ TFB_ColorMap *map;
+
+ map = alloc_colormap ();
+ if (!map)
+ {
+ log_add (log_Warning, "FATAL: clone_colormap(): "
+ "could not allocate a map");
+ exit (EXIT_FAILURE);
+ }
+ else
+ { // fresh new map
+ map->index = index;
+ if (from)
+ map->version = from->version;
+ }
+ map->version++;
+
+ return map;
+}
+
+static void
+delete_colormap (TFB_ColorMap *map)
+{
+ FreeNativePalette (map->palette);
+ HFree (map);
+}
+
+static inline void
+free_colormap (TFB_ColorMap *map)
+{
+ if (!map)
+ {
+ log_add (log_Warning, "free_colormap(): tried to free a NULL map");
+ return;
+ }
+
+ if (poolcount < SPARE_COLORMAPS)
+ { // return to the spare pool
+ map->next = poolhead;
+ poolhead = map;
+ ++poolcount;
+ }
+ else
+ { // don't need any more spares
+ delete_colormap (map);
+ }
+}
+
+static inline TFB_ColorMap *
+get_colormap (int index)
+{
+ TFB_ColorMap *map;
+
+ map = colormaps[index];
+ if (!map)
+ {
+ log_add (log_Fatal, "BUG: get_colormap(): map not present");
+ exit (EXIT_FAILURE);
+ }
+
+ map->refcount++;
+ return map;
+}
+
+static void
+release_colormap (TFB_ColorMap *map)
+{
+ if (!map)
+ return;
+
+ if (map->refcount <= 0)
+ {
+ log_add (log_Warning, "BUG: release_colormap(): refcount not >0");
+ return;
+ }
+
+ map->refcount--;
+ if (map->refcount == 0)
+ free_colormap (map);
+}
+
+void
+TFB_ReturnColorMap (TFB_ColorMap *map)
+{
+ LockMutex (maplock);
+ release_colormap (map);
+ UnlockMutex (maplock);
+}
+
+TFB_ColorMap *
+TFB_GetColorMap (int index)
+{
+ TFB_ColorMap *map;
+
+ LockMutex (maplock);
+ map = get_colormap (index);
+ UnlockMutex (maplock);
+
+ return map;
+}
+
+void
+GetColorMapColors (Color *colors, TFB_ColorMap *map)
+{
+ int i;
+
+ if (!map)
+ return;
+
+ for (i = 0; i < NUMBER_OF_PLUTVALS; ++i)
+ colors[i] = GetNativePaletteColor (map->palette, i);
+}
+
+BOOLEAN
+SetColorMap (COLORMAPPTR map)
+{
+ int start, end;
+ int total_size;
+ UBYTE *colors = (UBYTE*)map;
+ TFB_ColorMap **mpp;
+
+ if (!map)
+ return TRUE;
+
+ start = *colors++;
+ end = *colors++;
+ if (start > end)
+ {
+ log_add (log_Warning, "ERROR: SetColorMap(): "
+ "starting map (%d) not less or eq ending (%d)",
+ start, end);
+ return FALSE;
+ }
+ if (start >= MAX_COLORMAPS)
+ {
+ log_add (log_Warning, "ERROR: SetColorMap(): "
+ "starting map (%d) beyond range (0-%d)",
+ start, (int)MAX_COLORMAPS - 1);
+ return FALSE;
+ }
+ if (end >= MAX_COLORMAPS)
+ {
+ log_add (log_Warning, "SetColorMap(): "
+ "ending map (%d) beyond range (0-%d)\n",
+ end, (int)MAX_COLORMAPS - 1);
+ end = MAX_COLORMAPS - 1;
+ }
+
+ total_size = end + 1;
+
+ LockMutex (maplock);
+
+ if (total_size > mapcount)
+ mapcount = total_size;
+
+ // parse the supplied PLUTs into our colormaps
+ for (mpp = colormaps + start; start <= end; ++start, ++mpp)
+ {
+ int i;
+ TFB_ColorMap *newmap;
+ TFB_ColorMap *oldmap;
+
+ oldmap = *mpp;
+ newmap = clone_colormap (oldmap, start);
+
+ for (i = 0; i < NUMBER_OF_PLUTVALS; ++i, colors += PLUTVAL_BYTE_SIZE)
+ {
+ Color color;
+
+ color.a = 0xff;
+ color.r = colors[PLUTVAL_RED];
+ color.g = colors[PLUTVAL_GREEN];
+ color.b = colors[PLUTVAL_BLUE];
+ SetNativePaletteColor (newmap->palette, i, color);
+ }
+
+ *mpp = newmap;
+ release_colormap (oldmap);
+ }
+
+ UnlockMutex (maplock);
+
+ return TRUE;
+}
+
+/* Fade Transforms */
+
+int
+GetFadeAmount (void)
+{
+ int newAmount;
+
+ LockMutex (fadeLock);
+
+ if (fadeInterval)
+ { // have a pending fade
+ TimeCount Now = GetTimeCounter ();
+ sint32 elapsed;
+
+ elapsed = Now - fadeStartTime;
+ if (elapsed > fadeInterval)
+ elapsed = fadeInterval;
+
+ newAmount = fadeAmount + (long)fadeDelta * elapsed / fadeInterval;
+
+ if (elapsed >= fadeInterval)
+ { // fade is over
+ fadeAmount = newAmount;
+ fadeInterval = 0;
+ }
+ }
+ else
+ { // no fade pending, return the current
+ newAmount = fadeAmount;
+ }
+
+ UnlockMutex (fadeLock);
+
+ return newAmount;
+}
+
+static void
+finishPendingFade (void)
+{
+ if (fadeInterval)
+ { // end the fade immediately
+ fadeAmount += fadeDelta;
+ fadeInterval = 0;
+ }
+}
+
+static void
+FlushFadeXForms (void)
+{
+ LockMutex (fadeLock);
+ finishPendingFade ();
+ UnlockMutex (fadeLock);
+}
+
+DWORD
+FadeScreen (ScreenFadeType fadeType, SIZE TimeInterval)
+{
+ TimeCount TimeOut;
+ int FadeEnd;
+
+ switch (fadeType)
+ {
+ case FadeAllToBlack:
+ case FadeSomeToBlack:
+ FadeEnd = FADE_NO_INTENSITY;
+ break;
+ case FadeAllToColor:
+ case FadeSomeToColor:
+ FadeEnd = FADE_NORMAL_INTENSITY;
+ break;
+ case FadeAllToWhite:
+ case FadeSomeToWhite:
+ FadeEnd = FADE_FULL_INTENSITY;
+ break;
+ default:
+ return (GetTimeCounter ());
+ }
+
+ // Don't make users wait for fades
+ if (QuitPosted)
+ TimeInterval = 0;
+
+ LockMutex (fadeLock);
+
+ finishPendingFade ();
+
+ if (TimeInterval <= 0)
+ { // end the fade immediately
+ fadeAmount = FadeEnd;
+ // cancel any pending fades
+ fadeInterval = 0;
+ TimeOut = GetTimeCounter ();
+ }
+ else
+ {
+ fadeInterval = TimeInterval;
+ fadeDelta = FadeEnd - fadeAmount;
+ fadeStartTime = GetTimeCounter ();
+ TimeOut = fadeStartTime + TimeInterval + 1;
+ }
+
+ UnlockMutex (fadeLock);
+
+ return TimeOut;
+}
+
+/* Colormap Transforms */
+
+static void
+finish_colormap_xform (int which)
+{
+ SetColorMap (XFormControl.TaskControl[which].CMapPtr);
+ XFormControl.TaskControl[which].CMapIndex = -1;
+ // check Highest ptr
+ if (which == XFormControl.Highest)
+ {
+ do
+ --which;
+ while (which >= 0 && XFormControl.TaskControl[which].CMapIndex == -1);
+
+ XFormControl.Highest = which;
+ }
+}
+
+static inline BYTE
+blendChan (BYTE c1, BYTE c2, int weight, int scale)
+{
+ return c1 + ((int)c2 - c1) * weight / scale;
+}
+
+/* This gives the XFormColorMap task a timeslice to do its thing
+ * Only one thread should ever be allowed to be calling this at any time
+ */
+BOOLEAN
+XFormColorMap_step (void)
+{
+ BOOLEAN Changed = FALSE;
+ int x;
+ DWORD Now = GetTimeCounter ();
+
+ LockMutex (XFormControl.Lock);
+
+ for (x = 0; x <= XFormControl.Highest; ++x)
+ {
+ XFORM_CONTROL *control = &XFormControl.TaskControl[x];
+ int index = control->CMapIndex;
+ int TicksLeft = control->EndTime - Now;
+ TFB_ColorMap *curmap;
+
+ if (index < 0)
+ continue; // unused slot
+
+ LockMutex (maplock);
+
+ curmap = colormaps[index];
+ if (!curmap)
+ {
+ UnlockMutex (maplock);
+ log_add (log_Error, "BUG: XFormColorMap_step(): no current map");
+ finish_colormap_xform (x);
+ continue;
+ }
+
+ if (TicksLeft > 0)
+ {
+#define XFORM_SCALE 0x10000
+ TFB_ColorMap *newmap = NULL;
+ UBYTE *newClr;
+ Color *oldClr;
+ int frac;
+ int i;
+
+ newmap = clone_colormap (curmap, index);
+
+ oldClr = control->OldCMap;
+ newClr = (UBYTE*)control->CMapPtr + 2;
+
+ frac = (int)(control->Ticks - TicksLeft) * XFORM_SCALE
+ / control->Ticks;
+
+ for (i = 0; i < NUMBER_OF_PLUTVALS; ++i, ++oldClr,
+ newClr += PLUTVAL_BYTE_SIZE)
+ {
+ Color color;
+
+ color.a = 0xff;
+ color.r = blendChan (oldClr->r, newClr[PLUTVAL_RED],
+ frac, XFORM_SCALE);
+ color.g = blendChan (oldClr->g, newClr[PLUTVAL_GREEN],
+ frac, XFORM_SCALE);
+ color.b = blendChan (oldClr->b, newClr[PLUTVAL_BLUE],
+ frac, XFORM_SCALE);
+ SetNativePaletteColor (newmap->palette, i, color);
+ }
+
+ colormaps[index] = newmap;
+ release_colormap (curmap);
+ }
+
+ UnlockMutex (maplock);
+
+ if (TicksLeft <= 0)
+ { // asked for immediate xform or already done
+ finish_colormap_xform (x);
+ }
+
+ Changed = TRUE;
+ }
+
+ UnlockMutex (XFormControl.Lock);
+
+ return Changed;
+}
+
+static void
+FlushPLUTXForms (void)
+{
+ int i;
+
+ LockMutex (XFormControl.Lock);
+
+ for (i = 0; i <= XFormControl.Highest; ++i)
+ {
+ if (XFormControl.TaskControl[i].CMapIndex >= 0)
+ finish_colormap_xform (i);
+ }
+ XFormControl.Highest = -1; // all gone
+
+ UnlockMutex (XFormControl.Lock);
+}
+
+static DWORD
+XFormPLUT (COLORMAPPTR ColorMapPtr, SIZE TimeInterval)
+{
+ TFB_ColorMap *map;
+ XFORM_CONTROL *control;
+ int index;
+ int x;
+ int first_avail = -1;
+ DWORD EndTime;
+ DWORD Now;
+
+ Now = GetTimeCounter ();
+ index = *(UBYTE*)ColorMapPtr;
+
+ LockMutex (XFormControl.Lock);
+ // Find an available slot, or reuse if required
+ for (x = 0; x <= XFormControl.Highest
+ && index != XFormControl.TaskControl[x].CMapIndex;
+ ++x)
+ {
+ if (first_avail == -1 && XFormControl.TaskControl[x].CMapIndex == -1)
+ first_avail = x;
+ }
+
+ if (index == XFormControl.TaskControl[x].CMapIndex)
+ { // already xforming this colormap -- cancel and reuse slot
+ finish_colormap_xform (x);
+ }
+ else if (first_avail >= 0)
+ { // picked up a slot along the way
+ x = first_avail;
+ }
+ else if (x >= MAX_XFORMS)
+ { // flush some xforms if the queue is full
+ log_add (log_Debug, "WARNING: XFormPLUT(): no slots available");
+ x = XFormControl.Highest;
+ finish_colormap_xform (x);
+ }
+ // take next unused one
+ control = &XFormControl.TaskControl[x];
+ if (x > XFormControl.Highest)
+ XFormControl.Highest = x;
+
+ // make a copy of the current map
+ LockMutex (maplock);
+ map = colormaps[index];
+ if (!map)
+ {
+ UnlockMutex (maplock);
+ UnlockMutex (XFormControl.Lock);
+ log_add (log_Warning, "BUG: XFormPLUT(): no current map");
+ return (0);
+ }
+ GetColorMapColors (control->OldCMap, map);
+ UnlockMutex (maplock);
+
+ control->CMapIndex = index;
+ control->CMapPtr = ColorMapPtr;
+ control->Ticks = TimeInterval;
+ if (control->Ticks < 0)
+ control->Ticks = 0; /* prevent negative fade */
+ control->StartTime = Now;
+ control->EndTime = EndTime = Now + control->Ticks;
+
+ UnlockMutex (XFormControl.Lock);
+
+ return (EndTime);
+}
+
+DWORD
+XFormColorMap (COLORMAPPTR ColorMapPtr, SIZE TimeInterval)
+{
+ if (!ColorMapPtr)
+ return (0);
+
+ // Don't make users wait for transforms
+ if (QuitPosted)
+ TimeInterval = 0;
+
+ return XFormPLUT (ColorMapPtr, TimeInterval);
+}
+
+void
+FlushColorXForms (void)
+{
+ FlushFadeXForms ();
+ FlushPLUTXForms ();
+}
+
+// The type conversions are implicit and will generate errors
+// or warnings if types change imcompatibly
+COLORMAPPTR
+GetColorMapAddress (COLORMAP colormap)
+{
+ return GetStringAddress (colormap);
+}
diff --git a/src/libs/graphics/cmap.h b/src/libs/graphics/cmap.h
new file mode 100644
index 0000000..f27f789
--- /dev/null
+++ b/src/libs/graphics/cmap.h
@@ -0,0 +1,77 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef CMAP_H
+#define CMAP_H
+
+#include "libs/gfxlib.h"
+
+#define MAX_COLORMAPS 250
+
+// These are pertinent to colortable file format
+// We load colormaps as binary and parse them when needed
+#define PLUTVAL_BYTE_SIZE 3
+// Channel order in colormap tables
+#define PLUTVAL_RED 0
+#define PLUTVAL_GREEN 1
+#define PLUTVAL_BLUE 2
+
+#define NUMBER_OF_PLUTVALS 256
+// Size of the colormap in a colortable file
+#define PLUT_BYTE_SIZE (PLUTVAL_BYTE_SIZE * NUMBER_OF_PLUTVALS)
+
+#define FADE_NO_INTENSITY 0
+#define FADE_NORMAL_INTENSITY 255
+#define FADE_FULL_INTENSITY 510
+
+typedef struct NativePalette NativePalette;
+
+typedef struct tfb_colormap
+{
+ int index;
+ // Colormap index as the game sees it
+ int version;
+ // Version goes up every time the colormap changes. This may
+ // be due to SetColorMap() or at every transformation step
+ // of XFormColorMap(). Paletted TFB_Images track the last
+ // colormap version they were drawn with for optimization.
+ int refcount;
+ struct tfb_colormap *next;
+ // for spares linking
+ NativePalette *palette;
+} TFB_ColorMap;
+
+extern int GetFadeAmount (void);
+
+extern void InitColorMaps (void);
+extern void UninitColorMaps (void);
+
+extern void GetColorMapColors (Color *colors, TFB_ColorMap *);
+
+extern TFB_ColorMap * TFB_GetColorMap (int index);
+extern void TFB_ReturnColorMap (TFB_ColorMap *map);
+
+extern BOOLEAN XFormColorMap_step (void);
+
+// Native
+NativePalette* AllocNativePalette (void);
+void FreeNativePalette (NativePalette *);
+void SetNativePaletteColor (NativePalette *, int index, Color);
+Color GetNativePaletteColor (NativePalette *, int index);
+
+#endif /* CMAP_H */
diff --git a/src/libs/graphics/context.c b/src/libs/graphics/context.c
new file mode 100644
index 0000000..d609ded
--- /dev/null
+++ b/src/libs/graphics/context.c
@@ -0,0 +1,404 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "gfxintrn.h"
+
+GRAPHICS_STATUS _GraphicsStatusFlags;
+CONTEXT _pCurContext;
+
+#ifdef DEBUG
+// We keep track of all contexts
+CONTEXT firstContext;
+ // The first one in the list.
+CONTEXT *contextEnd = &firstContext;
+ // Where to put the next context.
+#endif
+
+PRIMITIVE _locPrim;
+
+FONT _CurFontPtr;
+
+#define DEFAULT_FORE_COLOR BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x1F), 0x0F)
+#define DEFAULT_BACK_COLOR BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x00), 0x00)
+
+#define DEFAULT_DRAW_MODE MAKE_DRAW_MODE (DRAW_DEFAULT, 255)
+
+CONTEXT
+SetContext (CONTEXT Context)
+{
+ CONTEXT LastContext;
+
+ LastContext = _pCurContext;
+ if (Context != LastContext)
+ {
+ if (LastContext)
+ {
+ UnsetContextFlags (
+ MAKE_WORD (0, GRAPHICS_ACTIVE | DRAWABLE_ACTIVE));
+ SetContextFlags (
+ MAKE_WORD (0, _GraphicsStatusFlags
+ & (GRAPHICS_ACTIVE | DRAWABLE_ACTIVE)));
+
+ DeactivateContext ();
+ }
+
+ _pCurContext = Context;
+ if (_pCurContext)
+ {
+ ActivateContext ();
+
+ _GraphicsStatusFlags &= ~(GRAPHICS_ACTIVE | DRAWABLE_ACTIVE);
+ _GraphicsStatusFlags |= HIBYTE (_get_context_flags ());
+
+ SetPrimColor (&_locPrim, _get_context_fg_color ());
+
+ _CurFramePtr = _get_context_fg_frame ();
+ _CurFontPtr = _get_context_font ();
+ }
+ }
+
+ return (LastContext);
+}
+
+#ifdef DEBUG
+CONTEXT
+CreateContextAux (const char *name)
+#else /* if !defined(DEBUG) */
+CONTEXT
+CreateContextAux (void)
+#endif /* !defined(DEBUG) */
+{
+ CONTEXT NewContext;
+
+ NewContext = AllocContext ();
+ if (NewContext)
+ {
+ /* initialize context */
+#ifdef DEBUG
+ NewContext->name = name;
+ NewContext->next = NULL;
+ *contextEnd = NewContext;
+ contextEnd = &NewContext->next;
+#endif /* DEBUG */
+
+ NewContext->Mode = DEFAULT_DRAW_MODE;
+ NewContext->ForeGroundColor = DEFAULT_FORE_COLOR;
+ NewContext->BackGroundColor = DEFAULT_BACK_COLOR;
+ }
+
+ return NewContext;
+}
+
+#ifdef DEBUG
+// Loop through the list of context to the pointer which points to the
+// specified context. This is either 'firstContext' or the address of
+// the 'next' field of some other context.
+static CONTEXT *
+FindContextPtr (CONTEXT context) {
+ CONTEXT *ptr;
+
+ for (ptr = &firstContext; *ptr != NULL; ptr = &(*ptr)->next) {
+ if (*ptr == context)
+ break;
+ }
+ return ptr;
+}
+#endif /* DEBUG */
+
+BOOLEAN
+DestroyContext (CONTEXT ContextRef)
+{
+ TFB_Image *img;
+
+ if (ContextRef == 0)
+ return (FALSE);
+
+ if (_pCurContext && _pCurContext == ContextRef)
+ SetContext ((CONTEXT)0);
+
+#ifdef DEBUG
+ // Unlink the context.
+ {
+ CONTEXT *contextPtr = FindContextPtr (ContextRef);
+ if (contextEnd == &ContextRef->next)
+ contextEnd = contextPtr;
+ *contextPtr = ContextRef->next;
+ }
+#endif /* DEBUG */
+
+ img = ContextRef->FontBacking;
+ if (img)
+ TFB_DrawImage_Delete (img);
+
+ FreeContext (ContextRef);
+ return TRUE;
+}
+
+Color
+SetContextForeGroundColor (Color color)
+{
+ Color oldColor;
+
+ if (!ContextActive ())
+ return DEFAULT_FORE_COLOR;
+
+ oldColor = _get_context_fg_color ();
+ if (!sameColor(oldColor, color))
+ {
+ SwitchContextForeGroundColor (color);
+
+ if (!(_get_context_fbk_flags () & FBK_IMAGE))
+ {
+ SetContextFBkFlags (FBK_DIRTY);
+ }
+ }
+ SetPrimColor (&_locPrim, color);
+
+ return (oldColor);
+}
+
+Color
+GetContextForeGroundColor (void)
+{
+ if (!ContextActive ())
+ return DEFAULT_FORE_COLOR;
+
+ return _get_context_fg_color ();
+}
+
+Color
+SetContextBackGroundColor (Color color)
+{
+ Color oldColor;
+
+ if (!ContextActive ())
+ return DEFAULT_BACK_COLOR;
+
+ oldColor = _get_context_bg_color ();
+ if (!sameColor(oldColor, color))
+ SwitchContextBackGroundColor (color);
+
+ return oldColor;
+}
+
+Color
+GetContextBackGroundColor (void)
+{
+ if (!ContextActive ())
+ return DEFAULT_BACK_COLOR;
+
+ return _get_context_bg_color ();
+}
+
+DrawMode
+SetContextDrawMode (DrawMode mode)
+{
+ DrawMode oldMode;
+
+ if (!ContextActive ())
+ return DEFAULT_DRAW_MODE;
+
+ oldMode = _get_context_draw_mode ();
+ SwitchContextDrawMode (mode);
+
+ return oldMode;
+}
+
+DrawMode
+GetContextDrawMode (void)
+{
+ if (!ContextActive ())
+ return DEFAULT_DRAW_MODE;
+
+ return _get_context_draw_mode ();
+}
+
+// Returns a rect based at 0,0 and the size of context foreground frame
+static inline RECT
+_get_context_fg_rect (void)
+{
+ RECT r = { {0, 0}, {0, 0} };
+ if (_CurFramePtr)
+ r.extent = GetFrameBounds (_CurFramePtr);
+ return r;
+}
+
+BOOLEAN
+SetContextClipRect (RECT *lpRect)
+{
+ if (!ContextActive ())
+ return (FALSE);
+
+ if (lpRect)
+ {
+ if (rectsEqual (*lpRect, _get_context_fg_rect ()))
+ { // Cliprect is undefined to mirror GetContextClipRect()
+ _pCurContext->ClipRect.extent.width = 0;
+ }
+ else
+ { // We have a cliprect
+ _pCurContext->ClipRect = *lpRect;
+ }
+ }
+ else
+ { // Set cliprect as undefined
+ _pCurContext->ClipRect.extent.width = 0;
+ }
+
+ return TRUE;
+}
+
+BOOLEAN
+GetContextClipRect (RECT *lpRect)
+{
+ if (!ContextActive ())
+ return (FALSE);
+
+ *lpRect = _pCurContext->ClipRect;
+ if (!_pCurContext->ClipRect.extent.width)
+ { // Though the cliprect is undefined, drawing will be clipped
+ // to the extent of the foreground frame
+ *lpRect = _get_context_fg_rect ();
+ }
+
+ return (_pCurContext->ClipRect.extent.width != 0);
+}
+
+POINT
+SetContextOrigin (POINT orgOffset)
+{
+ // XXX: This is a hack, kind of. But that's what the original did.
+ return SetFrameHot (_CurFramePtr, orgOffset);
+}
+
+FRAME
+SetContextFontEffect (FRAME EffectFrame)
+{
+ FRAME LastEffect;
+
+ if (!ContextActive ())
+ return (NULL);
+
+ LastEffect = _get_context_fonteff ();
+ if (EffectFrame != LastEffect)
+ {
+ SwitchContextFontEffect (EffectFrame);
+
+ if (EffectFrame != 0)
+ {
+ SetContextFBkFlags (FBK_IMAGE);
+ }
+ else
+ {
+ UnsetContextFBkFlags (FBK_IMAGE);
+ }
+ }
+
+ return LastEffect;
+}
+
+void
+FixContextFontEffect (void)
+{
+ SIZE w, h;
+ TFB_Image* img;
+
+ if (!ContextActive () || (_get_context_font_backing () != 0
+ && !(_get_context_fbk_flags () & FBK_DIRTY)))
+ return;
+
+ if (!GetContextFontLeading (&h) || !GetContextFontLeadingWidth (&w))
+ return;
+
+ img = _pCurContext->FontBacking;
+ if (img)
+ TFB_DrawScreen_DeleteImage (img);
+
+ img = TFB_DrawImage_CreateForScreen (w, h, TRUE);
+ if (_get_context_fbk_flags () & FBK_IMAGE)
+ { // image pattern backing
+ FRAME EffectFrame = _get_context_fonteff ();
+
+ TFB_DrawImage_Image (EffectFrame->image,
+ -EffectFrame->HotSpot.x, -EffectFrame->HotSpot.y,
+ 0, 0, NULL, DRAW_REPLACE_MODE, img);
+ }
+ else
+ { // solid color backing
+ RECT r = { {0, 0}, {w, h} };
+ Color color = _get_context_fg_color ();
+
+ TFB_DrawImage_Rect (&r, color, DRAW_REPLACE_MODE, img);
+ }
+
+ _pCurContext->FontBacking = img;
+ UnsetContextFBkFlags (FBK_DIRTY);
+}
+
+// 'area' may be NULL to copy the entire CONTEXT cliprect
+// 'area' is relative to the CONTEXT cliprect
+DRAWABLE
+CopyContextRect (const RECT* area)
+{
+ RECT clipRect;
+ RECT fgRect;
+ RECT r;
+
+ if (!ContextActive () || !_CurFramePtr)
+ return NULL;
+
+ fgRect = _get_context_fg_rect ();
+ GetContextClipRect (&clipRect);
+ r = clipRect;
+ if (area)
+ { // a portion of the context
+ r.corner.x += area->corner.x;
+ r.corner.y += area->corner.y;
+ r.extent = area->extent;
+ }
+ // TODO: Should this take CONTEXT origin into account too?
+ // validate the rect
+ if (!BoxIntersect (&r, &fgRect, &r))
+ return NULL;
+
+ if (_CurFramePtr->Type == SCREEN_DRAWABLE)
+ return LoadDisplayPixmap (&r, NULL);
+ else
+ return CopyFrameRect (_CurFramePtr, &r);
+}
+
+#ifdef DEBUG
+const char *
+GetContextName (CONTEXT context)
+{
+ return context->name;
+}
+
+CONTEXT
+GetFirstContext (void)
+{
+ return firstContext;
+}
+
+CONTEXT
+GetNextContext (CONTEXT context)
+{
+ return context->next;
+}
+#endif /* DEBUG */
+
diff --git a/src/libs/graphics/context.h b/src/libs/graphics/context.h
new file mode 100644
index 0000000..09b50cf
--- /dev/null
+++ b/src/libs/graphics/context.h
@@ -0,0 +1,147 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_GRAPHICS_CONTEXT_H_
+#define LIBS_GRAPHICS_CONTEXT_H_
+
+#include "tfb_draw.h"
+#include "libs/memlib.h"
+
+typedef UWORD FBK_FLAGS;
+#define FBK_DIRTY (1 << 0)
+#define FBK_IMAGE (1 << 1)
+
+struct context_desc
+{
+ UWORD Flags;
+ // Low nibble currently unused
+ // High nibble contains GRAPHICS_STATUS
+
+ Color ForeGroundColor, BackGroundColor;
+ DrawMode Mode;
+ FRAME ForeGroundFrame;
+ FONT Font;
+
+ RECT ClipRect;
+
+ FRAME FontEffect;
+ TFB_Image *FontBacking;
+ FBK_FLAGS BackingFlags;
+
+#ifdef DEBUG
+ const char *name;
+ CONTEXT next;
+#endif
+};
+
+#define AllocContext() HCalloc (sizeof (CONTEXT_DESC))
+#define FreeContext HFree
+
+extern CONTEXT _pCurContext;
+extern PRIMITIVE _locPrim;
+
+#define _get_context_fg_color() (_pCurContext->ForeGroundColor)
+#define _get_context_bg_color() (_pCurContext->BackGroundColor)
+#define _get_context_flags() (_pCurContext->Flags)
+#define _get_context_fg_frame() (_pCurContext->ForeGroundFrame)
+#define _get_context_font() (_pCurContext->Font)
+#define _get_context_fbk_flags() (_pCurContext->BackingFlags)
+#define _get_context_fonteff() (_pCurContext->FontEffect)
+#define _get_context_font_backing() (_pCurContext->FontBacking)
+#define _get_context_draw_mode() (_pCurContext->Mode)
+
+#define SwitchContextDrawMode(m) \
+{ \
+ _pCurContext->Mode = (m); \
+}
+#define SwitchContextForeGroundColor(c) \
+{ \
+ _pCurContext->ForeGroundColor = (c); \
+}
+#define SwitchContextBackGroundColor(c) \
+{ \
+ _pCurContext->BackGroundColor = (c); \
+}
+#define SetContextFlags(f) \
+{ \
+ _pCurContext->Flags |= (f); \
+}
+#define UnsetContextFlags(f) \
+{ \
+ _pCurContext->Flags &= ~(f); \
+}
+#define SwitchContextFGFrame(f) \
+{ \
+ _pCurContext->ForeGroundFrame = (f); \
+}
+#define SwitchContextFont(f) \
+{ \
+ _pCurContext->Font = (f); \
+ SetContextFBkFlags (FBK_DIRTY); \
+}
+#define SwitchContextBGFunc(f) \
+{ \
+ _pCurContext->BackGroundFunc = (f); \
+}
+#define SetContextFBkFlags(f) \
+{ \
+ _pCurContext->BackingFlags |= (f); \
+}
+#define UnsetContextFBkFlags(f) \
+{ \
+ _pCurContext->BackingFlags &= ~(f); \
+}
+#define SwitchContextFontEffect(f) \
+{ \
+ _pCurContext->FontEffect = (f); \
+ SetContextFBkFlags (FBK_DIRTY); \
+}
+
+typedef BYTE GRAPHICS_STATUS;
+
+extern GRAPHICS_STATUS _GraphicsStatusFlags;
+#define GRAPHICS_ACTIVE (GRAPHICS_STATUS)(1 << 0)
+#define GRAPHICS_VISIBLE (GRAPHICS_STATUS)(1 << 1)
+#define CONTEXT_ACTIVE (GRAPHICS_STATUS)(1 << 2)
+#define DRAWABLE_ACTIVE (GRAPHICS_STATUS)(1 << 3)
+#define DeactivateGraphics() (_GraphicsStatusFlags &= ~GRAPHICS_ACTIVE)
+#define ActivateGraphics() (_GraphicsStatusFlags |= GRAPHICS_ACTIVE)
+#define GraphicsActive() (_GraphicsStatusFlags & GRAPHICS_ACTIVE)
+#define DeactivateVisible() (_GraphicsStatusFlags &= ~GRAPHICS_VISIBLE)
+#define ActivateVisible() (_GraphicsStatusFlags |= GRAPHICS_VISIBLE)
+#define DeactivateContext() (_GraphicsStatusFlags &= ~CONTEXT_ACTIVE)
+#define ActivateContext() (_GraphicsStatusFlags |= CONTEXT_ACTIVE)
+#define ContextActive() (_GraphicsStatusFlags & CONTEXT_ACTIVE)
+#define DeactivateDrawable() (_GraphicsStatusFlags &= ~DRAWABLE_ACTIVE)
+#define ActivateDrawable() (_GraphicsStatusFlags |= DRAWABLE_ACTIVE)
+#define DrawableActive() (_GraphicsStatusFlags & DRAWABLE_ACTIVE)
+
+#define SYSTEM_ACTIVE (GRAPHICS_STATUS)(CONTEXT_ACTIVE | DRAWABLE_ACTIVE)
+
+#define GraphicsSystemActive() \
+ ((_GraphicsStatusFlags & SYSTEM_ACTIVE) == SYSTEM_ACTIVE)
+#define GraphicsStatus() \
+ (_GraphicsStatusFlags & (GRAPHICS_STATUS)(GRAPHICS_ACTIVE \
+ | GRAPHICS_VISIBLE))
+
+// pValidRect or origin may be NULL
+BOOLEAN GetContextValidRect (RECT *pValidRect, POINT *origin);
+extern void FixContextFontEffect (void);
+
+#endif /* LIBS_GRAPHICS_CONTEXT_H_ */
+
diff --git a/src/libs/graphics/dcqueue.c b/src/libs/graphics/dcqueue.c
new file mode 100644
index 0000000..70c0662
--- /dev/null
+++ b/src/libs/graphics/dcqueue.c
@@ -0,0 +1,670 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "port.h"
+#include "libs/threadlib.h"
+#include "libs/graphics/drawcmd.h"
+#include "libs/graphics/drawable.h"
+#include "libs/graphics/context.h"
+#include "libs/graphics/dcqueue.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/graphics/bbox.h"
+#include "libs/timelib.h"
+#include "libs/log.h"
+#include "libs/misc.h"
+ // for TFB_DEBUG_HALT
+
+
+static RecursiveMutex DCQ_Mutex;
+
+CondVar RenderingCond;
+
+TFB_DrawCommand DCQ[DCQ_MAX];
+
+TFB_DrawCommandQueue DrawCommandQueue;
+
+#define FPS_PERIOD (ONE_SECOND / 100)
+int RenderedFrames = 0;
+
+
+// Wait for the queue to be emptied.
+static void
+TFB_WaitForSpace (int requested_slots)
+{
+ int old_depth, i;
+ log_add (log_Debug, "DCQ overload (Size = %d, FullSize = %d, "
+ "Requested = %d). Sleeping until renderer is done.",
+ DrawCommandQueue.Size, DrawCommandQueue.FullSize,
+ requested_slots);
+ // Restore the DCQ locking level. I *think* this is
+ // always 1, but...
+ TFB_BatchReset ();
+ old_depth = GetRecursiveMutexDepth (DCQ_Mutex);
+ for (i = 0; i < old_depth; i++)
+ UnlockRecursiveMutex (DCQ_Mutex);
+ WaitCondVar (RenderingCond);
+ for (i = 0; i < old_depth; i++)
+ LockRecursiveMutex (DCQ_Mutex);
+ log_add (log_Debug, "DCQ clear (Size = %d, FullSize = %d). Continuing.",
+ DrawCommandQueue.Size, DrawCommandQueue.FullSize);
+}
+
+void
+Lock_DCQ (int slots)
+{
+ LockRecursiveMutex (DCQ_Mutex);
+ while (DrawCommandQueue.FullSize >= DCQ_MAX - slots)
+ {
+ TFB_WaitForSpace (slots);
+ }
+}
+
+void
+Unlock_DCQ (void)
+{
+ UnlockRecursiveMutex (DCQ_Mutex);
+}
+
+// Always have the DCQ locked when calling this.
+static void
+Synchronize_DCQ (void)
+{
+ if (!DrawCommandQueue.Batching)
+ {
+ int front = DrawCommandQueue.Front;
+ int back = DrawCommandQueue.InsertionPoint;
+ DrawCommandQueue.Back = DrawCommandQueue.InsertionPoint;
+ if (front <= back)
+ {
+ DrawCommandQueue.Size = (back - front);
+ }
+ else
+ {
+ DrawCommandQueue.Size = (back + DCQ_MAX - front);
+ }
+ DrawCommandQueue.FullSize = DrawCommandQueue.Size;
+ }
+}
+
+void
+TFB_BatchGraphics (void)
+{
+ LockRecursiveMutex (DCQ_Mutex);
+ DrawCommandQueue.Batching++;
+ UnlockRecursiveMutex (DCQ_Mutex);
+}
+
+void
+TFB_UnbatchGraphics (void)
+{
+ LockRecursiveMutex (DCQ_Mutex);
+ if (DrawCommandQueue.Batching)
+ {
+ DrawCommandQueue.Batching--;
+ }
+ Synchronize_DCQ ();
+ UnlockRecursiveMutex (DCQ_Mutex);
+}
+
+// Cancel all pending batch operations, making them unbatched. This will
+// cause a small amount of flicker when invoked, but prevents
+// batching problems from freezing the game.
+void
+TFB_BatchReset (void)
+{
+ LockRecursiveMutex (DCQ_Mutex);
+ DrawCommandQueue.Batching = 0;
+ Synchronize_DCQ ();
+ UnlockRecursiveMutex (DCQ_Mutex);
+}
+
+
+// Draw Command Queue Stuff
+
+void
+Init_DrawCommandQueue (void)
+{
+ DrawCommandQueue.Back = 0;
+ DrawCommandQueue.Front = 0;
+ DrawCommandQueue.InsertionPoint = 0;
+ DrawCommandQueue.Batching = 0;
+ DrawCommandQueue.FullSize = 0;
+ DrawCommandQueue.Size = 0;
+
+ TFB_BBox_Init (ScreenWidth, ScreenHeight);
+
+ DCQ_Mutex = CreateRecursiveMutex ("DCQ",
+ SYNC_CLASS_TOPLEVEL | SYNC_CLASS_VIDEO);
+
+ RenderingCond = CreateCondVar ("DCQ empty",
+ SYNC_CLASS_TOPLEVEL | SYNC_CLASS_VIDEO);
+}
+
+void
+Uninit_DrawCommandQueue (void)
+{
+ if (RenderingCond)
+ {
+ DestroyCondVar (RenderingCond);
+ RenderingCond = 0;
+ }
+
+ if (DCQ_Mutex)
+ {
+ DestroyRecursiveMutex (DCQ_Mutex);
+ DCQ_Mutex = 0;
+ }
+}
+
+void
+TFB_DrawCommandQueue_Push (TFB_DrawCommand* Command)
+{
+ Lock_DCQ (1);
+ DCQ[DrawCommandQueue.InsertionPoint] = *Command;
+ DrawCommandQueue.InsertionPoint = (DrawCommandQueue.InsertionPoint + 1)
+ % DCQ_MAX;
+ DrawCommandQueue.FullSize++;
+ Synchronize_DCQ ();
+ Unlock_DCQ ();
+}
+
+int
+TFB_DrawCommandQueue_Pop (TFB_DrawCommand *target)
+{
+ LockRecursiveMutex (DCQ_Mutex);
+
+ if (DrawCommandQueue.Size == 0)
+ {
+ Unlock_DCQ ();
+ return (0);
+ }
+
+ if (DrawCommandQueue.Front == DrawCommandQueue.Back &&
+ DrawCommandQueue.Size != DCQ_MAX)
+ {
+ log_add (log_Debug, "Augh! Assertion failure in DCQ! "
+ "Front == Back, Size != DCQ_MAX");
+ DrawCommandQueue.Size = 0;
+ Unlock_DCQ ();
+ return (0);
+ }
+
+ *target = DCQ[DrawCommandQueue.Front];
+ DrawCommandQueue.Front = (DrawCommandQueue.Front + 1) % DCQ_MAX;
+
+ DrawCommandQueue.Size--;
+ DrawCommandQueue.FullSize--;
+ UnlockRecursiveMutex (DCQ_Mutex);
+
+ return 1;
+}
+
+void
+TFB_DrawCommandQueue_Clear ()
+{
+ LockRecursiveMutex (DCQ_Mutex);
+ DrawCommandQueue.Size = 0;
+ DrawCommandQueue.Front = 0;
+ DrawCommandQueue.Back = 0;
+ DrawCommandQueue.Batching = 0;
+ DrawCommandQueue.FullSize = 0;
+ DrawCommandQueue.InsertionPoint = 0;
+ UnlockRecursiveMutex (DCQ_Mutex);
+}
+
+static void
+checkExclusiveThread (TFB_DrawCommand* DrawCommand)
+{
+#ifdef DEBUG_DCQ_THREADS
+ static uint32 exclusiveThreadId;
+ extern uint32 SDL_ThreadID(void);
+
+ // Only one thread is currently allowed to enqueue commands
+ // This is not a technical limitation but rather a semantical one atm.
+ if (DrawCommand->Type == TFB_DRAWCOMMANDTYPE_REINITVIDEO)
+ { // TFB_DRAWCOMMANDTYPE_REINITVIDEO is an exception
+ // It is queued from the main() thread, which is safe to do
+ return;
+ }
+
+ if (!exclusiveThreadId)
+ exclusiveThreadId = SDL_ThreadID();
+ else
+ assert (SDL_ThreadID() == exclusiveThreadId);
+#else
+ (void) DrawCommand; // suppress unused warning
+#endif
+}
+
+void
+TFB_EnqueueDrawCommand (TFB_DrawCommand* DrawCommand)
+{
+ if (TFB_DEBUG_HALT)
+ {
+ return;
+ }
+
+ checkExclusiveThread (DrawCommand);
+
+ if (DrawCommand->Type <= TFB_DRAWCOMMANDTYPE_COPYTOIMAGE
+ && _CurFramePtr->Type == SCREEN_DRAWABLE)
+ {
+ static RECT scissor_rect;
+
+ // Set the clipping region.
+ // We allow drawing with no current context set, so the whole screen
+ if ((_pCurContext && !rectsEqual (scissor_rect, _pCurContext->ClipRect))
+ || (!_pCurContext && scissor_rect.extent.width != 0))
+ {
+ // Enqueue command to set the glScissor spec
+ TFB_DrawCommand DC;
+
+ if (_pCurContext)
+ scissor_rect = _pCurContext->ClipRect;
+ else
+ scissor_rect.extent.width = 0;
+
+ if (scissor_rect.extent.width)
+ {
+ DC.Type = TFB_DRAWCOMMANDTYPE_SCISSORENABLE;
+ DC.data.scissor.rect = scissor_rect;
+ }
+ else
+ {
+ DC.Type = TFB_DRAWCOMMANDTYPE_SCISSORDISABLE;
+ }
+
+ TFB_EnqueueDrawCommand(&DC);
+ }
+ }
+
+ TFB_DrawCommandQueue_Push (DrawCommand);
+}
+
+static void
+computeFPS (void)
+{
+ static TimeCount last_time;
+ static TimePeriod fps_counter;
+ TimeCount current_time;
+ TimePeriod delta_time;
+
+ current_time = GetTimeCounter ();
+ delta_time = current_time - last_time;
+ last_time = current_time;
+
+ fps_counter += delta_time;
+ if (fps_counter > FPS_PERIOD)
+ {
+ log_add (log_User, "fps %.2f, effective %.2f",
+ (float)ONE_SECOND / delta_time,
+ (float)ONE_SECOND * RenderedFrames / fps_counter);
+
+ fps_counter = 0;
+ RenderedFrames = 0;
+ }
+}
+
+// Only call from main() thread!!
+void
+TFB_FlushGraphics (void)
+{
+ int commands_handled;
+ BOOLEAN livelock_deterrence;
+
+ // This is technically a locking violation on DrawCommandQueue.Size,
+ // but it is likely to not be very destructive.
+ if (DrawCommandQueue.Size == 0)
+ {
+ static int last_fade = 255;
+ static int last_transition = 255;
+ int current_fade = GetFadeAmount ();
+ int current_transition = TransitionAmount;
+
+ if ((current_fade != 255 && current_fade != last_fade) ||
+ (current_transition != 255 &&
+ current_transition != last_transition) ||
+ (current_fade == 255 && last_fade != 255) ||
+ (current_transition == 255 && last_transition != 255))
+ {
+ TFB_SwapBuffers (TFB_REDRAW_FADING);
+ // if fading, redraw every frame
+ }
+ else
+ {
+ TaskSwitch ();
+ }
+
+ last_fade = current_fade;
+ last_transition = current_transition;
+ BroadcastCondVar (RenderingCond);
+ return;
+ }
+
+ if (GfxFlags & TFB_GFXFLAGS_SHOWFPS)
+ computeFPS ();
+
+ commands_handled = 0;
+ livelock_deterrence = FALSE;
+
+ if (DrawCommandQueue.FullSize > DCQ_FORCE_BREAK_SIZE)
+ {
+ TFB_BatchReset ();
+ }
+
+ if (DrawCommandQueue.Size > DCQ_FORCE_SLOWDOWN_SIZE)
+ {
+ Lock_DCQ (-1);
+ livelock_deterrence = TRUE;
+ }
+
+ TFB_BBox_Reset ();
+
+ for (;;)
+ {
+ TFB_DrawCommand DC;
+
+ if (!TFB_DrawCommandQueue_Pop (&DC))
+ {
+ // the Queue is now empty.
+ break;
+ }
+
+ ++commands_handled;
+ if (!livelock_deterrence && commands_handled + DrawCommandQueue.Size
+ > DCQ_LIVELOCK_MAX)
+ {
+ // log_add (log_Debug, "Initiating livelock deterrence!");
+ livelock_deterrence = TRUE;
+
+ Lock_DCQ (-1);
+ }
+
+ switch (DC.Type)
+ {
+ case TFB_DRAWCOMMANDTYPE_SETMIPMAP:
+ {
+ TFB_DrawCommand_SetMipmap *cmd = &DC.data.setmipmap;
+ TFB_DrawImage_SetMipmap (cmd->image, cmd->mipmap,
+ cmd->hotx, cmd->hoty);
+ break;
+ }
+
+ case TFB_DRAWCOMMANDTYPE_IMAGE:
+ {
+ TFB_DrawCommand_Image *cmd = &DC.data.image;
+ TFB_Image *DC_image = cmd->image;
+ const int x = cmd->x;
+ const int y = cmd->y;
+
+ TFB_DrawCanvas_Image (DC_image, x, y,
+ cmd->scale, cmd->scaleMode, cmd->colormap,
+ cmd->drawMode,
+ TFB_GetScreenCanvas (cmd->destBuffer));
+
+ if (cmd->destBuffer == TFB_SCREEN_MAIN)
+ {
+ LockMutex (DC_image->mutex);
+ if (cmd->scale)
+ TFB_BBox_RegisterCanvas (DC_image->ScaledImg,
+ x - DC_image->last_scale_hs.x,
+ y - DC_image->last_scale_hs.y);
+ else
+ TFB_BBox_RegisterCanvas (DC_image->NormalImg,
+ x - DC_image->NormalHs.x,
+ y - DC_image->NormalHs.y);
+ UnlockMutex (DC_image->mutex);
+ }
+
+ break;
+ }
+
+ case TFB_DRAWCOMMANDTYPE_FILLEDIMAGE:
+ {
+ TFB_DrawCommand_FilledImage *cmd = &DC.data.filledimage;
+ TFB_Image *DC_image = cmd->image;
+ const int x = cmd->x;
+ const int y = cmd->y;
+
+ TFB_DrawCanvas_FilledImage (DC_image, x, y,
+ cmd->scale, cmd->scaleMode, cmd->color,
+ cmd->drawMode,
+ TFB_GetScreenCanvas (cmd->destBuffer));
+
+ if (cmd->destBuffer == TFB_SCREEN_MAIN)
+ {
+ LockMutex (DC_image->mutex);
+ if (cmd->scale)
+ TFB_BBox_RegisterCanvas (DC_image->ScaledImg,
+ x - DC_image->last_scale_hs.x,
+ y - DC_image->last_scale_hs.y);
+ else
+ TFB_BBox_RegisterCanvas (DC_image->NormalImg,
+ x - DC_image->NormalHs.x,
+ y - DC_image->NormalHs.y);
+ UnlockMutex (DC_image->mutex);
+ }
+
+ break;
+ }
+
+ case TFB_DRAWCOMMANDTYPE_FONTCHAR:
+ {
+ TFB_DrawCommand_FontChar *cmd = &DC.data.fontchar;
+ TFB_Char *DC_char = cmd->fontchar;
+ const int x = cmd->x;
+ const int y = cmd->y;
+
+ TFB_DrawCanvas_FontChar (DC_char, cmd->backing, x, y,
+ cmd->drawMode, TFB_GetScreenCanvas (cmd->destBuffer));
+
+ if (cmd->destBuffer == TFB_SCREEN_MAIN)
+ {
+ RECT r;
+
+ r.corner.x = x - DC_char->HotSpot.x;
+ r.corner.y = y - DC_char->HotSpot.y;
+ r.extent.width = DC_char->extent.width;
+ r.extent.height = DC_char->extent.height;
+
+ TFB_BBox_RegisterRect (&r);
+ }
+
+ break;
+ }
+
+ case TFB_DRAWCOMMANDTYPE_LINE:
+ {
+ TFB_DrawCommand_Line *cmd = &DC.data.line;
+
+ if (cmd->destBuffer == TFB_SCREEN_MAIN)
+ {
+ TFB_BBox_RegisterPoint (cmd->x1, cmd->y1);
+ TFB_BBox_RegisterPoint (cmd->x2, cmd->y2);
+ }
+ TFB_DrawCanvas_Line (cmd->x1, cmd->y1, cmd->x2, cmd->y2,
+ cmd->color, cmd->drawMode,
+ TFB_GetScreenCanvas (cmd->destBuffer));
+ break;
+ }
+
+ case TFB_DRAWCOMMANDTYPE_RECTANGLE:
+ {
+ TFB_DrawCommand_Rect *cmd = &DC.data.rect;
+
+ if (cmd->destBuffer == TFB_SCREEN_MAIN)
+ TFB_BBox_RegisterRect (&cmd->rect);
+ TFB_DrawCanvas_Rect (&cmd->rect, cmd->color, cmd->drawMode,
+ TFB_GetScreenCanvas (cmd->destBuffer));
+
+ break;
+ }
+
+ case TFB_DRAWCOMMANDTYPE_SCISSORENABLE:
+ {
+ TFB_DrawCommand_Scissor *cmd = &DC.data.scissor;
+
+ TFB_DrawCanvas_SetClipRect (
+ TFB_GetScreenCanvas (TFB_SCREEN_MAIN), &cmd->rect);
+ TFB_BBox_SetClipRect (&DC.data.scissor.rect);
+ break;
+ }
+
+ case TFB_DRAWCOMMANDTYPE_SCISSORDISABLE:
+ TFB_DrawCanvas_SetClipRect (
+ TFB_GetScreenCanvas (TFB_SCREEN_MAIN), NULL);
+ TFB_BBox_SetClipRect (NULL);
+ break;
+
+ case TFB_DRAWCOMMANDTYPE_COPYTOIMAGE:
+ {
+ TFB_DrawCommand_CopyToImage *cmd = &DC.data.copytoimage;
+ TFB_Image *DC_image = cmd->image;
+ const POINT dstPt = {0, 0};
+
+ if (DC_image == 0)
+ {
+ log_add (log_Debug, "DCQ ERROR: COPYTOIMAGE passed null "
+ "image ptr");
+ break;
+ }
+ LockMutex (DC_image->mutex);
+ TFB_DrawCanvas_CopyRect (
+ TFB_GetScreenCanvas (cmd->srcBuffer), &cmd->rect,
+ DC_image->NormalImg, dstPt);
+ UnlockMutex (DC_image->mutex);
+ break;
+ }
+
+ case TFB_DRAWCOMMANDTYPE_COPY:
+ {
+ TFB_DrawCommand_Copy *cmd = &DC.data.copy;
+ const RECT r = cmd->rect;
+
+ if (cmd->destBuffer == TFB_SCREEN_MAIN)
+ TFB_BBox_RegisterRect (&cmd->rect);
+
+ TFB_DrawCanvas_CopyRect (
+ TFB_GetScreenCanvas (cmd->srcBuffer), &r,
+ TFB_GetScreenCanvas (cmd->destBuffer), r.corner);
+ break;
+ }
+
+ case TFB_DRAWCOMMANDTYPE_DELETEIMAGE:
+ {
+ TFB_Image *DC_image = DC.data.deleteimage.image;
+ TFB_DrawImage_Delete (DC_image);
+ break;
+ }
+
+ case TFB_DRAWCOMMANDTYPE_DELETEDATA:
+ {
+ void *data = DC.data.deletedata.data;
+ HFree (data);
+ break;
+ }
+
+ case TFB_DRAWCOMMANDTYPE_SENDSIGNAL:
+ ClearSemaphore (DC.data.sendsignal.sem);
+ break;
+
+ case TFB_DRAWCOMMANDTYPE_REINITVIDEO:
+ {
+ TFB_DrawCommand_ReinitVideo *cmd = &DC.data.reinitvideo;
+ int oldDriver = GraphicsDriver;
+ int oldFlags = GfxFlags;
+ int oldWidth = ScreenWidthActual;
+ int oldHeight = ScreenHeightActual;
+ if (TFB_ReInitGraphics (cmd->driver, cmd->flags,
+ cmd->width, cmd->height))
+ {
+ log_add (log_Error, "Could not provide requested mode: "
+ "reverting to last known driver.");
+ // We don't know what exactly failed, so roll it all back
+ if (TFB_ReInitGraphics (oldDriver, oldFlags,
+ oldWidth, oldHeight))
+ {
+ log_add (log_Fatal,
+ "Couldn't reinit at that point either. "
+ "Your video has been somehow tied in knots.");
+ exit (EXIT_FAILURE);
+ }
+ }
+ TFB_SwapBuffers (TFB_REDRAW_YES);
+ break;
+ }
+
+ case TFB_DRAWCOMMANDTYPE_CALLBACK:
+ {
+ DC.data.callback.callback (DC.data.callback.arg);
+ break;
+ }
+ }
+ }
+
+ if (livelock_deterrence)
+ Unlock_DCQ ();
+
+ TFB_SwapBuffers (TFB_REDRAW_NO);
+ RenderedFrames++;
+ BroadcastCondVar (RenderingCond);
+}
+
+void
+TFB_PurgeDanglingGraphics (void)
+{
+ Lock_DCQ (-1);
+
+ for (;;)
+ {
+ TFB_DrawCommand DC;
+
+ if (!TFB_DrawCommandQueue_Pop (&DC))
+ {
+ // the Queue is now empty.
+ break;
+ }
+
+ switch (DC.Type)
+ {
+ case TFB_DRAWCOMMANDTYPE_DELETEIMAGE:
+ {
+ TFB_Image *DC_image = DC.data.deleteimage.image;
+ TFB_DrawImage_Delete (DC_image);
+ break;
+ }
+ case TFB_DRAWCOMMANDTYPE_DELETEDATA:
+ {
+ void *data = DC.data.deletedata.data;
+ HFree (data);
+ break;
+ }
+ case TFB_DRAWCOMMANDTYPE_IMAGE:
+ {
+ TFB_ColorMap *cmap = DC.data.image.colormap;
+ if (cmap)
+ TFB_ReturnColorMap (cmap);
+ break;
+ }
+ case TFB_DRAWCOMMANDTYPE_SENDSIGNAL:
+ {
+ ClearSemaphore (DC.data.sendsignal.sem);
+ break;
+ }
+ }
+ }
+ Unlock_DCQ ();
+}
diff --git a/src/libs/graphics/dcqueue.h b/src/libs/graphics/dcqueue.h
new file mode 100644
index 0000000..deed685
--- /dev/null
+++ b/src/libs/graphics/dcqueue.h
@@ -0,0 +1,55 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef DCQUEUE_H
+#define DCQUEUE_H
+
+// Maximum size of the DCQ. The larger the DCQ, the larger frameskips
+// become tolerable before initiating livelock deterrence and game
+// slowdown. Other constants for controlling the frameskip/slowdown
+// balance may be found in sdl_common.c near TFB_FlushGraphics.
+
+// Livelock deterrance constants. Because the entire screen is rarely
+// refreshed, we may not drop draw commands on the floor with abandon.
+// Furthermore, if the main program is queuing commands at a speed
+// comparable to our processing of the commands, we never finish and
+// the game freezes. Thus, if the queue starts out larger than
+// DCQ_FORCE_SLOWDOWN_SIZE, or DCQ_LIVELOCK_MAX commands find
+// themselves being processed in one go, livelock deterrence is
+// enabled, and TFB_FlushGraphics locks the DCQ until it has processed
+// all entries. If batched but pending commands exceed DCQ_FORCE_BREAK_SIZE,
+// a continuity break is performed. This will effectively slow down the
+// game logic, a fate we seek to avoid - however, it seems to be unavoidable
+// on slower machines. Even there, it's seems nonexistent outside of
+// communications screens. --Michael
+
+#ifdef DCQ_OF_DOOM
+#define DCQ_MAX 512
+#define DCQ_FORCE_SLOWDOWN_SIZE 128
+#define DCQ_FORCE_BREAK_SIZE 512
+#define DCQ_LIVELOCK_MAX 256
+#else
+#define DCQ_MAX 16384
+#define DCQ_FORCE_SLOWDOWN_SIZE 4096
+#define DCQ_FORCE_BREAK_SIZE 16384
+#define DCQ_LIVELOCK_MAX 4096
+#endif
+
+extern CondVar RenderingCond;
+
+#endif
+
+
diff --git a/src/libs/graphics/drawable.c b/src/libs/graphics/drawable.c
new file mode 100644
index 0000000..9766bc7
--- /dev/null
+++ b/src/libs/graphics/drawable.c
@@ -0,0 +1,501 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "libs/gfxlib.h"
+#include "libs/graphics/context.h"
+#include "libs/graphics/drawable.h"
+#include "libs/graphics/tfb_draw.h"
+#include "libs/memlib.h"
+#include "tfb_draw.h"
+#include <math.h>
+
+#ifndef M_PI
+# define M_PI 3.14159265358979323846
+#endif
+
+FRAME _CurFramePtr;
+
+FRAME
+SetContextFGFrame (FRAME Frame)
+{
+ FRAME LastFrame;
+
+ if (Frame != (LastFrame = (FRAME)_CurFramePtr))
+ {
+ if (LastFrame)
+ DeactivateDrawable ();
+
+ _CurFramePtr = Frame;
+ if (_CurFramePtr)
+ ActivateDrawable ();
+
+ if (ContextActive ())
+ {
+ SwitchContextFGFrame (Frame);
+ }
+ }
+
+ return (LastFrame);
+}
+
+FRAME
+GetContextFGFrame (void)
+{
+ return _CurFramePtr;
+}
+
+static DRAWABLE
+request_drawable (COUNT NumFrames, DRAWABLE_TYPE DrawableType,
+ CREATE_FLAGS flags, SIZE width, SIZE height)
+{
+ DRAWABLE Drawable;
+ COUNT i;
+
+ Drawable = AllocDrawable (NumFrames);
+ if (!Drawable)
+ return NULL;
+
+ Drawable->Flags = flags;
+ Drawable->MaxIndex = NumFrames - 1;
+
+ for (i = 0; i < NumFrames; ++i)
+ {
+ FRAME FramePtr = &Drawable->Frame[i];
+
+ if (DrawableType == RAM_DRAWABLE && width > 0 && height > 0)
+ {
+ FramePtr->image = TFB_DrawImage_New (TFB_DrawCanvas_New_TrueColor (
+ width, height, (flags & WANT_ALPHA) ? TRUE : FALSE));
+ }
+
+ FramePtr->Type = DrawableType;
+ FramePtr->Index = i;
+ SetFrameBounds (FramePtr, width, height);
+ }
+
+ return Drawable;
+}
+
+DRAWABLE
+CreateDisplay (CREATE_FLAGS CreateFlags, SIZE *pwidth, SIZE *pheight)
+{
+ DRAWABLE Drawable;
+
+ // TODO: ScreenWidth and ScreenHeight should be passed in
+ // instead of returned.
+ Drawable = request_drawable (1, SCREEN_DRAWABLE,
+ (CreateFlags & (WANT_PIXMAP | WANT_MASK)),
+ ScreenWidth, ScreenHeight);
+ if (Drawable)
+ {
+ FRAME F;
+
+ F = CaptureDrawable (Drawable);
+ if (F == 0)
+ DestroyDrawable (Drawable);
+ else
+ {
+ *pwidth = GetFrameWidth (F);
+ *pheight = GetFrameHeight (F);
+
+ ReleaseDrawable (F);
+ return (Drawable);
+ }
+ }
+
+ *pwidth = *pheight = 0;
+ return (0);
+}
+
+DRAWABLE
+AllocDrawable (COUNT n)
+{
+ DRAWABLE Drawable;
+ Drawable = (DRAWABLE) HCalloc(sizeof (DRAWABLE_DESC));
+ if (Drawable)
+ {
+ int i;
+ Drawable->Frame = (FRAME)HMalloc (sizeof (FRAME_DESC) * n);
+ if (Drawable->Frame == NULL)
+ {
+ HFree (Drawable);
+ return NULL;
+ }
+
+ /* Zero out the newly allocated frames, since HMalloc doesn't have
+ * MEM_ZEROINIT. */
+ for (i = 0; i < n; i++) {
+ FRAME F;
+ F = &Drawable->Frame[i];
+ F->parent = Drawable;
+ F->Type = 0;
+ F->Index = 0;
+ F->image = 0;
+ F->Bounds.width = 0;
+ F->Bounds.height = 0;
+ F->HotSpot.x = 0;
+ F->HotSpot.y = 0;
+ }
+ }
+ return Drawable;
+}
+
+DRAWABLE
+CreateDrawable (CREATE_FLAGS CreateFlags, SIZE width, SIZE height, COUNT
+ num_frames)
+{
+ DRAWABLE Drawable;
+
+ Drawable = request_drawable (num_frames, RAM_DRAWABLE,
+ (CreateFlags & (WANT_MASK | WANT_PIXMAP
+ | WANT_ALPHA | MAPPED_TO_DISPLAY)),
+ width, height);
+ if (Drawable)
+ {
+ FRAME F;
+
+ F = CaptureDrawable (Drawable);
+ if (F)
+ {
+ ReleaseDrawable (F);
+
+ return (Drawable);
+ }
+ }
+
+ return (0);
+}
+
+BOOLEAN
+DestroyDrawable (DRAWABLE Drawable)
+{
+ if (_CurFramePtr && (Drawable == _CurFramePtr->parent))
+ SetContextFGFrame ((FRAME)NULL);
+
+ if (Drawable)
+ {
+ FreeDrawable (Drawable);
+
+ return (TRUE);
+ }
+
+ return (FALSE);
+}
+
+BOOLEAN
+GetFrameRect (FRAME FramePtr, RECT *pRect)
+{
+ if (FramePtr)
+ {
+ pRect->corner.x = -FramePtr->HotSpot.x;
+ pRect->corner.y = -FramePtr->HotSpot.y;
+ pRect->extent = GetFrameBounds (FramePtr);
+
+ return (TRUE);
+ }
+
+ return (FALSE);
+}
+
+HOT_SPOT
+SetFrameHot (FRAME FramePtr, HOT_SPOT HotSpot)
+{
+ if (FramePtr)
+ {
+ HOT_SPOT OldHot;
+
+ OldHot = FramePtr->HotSpot;
+ FramePtr->HotSpot = HotSpot;
+
+ return (OldHot);
+ }
+
+ return (MAKE_HOT_SPOT (0, 0));
+}
+
+HOT_SPOT
+GetFrameHot (FRAME FramePtr)
+{
+ if (FramePtr)
+ {
+ return FramePtr->HotSpot;
+ }
+
+ return (MAKE_HOT_SPOT (0, 0));
+}
+
+DRAWABLE
+RotateFrame (FRAME Frame, int angle_deg)
+{
+ DRAWABLE Drawable;
+ FRAME RotFramePtr;
+ double dx, dy;
+ double d;
+ double angle = angle_deg * M_PI / 180;
+
+ if (!Frame)
+ return NULL;
+
+ assert (Frame->Type != SCREEN_DRAWABLE);
+
+ Drawable = request_drawable (1, RAM_DRAWABLE, WANT_PIXMAP, 0, 0);
+ if (!Drawable)
+ return 0;
+ RotFramePtr = CaptureDrawable (Drawable);
+ if (!RotFramePtr)
+ {
+ FreeDrawable (Drawable);
+ return 0;
+ }
+
+ RotFramePtr->image = TFB_DrawImage_New_Rotated (
+ Frame->image, angle_deg);
+ SetFrameBounds (RotFramePtr, RotFramePtr->image->extent.width,
+ RotFramePtr->image->extent.height);
+
+ /* now we need to rotate the hot-spot, eww */
+ dx = Frame->HotSpot.x - (GetFrameWidth (Frame) / 2);
+ dy = Frame->HotSpot.y - (GetFrameHeight (Frame) / 2);
+ d = sqrt ((double)dx*dx + (double)dy*dy);
+ if ((int)d != 0)
+ {
+ double organg = atan2 (-dy, dx);
+ dx = cos (organg + angle) * d;
+ dy = -sin (organg + angle) * d;
+ }
+ RotFramePtr->HotSpot.x = (GetFrameWidth (RotFramePtr) / 2) + (int)dx;
+ RotFramePtr->HotSpot.y = (GetFrameHeight (RotFramePtr) / 2) + (int)dy;
+
+ ReleaseDrawable (RotFramePtr);
+
+ return Drawable;
+}
+
+// color.a is ignored
+void
+SetFrameTransparentColor (FRAME frame, Color color)
+{
+ TFB_Image *img;
+
+ if (!frame)
+ return;
+
+ assert (frame->Type != SCREEN_DRAWABLE);
+
+ img = frame->image;
+ LockMutex (img->mutex);
+
+ // TODO: This should defer to TFB_DrawImage instead
+ TFB_DrawCanvas_SetTransparentColor (img->NormalImg, color, FALSE);
+
+ UnlockMutex (img->mutex);
+}
+
+Color
+GetFramePixel (FRAME frame, POINT pixelPt)
+{
+ TFB_Image *img;
+ Color ret;
+
+ if (!frame)
+ return BUILD_COLOR_RGBA (0, 0, 0, 0);
+
+ assert (frame->Type != SCREEN_DRAWABLE);
+
+ img = frame->image;
+ LockMutex (img->mutex);
+
+ // TODO: This should defer to TFB_DrawImage instead
+ ret = TFB_DrawCanvas_GetPixel (img->NormalImg, pixelPt.x, pixelPt.y);
+
+ UnlockMutex (img->mutex);
+
+ return ret;
+}
+
+static FRAME
+makeMatchingFrame (FRAME frame, int width, int height)
+{
+ DRAWABLE drawable;
+ FRAME newFrame;
+ CREATE_FLAGS flags;
+
+ flags = GetFrameParentDrawable (frame)->Flags;
+ drawable = CreateDrawable (flags, width, height, 1);
+ if (!drawable)
+ return NULL;
+ newFrame = CaptureDrawable (drawable);
+ if (!newFrame)
+ {
+ FreeDrawable (drawable);
+ return NULL;
+ }
+
+ return newFrame;
+}
+
+// Creates an new DRAWABLE containing a copy of specified FRAME's rect
+// Source FRAME must not be a SCREEN_DRAWABLE
+DRAWABLE
+CopyFrameRect (FRAME frame, const RECT *area)
+{
+ FRAME newFrame;
+ POINT nullPt = MAKE_POINT (0, 0);
+
+ if (!frame)
+ return NULL;
+
+ assert (frame->Type != SCREEN_DRAWABLE);
+
+ newFrame = makeMatchingFrame (frame, area->extent.width,
+ area->extent.height);
+ if (!newFrame)
+ return NULL;
+
+ TFB_DrawImage_CopyRect (frame->image, area, newFrame->image, nullPt);
+
+ return ReleaseDrawable (newFrame);
+}
+
+// Creates an new DRAWABLE mostly identical to specified FRAME
+// Source FRAME must not be a SCREEN_DRAWABLE
+DRAWABLE
+CloneFrame (FRAME frame)
+{
+ FRAME newFrame;
+ RECT r;
+
+ if (!frame)
+ return NULL;
+
+ assert (frame->Type != SCREEN_DRAWABLE);
+
+ GetFrameRect (frame, &r);
+ r.corner.x = 0;
+ r.corner.y = 0;
+
+ newFrame = CaptureDrawable (CopyFrameRect (frame, &r));
+ if (!newFrame)
+ return NULL;
+
+ // copy the hot-spot
+ newFrame->HotSpot = frame->HotSpot;
+
+ return ReleaseDrawable (newFrame);
+}
+
+// Creates a new DRAWABLE of specified size and scales the passed
+// frame onto it. The aspect ratio is not preserved.
+DRAWABLE
+RescaleFrame (FRAME frame, int width, int height)
+{
+ FRAME newFrame;
+ TFB_Image *img;
+ TFB_Canvas src, dst;
+
+ if (!frame)
+ return NULL;
+
+ assert (frame->Type != SCREEN_DRAWABLE);
+
+ newFrame = makeMatchingFrame (frame, width, height);
+ if (!newFrame)
+ return NULL;
+
+ // scale the hot-spot
+ newFrame->HotSpot.x = frame->HotSpot.x * width / frame->Bounds.width;
+ newFrame->HotSpot.y = frame->HotSpot.y * height / frame->Bounds.height;
+
+ img = frame->image;
+ LockMutex (img->mutex);
+ // NOTE: We do not lock the target image because nothing has a
+ // reference to it yet!
+ src = img->NormalImg;
+ dst = newFrame->image->NormalImg;
+ TFB_DrawCanvas_Rescale_Nearest (src, dst, -1, NULL, NULL, NULL);
+
+ UnlockMutex (img->mutex);
+
+ return ReleaseDrawable (newFrame);
+}
+
+BOOLEAN
+ReadFramePixelColors (FRAME frame, Color *pixels, int width, int height)
+{
+ TFB_Image *img;
+
+ if (!frame)
+ return FALSE;
+
+ assert (frame->Type != SCREEN_DRAWABLE);
+
+ // TODO: Do we need to lock the img->mutex here?
+ img = frame->image;
+ return TFB_DrawCanvas_GetPixelColors (img->NormalImg, pixels,
+ width, height);
+}
+
+// Warning: this functions bypasses DCQ, which is why it is not a DrawXXX
+BOOLEAN
+WriteFramePixelColors (FRAME frame, const Color *pixels, int width, int height)
+{
+ TFB_Image *img;
+
+ if (!frame)
+ return FALSE;
+
+ assert (frame->Type != SCREEN_DRAWABLE);
+
+ // TODO: Do we need to lock the img->mutex here?
+ img = frame->image;
+ return TFB_DrawCanvas_SetPixelColors (img->NormalImg, pixels,
+ width, height);
+}
+
+BOOLEAN
+ReadFramePixelIndexes (FRAME frame, BYTE *pixels, int width, int height)
+{
+ TFB_Image *img;
+
+ if (!frame)
+ return FALSE;
+
+ assert (frame->Type != SCREEN_DRAWABLE);
+
+ // TODO: Do we need to lock the img->mutex here?
+ img = frame->image;
+ return TFB_DrawCanvas_GetPixelIndexes (img->NormalImg, pixels,
+ width, height);
+}
+
+// Warning: this functions bypasses DCQ, which is why it is not a DrawXXX
+BOOLEAN
+WriteFramePixelIndexes (FRAME frame, const BYTE *pixels, int width, int height)
+{
+ TFB_Image *img;
+
+ if (!frame)
+ return FALSE;
+
+ assert (frame->Type != SCREEN_DRAWABLE);
+
+ // TODO: Do we need to lock the img->mutex here?
+ img = frame->image;
+ return TFB_DrawCanvas_SetPixelIndexes (img->NormalImg, pixels,
+ width, height);
+}
diff --git a/src/libs/graphics/drawable.h b/src/libs/graphics/drawable.h
new file mode 100644
index 0000000..98f5ada
--- /dev/null
+++ b/src/libs/graphics/drawable.h
@@ -0,0 +1,88 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_GRAPHICS_DRAWABLE_H_
+#define LIBS_GRAPHICS_DRAWABLE_H_
+
+#include <stdio.h>
+#include "tfb_draw.h"
+
+#define ValidPrimType(pt) ((pt)<NUM_PRIMS)
+
+typedef struct bresenham_line
+{
+ POINT first, second;
+ SIZE abs_delta_x, abs_delta_y;
+ SIZE error_term;
+ BOOLEAN end_points_exchanged;
+ INTERSECT_CODE intersect_code;
+} BRESENHAM_LINE;
+
+typedef UWORD DRAWABLE_TYPE;
+#define ROM_DRAWABLE 0
+#define RAM_DRAWABLE 1
+#define SCREEN_DRAWABLE 2
+
+struct frame_desc
+{
+ DRAWABLE_TYPE Type;
+ UWORD Index;
+ HOT_SPOT HotSpot;
+ EXTENT Bounds;
+ TFB_Image *image;
+ struct drawable_desc *parent;
+};
+
+struct drawable_desc
+{
+ CREATE_FLAGS Flags;
+ UWORD MaxIndex;
+ FRAME_DESC *Frame;
+};
+
+#define GetFrameWidth(f) ((f)->Bounds.width)
+#define GetFrameHeight(f) ((f)->Bounds.height)
+#define GetFrameBounds(f) ((f)->Bounds)
+#define SetFrameBounds(f,w,h) \
+ ((f)->Bounds.width=(w), \
+ ((f))->Bounds.height=(h))
+
+#define DRAWABLE_PRIORITY DEFAULT_MEM_PRIORITY
+
+extern DRAWABLE AllocDrawable (COUNT num_frames);
+#define FreeDrawable(D) _ReleaseCelData (D)
+
+typedef struct
+{
+ RECT Box;
+ FRAME FramePtr;
+} IMAGE_BOX;
+
+extern INTERSECT_CODE _clip_line (const RECT *pClipRect,
+ BRESENHAM_LINE *pLine);
+
+extern void *_GetCelData (uio_Stream *fp, DWORD length);
+extern BOOLEAN _ReleaseCelData (void *handle);
+
+extern FRAME _CurFramePtr;
+
+// ClipRect is relative to ctxOrigin
+extern void _text_blt (RECT *pClipRect, TEXT *TextPtr, POINT ctxOrigin);
+
+#endif /* LIBS_GRAPHICS_DRAWABLE_H_ */
+
diff --git a/src/libs/graphics/drawcmd.h b/src/libs/graphics/drawcmd.h
new file mode 100644
index 0000000..6d44153
--- /dev/null
+++ b/src/libs/graphics/drawcmd.h
@@ -0,0 +1,202 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef DRAWCMD_H
+#define DRAWCMD_H
+
+#include "libs/graphics/tfb_draw.h"
+
+enum
+{
+ TFB_DRAWCOMMANDTYPE_LINE,
+ TFB_DRAWCOMMANDTYPE_RECTANGLE,
+ TFB_DRAWCOMMANDTYPE_IMAGE,
+ TFB_DRAWCOMMANDTYPE_FILLEDIMAGE,
+ TFB_DRAWCOMMANDTYPE_FONTCHAR,
+
+ TFB_DRAWCOMMANDTYPE_COPY,
+ TFB_DRAWCOMMANDTYPE_COPYTOIMAGE,
+
+ TFB_DRAWCOMMANDTYPE_SCISSORENABLE,
+ TFB_DRAWCOMMANDTYPE_SCISSORDISABLE,
+
+ TFB_DRAWCOMMANDTYPE_SETMIPMAP,
+ TFB_DRAWCOMMANDTYPE_DELETEIMAGE,
+ TFB_DRAWCOMMANDTYPE_DELETEDATA,
+ TFB_DRAWCOMMANDTYPE_SENDSIGNAL,
+ TFB_DRAWCOMMANDTYPE_REINITVIDEO,
+ TFB_DRAWCOMMANDTYPE_CALLBACK,
+};
+
+typedef struct tfb_dc_line
+{
+ int x1, y1, x2, y2;
+ Color color;
+ DrawMode drawMode;
+ SCREEN destBuffer;
+} TFB_DrawCommand_Line;
+
+typedef struct tfb_dc_rect
+{
+ RECT rect;
+ Color color;
+ DrawMode drawMode;
+ SCREEN destBuffer;
+} TFB_DrawCommand_Rect;
+
+typedef struct tfb_dc_img
+{
+ TFB_Image *image;
+ int x, y;
+ SCREEN destBuffer;
+ TFB_ColorMap *colormap;
+ DrawMode drawMode;
+ int scale;
+ int scaleMode;
+} TFB_DrawCommand_Image;
+
+typedef struct tfb_dc_filledimg
+{
+ TFB_Image *image;
+ int x, y;
+ Color color;
+ SCREEN destBuffer;
+ DrawMode drawMode;
+ int scale;
+ int scaleMode;
+} TFB_DrawCommand_FilledImage;
+
+typedef struct tfb_dc_fontchar
+{
+ TFB_Char *fontchar;
+ TFB_Image *backing;
+ int x, y;
+ DrawMode drawMode;
+ SCREEN destBuffer;
+} TFB_DrawCommand_FontChar;
+
+typedef struct tfb_dc_copy
+{
+ RECT rect;
+ SCREEN srcBuffer, destBuffer;
+} TFB_DrawCommand_Copy;
+
+typedef struct tfb_dc_copyimg
+{
+ TFB_Image *image;
+ RECT rect;
+ SCREEN srcBuffer;
+} TFB_DrawCommand_CopyToImage;
+
+typedef struct tfb_dc_scissor
+{
+ RECT rect;
+} TFB_DrawCommand_Scissor;
+
+typedef struct tfb_dc_setmip
+{
+ TFB_Image *image;
+ TFB_Image *mipmap;
+ int hotx, hoty;
+} TFB_DrawCommand_SetMipmap;
+
+typedef struct tfb_dc_delimg
+{
+ TFB_Image *image;
+} TFB_DrawCommand_DeleteImage;
+
+typedef struct tfb_dc_deldata
+{
+ void *data;
+ // data must be a result of HXalloc() call
+} TFB_DrawCommand_DeleteData;
+
+typedef struct tfb_dc_signal
+{
+ Semaphore sem;
+} TFB_DrawCommand_SendSignal;
+
+typedef struct tfb_dc_reinit_video
+{
+ int driver, flags, width, height;
+} TFB_DrawCommand_ReinitVideo;
+
+typedef struct tfb_dc_callback
+{
+ void (*callback)(void *arg);
+ void *arg;
+} TFB_DrawCommand_Callback;
+
+typedef struct tfb_drawcommand
+{
+ int Type;
+ union {
+ TFB_DrawCommand_Line line;
+ TFB_DrawCommand_Rect rect;
+ TFB_DrawCommand_Image image;
+ TFB_DrawCommand_FilledImage filledimage;
+ TFB_DrawCommand_FontChar fontchar;
+ TFB_DrawCommand_Copy copy;
+ TFB_DrawCommand_CopyToImage copytoimage;
+ TFB_DrawCommand_Scissor scissor;
+ TFB_DrawCommand_SetMipmap setmipmap;
+ TFB_DrawCommand_DeleteImage deleteimage;
+ TFB_DrawCommand_DeleteData deletedata;
+ TFB_DrawCommand_SendSignal sendsignal;
+ TFB_DrawCommand_ReinitVideo reinitvideo;
+ TFB_DrawCommand_Callback callback;
+ } data;
+} TFB_DrawCommand;
+
+// Queue Stuff
+
+typedef struct tfb_drawcommandqueue
+{
+ int Front;
+ int Back;
+ int InsertionPoint;
+ int Batching;
+ volatile int FullSize;
+ volatile int Size;
+} TFB_DrawCommandQueue;
+
+void Init_DrawCommandQueue (void);
+
+void Uninit_DrawCommandQueue (void);
+
+void TFB_BatchGraphics (void);
+
+void TFB_UnbatchGraphics (void);
+
+void TFB_BatchReset (void);
+
+void TFB_DrawCommandQueue_Push (TFB_DrawCommand* Command);
+
+int TFB_DrawCommandQueue_Pop (TFB_DrawCommand* Command);
+
+void TFB_DrawCommandQueue_Clear (void);
+
+extern TFB_DrawCommandQueue DrawCommandQueue;
+
+void TFB_EnqueueDrawCommand (TFB_DrawCommand* DrawCommand);
+
+void Lock_DCQ (int slots);
+
+void Unlock_DCQ (void);
+
+#endif
diff --git a/src/libs/graphics/filegfx.c b/src/libs/graphics/filegfx.c
new file mode 100644
index 0000000..c6af7cd
--- /dev/null
+++ b/src/libs/graphics/filegfx.c
@@ -0,0 +1,72 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "gfxintrn.h"
+#include "options.h"
+#include "libs/reslib.h"
+
+
+DRAWABLE
+LoadGraphicFile (const char *pStr)
+{
+ uio_Stream *fp;
+
+ // FIXME: this theoretically needs a mechanism to prevent races
+ if (_cur_resfile_name)
+ // something else is loading resources atm
+ return 0;
+
+ fp = res_OpenResFile (contentDir, pStr, "rb");
+ if (fp != NULL)
+ {
+ DRAWABLE hData;
+
+ _cur_resfile_name = pStr;
+ hData = (DRAWABLE)_GetCelData (fp, LengthResFile (fp));
+ _cur_resfile_name = 0;
+ res_CloseResFile (fp);
+ return hData;
+ }
+
+ return (NULL);
+}
+
+FONT
+LoadFontFile (const char *pStr)
+{
+ uio_Stream *fp;
+
+ // FIXME: this theoretically needs a mechanism to prevent races
+ if (_cur_resfile_name)
+ // something else is loading resources atm
+ return 0;
+
+ fp = res_OpenResFile (contentDir, pStr, "rb");
+ if (fp != NULL)
+ {
+ FONT hData;
+
+ _cur_resfile_name = pStr;
+ hData = (FONT)_GetFontData (fp, LengthResFile (fp));
+ _cur_resfile_name = 0;
+ res_CloseResFile (fp);
+ return hData;
+ }
+
+ return (0);
+}
diff --git a/src/libs/graphics/font.c b/src/libs/graphics/font.c
new file mode 100644
index 0000000..638c814
--- /dev/null
+++ b/src/libs/graphics/font.c
@@ -0,0 +1,334 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "gfxintrn.h"
+#include "tfb_prim.h"
+#include "libs/log.h"
+
+static inline TFB_Char *getCharFrame (FONT_DESC *fontPtr, UniChar ch);
+
+
+FONT
+SetContextFont (FONT Font)
+{
+ FONT LastFont;
+
+ LastFont = _CurFontPtr;
+ _CurFontPtr = Font;
+ if (ContextActive ())
+ SwitchContextFont (Font);
+
+ return (LastFont);
+}
+
+BOOLEAN
+DestroyFont (FONT FontRef)
+{
+ if (FontRef == NULL)
+ return (FALSE);
+
+ if (_CurFontPtr && _CurFontPtr == FontRef)
+ SetContextFont ((FONT)NULL);
+
+ return (FreeFont (FontRef));
+}
+
+// XXX: Should be in frame.c (renamed to something decent?)
+void
+font_DrawText (TEXT *lpText)
+{
+ RECT ClipRect;
+ POINT origin;
+ TEXT text;
+
+ FixContextFontEffect ();
+ if (!GraphicsSystemActive () || !GetContextValidRect (NULL, &origin))
+ return;
+
+ // TextRect() clobbers TEXT.CharCount so we have to make a copy
+ text = *lpText;
+ if (!TextRect (&text, &ClipRect, NULL))
+ return;
+ // ClipRect is relative to origin
+ _text_blt (&ClipRect, &text, origin);
+}
+
+/* Draw the stroke by drawing the same text in the
+ * background color one pixel shifted to all 4 directions.
+ */
+void
+font_DrawTracedText (TEXT *pText, Color text, Color trace)
+{
+ // Preserve current foreground color for full correctness
+ Color oldfg = SetContextForeGroundColor (trace);
+ pText->baseline.x--;
+ font_DrawText (pText);
+ pText->baseline.x += 2;
+ font_DrawText (pText);
+ pText->baseline.x--;
+ pText->baseline.y--;
+ font_DrawText (pText);
+ pText->baseline.y += 2;
+ font_DrawText (pText);
+ pText->baseline.y--;
+ SetContextForeGroundColor (text);
+ font_DrawText (pText);
+ SetContextForeGroundColor (oldfg);
+}
+
+BOOLEAN
+GetContextFontLeading (SIZE *pheight)
+{
+ if (_CurFontPtr != 0)
+ {
+ *pheight = (SIZE)_CurFontPtr->Leading;
+ return (TRUE);
+ }
+
+ *pheight = 0;
+ return (FALSE);
+}
+
+BOOLEAN
+GetContextFontLeadingWidth (SIZE *pwidth)
+{
+ if (_CurFontPtr != 0)
+ {
+ *pwidth = (SIZE)_CurFontPtr->LeadingWidth;
+ return (TRUE);
+ }
+
+ *pwidth = 0;
+ return (FALSE);
+}
+
+BOOLEAN
+TextRect (TEXT *lpText, RECT *pRect, BYTE *pdelta)
+{
+ BYTE char_delta_array[MAX_DELTAS];
+ FONT FontPtr;
+
+ FontPtr = _CurFontPtr;
+ if (FontPtr != 0 && lpText->CharCount != 0)
+ {
+ COORD top_y, bot_y;
+ SIZE width;
+ UniChar next_ch = 0;
+ const char *pStr;
+ COUNT num_chars;
+
+ num_chars = lpText->CharCount;
+ /* At this point lpText->CharCount contains the *maximum* number of
+ * characters that lpText->pStr may contain.
+ * After the while loop below, it will contain the actual number.
+ */
+ if (pdelta == 0)
+ {
+ pdelta = char_delta_array;
+ if (num_chars > MAX_DELTAS)
+ {
+ num_chars = MAX_DELTAS;
+ lpText->CharCount = MAX_DELTAS;
+ }
+ }
+
+ top_y = 0;
+ bot_y = 0;
+ width = 0;
+ pStr = lpText->pStr;
+ if (num_chars > 0)
+ {
+ next_ch = getCharFromString (&pStr);
+ if (next_ch == '\0')
+ num_chars = 0;
+ }
+ while (num_chars--)
+ {
+ UniChar ch;
+ SIZE last_width;
+ TFB_Char *charFrame;
+
+ last_width = width;
+
+ ch = next_ch;
+ if (num_chars > 0)
+ {
+ next_ch = getCharFromString (&pStr);
+ if (next_ch == '\0')
+ {
+ lpText->CharCount -= num_chars;
+ num_chars = 0;
+ }
+ }
+
+ charFrame = getCharFrame (FontPtr, ch);
+ if (charFrame != NULL && charFrame->disp.width)
+ {
+ COORD y;
+
+ y = -charFrame->HotSpot.y;
+ if (y < top_y)
+ top_y = y;
+ y += charFrame->disp.height;
+ if (y > bot_y)
+ bot_y = y;
+
+ width += charFrame->disp.width;
+#if 0
+ if (num_chars && next_ch < (UNICODE) MAX_CHARS
+ && !(FontPtr->KernTab[ch]
+ & (FontPtr->KernTab[next_ch] >> 2)))
+ width -= FontPtr->KernAmount;
+#endif
+ }
+
+ *pdelta++ = (BYTE)(width - last_width);
+ }
+
+ if (width > 0 && (bot_y -= top_y) > 0)
+ {
+ /* subtract off default character spacing */
+ if (pdelta[-1] > 0)
+ {
+ --pdelta[-1];
+ --width;
+ }
+
+ if (lpText->align == ALIGN_LEFT)
+ pRect->corner.x = 0;
+ else if (lpText->align == ALIGN_CENTER)
+ pRect->corner.x = -(width >> 1);
+ else
+ pRect->corner.x = -width;
+ pRect->corner.y = top_y;
+ pRect->extent.width = width;
+ pRect->extent.height = bot_y;
+
+ pRect->corner.x += lpText->baseline.x;
+ pRect->corner.y += lpText->baseline.y;
+
+ return (TRUE);
+ }
+ }
+
+ pRect->corner = lpText->baseline;
+ pRect->extent.width = 0;
+ pRect->extent.height = 0;
+
+ return (FALSE);
+}
+
+void
+_text_blt (RECT *pClipRect, TEXT *TextPtr, POINT ctxOrigin)
+{
+ FONT FontPtr;
+ COUNT num_chars;
+ UniChar next_ch;
+ const char *pStr;
+ POINT origin;
+ TFB_Image *backing;
+ DrawMode mode = _get_context_draw_mode ();
+
+ FontPtr = _CurFontPtr;
+ if (FontPtr == NULL)
+ return;
+ backing = _get_context_font_backing ();
+ if (!backing)
+ return;
+
+ origin.x = pClipRect->corner.x;
+ origin.y = TextPtr->baseline.y;
+ num_chars = TextPtr->CharCount;
+ if (num_chars == 0)
+ return;
+
+ pStr = TextPtr->pStr;
+
+ next_ch = getCharFromString (&pStr);
+ if (next_ch == '\0')
+ num_chars = 0;
+ while (num_chars--)
+ {
+ UniChar ch;
+ TFB_Char* fontChar;
+
+ ch = next_ch;
+ if (num_chars > 0)
+ {
+ next_ch = getCharFromString (&pStr);
+ if (next_ch == '\0')
+ num_chars = 0;
+ }
+
+ fontChar = getCharFrame (FontPtr, ch);
+ if (fontChar != NULL && fontChar->disp.width)
+ {
+ RECT r;
+
+ r.corner.x = origin.x - fontChar->HotSpot.x;
+ r.corner.y = origin.y - fontChar->HotSpot.y;
+ r.extent.width = fontChar->disp.width;
+ r.extent.height = fontChar->disp.height;
+ if (BoxIntersect (&r, pClipRect, &r))
+ {
+ TFB_Prim_FontChar (origin, fontChar, backing, mode,
+ ctxOrigin);
+ }
+
+ origin.x += fontChar->disp.width;
+#if 0
+ if (num_chars && next_ch < (UNICODE) MAX_CHARS
+ && !(FontPtr->KernTab[ch]
+ & (FontPtr->KernTab[next_ch] >> 2)))
+ origin.x -= FontPtr->KernAmount;
+#endif
+ }
+ }
+}
+
+static inline TFB_Char *
+getCharFrame (FONT_DESC *fontPtr, UniChar ch)
+{
+ UniChar pageStart = ch & CHARACTER_PAGE_MASK;
+ size_t charIndex;
+
+ FONT_PAGE *page = fontPtr->fontPages;
+ for (;;)
+ {
+ if (page == NULL)
+ return NULL;
+
+ if (page->pageStart == pageStart)
+ break;
+
+ page = page->next;
+ }
+
+ charIndex = ch - page->firstChar;
+ if (ch >= page->firstChar && charIndex < page->numChars
+ && page->charDesc[charIndex].data)
+ {
+ return &page->charDesc[charIndex];
+ }
+ else
+ {
+ //log_add (log_Debug, "Character %u not present", (unsigned int) ch);
+ return NULL;
+ }
+}
+
diff --git a/src/libs/graphics/font.h b/src/libs/graphics/font.h
new file mode 100644
index 0000000..b6bd13b
--- /dev/null
+++ b/src/libs/graphics/font.h
@@ -0,0 +1,71 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_GRAPHICS_FONT_H_
+#define LIBS_GRAPHICS_FONT_H_
+
+#include "libs/memlib.h"
+
+#define MAX_DELTAS 100
+
+typedef struct FontPage
+{
+ struct FontPage *next;
+ UniChar pageStart;
+#define CHARACTER_PAGE_MASK 0xfffff800
+ UniChar firstChar;
+ size_t numChars;
+ TFB_Char *charDesc;
+} FONT_PAGE;
+
+static inline FONT_PAGE *
+AllocFontPage (int numChars)
+{
+ FONT_PAGE *result = HMalloc (sizeof (FONT_PAGE));
+ result->charDesc = HCalloc (numChars * sizeof *result->charDesc);
+ return result;
+}
+
+static inline void
+FreeFontPage (FONT_PAGE *page)
+{
+ HFree (page->charDesc);
+ HFree (page);
+}
+
+struct font_desc
+{
+ UWORD Leading;
+ UWORD LeadingWidth;
+ FONT_PAGE *fontPages;
+};
+
+#define CHAR_DESCPTR PCHAR_DESC
+
+#define FONT_PRIORITY DEFAULT_MEM_PRIORITY
+
+#define AllocFont(size) (FONT)HCalloc (sizeof (FONT_DESC) + (size))
+#define FreeFont _ReleaseFontData
+
+extern FONT _CurFontPtr;
+
+extern void *_GetFontData (uio_Stream *fp, DWORD length);
+extern BOOLEAN _ReleaseFontData (void *handle);
+
+#endif /* LIBS_GRAPHICS_FONT_H_ */
+
diff --git a/src/libs/graphics/frame.c b/src/libs/graphics/frame.c
new file mode 100644
index 0000000..0539746
--- /dev/null
+++ b/src/libs/graphics/frame.c
@@ -0,0 +1,266 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "gfxintrn.h"
+#include "gfx_common.h"
+#include "tfb_draw.h"
+#include "tfb_prim.h"
+
+HOT_SPOT
+MAKE_HOT_SPOT (COORD x, COORD y)
+{
+ HOT_SPOT hs;
+ hs.x = x;
+ hs.y = y;
+ return hs;
+}
+
+// XXX: INTERNAL_PRIMITIVE and INTERNAL_PRIM_DESC are not used
+typedef union
+{
+ POINT Point;
+ STAMP Stamp;
+ BRESENHAM_LINE Line;
+ TEXT Text;
+ RECT Rect;
+} INTERNAL_PRIM_DESC;
+
+typedef struct
+{
+ PRIM_LINKS Links;
+ GRAPHICS_PRIM Type;
+ Color Color;
+ INTERNAL_PRIM_DESC Object;
+} INTERNAL_PRIMITIVE;
+
+
+// pValidRect or origin may be NULL
+BOOLEAN
+GetContextValidRect (RECT *pValidRect, POINT *origin)
+{
+ RECT tempRect;
+ POINT tempPt;
+
+ if (!pValidRect)
+ pValidRect = &tempRect;
+ if (!origin)
+ origin = &tempPt;
+
+ // Start with a rect the size of foreground frame
+ pValidRect->corner.x = 0;
+ pValidRect->corner.y = 0;
+ pValidRect->extent = GetFrameBounds (_CurFramePtr);
+ *origin = _CurFramePtr->HotSpot;
+
+ if (_pCurContext->ClipRect.extent.width)
+ {
+ // If the cliprect is completely outside of the valid frame
+ // bounds we have nothing to draw
+ if (!BoxIntersect (&_pCurContext->ClipRect,
+ pValidRect, pValidRect))
+ return (FALSE);
+
+ // Foreground frame hotspot defines a drawing position offset
+ // WRT the context cliprect
+ origin->x += _pCurContext->ClipRect.corner.x;
+ origin->y += _pCurContext->ClipRect.corner.y;
+ }
+
+ return (TRUE);
+}
+
+static void
+ClearBackGround (RECT *pClipRect)
+{
+ RECT clearRect;
+ Color color = _get_context_bg_color ();
+ clearRect.corner.x = 0;
+ clearRect.corner.y = 0;
+ clearRect.extent = pClipRect->extent;
+ TFB_Prim_FillRect (&clearRect, color, DRAW_REPLACE_MODE,
+ pClipRect->corner);
+}
+
+void
+DrawBatch (PRIMITIVE *lpBasePrim, PRIM_LINKS PrimLinks,
+ BATCH_FLAGS BatchFlags)
+{
+ RECT ValidRect;
+ POINT origin;
+
+ if (GraphicsSystemActive () && GetContextValidRect (&ValidRect, &origin))
+ {
+ COUNT CurIndex;
+ PRIMITIVE *lpPrim;
+ DrawMode mode = _get_context_draw_mode ();
+
+ BatchGraphics ();
+
+ if (BatchFlags & BATCH_BUILD_PAGE)
+ {
+ ClearBackGround (&ValidRect);
+ }
+
+ CurIndex = GetPredLink (PrimLinks);
+
+ for (; CurIndex != END_OF_LIST;
+ CurIndex = GetSuccLink (GetPrimLinks (lpPrim)))
+ {
+ GRAPHICS_PRIM PrimType;
+ PRIMITIVE *lpWorkPrim;
+ RECT ClipRect;
+ Color color;
+
+ lpPrim = &lpBasePrim[CurIndex];
+ PrimType = GetPrimType (lpPrim);
+ if (!ValidPrimType (PrimType))
+ continue;
+
+ lpWorkPrim = lpPrim;
+
+ switch (PrimType)
+ {
+ case POINT_PRIM:
+ color = GetPrimColor (lpWorkPrim);
+ TFB_Prim_Point (&lpWorkPrim->Object.Point, color,
+ mode, origin);
+ break;
+ case STAMP_PRIM:
+ TFB_Prim_Stamp (&lpWorkPrim->Object.Stamp, mode, origin);
+ break;
+ case STAMPFILL_PRIM:
+ color = GetPrimColor (lpWorkPrim);
+ TFB_Prim_StampFill (&lpWorkPrim->Object.Stamp, color,
+ mode, origin);
+ break;
+ case LINE_PRIM:
+ color = GetPrimColor (lpWorkPrim);
+ TFB_Prim_Line (&lpWorkPrim->Object.Line, color,
+ mode, origin);
+ break;
+ case TEXT_PRIM:
+ if (!TextRect (&lpWorkPrim->Object.Text, &ClipRect, NULL))
+ continue;
+ // ClipRect is relative to origin
+ _text_blt (&ClipRect, &lpWorkPrim->Object.Text, origin);
+ break;
+ case RECT_PRIM:
+ color = GetPrimColor (lpWorkPrim);
+ TFB_Prim_Rect (&lpWorkPrim->Object.Rect, color,
+ mode, origin);
+ break;
+ case RECTFILL_PRIM:
+ color = GetPrimColor (lpWorkPrim);
+ TFB_Prim_FillRect (&lpWorkPrim->Object.Rect, color,
+ mode, origin);
+ break;
+ }
+ }
+
+ UnbatchGraphics ();
+ }
+}
+
+void
+ClearDrawable (void)
+{
+ RECT ValidRect;
+
+ if (GraphicsSystemActive () && GetContextValidRect (&ValidRect, NULL))
+ {
+ ClearBackGround (&ValidRect);
+ }
+}
+
+void
+DrawPoint (POINT *lpPoint)
+{
+ POINT origin;
+
+ if (GraphicsSystemActive () && GetContextValidRect (NULL, &origin))
+ {
+ Color color = GetPrimColor (&_locPrim);
+ DrawMode mode = _get_context_draw_mode ();
+ TFB_Prim_Point (lpPoint, color, mode, origin);
+ }
+}
+
+void
+DrawRectangle (RECT *lpRect)
+{
+ POINT origin;
+
+ if (GraphicsSystemActive () && GetContextValidRect (NULL, &origin))
+ {
+ Color color = GetPrimColor (&_locPrim);
+ DrawMode mode = _get_context_draw_mode ();
+ TFB_Prim_Rect (lpRect, color, mode, origin);
+ }
+}
+
+void
+DrawFilledRectangle (RECT *lpRect)
+{
+ POINT origin;
+
+ if (GraphicsSystemActive () && GetContextValidRect (NULL, &origin))
+ {
+ Color color = GetPrimColor (&_locPrim);
+ DrawMode mode = _get_context_draw_mode ();
+ TFB_Prim_FillRect (lpRect, color, mode, origin);
+ }
+}
+
+void
+DrawLine (LINE *lpLine)
+{
+ POINT origin;
+
+ if (GraphicsSystemActive () && GetContextValidRect (NULL, &origin))
+ {
+ Color color = GetPrimColor (&_locPrim);
+ DrawMode mode = _get_context_draw_mode ();
+ TFB_Prim_Line (lpLine, color, mode, origin);
+ }
+}
+
+void
+DrawStamp (STAMP *stmp)
+{
+ POINT origin;
+
+ if (GraphicsSystemActive () && GetContextValidRect (NULL, &origin))
+ {
+ DrawMode mode = _get_context_draw_mode ();
+ TFB_Prim_Stamp (stmp, mode, origin);
+ }
+}
+
+void
+DrawFilledStamp (STAMP *stmp)
+{
+ POINT origin;
+
+ if (GraphicsSystemActive () && GetContextValidRect (NULL, &origin))
+ {
+ Color color = GetPrimColor (&_locPrim);
+ DrawMode mode = _get_context_draw_mode ();
+ TFB_Prim_StampFill (stmp, color, mode, origin);
+ }
+}
+
diff --git a/src/libs/graphics/gfx_common.c b/src/libs/graphics/gfx_common.c
new file mode 100644
index 0000000..405f614
--- /dev/null
+++ b/src/libs/graphics/gfx_common.c
@@ -0,0 +1,196 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "gfxintrn.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/graphics/drawcmd.h"
+#include "libs/timelib.h"
+#include "libs/misc.h"
+ // for TFB_DEBUG_HALT
+
+
+int ScreenWidth;
+int ScreenHeight;
+int ScreenWidthActual;
+int ScreenHeightActual;
+int ScreenColorDepth;
+int GraphicsDriver;
+int TFB_DEBUG_HALT = 0;
+
+volatile int TransitionAmount = 255;
+RECT TransitionClipRect;
+
+static int gscale = GSCALE_IDENTITY;
+static int gscale_mode = TFB_SCALE_NEAREST;
+
+void
+DrawFromExtraScreen (RECT *r)
+{
+ TFB_DrawScreen_Copy(r, TFB_SCREEN_EXTRA, TFB_SCREEN_MAIN);
+}
+
+void
+LoadIntoExtraScreen (RECT *r)
+{
+ TFB_DrawScreen_Copy(r, TFB_SCREEN_MAIN, TFB_SCREEN_EXTRA);
+}
+
+int
+SetGraphicScale (int scale)
+{
+ int old_scale = gscale;
+ gscale = (scale ? scale : GSCALE_IDENTITY);
+ return old_scale;
+}
+
+int
+GetGraphicScale (void)
+{
+ return gscale;
+}
+
+int
+SetGraphicScaleMode (int mode)
+{
+ int old_mode = gscale_mode;
+ assert (mode >= TFB_SCALE_NEAREST && mode <= TFB_SCALE_TRILINEAR);
+ gscale_mode = mode;
+ return old_mode;
+}
+
+int
+GetGraphicScaleMode (void)
+{
+ return gscale_mode;
+}
+
+/* Batching and Unbatching functions. A "Batch" is a collection of
+ DrawCommands that will never be flipped to the screen half-rendered.
+ BatchGraphics and UnbatchGraphics function vaguely like a non-blocking
+ recursive lock to do this respect. */
+void
+BatchGraphics (void)
+{
+ TFB_BatchGraphics ();
+}
+
+void
+UnbatchGraphics (void)
+{
+ TFB_UnbatchGraphics ();
+}
+
+/* Sleeps this thread until all Draw Commands queued by that thread have
+ been processed. */
+
+void
+FlushGraphics (void)
+{
+ TFB_DrawScreen_WaitForSignal ();
+}
+
+static void
+ExpandRect (RECT *rect, int expansion)
+{
+ if (rect->corner.x - expansion >= 0)
+ {
+ rect->extent.width += expansion;
+ rect->corner.x -= expansion;
+ }
+ else
+ {
+ rect->extent.width += rect->corner.x;
+ rect->corner.x = 0;
+ }
+
+ if (rect->corner.y - expansion >= 0)
+ {
+ rect->extent.height += expansion;
+ rect->corner.y -= expansion;
+ }
+ else
+ {
+ rect->extent.height += rect->corner.y;
+ rect->corner.y = 0;
+ }
+
+ if (rect->corner.x + rect->extent.width + expansion <= ScreenWidth)
+ rect->extent.width += expansion;
+ else
+ rect->extent.width = ScreenWidth - rect->corner.x;
+
+ if (rect->corner.y + rect->extent.height + expansion <= ScreenHeight)
+ rect->extent.height += expansion;
+ else
+ rect->extent.height = ScreenHeight - rect->corner.y;
+}
+
+void
+SetTransitionSource (const RECT *pRect)
+{
+ RECT ActualRect;
+
+ if (pRect)
+ { /* expand the rect to accomodate scalers in OpenGL mode */
+ ActualRect = *pRect;
+ pRect = &ActualRect;
+ ExpandRect (&ActualRect, 2);
+ }
+ TFB_DrawScreen_Copy (pRect, TFB_SCREEN_MAIN, TFB_SCREEN_TRANSITION);
+}
+
+// ScreenTransition() is synchronous (does not return until transition done)
+void
+ScreenTransition (int TransType, const RECT *pRect)
+{
+ const TimePeriod DURATION = ONE_SECOND * 31 / 60;
+ TimeCount startTime;
+ (void) TransType; /* dodge compiler warning */
+
+ if (pRect)
+ {
+ TransitionClipRect = *pRect;
+ }
+ else
+ {
+ TransitionClipRect.corner.x = 0;
+ TransitionClipRect.corner.y = 0;
+ TransitionClipRect.extent.width = ScreenWidth;
+ TransitionClipRect.extent.height = ScreenHeight;
+ }
+
+ TFB_UploadTransitionScreen ();
+
+ TransitionAmount = 0;
+ FlushGraphics ();
+ startTime = GetTimeCounter ();
+ while (TransitionAmount < 255)
+ {
+ TimePeriod deltaT;
+ int newAmount;
+
+ SleepThread (ONE_SECOND / 100);
+
+ deltaT = GetTimeCounter () - startTime;
+ newAmount = deltaT * 255 / DURATION;
+ if (newAmount > 255)
+ newAmount = 255;
+
+ TransitionAmount = newAmount;
+ }
+}
diff --git a/src/libs/graphics/gfx_common.h b/src/libs/graphics/gfx_common.h
new file mode 100644
index 0000000..e9e421a
--- /dev/null
+++ b/src/libs/graphics/gfx_common.h
@@ -0,0 +1,112 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef GFX_COMMON_H
+#define GFX_COMMON_H
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "libs/gfxlib.h"
+
+// driver for TFB_InitGraphics
+enum
+{
+ TFB_GFXDRIVER_SDL_OPENGL,
+ TFB_GFXDRIVER_SDL_PURE,
+};
+
+// forced redraw
+enum
+{
+ TFB_REDRAW_NO = 0,
+ TFB_REDRAW_FADING,
+ TFB_REDRAW_EXPOSE,
+ TFB_REDRAW_YES
+};
+
+// flags for TFB_InitGraphics
+#define TFB_GFXFLAGS_FULLSCREEN (1<<0)
+#define TFB_GFXFLAGS_SHOWFPS (1<<1)
+#define TFB_GFXFLAGS_SCANLINES (1<<2)
+#define TFB_GFXFLAGS_SCALE_BILINEAR (1<<3)
+#define TFB_GFXFLAGS_SCALE_BIADAPT (1<<4)
+#define TFB_GFXFLAGS_SCALE_BIADAPTADV (1<<5)
+#define TFB_GFXFLAGS_SCALE_TRISCAN (1<<6)
+#define TFB_GFXFLAGS_SCALE_HQXX (1<<7)
+#define TFB_GFXFLAGS_SCALE_ANY \
+ ( TFB_GFXFLAGS_SCALE_BILINEAR | \
+ TFB_GFXFLAGS_SCALE_BIADAPT | \
+ TFB_GFXFLAGS_SCALE_BIADAPTADV | \
+ TFB_GFXFLAGS_SCALE_TRISCAN | \
+ TFB_GFXFLAGS_SCALE_HQXX )
+#define TFB_GFXFLAGS_SCALE_SOFT_ONLY \
+ ( TFB_GFXFLAGS_SCALE_ANY & ~TFB_GFXFLAGS_SCALE_BILINEAR )
+
+// The flag variable itself
+extern int GfxFlags;
+
+// The following functions are driver-defined
+void TFB_PreInit (void);
+int TFB_InitGraphics (int driver, int flags, const char *renderer,
+ int width, int height);
+int TFB_ReInitGraphics (int driver, int flags, int width, int height);
+void TFB_UninitGraphics (void);
+void TFB_ProcessEvents (void);
+bool TFB_SetGamma (float gamma);
+void TFB_UploadTransitionScreen (void);
+int TFB_SupportsHardwareScaling (void);
+// This function should not be called directly
+void TFB_SwapBuffers (int force_full_redraw);
+
+#define GSCALE_IDENTITY 256
+
+typedef enum {
+ TFB_SCALE_STEP, /* not really a scaler */
+ TFB_SCALE_NEAREST,
+ TFB_SCALE_BILINEAR,
+ TFB_SCALE_TRILINEAR
+} SCALE;
+
+void LoadIntoExtraScreen (RECT *r);
+void DrawFromExtraScreen (RECT *r);
+int SetGraphicScale (int scale);
+int GetGraphicScale (void);
+int SetGraphicScaleMode (int mode /* enum SCALE */);
+int GetGraphicScaleMode (void);
+void SetTransitionSource (const RECT *pRect);
+void ScreenTransition (int transition, const RECT *pRect);
+
+// TODO: there should be accessor functions for these
+extern volatile int TransitionAmount;
+extern RECT TransitionClipRect;
+
+extern float FrameRate;
+extern int FrameRateTickBase;
+
+void TFB_FlushGraphics (void); // Only call from main thread!!
+void TFB_PurgeDanglingGraphics (void); // Only call from main thread as part of shutdown.
+
+extern int ScreenWidth;
+extern int ScreenHeight;
+extern int ScreenWidthActual;
+extern int ScreenHeightActual;
+extern int ScreenColorDepth;
+extern int GraphicsDriver;
+
+#endif
diff --git a/src/libs/graphics/gfxintrn.h b/src/libs/graphics/gfxintrn.h
new file mode 100644
index 0000000..72281e6
--- /dev/null
+++ b/src/libs/graphics/gfxintrn.h
@@ -0,0 +1,32 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_GRAPHICS_GFXINTRN_H_
+#define LIBS_GRAPHICS_GFXINTRN_H_
+
+#include <stdio.h>
+#include <string.h>
+
+#include "libs/gfxlib.h"
+#include "libs/reslib.h"
+#include "context.h"
+#include "drawable.h"
+#include "font.h"
+
+#endif /* LIBS_GRAPHICS_GFXINTRN_H_ */
+
diff --git a/src/libs/graphics/gfxload.c b/src/libs/graphics/gfxload.c
new file mode 100644
index 0000000..969a910
--- /dev/null
+++ b/src/libs/graphics/gfxload.c
@@ -0,0 +1,597 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <stdio.h>
+
+#include "options.h"
+#include "port.h"
+#include "libs/uio.h"
+#include "libs/reslib.h"
+ // for _cur_resfile_name
+#include "libs/log.h"
+#include "libs/memlib.h"
+#include "libs/graphics/tfb_draw.h"
+#include "libs/graphics/drawable.h"
+#include "libs/graphics/font.h"
+
+
+typedef struct anidata
+{
+ int transparent_color;
+ int colormap_index;
+ int hotspot_x;
+ int hotspot_y;
+} AniData;
+
+extern uio_Repository *repository;
+static uio_AutoMount *autoMount[] = { NULL };
+
+static void
+process_image (FRAME FramePtr, TFB_Canvas img[], AniData *ani, int cel_ct)
+{
+ TFB_Image *tfbimg;
+ int hx, hy;
+
+ FramePtr->Type = ROM_DRAWABLE;
+ FramePtr->Index = cel_ct;
+
+ // handle transparency cases
+ if (TFB_DrawCanvas_IsPaletted (img[cel_ct]))
+ { // indexed color image
+ if (ani[cel_ct].transparent_color >= 0)
+ {
+ TFB_DrawCanvas_SetTransparentIndex (img[cel_ct],
+ ani[cel_ct].transparent_color, FALSE);
+ }
+ }
+ else
+ { // special transparency cases for truecolor images
+ if (ani[cel_ct].transparent_color == 0)
+ { // make RGB=0,0,0 transparent
+ Color color = {0, 0, 0, 0};
+ TFB_DrawCanvas_SetTransparentColor (img[cel_ct], color, FALSE);
+ }
+ }
+ if (ani[cel_ct].transparent_color == -1)
+ { // enforce -1 to mean 'no transparency'
+ TFB_DrawCanvas_SetTransparentIndex (img[cel_ct], -1, FALSE);
+ // set transparent_color == -2 to use PNG tRNS transparency
+ }
+
+ hx = ani[cel_ct].hotspot_x;
+ hy = ani[cel_ct].hotspot_y;
+
+ FramePtr->image = TFB_DrawImage_New (img[cel_ct]);
+
+ tfbimg = FramePtr->image;
+ tfbimg->colormap_index = ani[cel_ct].colormap_index;
+ img[cel_ct] = tfbimg->NormalImg;
+
+ FramePtr->HotSpot = MAKE_HOT_SPOT (hx, hy);
+ SetFrameBounds (FramePtr, tfbimg->extent.width, tfbimg->extent.height);
+
+#ifdef CLIPDEBUG
+ {
+ /* for debugging clipping:
+ draws white (or most matching color from palette) pixels to
+ every corner of the image
+ */
+ Color color = {0xff, 0xff, 0xff, 0xff};
+ RECT r = {{0, 0}, {1, 1}};
+ if (tfbimg->extent.width > 2 && tfbimg->extent.height > 2)
+ {
+ TFB_DrawImage_Rect (&r, color, tfbimg);
+ r.corner.x = tfbimg->extent.width - 1;
+ TFB_DrawImage_Rect (&r, color, tfbimg);
+ r.corner.y = tfbimg->extent.height - 1;
+ TFB_DrawImage_Rect (&r, color, tfbimg);
+ r.corner.x = 0;
+ TFB_DrawImage_Rect (&r, color, tfbimg);
+ }
+ }
+#endif
+}
+
+static void
+processFontChar (TFB_Char* CharPtr, TFB_Canvas canvas)
+{
+ BYTE* newdata;
+ size_t dpitch;
+
+ TFB_DrawCanvas_GetExtent (canvas, &CharPtr->extent);
+
+ // Currently, each font char has its own separate data
+ // but that can change to common mem area
+ dpitch = CharPtr->extent.width;
+ newdata = HMalloc (dpitch * CharPtr->extent.height * sizeof (BYTE));
+ TFB_DrawCanvas_GetFontCharData (canvas, newdata, dpitch);
+
+ CharPtr->data = newdata;
+ CharPtr->pitch = dpitch;
+ CharPtr->disp.width = CharPtr->extent.width + 1;
+ CharPtr->disp.height = CharPtr->extent.height + 1;
+ // XXX: why the +1?
+ // I brought it into this function from the only calling
+ // function, but I don't know why it was there in the first
+ // place.
+ // XXX: the +1 appears to be for character and line spacing
+ // text_blt just adds the frame width to move to the next char
+
+ {
+ // This tunes the font positioning to be about what it should
+ // TODO: prolly needs a little tweaking still
+
+ int tune_amount = 0;
+
+ if (CharPtr->extent.height == 8)
+ tune_amount = -1;
+ else if (CharPtr->extent.height == 9)
+ tune_amount = -2;
+ else if (CharPtr->extent.height > 9)
+ tune_amount = -3;
+
+ CharPtr->HotSpot = MAKE_HOT_SPOT (0,
+ CharPtr->extent.height + tune_amount);
+ }
+}
+
+void *
+_GetCelData (uio_Stream *fp, DWORD length)
+{
+ int cel_total, cel_index, n;
+ DWORD opos;
+ char CurrentLine[1024], filename[PATH_MAX];
+ TFB_Canvas *img;
+ AniData *ani;
+ DRAWABLE Drawable;
+ uio_MountHandle *aniMount = 0;
+ uio_DirHandle *aniDir = 0;
+ uio_Stream *aniFile = 0;
+
+ opos = uio_ftell (fp);
+
+ {
+ char *s1, *s2;
+ char aniDirName[PATH_MAX];
+ const char *aniFileName;
+ uint8 buf[4] = { 0, 0, 0, 0 };
+ uint32 header;
+
+ if (_cur_resfile_name == 0
+ || (((s2 = 0), (s1 = strrchr (_cur_resfile_name, '/')) == 0)
+ && (s2 = strrchr (_cur_resfile_name, '\\')) == 0))
+ {
+ n = 0;
+ }
+ else
+ {
+ if (s2 > s1)
+ s1 = s2;
+ n = s1 - _cur_resfile_name + 1;
+ }
+
+ uio_fread(buf, 4, 1, fp);
+ header = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
+ if (_cur_resfile_name && header == 0x04034b50)
+ {
+ // zipped ani file
+ if (n)
+ {
+ strncpy (aniDirName, _cur_resfile_name, n - 1);
+ aniDirName[n - 1] = 0;
+ aniFileName = _cur_resfile_name + n;
+ }
+ else
+ {
+ strcpy(aniDirName, ".");
+ aniFileName = _cur_resfile_name;
+ }
+ aniDir = uio_openDir (repository, aniDirName, 0);
+ aniMount = uio_mountDir (repository, aniDirName, uio_FSTYPE_ZIP,
+ aniDir, aniFileName, "/", autoMount,
+ uio_MOUNT_RDONLY | uio_MOUNT_TOP,
+ NULL);
+ aniFile = uio_fopen (aniDir, aniFileName, "r");
+ opos = 0;
+ n = 0;
+ }
+ else
+ {
+ // unpacked ani file
+ strncpy (filename, _cur_resfile_name, n);
+ aniFile = fp;
+ aniDir = contentDir;
+ }
+ }
+
+ cel_total = 0;
+ uio_fseek (aniFile, opos, SEEK_SET);
+ while (uio_fgets (CurrentLine, sizeof (CurrentLine), aniFile))
+ {
+ ++cel_total;
+ }
+
+ img = HMalloc (sizeof (TFB_Canvas) * cel_total);
+ ani = HMalloc (sizeof (AniData) * cel_total);
+ if (!img || !ani)
+ {
+ log_add (log_Warning, "Couldn't allocate space for '%s'", _cur_resfile_name);
+ if (aniMount)
+ {
+ uio_fclose(aniFile);
+ uio_closeDir(aniDir);
+ uio_unmountDir(aniMount);
+ }
+ HFree (img);
+ HFree (ani);
+ return NULL;
+ }
+
+ cel_index = 0;
+ uio_fseek (aniFile, opos, SEEK_SET);
+ while (uio_fgets (CurrentLine, sizeof (CurrentLine), aniFile) && cel_index < cel_total)
+ {
+ sscanf (CurrentLine, "%s %d %d %d %d", &filename[n],
+ &ani[cel_index].transparent_color, &ani[cel_index].colormap_index,
+ &ani[cel_index].hotspot_x, &ani[cel_index].hotspot_y);
+
+ img[cel_index] = TFB_DrawCanvas_LoadFromFile (aniDir, filename);
+ if (img[cel_index] == NULL)
+ {
+ const char *err;
+
+ err = TFB_DrawCanvas_GetError ();
+ log_add (log_Warning, "_GetCelData: Unable to load image!");
+ if (err != NULL)
+ log_add (log_Warning, "Gfx Driver reports: %s", err);
+ }
+ else
+ {
+ ++cel_index;
+ }
+
+ if ((int)uio_ftell (aniFile) - (int)opos >= (int)length)
+ break;
+ }
+
+ Drawable = NULL;
+ if (cel_index && (Drawable = AllocDrawable (cel_index)))
+ {
+ if (!Drawable)
+ {
+ while (cel_index--)
+ TFB_DrawCanvas_Delete (img[cel_index]);
+
+ HFree (Drawable);
+ Drawable = NULL;
+ }
+ else
+ {
+ FRAME FramePtr;
+
+ Drawable->Flags = WANT_PIXMAP;
+ Drawable->MaxIndex = cel_index - 1;
+
+ FramePtr = &Drawable->Frame[cel_index];
+ while (--FramePtr, cel_index--)
+ process_image (FramePtr, img, ani, cel_index);
+ }
+ }
+
+ if (Drawable == NULL)
+ log_add (log_Warning, "Couldn't get cel data for '%s'",
+ _cur_resfile_name);
+
+ if (aniMount)
+ {
+ uio_fclose(aniFile);
+ uio_closeDir(aniDir);
+ uio_unmountDir(aniMount);
+ }
+
+ HFree (img);
+ HFree (ani);
+ return Drawable;
+}
+
+BOOLEAN
+_ReleaseCelData (void *handle)
+{
+ DRAWABLE DrawablePtr;
+ int cel_ct;
+ FRAME FramePtr = NULL;
+
+ if ((DrawablePtr = handle) == 0)
+ return (FALSE);
+
+ cel_ct = DrawablePtr->MaxIndex + 1;
+ FramePtr = DrawablePtr->Frame;
+
+ HFree (handle);
+ if (FramePtr)
+ {
+ int i;
+ for (i = 0; i < cel_ct; i++)
+ {
+ TFB_Image *img = FramePtr[i].image;
+ if (img)
+ {
+ FramePtr[i].image = NULL;
+ TFB_DrawScreen_DeleteImage (img);
+ }
+ }
+ HFree (FramePtr);
+ }
+
+ return (TRUE);
+}
+
+typedef struct BuildCharDesc
+{
+ TFB_Canvas canvas;
+ UniChar index;
+} BuildCharDesc;
+
+static int
+compareBCDIndex (const void *arg1, const void *arg2)
+{
+ const BuildCharDesc *bcd1 = (const BuildCharDesc *) arg1;
+ const BuildCharDesc *bcd2 = (const BuildCharDesc *) arg2;
+
+ return (int) bcd1->index - (int) bcd2->index;
+}
+
+void *
+_GetFontData (uio_Stream *fp, DWORD length)
+{
+ COUNT numDirEntries;
+ DIRENTRY fontDir = NULL;
+ BuildCharDesc *bcds = NULL;
+ size_t numBCDs = 0;
+ int dirEntryI;
+ uio_DirHandle *fontDirHandle = NULL;
+ uio_MountHandle *fontMount = NULL;
+ FONT fontPtr = NULL;
+
+ if (_cur_resfile_name == 0)
+ goto err;
+
+ if (fp != (uio_Stream*)~0)
+ {
+ // font is zipped instead of being in a directory
+
+ char *s1, *s2;
+ int n;
+ const char *fontZipName;
+ char fontDirName[PATH_MAX];
+
+ if ((((s2 = 0), (s1 = strrchr (_cur_resfile_name, '/')) == 0)
+ && (s2 = strrchr (_cur_resfile_name, '\\')) == 0))
+ {
+ strcpy(fontDirName, ".");
+ fontZipName = _cur_resfile_name;
+ }
+ else
+ {
+ if (s2 > s1)
+ s1 = s2;
+ n = s1 - _cur_resfile_name + 1;
+ strncpy (fontDirName, _cur_resfile_name, n - 1);
+ fontDirName[n - 1] = 0;
+ fontZipName = _cur_resfile_name + n;
+ }
+
+ fontDirHandle = uio_openDir (repository, fontDirName, 0);
+ fontMount = uio_mountDir (repository, _cur_resfile_name, uio_FSTYPE_ZIP,
+ fontDirHandle, fontZipName, "/", autoMount,
+ uio_MOUNT_RDONLY | uio_MOUNT_TOP,
+ NULL);
+ uio_closeDir (fontDirHandle);
+ }
+
+ fontDir = CaptureDirEntryTable (LoadDirEntryTable (contentDir,
+ _cur_resfile_name, ".", match_MATCH_SUBSTRING));
+ if (fontDir == 0)
+ goto err;
+ numDirEntries = GetDirEntryTableCount (fontDir);
+
+ fontDirHandle = uio_openDirRelative (contentDir, _cur_resfile_name, 0);
+ if (fontDirHandle == NULL)
+ goto err;
+
+ bcds = HMalloc (numDirEntries * sizeof (BuildCharDesc));
+ if (bcds == NULL)
+ goto err;
+
+ // Load the surfaces for all dir Entries
+ for (dirEntryI = 0; dirEntryI < numDirEntries; dirEntryI++)
+ {
+ char *char_name;
+ unsigned int charIndex;
+ TFB_Canvas canvas;
+ EXTENT size;
+
+ char_name = GetDirEntryAddress (SetAbsDirEntryTableIndex (
+ fontDir, dirEntryI));
+ if (sscanf (char_name, "%x.", &charIndex) != 1)
+ continue;
+
+ if (charIndex > 0xffff)
+ continue;
+
+ canvas = TFB_DrawCanvas_LoadFromFile (fontDirHandle, char_name);
+ if (canvas == NULL)
+ continue;
+
+ TFB_DrawCanvas_GetExtent (canvas, &size);
+ if (size.width == 0 || size.height == 0)
+ {
+ TFB_DrawCanvas_Delete (canvas);
+ continue;
+ }
+
+ bcds[numBCDs].canvas = canvas;
+ bcds[numBCDs].index = charIndex;
+ numBCDs++;
+ }
+ uio_closeDir (fontDirHandle);
+ DestroyDirEntryTable (ReleaseDirEntryTable (fontDir));
+ if (fontMount != 0)
+ uio_unmountDir(fontMount);
+
+#if 0
+ if (numBCDs == 0)
+ goto err;
+#endif
+
+ // sort on the character index
+ qsort (bcds, numBCDs, sizeof (BuildCharDesc), compareBCDIndex);
+
+ fontPtr = AllocFont (0);
+ if (fontPtr == NULL)
+ goto err;
+
+ fontPtr->Leading = 0;
+ fontPtr->LeadingWidth = 0;
+
+ {
+ size_t startBCD = 0;
+ UniChar pageStart;
+ FONT_PAGE **pageEndPtr = &fontPtr->fontPages;
+ while (startBCD < numBCDs)
+ {
+ // Process one character page.
+ size_t endBCD;
+ pageStart = bcds[startBCD].index & CHARACTER_PAGE_MASK;
+
+ endBCD = startBCD;
+ while (endBCD < numBCDs &&
+ (bcds[endBCD].index & CHARACTER_PAGE_MASK) == pageStart)
+ endBCD++;
+
+ {
+ size_t bcdI;
+ int numChars = bcds[endBCD - 1].index + 1
+ - bcds[startBCD].index;
+ FONT_PAGE *page = AllocFontPage (numChars);
+ page->pageStart = pageStart;
+ page->firstChar = bcds[startBCD].index;
+ page->numChars = numChars;
+ *pageEndPtr = page;
+ pageEndPtr = &page->next;
+
+ for (bcdI = startBCD; bcdI < endBCD; bcdI++)
+ {
+ // Process one character.
+ BuildCharDesc *bcd = &bcds[bcdI];
+ TFB_Char *destChar =
+ &page->charDesc[bcd->index - page->firstChar];
+
+ if (destChar->data != NULL)
+ {
+ // There's already an image for this character.
+ log_add (log_Debug, "Duplicate image for character %d "
+ "for font %s.", (int) bcd->index,
+ _cur_resfile_name);
+ TFB_DrawCanvas_Delete (bcd->canvas);
+ continue;
+ }
+
+ processFontChar (destChar, bcd->canvas);
+ TFB_DrawCanvas_Delete (bcd->canvas);
+
+ if (destChar->disp.height > fontPtr->Leading)
+ fontPtr->Leading = destChar->disp.height;
+ if (destChar->disp.width > fontPtr->LeadingWidth)
+ fontPtr->LeadingWidth = destChar->disp.width;
+ }
+ }
+
+ startBCD = endBCD;
+ }
+ *pageEndPtr = NULL;
+ }
+
+ fontPtr->Leading++;
+
+ HFree (bcds);
+
+ (void) fp; /* Satisfying compiler (unused parameter) */
+ (void) length; /* Satisfying compiler (unused parameter) */
+ return fontPtr;
+
+err:
+ if (fontPtr != 0)
+ HFree (fontPtr);
+
+ if (bcds != NULL)
+ {
+ size_t bcdI;
+ for (bcdI = 0; bcdI < numBCDs; bcdI++)
+ TFB_DrawCanvas_Delete (bcds[bcdI].canvas);
+ HFree (bcds);
+ }
+
+ if (fontDirHandle != NULL)
+ uio_closeDir (fontDirHandle);
+
+ if (fontDir != 0)
+ DestroyDirEntryTable (ReleaseDirEntryTable (fontDir));
+
+ if (fontMount != 0)
+ uio_unmountDir(fontMount);
+
+ return 0;
+}
+
+BOOLEAN
+_ReleaseFontData (void *handle)
+{
+ FONT font = (FONT) handle;
+ if (font == NULL)
+ return FALSE;
+
+ {
+ FONT_PAGE *page;
+ FONT_PAGE *nextPage;
+
+ for (page = font->fontPages; page != NULL; page = nextPage)
+ {
+ size_t charI;
+ for (charI = 0; charI < page->numChars; charI++)
+ {
+ TFB_Char *c = &page->charDesc[charI];
+
+ if (c->data == NULL)
+ continue;
+
+ // XXX: fix this if fonts get per-page data
+ // rather than per-char
+ TFB_DrawScreen_DeleteData (c->data);
+ }
+
+ nextPage = page->next;
+ FreeFontPage (page);
+ }
+ }
+
+ HFree (font);
+
+ return TRUE;
+}
diff --git a/src/libs/graphics/intersec.c b/src/libs/graphics/intersec.c
new file mode 100644
index 0000000..02a5226
--- /dev/null
+++ b/src/libs/graphics/intersec.c
@@ -0,0 +1,415 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "libs/graphics/context.h"
+#include "libs/graphics/drawable.h"
+#include "libs/graphics/tfb_draw.h"
+#include "libs/log.h"
+
+//#define DEBUG_INTERSEC
+
+static inline BOOLEAN
+images_intersect (IMAGE_BOX *box1, IMAGE_BOX *box2, const RECT *rect)
+{
+ return TFB_DrawImage_Intersect (box1->FramePtr->image, box1->Box.corner,
+ box2->FramePtr->image, box2->Box.corner, rect);
+}
+
+static TIME_VALUE
+frame_intersect (INTERSECT_CONTROL *pControl0, RECT *pr0,
+ INTERSECT_CONTROL *pControl1, RECT *pr1, TIME_VALUE t0,
+ TIME_VALUE t1)
+{
+ SIZE time_error0, time_error1;
+ SIZE cycle0, cycle1;
+ SIZE dx_0, dy_0, dx_1, dy_1;
+ SIZE xincr0, yincr0, xincr1, yincr1;
+ SIZE xerror0, xerror1, yerror0, yerror1;
+ RECT r_intersect;
+ IMAGE_BOX IB0, IB1;
+ BOOLEAN check0, check1;
+
+ IB0.FramePtr = pControl0->IntersectStamp.frame;
+ IB0.Box.corner = pr0->corner;
+ IB0.Box.extent.width = GetFrameWidth (IB0.FramePtr);
+ IB0.Box.extent.height = GetFrameHeight (IB0.FramePtr);
+ IB1.FramePtr = pControl1->IntersectStamp.frame;
+ IB1.Box.corner = pr1->corner;
+ IB1.Box.extent.width = GetFrameWidth (IB1.FramePtr);
+ IB1.Box.extent.height = GetFrameHeight (IB1.FramePtr);
+
+ dx_0 = pr0->extent.width;
+ dy_0 = pr0->extent.height;
+ if (dx_0 >= 0)
+ xincr0 = 1;
+ else
+ {
+ xincr0 = -1;
+ dx_0 = -dx_0;
+ }
+ if (dy_0 >= 0)
+ yincr0 = 1;
+ else
+ {
+ yincr0 = -1;
+ dy_0 = -dy_0;
+ }
+ if (dx_0 >= dy_0)
+ cycle0 = dx_0;
+ else
+ cycle0 = dy_0;
+ xerror0 = yerror0 = cycle0;
+
+ dx_1 = pr1->extent.width;
+ dy_1 = pr1->extent.height;
+ if (dx_1 >= 0)
+ xincr1 = 1;
+ else
+ {
+ xincr1 = -1;
+ dx_1 = -dx_1;
+ }
+ if (dy_1 >= 0)
+ yincr1 = 1;
+ else
+ {
+ yincr1 = -1;
+ dy_1 = -dy_1;
+ }
+ if (dx_1 >= dy_1)
+ cycle1 = dx_1;
+ else
+ cycle1 = dy_1;
+ xerror1 = yerror1 = cycle1;
+
+ check0 = check1 = FALSE;
+ if (t0 <= 1)
+ {
+ time_error0 = time_error1 = 0;
+ if (t0 == 0)
+ {
+ ++t0;
+ goto CheckFirstIntersection;
+ }
+ }
+ else
+ {
+ SIZE delta;
+ COUNT start;
+ long error;
+
+ start = (COUNT)cycle0 * (COUNT)(t0 - 1);
+ time_error0 = start & ((1 << TIME_SHIFT) - 1);
+ if ((start >>= (COUNT)TIME_SHIFT) > 0)
+ {
+ if ((error = (long)xerror0
+ - (long)dx_0 * (long)start) > 0)
+ xerror0 = (SIZE)error;
+ else
+ {
+ delta = -(SIZE)(error / (long)cycle0) + 1;
+ IB0.Box.corner.x += xincr0 * delta;
+ xerror0 = (SIZE)(error + (long)cycle0 * (long)delta);
+ }
+ if ((error = (long)yerror0
+ - (long)dy_0 * (long)start) > 0)
+ yerror0 = (SIZE)error;
+ else
+ {
+ delta = -(SIZE)(error / (long)cycle0) + 1;
+ IB0.Box.corner.y += yincr0 * delta;
+ yerror0 = (SIZE)(error + (long)cycle0 * (long)delta);
+ }
+ pr0->corner = IB0.Box.corner;
+ }
+
+ start = (COUNT)cycle1 * (COUNT)(t0 - 1);
+ time_error1 = start & ((1 << TIME_SHIFT) - 1);
+ if ((start >>= (COUNT)TIME_SHIFT) > 0)
+ {
+ if ((error = (long)xerror1
+ - (long)dx_1 * (long)start) > 0)
+ xerror1 = (SIZE)error;
+ else
+ {
+ delta = -(SIZE)(error / (long)cycle1) + 1;
+ IB1.Box.corner.x += xincr1 * delta;
+ xerror1 = (SIZE)(error + (long)cycle1 * (long)delta);
+ }
+ if ((error = (long)yerror1
+ - (long)dy_1 * (long)start) > 0)
+ yerror1 = (SIZE)error;
+ else
+ {
+ delta = -(SIZE)(error / (long)cycle1) + 1;
+ IB1.Box.corner.y += yincr1 * delta;
+ yerror1 = (SIZE)(error + (long)cycle1 * (long)delta);
+ }
+ pr1->corner = IB1.Box.corner;
+ }
+ }
+
+ pControl0->last_time_val = pControl1->last_time_val = t0;
+ do
+ {
+ ++t0;
+ if ((time_error0 += cycle0) >= (1 << TIME_SHIFT))
+ {
+ if ((xerror0 -= dx_0) <= 0)
+ {
+ IB0.Box.corner.x += xincr0;
+ xerror0 += cycle0;
+ }
+ if ((yerror0 -= dy_0) <= 0)
+ {
+ IB0.Box.corner.y += yincr0;
+ yerror0 += cycle0;
+ }
+
+ check0 = TRUE;
+ time_error0 -= (1 << TIME_SHIFT);
+ }
+
+ if ((time_error1 += cycle1) >= (1 << TIME_SHIFT))
+ {
+ if ((xerror1 -= dx_1) <= 0)
+ {
+ IB1.Box.corner.x += xincr1;
+ xerror1 += cycle1;
+ }
+ if ((yerror1 -= dy_1) <= 0)
+ {
+ IB1.Box.corner.y += yincr1;
+ yerror1 += cycle1;
+ }
+
+ check1 = TRUE;
+ time_error1 -= (1 << TIME_SHIFT);
+ }
+
+ if (check0 || check1)
+ { /* if check0 && check1, this may not be quite right --
+ * if shapes had a pixel's separation to begin with
+ * and both moved toward each other, you would actually
+ * get a pixel overlap but since the last positions were
+ * separated by a pixel, the shapes wouldn't be touching
+ * each other.
+ */
+CheckFirstIntersection:
+ if (BoxIntersect (&IB0.Box, &IB1.Box, &r_intersect)
+ && images_intersect (&IB0, &IB1, &r_intersect))
+ return (t0);
+
+ if (check0)
+ {
+ pr0->corner = IB0.Box.corner;
+ pControl0->last_time_val = t0;
+ check0 = FALSE;
+ }
+ if (check1)
+ {
+ pr1->corner = IB1.Box.corner;
+ pControl1->last_time_val = t0;
+ check1 = FALSE;
+ }
+ }
+ } while (t0 <= t1);
+
+ return ((TIME_VALUE)0);
+}
+
+TIME_VALUE
+DrawablesIntersect (INTERSECT_CONTROL *pControl0,
+ INTERSECT_CONTROL *pControl1, TIME_VALUE max_time_val)
+{
+ SIZE dy;
+ SIZE time_y_0, time_y_1;
+ RECT r0, r1;
+ FRAME FramePtr0, FramePtr1;
+
+ if (!ContextActive () || max_time_val == 0)
+ return ((TIME_VALUE)0);
+ else if (max_time_val > MAX_TIME_VALUE)
+ max_time_val = MAX_TIME_VALUE;
+
+ pControl0->last_time_val = pControl1->last_time_val = 0;
+
+ r0.corner = pControl0->IntersectStamp.origin;
+ r1.corner = pControl1->IntersectStamp.origin;
+
+ r0.extent.width = pControl0->EndPoint.x - r0.corner.x;
+ r0.extent.height = pControl0->EndPoint.y - r0.corner.y;
+ r1.extent.width = pControl1->EndPoint.x - r1.corner.x;
+ r1.extent.height = pControl1->EndPoint.y - r1.corner.y;
+
+ FramePtr0 = pControl0->IntersectStamp.frame;
+ if (FramePtr0 == 0)
+ return(0);
+ r0.corner.x -= FramePtr0->HotSpot.x;
+ r0.corner.y -= FramePtr0->HotSpot.y;
+
+ FramePtr1 = pControl1->IntersectStamp.frame;
+ if (FramePtr1 == 0)
+ return(0);
+ r1.corner.x -= FramePtr1->HotSpot.x;
+ r1.corner.y -= FramePtr1->HotSpot.y;
+
+ dy = r1.corner.y - r0.corner.y;
+ time_y_0 = dy - GetFrameHeight (FramePtr0) + 1;
+ time_y_1 = dy + GetFrameHeight (FramePtr1) - 1;
+ dy = r0.extent.height - r1.extent.height;
+
+ if ((time_y_0 <= 0 && time_y_1 >= 0)
+ || (time_y_0 > 0 && dy >= time_y_0)
+ || (time_y_1 < 0 && dy <= time_y_1))
+ {
+ SIZE dx;
+ SIZE time_x_0, time_x_1;
+
+ dx = r1.corner.x - r0.corner.x;
+ time_x_0 = dx - GetFrameWidth (FramePtr0) + 1;
+ time_x_1 = dx + GetFrameWidth (FramePtr1) - 1;
+ dx = r0.extent.width - r1.extent.width;
+
+ if ((time_x_0 <= 0 && time_x_1 >= 0)
+ || (time_x_0 > 0 && dx >= time_x_0)
+ || (time_x_1 < 0 && dx <= time_x_1))
+ {
+ TIME_VALUE intersect_time;
+
+ if (dx == 0 && dy == 0)
+ time_y_0 = time_y_1 = 0;
+ else
+ {
+ SIZE t;
+ long time_beg, time_end, fract;
+
+ if (time_y_1 < 0)
+ {
+ t = time_y_0;
+ time_y_0 = -time_y_1;
+ time_y_1 = -t;
+ }
+ else if (time_y_0 <= 0)
+ {
+ if (dy < 0)
+ time_y_1 = -time_y_0;
+ time_y_0 = 0;
+ }
+ if (dy < 0)
+ dy = -dy;
+ if (dy < time_y_1)
+ time_y_1 = dy;
+ /* just to be safe, widen search area */
+ --time_y_0;
+ ++time_y_1;
+
+ if (time_x_1 < 0)
+ {
+ t = time_x_0;
+ time_x_0 = -time_x_1;
+ time_x_1 = -t;
+ }
+ else if (time_x_0 <= 0)
+ {
+ if (dx < 0)
+ time_x_1 = -time_x_0;
+ time_x_0 = 0;
+ }
+ if (dx < 0)
+ dx = -dx;
+ if (dx < time_x_1)
+ time_x_1 = dx;
+ /* just to be safe, widen search area */
+ --time_x_0;
+ ++time_x_1;
+
+#ifdef DEBUG_INTERSEC
+ log_add (log_Debug, "FramePtr0<%d, %d> --> <%d, %d>",
+ GetFrameWidth (FramePtr0), GetFrameHeight (FramePtr0),
+ r0.corner.x, r0.corner.y);
+ log_add (log_Debug, "FramePtr1<%d, %d> --> <%d, %d>",
+ GetFrameWidth (FramePtr1), GetFrameHeight (FramePtr1),
+ r1.corner.x, r1.corner.y);
+ log_add (log_Debug, "time_x(%d, %d)-%d, time_y(%d, %d)-%d",
+ time_x_0, time_x_1, dx, time_y_0, time_y_1, dy);
+#endif /* DEBUG_INTERSEC */
+ if (dx == 0)
+ {
+ time_beg = time_y_0;
+ time_end = time_y_1;
+ fract = dy;
+ }
+ else if (dy == 0)
+ {
+ time_beg = time_x_0;
+ time_end = time_x_1;
+ fract = dx;
+ }
+ else
+ {
+ long time_x, time_y;
+
+ time_x = (long)time_x_0 * (long)dy;
+ time_y = (long)time_y_0 * (long)dx;
+ time_beg = time_x < time_y ? time_y : time_x;
+
+ time_x = (long)time_x_1 * (long)dy;
+ time_y = (long)time_y_1 * (long)dx;
+ time_end = time_x > time_y ? time_y : time_x;
+
+ fract = (long)dx * (long)dy;
+ }
+
+ if ((time_beg <<= TIME_SHIFT) < fract)
+ time_y_0 = 0;
+ else
+ time_y_0 = (SIZE)(time_beg / fract);
+
+ if (time_end >= fract /* just in case of overflow */
+ || (time_end <<= TIME_SHIFT) >=
+ fract * (long)max_time_val)
+ time_y_1 = max_time_val - 1;
+ else
+ time_y_1 = (SIZE)((time_end + fract - 1) / fract) - 1;
+ }
+
+#ifdef DEBUG_INTERSEC
+ log_add (log_Debug, "start_time = %d, end_time = %d",
+ time_y_0, time_y_1);
+#endif /* DEBUG_INTERSEC */
+ if (time_y_0 <= time_y_1
+ && (intersect_time = frame_intersect (
+ pControl0, &r0, pControl1, &r1,
+ (TIME_VALUE)time_y_0, (TIME_VALUE)time_y_1)))
+ {
+ FramePtr0 = pControl0->IntersectStamp.frame;
+ pControl0->EndPoint.x = r0.corner.x + FramePtr0->HotSpot.x;
+ pControl0->EndPoint.y = r0.corner.y + FramePtr0->HotSpot.y;
+ FramePtr1 = pControl1->IntersectStamp.frame;
+ pControl1->EndPoint.x = r1.corner.x + FramePtr1->HotSpot.x;
+ pControl1->EndPoint.y = r1.corner.y + FramePtr1->HotSpot.y;
+
+ return (intersect_time);
+ }
+ }
+ }
+
+ return ((TIME_VALUE)0);
+}
+
diff --git a/src/libs/graphics/loaddisp.c b/src/libs/graphics/loaddisp.c
new file mode 100644
index 0000000..ccc7919
--- /dev/null
+++ b/src/libs/graphics/loaddisp.c
@@ -0,0 +1,65 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "libs/gfxlib.h"
+#include "libs/graphics/drawable.h"
+#include "libs/log.h"
+
+
+// Reads a piece of screen into a passed FRAME or a newly created one
+DRAWABLE
+LoadDisplayPixmap (const RECT *area, FRAME frame)
+{
+ // TODO: This should just return a FRAME instead of DRAWABLE
+ DRAWABLE buffer = GetFrameParentDrawable (frame);
+ COUNT index;
+
+ if (!buffer)
+ { // asked to create a new DRAWABLE instead
+ buffer = CreateDrawable (WANT_PIXMAP | MAPPED_TO_DISPLAY,
+ area->extent.width, area->extent.height, 1);
+ if (!buffer)
+ return NULL;
+
+ index = 0;
+ }
+ else
+ {
+ index = GetFrameIndex (frame);
+ }
+
+ frame = SetAbsFrameIndex (CaptureDrawable (buffer), index);
+
+ if (_CurFramePtr->Type != SCREEN_DRAWABLE
+ || frame->Type == SCREEN_DRAWABLE
+ || !(GetFrameParentDrawable (frame)->Flags & MAPPED_TO_DISPLAY))
+ {
+ log_add (log_Warning, "Unimplemented function activated: "
+ "LoadDisplayPixmap()");
+ }
+ else
+ {
+ TFB_Image *img = frame->image;
+ TFB_DrawScreen_CopyToImage (img, area, TFB_SCREEN_MAIN);
+ }
+
+ ReleaseDrawable (frame);
+
+ return buffer;
+}
+
diff --git a/src/libs/graphics/pixmap.c b/src/libs/graphics/pixmap.c
new file mode 100644
index 0000000..6e68244
--- /dev/null
+++ b/src/libs/graphics/pixmap.c
@@ -0,0 +1,170 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "gfxintrn.h"
+#include "libs/log.h"
+
+DRAWABLE
+GetFrameParentDrawable (FRAME f)
+{
+ if (f != NULL)
+ {
+ return f->parent;
+ }
+ return NULL;
+}
+
+FRAME
+CaptureDrawable (DRAWABLE DrawablePtr)
+{
+ if (DrawablePtr)
+ {
+ return &DrawablePtr->Frame[0];
+ }
+
+ return NULL;
+}
+
+DRAWABLE
+ReleaseDrawable (FRAME FramePtr)
+{
+ if (FramePtr != 0)
+ {
+ DRAWABLE Drawable;
+
+ Drawable = GetFrameParentDrawable (FramePtr);
+
+ return (Drawable);
+ }
+
+ return NULL;
+}
+
+COUNT
+GetFrameCount (FRAME FramePtr)
+{
+ DRAWABLE_DESC *DrawablePtr;
+
+ if (FramePtr == 0)
+ return (0);
+
+ DrawablePtr = GetFrameParentDrawable (FramePtr);
+ return DrawablePtr->MaxIndex + 1;
+}
+
+COUNT
+GetFrameIndex (FRAME FramePtr)
+{
+ if (FramePtr == 0)
+ return (0);
+
+ return FramePtr->Index;
+}
+
+FRAME
+SetAbsFrameIndex (FRAME FramePtr, COUNT FrameIndex)
+{
+ if (FramePtr != 0)
+ {
+ DRAWABLE_DESC *DrawablePtr;
+
+ DrawablePtr = GetFrameParentDrawable (FramePtr);
+
+ FrameIndex = FrameIndex % (DrawablePtr->MaxIndex + 1);
+ FramePtr = &DrawablePtr->Frame[FrameIndex];
+ }
+
+ return FramePtr;
+}
+
+FRAME
+SetRelFrameIndex (FRAME FramePtr, SIZE FrameOffs)
+{
+ if (FramePtr != 0)
+ {
+ COUNT num_frames;
+ DRAWABLE_DESC *DrawablePtr;
+
+ DrawablePtr = GetFrameParentDrawable (FramePtr);
+ num_frames = DrawablePtr->MaxIndex + 1;
+ if (FrameOffs < 0)
+ {
+ while ((FrameOffs += num_frames) < 0)
+ ;
+ }
+
+ FrameOffs = ((SWORD)FramePtr->Index + FrameOffs) % num_frames;
+ FramePtr = &DrawablePtr->Frame[FrameOffs];
+ }
+
+ return FramePtr;
+}
+
+FRAME
+SetEquFrameIndex (FRAME DstFramePtr, FRAME SrcFramePtr)
+{
+ COUNT Index;
+
+ if (!DstFramePtr || !SrcFramePtr)
+ return 0;
+
+ Index = GetFrameIndex (SrcFramePtr);
+#ifdef DEBUG
+ {
+ DRAWABLE_DESC *DrawablePtr = GetFrameParentDrawable (DstFramePtr);
+ if (Index > DrawablePtr->MaxIndex)
+ log_add (log_Debug, "SetEquFrameIndex: source index (%d) beyond "
+ "destination range (%d)", (int)Index,
+ (int)DrawablePtr->MaxIndex);
+ }
+#endif
+
+ return SetAbsFrameIndex (DstFramePtr, Index);
+}
+
+FRAME
+IncFrameIndex (FRAME FramePtr)
+{
+ DRAWABLE_DESC *DrawablePtr;
+
+ if (FramePtr == 0)
+ return (0);
+
+ DrawablePtr = GetFrameParentDrawable (FramePtr);
+ if (FramePtr->Index < DrawablePtr->MaxIndex)
+ return ++FramePtr;
+ else
+ return DrawablePtr->Frame;
+}
+
+FRAME
+DecFrameIndex (FRAME FramePtr)
+{
+ if (FramePtr == 0)
+ return (0);
+
+ if (FramePtr->Index > 0)
+ return --FramePtr;
+ else
+ {
+ DRAWABLE_DESC *DrawablePtr;
+
+ DrawablePtr = GetFrameParentDrawable (FramePtr);
+ return &DrawablePtr->Frame[DrawablePtr->MaxIndex];
+ }
+}
diff --git a/src/libs/graphics/prim.h b/src/libs/graphics/prim.h
new file mode 100644
index 0000000..bce3c2a
--- /dev/null
+++ b/src/libs/graphics/prim.h
@@ -0,0 +1,80 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef LIBS_GRAPHICS_PRIM_H_
+#define LIBS_GRAPHICS_PRIM_H_
+
+enum gfx_object
+{
+ POINT_PRIM = 0,
+ STAMP_PRIM,
+ STAMPFILL_PRIM,
+ LINE_PRIM,
+ TEXT_PRIM,
+ RECT_PRIM,
+ RECTFILL_PRIM,
+
+ NUM_PRIMS
+};
+typedef BYTE GRAPHICS_PRIM;
+
+typedef union
+{
+ POINT Point;
+ STAMP Stamp;
+ LINE Line;
+ TEXT Text;
+ RECT Rect;
+} PRIM_DESC;
+
+typedef DWORD PRIM_LINKS;
+
+typedef struct
+{
+ PRIM_LINKS Links;
+ GRAPHICS_PRIM Type;
+ Color color;
+ PRIM_DESC Object;
+} PRIMITIVE;
+
+#define END_OF_LIST ((COUNT)0xFFFF)
+
+#define GetPredLink(l) LOWORD(l)
+#define GetSuccLink(l) HIWORD(l)
+#define MakeLinks MAKE_DWORD
+#define SetPrimLinks(pPrim,p,s) ((pPrim)->Links = MakeLinks (p, s))
+#define GetPrimLinks(pPrim) ((pPrim)->Links)
+#define SetPrimType(pPrim,t) ((pPrim)->Type = t)
+#define GetPrimType(pPrim) ((pPrim)->Type)
+#define SetPrimColor(pPrim,c) ((pPrim)->color = c)
+#define GetPrimColor(pPrim) ((pPrim)->color)
+
+static inline void
+SetPrimNextLink (PRIMITIVE *pPrim, COUNT Link)
+{
+ SetPrimLinks (pPrim, END_OF_LIST, Link);
+}
+
+
+static inline COUNT
+GetPrimNextLink (PRIMITIVE *pPrim)
+{
+ return GetSuccLink (GetPrimLinks (pPrim));
+}
+
+
+#endif /* PRIM_H */
+
+
diff --git a/src/libs/graphics/resgfx.c b/src/libs/graphics/resgfx.c
new file mode 100644
index 0000000..c0760dc
--- /dev/null
+++ b/src/libs/graphics/resgfx.c
@@ -0,0 +1,54 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "gfxintrn.h"
+
+static void
+GetCelFileData (const char *pathname, RESOURCE_DATA *resdata)
+{
+ resdata->ptr = LoadResourceFromPath (pathname, _GetCelData);
+}
+
+static void
+GetFontFileData (const char *pathname, RESOURCE_DATA *resdata)
+{
+ resdata->ptr = LoadResourceFromPath (pathname, _GetFontData);
+}
+
+
+BOOLEAN
+InstallGraphicResTypes (void)
+{
+ InstallResTypeVectors ("GFXRES", GetCelFileData, _ReleaseCelData, NULL);
+ InstallResTypeVectors ("FONTRES", GetFontFileData, _ReleaseFontData, NULL);
+ return (TRUE);
+}
+
+/* Needs to be void * because it could be either a DRAWABLE or a FONT. */
+void *
+LoadGraphicInstance (RESOURCE res)
+{
+ void *hData;
+
+ hData = res_GetResource (res);
+ if (hData)
+ res_DetachResource (res);
+
+ return (hData);
+}
+
diff --git a/src/libs/graphics/sdl/2xscalers.c b/src/libs/graphics/sdl/2xscalers.c
new file mode 100644
index 0000000..a612d2c
--- /dev/null
+++ b/src/libs/graphics/sdl/2xscalers.c
@@ -0,0 +1,260 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "libs/graphics/sdl/sdl_common.h"
+#include "types.h"
+#include "scalers.h"
+#include "scaleint.h"
+#include "2xscalers.h"
+
+
+// Scaler function lookup table
+//
+const Scale_FuncDef_t
+Scale_C_Functions[] =
+{
+ {TFB_GFXFLAGS_SCALE_BILINEAR, Scale_BilinearFilter},
+ {TFB_GFXFLAGS_SCALE_BIADAPT, Scale_BiAdaptFilter},
+ {TFB_GFXFLAGS_SCALE_BIADAPTADV, Scale_BiAdaptAdvFilter},
+ {TFB_GFXFLAGS_SCALE_TRISCAN, Scale_TriScanFilter},
+ {TFB_GFXFLAGS_SCALE_HQXX, Scale_HqFilter},
+ // Default
+ {0, Scale_Nearest}
+};
+
+// See
+// nearest2x.c -- Nearest Neighboor scaling
+// bilinear2x.c -- Bilinear scaling
+// biadv2x.c -- Advanced Biadapt scaling
+// triscan2x.c -- Triscan scaling
+
+// Biadapt scaling to 2x
+void
+SCALE_(BiAdaptFilter) (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r)
+{
+ int x, y;
+ const int w = src->w, h = src->h;
+ int xend, yend;
+ int dsrc, ddst;
+ SDL_Rect *region = r;
+ SDL_Rect limits;
+ SDL_PixelFormat *fmt = dst->format;
+ const int sp = src->pitch, dp = dst->pitch;
+ const int bpp = fmt->BytesPerPixel;
+ const int slen = sp / bpp, dlen = dp / bpp;
+ Uint32 *src_p = (Uint32 *)src->pixels;
+ Uint32 *dst_p = (Uint32 *)dst->pixels;
+ Uint32 pixval_tl, pixval_tr, pixval_bl, pixval_br;
+
+ // these macros are for clarity; they make the current pixel (0,0)
+ // and allow to access pixels in all directions
+ #define SRC(x, y) (src_p + (x) + ((y) * slen))
+
+ SCALE_(PlatInit) ();
+
+ // expand updated region if necessary
+ // pixels neighbooring the updated region may
+ // change as a result of updates
+ limits.x = 0;
+ limits.y = 0;
+ limits.w = src->w;
+ limits.h = src->h;
+ Scale_ExpandRect (region, 2, &limits);
+
+ xend = region->x + region->w;
+ yend = region->y + region->h;
+ dsrc = slen - region->w;
+ ddst = (dlen - region->w) * 2;
+
+ // move ptrs to the first updated pixel
+ src_p += slen * region->y + region->x;
+ dst_p += (dlen * region->y + region->x) * 2;
+
+ for (y = region->y; y < yend; ++y, dst_p += ddst, src_p += dsrc)
+ {
+ for (x = region->x; x < xend; ++x, ++src_p, ++dst_p)
+ {
+ pixval_tl = SCALE_GETPIX (SRC (0, 0));
+
+ SCALE_SETPIX (dst_p, pixval_tl);
+
+ if (y + 1 < h)
+ {
+ // check pixel below the current one
+ pixval_bl = SCALE_GETPIX (SRC (0, 1));
+
+ if (pixval_tl == pixval_bl)
+ SCALE_SETPIX (dst_p + dlen, pixval_tl);
+ else
+ SCALE_SETPIX (dst_p + dlen, Scale_Blend_11 (
+ pixval_tl, pixval_bl)
+ );
+ }
+ else
+ {
+ // last pixel in column - propagate
+ SCALE_SETPIX (dst_p + dlen, pixval_tl);
+ pixval_bl = pixval_tl;
+ }
+ ++dst_p;
+
+ if (x + 1 >= w)
+ {
+ // last pixel in row - propagate
+ SCALE_SETPIX (dst_p, pixval_tl);
+
+ if (pixval_tl == pixval_bl)
+ SCALE_SETPIX (dst_p + dlen, pixval_tl);
+ else
+ SCALE_SETPIX (dst_p + dlen, Scale_Blend_11 (
+ pixval_tl, pixval_bl)
+ );
+ continue;
+ }
+
+ // check pixel to the right from the current one
+ pixval_tr = SCALE_GETPIX (SRC (1, 0));
+
+ if (pixval_tl == pixval_tr)
+ SCALE_SETPIX (dst_p, pixval_tr);
+ else
+ SCALE_SETPIX (dst_p, Scale_Blend_11 (
+ pixval_tl, pixval_tr)
+ );
+
+ if (y + 1 >= h)
+ {
+ // last pixel in column - propagate
+ SCALE_SETPIX (dst_p + dlen, pixval_tl);
+ continue;
+ }
+
+ // check pixel to the bottom-right
+ pixval_br = SCALE_GETPIX (SRC (1, 1));
+
+ if (pixval_tl == pixval_br && pixval_tr == pixval_bl)
+ {
+ int cl, cr;
+ Uint32 clr;
+
+ if (pixval_tl == pixval_tr)
+ {
+ // all 4 are equal - propagate
+ SCALE_SETPIX (dst_p + dlen, pixval_tl);
+ continue;
+ }
+
+ // both pairs are equal, have to resolve the pixel
+ // race; we try detecting which color is
+ // the background by looking for a line or an edge
+ // examine 8 pixels surrounding the current quad
+
+ cl = cr = 1;
+
+ if (x > 0)
+ {
+ clr = SCALE_GETPIX (SRC (-1, 0));
+ if (clr == pixval_tl)
+ cl++;
+ else if (clr == pixval_tr)
+ cr++;
+
+ clr = SCALE_GETPIX (SRC (-1, 1));
+ if (clr == pixval_tl)
+ cl++;
+ else if (clr == pixval_tr)
+ cr++;
+ }
+
+ if (y > 0)
+ {
+ clr = SCALE_GETPIX (SRC (0, -1));
+ if (clr == pixval_tl)
+ cl++;
+ else if (clr == pixval_tr)
+ cr++;
+
+ clr = SCALE_GETPIX (SRC (1, -1));
+ if (clr == pixval_tl)
+ cl++;
+ else if (clr == pixval_tr)
+ cr++;
+ }
+
+ if (x + 2 < w)
+ {
+ clr = SCALE_GETPIX (SRC (2, 0));
+ if (clr == pixval_tl)
+ cl++;
+ else if (clr == pixval_tr)
+ cr++;
+
+ clr = SCALE_GETPIX (SRC (2, 1));
+ if (clr == pixval_tl)
+ cl++;
+ else if (clr == pixval_tr)
+ cr++;
+ }
+
+ if (y + 2 < h)
+ {
+ clr = SCALE_GETPIX (SRC (0, 2));
+ if (clr == pixval_tl)
+ cl++;
+ else if (clr == pixval_tr)
+ cr++;
+
+ clr = SCALE_GETPIX (SRC (1, 2));
+ if (clr == pixval_tl)
+ cl++;
+ else if (clr == pixval_tr)
+ cr++;
+ }
+
+ // least count wins
+ if (cl > cr)
+ SCALE_SETPIX (dst_p + dlen, pixval_tr);
+ else if (cr > cl)
+ SCALE_SETPIX (dst_p + dlen, pixval_tl);
+ else
+ SCALE_SETPIX (dst_p + dlen,
+ Scale_Blend_11 (pixval_tl, pixval_tr));
+ }
+ else if (pixval_tl == pixval_br)
+ {
+ // main diagonal is same color
+ // use its value
+ SCALE_SETPIX (dst_p + dlen, pixval_tl);
+ }
+ else if (pixval_tr == pixval_bl)
+ {
+ // 2nd diagonal is same color
+ // use its value
+ SCALE_SETPIX (dst_p + dlen, pixval_tr);
+ }
+ else
+ {
+ // blend all 4
+ SCALE_SETPIX (dst_p + dlen, Scale_Blend_1111 (
+ pixval_tl, pixval_bl, pixval_tr, pixval_br
+ ));
+ }
+ }
+ }
+
+ SCALE_(PlatDone) ();
+}
+
diff --git a/src/libs/graphics/sdl/2xscalers.h b/src/libs/graphics/sdl/2xscalers.h
new file mode 100644
index 0000000..f004fcd
--- /dev/null
+++ b/src/libs/graphics/sdl/2xscalers.h
@@ -0,0 +1,30 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_GRAPHICS_SDL_2XSCALERS_H_
+#define LIBS_GRAPHICS_SDL_2XSCALERS_H_
+
+void Scale_Nearest (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+void Scale_BilinearFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+void Scale_BiAdaptFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+void Scale_BiAdaptAdvFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+void Scale_TriScanFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+void Scale_HqFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+
+extern const Scale_FuncDef_t Scale_C_Functions[];
+
+
+#endif /* LIBS_GRAPHICS_SDL_2XSCALERS_H_ */
diff --git a/src/libs/graphics/sdl/2xscalers_3dnow.c b/src/libs/graphics/sdl/2xscalers_3dnow.c
new file mode 100644
index 0000000..da34e82
--- /dev/null
+++ b/src/libs/graphics/sdl/2xscalers_3dnow.c
@@ -0,0 +1,102 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "port.h"
+#include "libs/platform.h"
+
+#if defined(MMX_ASM)
+
+#include "libs/graphics/sdl/sdl_common.h"
+#include "types.h"
+#include "scalers.h"
+#include "scaleint.h"
+#include "2xscalers.h"
+#include "2xscalers_mmx.h"
+
+// 3DNow! name for all functions
+#undef SCALE_
+#define SCALE_(name) Scale ## _3DNow_ ## name
+
+// Tell them which opcodes we want to support
+#undef USE_MOVNTQ
+#define USE_PREFETCH AMD_PREFETCH
+#undef USE_PSADBW
+// Bring in inline asm functions
+#include "scalemmx.h"
+
+
+// Scaler function lookup table
+//
+const Scale_FuncDef_t
+Scale_3DNow_Functions[] =
+{
+ {TFB_GFXFLAGS_SCALE_BILINEAR, Scale_3DNow_BilinearFilter},
+ {TFB_GFXFLAGS_SCALE_BIADAPT, Scale_BiAdaptFilter},
+ {TFB_GFXFLAGS_SCALE_BIADAPTADV, Scale_MMX_BiAdaptAdvFilter},
+ {TFB_GFXFLAGS_SCALE_TRISCAN, Scale_MMX_TriScanFilter},
+ {TFB_GFXFLAGS_SCALE_HQXX, Scale_MMX_HqFilter},
+ // Default
+ {0, Scale_3DNow_Nearest}
+};
+
+
+void
+Scale_3DNow_PrepPlatform (const SDL_PixelFormat* fmt)
+{
+ Scale_MMX_PrepPlatform (fmt);
+}
+
+// Nearest Neighbor scaling to 2x
+// void Scale_3DNow_Nearest (SDL_Surface *src,
+// SDL_Surface *dst, SDL_Rect *r)
+
+#include "nearest2x.c"
+
+
+// Bilinear scaling to 2x
+// void Scale_3DNow_BilinearFilter (SDL_Surface *src,
+// SDL_Surface *dst, SDL_Rect *r)
+
+#include "bilinear2x.c"
+
+
+#if 0 && NO_IMPROVEMENT
+
+// Advanced Biadapt scaling to 2x
+// void Scale_3DNow_BiAdaptAdvFilter (SDL_Surface *src,
+// SDL_Surface *dst, SDL_Rect *r)
+
+#include "biadv2x.c"
+
+
+// Triscan scaling to 2x
+// derivative of scale2x -- scale2x.sf.net
+// void Scale_3DNow_TriScanFilter (SDL_Surface *src,
+// SDL_Surface *dst, SDL_Rect *r)
+
+#include "triscan2x.c"
+
+// Hq2x scaling
+// (adapted from 'hq2x' by Maxim Stepin -- www.hiend3d.com/hq2x.html)
+// void Scale_3DNow_HqFilter (SDL_Surface *src,
+// SDL_Surface *dst, SDL_Rect *r)
+
+#include "hq2x.c"
+
+#endif /* NO_IMPROVEMENT */
+
+#endif /* MMX_ASM */
+
diff --git a/src/libs/graphics/sdl/2xscalers_mmx.c b/src/libs/graphics/sdl/2xscalers_mmx.c
new file mode 100644
index 0000000..c321b16
--- /dev/null
+++ b/src/libs/graphics/sdl/2xscalers_mmx.c
@@ -0,0 +1,136 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "port.h"
+#include "libs/platform.h"
+
+#if defined(MMX_ASM)
+
+#include "libs/graphics/sdl/sdl_common.h"
+#include "types.h"
+#include "scalers.h"
+#include "scaleint.h"
+#include "2xscalers.h"
+#include "2xscalers_mmx.h"
+
+// MMX name for all functions
+#undef SCALE_
+#define SCALE_(name) Scale ## _MMX_ ## name
+
+// Tell them which opcodes we want to support
+#undef USE_MOVNTQ
+#undef USE_PREFETCH
+#undef USE_PSADBW
+// And Bring in inline asm functions
+#include "scalemmx.h"
+
+
+// Scaler function lookup table
+//
+const Scale_FuncDef_t
+Scale_MMX_Functions[] =
+{
+ {TFB_GFXFLAGS_SCALE_BILINEAR, Scale_MMX_BilinearFilter},
+ {TFB_GFXFLAGS_SCALE_BIADAPT, Scale_BiAdaptFilter},
+ {TFB_GFXFLAGS_SCALE_BIADAPTADV, Scale_MMX_BiAdaptAdvFilter},
+ {TFB_GFXFLAGS_SCALE_TRISCAN, Scale_MMX_TriScanFilter},
+ {TFB_GFXFLAGS_SCALE_HQXX, Scale_MMX_HqFilter},
+ // Default
+ {0, Scale_MMX_Nearest}
+};
+
+// MMX transformation multipliers
+Uint64 mmx_888to555_mult;
+Uint64 mmx_Y_mult;
+Uint64 mmx_U_mult;
+Uint64 mmx_V_mult;
+// Uint64 mmx_YUV_threshold = 0x00300706; original hq2x threshold
+//Uint64 mmx_YUV_threshold = 0x0030100e;
+Uint64 mmx_YUV_threshold = 0x0040120c;
+
+void
+Scale_MMX_PrepPlatform (const SDL_PixelFormat* fmt)
+{
+ // prepare the channel-shuffle multiplier
+ mmx_888to555_mult = ((Uint64)0x0400) << (fmt->Rshift * 2)
+ | ((Uint64)0x0020) << (fmt->Gshift * 2)
+ | ((Uint64)0x0001) << (fmt->Bshift * 2);
+
+ // prepare the RGB->YUV multipliers
+ mmx_Y_mult = ((Uint64)(uint16)YUV_matrix[YUV_XFORM_R][YUV_XFORM_Y])
+ << (fmt->Rshift * 2)
+ | ((Uint64)(uint16)YUV_matrix[YUV_XFORM_G][YUV_XFORM_Y])
+ << (fmt->Gshift * 2)
+ | ((Uint64)(uint16)YUV_matrix[YUV_XFORM_B][YUV_XFORM_Y])
+ << (fmt->Bshift * 2);
+
+ mmx_U_mult = ((Uint64)(uint16)YUV_matrix[YUV_XFORM_R][YUV_XFORM_U])
+ << (fmt->Rshift * 2)
+ | ((Uint64)(uint16)YUV_matrix[YUV_XFORM_G][YUV_XFORM_U])
+ << (fmt->Gshift * 2)
+ | ((Uint64)(uint16)YUV_matrix[YUV_XFORM_B][YUV_XFORM_U])
+ << (fmt->Bshift * 2);
+
+ mmx_V_mult = ((Uint64)(uint16)YUV_matrix[YUV_XFORM_R][YUV_XFORM_V])
+ << (fmt->Rshift * 2)
+ | ((Uint64)(uint16)YUV_matrix[YUV_XFORM_G][YUV_XFORM_V])
+ << (fmt->Gshift * 2)
+ | ((Uint64)(uint16)YUV_matrix[YUV_XFORM_B][YUV_XFORM_V])
+ << (fmt->Bshift * 2);
+
+ mmx_YUV_threshold = (SCALE_DIFFYUV_TY << 16) | (SCALE_DIFFYUV_TU << 8)
+ | SCALE_DIFFYUV_TV;
+}
+
+
+// Nearest Neighbor scaling to 2x
+// void Scale_MMX_Nearest (SDL_Surface *src,
+// SDL_Surface *dst, SDL_Rect *r)
+
+#include "nearest2x.c"
+
+
+// Bilinear scaling to 2x
+// void Scale_MMX_BilinearFilter (SDL_Surface *src,
+// SDL_Surface *dst, SDL_Rect *r)
+
+#include "bilinear2x.c"
+
+
+// Advanced Biadapt scaling to 2x
+// void Scale_MMX_BiAdaptAdvFilter (SDL_Surface *src,
+// SDL_Surface *dst, SDL_Rect *r)
+
+#include "biadv2x.c"
+
+
+// Triscan scaling to 2x
+// derivative of 'scale2x' -- scale2x.sf.net
+// void Scale_MMX_TriScanFilter (SDL_Surface *src,
+// SDL_Surface *dst, SDL_Rect *r)
+
+#include "triscan2x.c"
+
+// Hq2x scaling
+// (adapted from 'hq2x' by Maxim Stepin -- www.hiend3d.com/hq2x.html)
+// void Scale_MMX_HqFilter (SDL_Surface *src,
+// SDL_Surface *dst, SDL_Rect *r)
+
+#include "hq2x.c"
+
+
+#endif /* MMX_ASM */
+
diff --git a/src/libs/graphics/sdl/2xscalers_mmx.h b/src/libs/graphics/sdl/2xscalers_mmx.h
new file mode 100644
index 0000000..c8dba32
--- /dev/null
+++ b/src/libs/graphics/sdl/2xscalers_mmx.h
@@ -0,0 +1,56 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_GRAPHICS_SDL_2XSCALERS_MMX_H_
+#define LIBS_GRAPHICS_SDL_2XSCALERS_MMX_H_
+
+// MMX versions
+void Scale_MMX_PrepPlatform (const SDL_PixelFormat* fmt);
+
+void Scale_MMX_Nearest (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+void Scale_MMX_BilinearFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+void Scale_MMX_BiAdaptAdvFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+void Scale_MMX_TriScanFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+void Scale_MMX_HqFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+
+extern const Scale_FuncDef_t Scale_MMX_Functions[];
+
+
+// SSE (Intel)/MMX Ext (Athlon) versions
+void Scale_SSE_PrepPlatform (const SDL_PixelFormat* fmt);
+
+void Scale_SSE_Nearest (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+void Scale_SSE_BilinearFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+void Scale_SSE_BiAdaptAdvFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+void Scale_SSE_TriScanFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+void Scale_SSE_HqFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+
+extern const Scale_FuncDef_t Scale_SSE_Functions[];
+
+
+// 3DNow (AMD K6/Athlon) versions
+void Scale_3DNow_PrepPlatform (const SDL_PixelFormat* fmt);
+
+void Scale_3DNow_Nearest (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+void Scale_3DNow_BilinearFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+void Scale_3DNow_BiAdaptAdvFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+void Scale_3DNow_TriScanFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+void Scale_3DNow_HqFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+
+extern const Scale_FuncDef_t Scale_3DNow_Functions[];
+
+
+#endif /* LIBS_GRAPHICS_SDL_2XSCALERS_MMX_H_ */
diff --git a/src/libs/graphics/sdl/2xscalers_sse.c b/src/libs/graphics/sdl/2xscalers_sse.c
new file mode 100644
index 0000000..a089744
--- /dev/null
+++ b/src/libs/graphics/sdl/2xscalers_sse.c
@@ -0,0 +1,100 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "port.h"
+#include "libs/platform.h"
+
+#if defined(MMX_ASM)
+
+#include "libs/graphics/sdl/sdl_common.h"
+#include "types.h"
+#include "scalers.h"
+#include "scaleint.h"
+#include "2xscalers.h"
+#include "2xscalers_mmx.h"
+
+// SSE name for all functions
+#undef SCALE_
+#define SCALE_(name) Scale ## _SSE_ ## name
+
+// Tell them which opcodes we want to support
+#define USE_MOVNTQ
+#define USE_PREFETCH INTEL_PREFETCH
+#define USE_PSADBW
+// Bring in inline asm functions
+#include "scalemmx.h"
+
+
+// Scaler function lookup table
+//
+const Scale_FuncDef_t
+Scale_SSE_Functions[] =
+{
+ {TFB_GFXFLAGS_SCALE_BILINEAR, Scale_SSE_BilinearFilter},
+ {TFB_GFXFLAGS_SCALE_BIADAPT, Scale_BiAdaptFilter},
+ {TFB_GFXFLAGS_SCALE_BIADAPTADV, Scale_SSE_BiAdaptAdvFilter},
+ {TFB_GFXFLAGS_SCALE_TRISCAN, Scale_SSE_TriScanFilter},
+ {TFB_GFXFLAGS_SCALE_HQXX, Scale_MMX_HqFilter},
+ // Default
+ {0, Scale_SSE_Nearest}
+};
+
+
+void
+Scale_SSE_PrepPlatform (const SDL_PixelFormat* fmt)
+{
+ Scale_MMX_PrepPlatform (fmt);
+}
+
+// Nearest Neighbor scaling to 2x
+// void Scale_SSE_Nearest (SDL_Surface *src,
+// SDL_Surface *dst, SDL_Rect *r)
+
+#include "nearest2x.c"
+
+
+// Bilinear scaling to 2x
+// void Scale_SSE_BilinearFilter (SDL_Surface *src,
+// SDL_Surface *dst, SDL_Rect *r)
+
+#include "bilinear2x.c"
+
+
+// Advanced Biadapt scaling to 2x
+// void Scale_SSE_BiAdaptAdvFilter (SDL_Surface *src,
+// SDL_Surface *dst, SDL_Rect *r)
+
+#include "biadv2x.c"
+
+
+// Triscan scaling to 2x
+// derivative of scale2x -- scale2x.sf.net
+// void Scale_SSE_TriScanFilter (SDL_Surface *src,
+// SDL_Surface *dst, SDL_Rect *r)
+
+#include "triscan2x.c"
+
+#if 0 && NO_IMPROVEMENT
+// Hq2x scaling
+// (adapted from 'hq2x' by Maxim Stepin -- www.hiend3d.com/hq2x.html)
+// void Scale_SSE_HqFilter (SDL_Surface *src,
+// SDL_Surface *dst, SDL_Rect *r)
+
+#include "hq2x.c"
+#endif
+
+#endif /* MMX_ASM */
+
diff --git a/src/libs/graphics/sdl/Makeinfo b/src/libs/graphics/sdl/Makeinfo
new file mode 100644
index 0000000..f940b76
--- /dev/null
+++ b/src/libs/graphics/sdl/Makeinfo
@@ -0,0 +1,9 @@
+uqm_CFILES="opengl.c palette.c primitives.c pure.c sdl2_pure.c
+ sdl_common.c sdl1_common.c sdl2_common.c
+ scalers.c 2xscalers.c
+ 2xscalers_mmx.c 2xscalers_sse.c 2xscalers_3dnow.c
+ nearest2x.c bilinear2x.c biadv2x.c triscan2x.c hq2x.c
+ canvas.c png2sdl.c sdluio.c rotozoom.c"
+uqm_HFILES="2xscalers.h 2xscalers_mmx.h opengl.h palette.h png2sdl.h
+ primitives.h pure.h rotozoom.h scaleint.h scalemmx.h
+ scalers.h sdl_common.h sdluio.h"
diff --git a/src/libs/graphics/sdl/biadv2x.c b/src/libs/graphics/sdl/biadv2x.c
new file mode 100644
index 0000000..b38cdf7
--- /dev/null
+++ b/src/libs/graphics/sdl/biadv2x.c
@@ -0,0 +1,532 @@
+/*
+ * Portions Copyright (C) 2003-2005 Alex Volkov (codepro@usa.net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+// Core algorithm of the Advanced BiAdaptive screen scaler
+// Template
+// When this file is built standalone is produces a plain C version
+// Also #included by 2xscalers_mmx.c for an MMX version
+
+#include "libs/graphics/sdl/sdl_common.h"
+#include "types.h"
+#include "scalers.h"
+#include "scaleint.h"
+#include "2xscalers.h"
+
+
+// Advanced biadapt scaling to 2x
+// The name expands to either
+// Scale_BiAdaptAdvFilter (for plain C) or
+// Scale_MMX_BiAdaptAdvFilter (for MMX)
+// [others when platforms are added]
+void
+SCALE_(BiAdaptAdvFilter) (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r)
+{
+ int x, y;
+ const int w = src->w, h = src->h;
+ int xend, yend;
+ int dsrc, ddst;
+ SDL_Rect *region = r;
+ SDL_Rect limits;
+ SDL_PixelFormat *fmt = dst->format;
+ const int sp = src->pitch, dp = dst->pitch;
+ const int bpp = fmt->BytesPerPixel;
+ const int slen = sp / bpp, dlen = dp / bpp;
+ // for clarity purposes, the 'pixels' array here is transposed
+ Uint32 pixels[4][4];
+ static int resolve_coord[][2] =
+ {
+ {0, -1}, {1, -1}, { 2, 0}, { 2, 1},
+ {1, 2}, {0, 2}, {-1, 1}, {-1, 0},
+ {100, 100} // term
+ };
+ Uint32 *src_p = (Uint32 *)src->pixels;
+ Uint32 *dst_p = (Uint32 *)dst->pixels;
+
+ // these macros are for clarity; they make the current pixel (0,0)
+ // and allow to access pixels in all directions
+ #define PIX(x, y) (pixels[1 + (x)][1 + (y)])
+ #define SRC(x, y) (src_p + (x) + ((y) * slen))
+ // commonly used operations, for clarity also
+ // others are defined at their respective bpp levels
+ #define BIADAPT_RGBHIGH 8000
+ #define BIADAPT_YUVLOW 30
+ #define BIADAPT_YUVMED 70
+ #define BIADAPT_YUVHIGH 130
+
+ // high tolerance pixel comparison
+ #define BIADAPT_CMPRGB_HIGH(p1, p2) \
+ (p1 == p2 || SCALE_CMPRGB (p1, p2) <= BIADAPT_RGBHIGH)
+
+ // low tolerance pixel comparison
+ #define BIADAPT_CMPYUV_LOW(p1, p2) \
+ (p1 == p2 || SCALE_CMPYUV (p1, p2, BIADAPT_YUVLOW))
+ // medium tolerance pixel comparison
+ #define BIADAPT_CMPYUV_MED(p1, p2) \
+ (p1 == p2 || SCALE_CMPYUV (p1, p2, BIADAPT_YUVMED))
+ // high tolerance pixel comparison
+ #define BIADAPT_CMPYUV_HIGH(p1, p2) \
+ (p1 == p2 || SCALE_CMPYUV (p1, p2, BIADAPT_YUVHIGH))
+
+ SCALE_(PlatInit) ();
+
+ // expand updated region if necessary
+ // pixels neighbooring the updated region may
+ // change as a result of updates
+ limits.x = 0;
+ limits.y = 0;
+ limits.w = src->w;
+ limits.h = src->h;
+ Scale_ExpandRect (region, 2, &limits);
+
+ xend = region->x + region->w;
+ yend = region->y + region->h;
+ dsrc = slen - region->w;
+ ddst = (dlen - region->w) * 2;
+
+ #define SCALE_GETPIX(p) ( *(Uint32 *)(p) )
+ #define SCALE_SETPIX(p, c) ( *(Uint32 *)(p) = (c) )
+
+ // move ptrs to the first updated pixel
+ src_p += slen * region->y + region->x;
+ dst_p += (dlen * region->y + region->x) * 2;
+
+ for (y = region->y; y < yend; ++y, dst_p += ddst, src_p += dsrc)
+ {
+ for (x = region->x; x < xend; ++x, ++src_p, ++dst_p)
+ {
+ // pixel equality counter
+ int cmatch;
+
+ // most pixels will fall into 'all 4 equal'
+ // pattern, so we check it first
+ cmatch = 0;
+
+ PIX (0, 0) = SCALE_GETPIX (SRC (0, 0));
+
+ SCALE_SETPIX (dst_p, PIX (0, 0));
+
+ if (y + 1 < h)
+ {
+ // check pixel below the current one
+ PIX (0, 1) = SCALE_GETPIX (SRC (0, 1));
+
+ if (PIX (0, 0) == PIX (0, 1))
+ {
+ SCALE_SETPIX (dst_p + dlen, PIX (0, 0));
+ cmatch |= 1;
+ }
+ }
+ else
+ {
+ // last pixel in column - propagate
+ PIX (0, 1) = PIX (0, 0);
+ SCALE_SETPIX (dst_p + dlen, PIX (0, 0));
+ cmatch |= 1;
+
+ }
+
+ if (x + 1 < w)
+ {
+ // check pixel to the right from the current one
+ PIX (1, 0) = SCALE_GETPIX (SRC (1, 0));
+
+ if (PIX (0, 0) == PIX (1, 0))
+ {
+ SCALE_SETPIX (dst_p + 1, PIX (0, 0));
+ cmatch |= 2;
+ }
+ }
+ else
+ {
+ // last pixel in row - propagate
+ PIX (1, 0) = PIX (0, 0);
+ SCALE_SETPIX (dst_p + 1, PIX (0, 0));
+ cmatch |= 2;
+ }
+
+ if (cmatch == 3)
+ {
+ if (y + 1 >= h || x + 1 >= w)
+ {
+ // last pixel in row/column and nearest
+ // neighboor is identical
+ dst_p++;
+ SCALE_SETPIX (dst_p + dlen, PIX (0, 0));
+ continue;
+ }
+
+ // check pixel to the bottom-right
+ PIX (1, 1) = SCALE_GETPIX (SRC (1, 1));
+
+ if (PIX (0, 0) == PIX (1, 1))
+ {
+ // all 4 are equal - propagate
+ dst_p++;
+ SCALE_SETPIX (dst_p + dlen, PIX (0, 0));
+ continue;
+ }
+ }
+
+ // some neighboors are different, lets check them
+
+ if (x > 0)
+ PIX (-1, 0) = SCALE_GETPIX (SRC (-1, 0));
+ else
+ PIX (-1, 0) = PIX (0, 0);
+
+ if (x + 2 < w)
+ PIX (2, 0) = SCALE_GETPIX (SRC (2, 0));
+ else
+ PIX (2, 0) = PIX (1, 0);
+
+ if (y + 1 < h)
+ {
+ if (x > 0)
+ PIX (-1, 1) = SCALE_GETPIX (SRC (-1, 1));
+ else
+ PIX (-1, 1) = PIX (0, 1);
+
+ if (x + 2 < w)
+ {
+ PIX (1, 1) = SCALE_GETPIX (SRC (1, 1));
+ PIX (2, 1) = SCALE_GETPIX (SRC (2, 1));
+ }
+ else if (x + 1 < w)
+ {
+ PIX (1, 1) = SCALE_GETPIX (SRC (1, 1));
+ PIX (2, 1) = PIX (1, 1);
+ }
+ else
+ {
+ PIX (1, 1) = PIX (0, 1);
+ PIX (2, 1) = PIX (0, 1);
+ }
+ }
+ else
+ {
+ // last pixel in column
+ PIX (-1, 1) = PIX (-1, 0);
+ PIX (1, 1) = PIX (1, 0);
+ PIX (2, 1) = PIX (2, 0);
+ }
+
+ if (y + 2 < h)
+ {
+ PIX (0, 2) = SCALE_GETPIX (SRC (0, 2));
+
+ if (x > 0)
+ PIX (-1, 2) = SCALE_GETPIX (SRC (-1, 2));
+ else
+ PIX (-1, 2) = PIX (0, 2);
+
+ if (x + 2 < w)
+ {
+ PIX (1, 2) = SCALE_GETPIX (SRC (1, 2));
+ PIX (2, 2) = SCALE_GETPIX (SRC (2, 2));
+ }
+ else if (x + 1 < w)
+ {
+ PIX (1, 2) = SCALE_GETPIX (SRC (1, 2));
+ PIX (2, 2) = PIX (1, 2);
+ }
+ else
+ {
+ PIX (1, 2) = PIX (0, 2);
+ PIX (2, 2) = PIX (0, 2);
+ }
+ }
+ else
+ {
+ // last pixel in column
+ PIX (-1, 2) = PIX (-1, 1);
+ PIX (0, 2) = PIX (0, 1);
+ PIX (1, 2) = PIX (1, 1);
+ PIX (2, 2) = PIX (2, 1);
+ }
+
+ if (y > 0)
+ {
+ PIX (0, -1) = SCALE_GETPIX (SRC (0, -1));
+
+ if (x > 0)
+ PIX (-1, -1) = SCALE_GETPIX (SRC (-1, -1));
+ else
+ PIX (-1, -1) = PIX (0, -1);
+
+ if (x + 2 < w)
+ {
+ PIX (1, -1) = SCALE_GETPIX (SRC (1, -1));
+ PIX (2, -1) = SCALE_GETPIX (SRC (2, -1));
+ }
+ else if (x + 1 < w)
+ {
+ PIX (1, -1) = SCALE_GETPIX (SRC (1, -1));
+ PIX (2, -1) = PIX (1, -1);
+ }
+ else
+ {
+ PIX (1, -1) = PIX (0, -1);
+ PIX (2, -1) = PIX (0, -1);
+ }
+ }
+ else
+ {
+ PIX (-1, -1) = PIX (-1, 0);
+ PIX (0, -1) = PIX (0, 0);
+ PIX (1, -1) = PIX (1, 0);
+ PIX (2, -1) = PIX (2, 0);
+ }
+
+ // check pixel below the current one
+ if (!(cmatch & 1))
+ {
+ if (SCALE_CMPYUV (PIX (0, 0), PIX (0, 1), BIADAPT_YUVLOW))
+ {
+ SCALE_SETPIX (dst_p + dlen, Scale_Blend_11 (
+ PIX (0, 0), PIX (0, 1))
+ );
+ cmatch |= 1;
+ }
+ // detect a 2:1 line going across the current pixel
+ else if ( (PIX (0, 0) == PIX (-1, 0)
+ && PIX (0, 0) == PIX (1, 1)
+ && PIX (0, 0) == PIX (2, 1) &&
+
+ ((!BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (-1, -1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (0, -1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (1, 0))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (2, 0))) ||
+ (!BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (-1, 1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (1, 2))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (2, 2))))) ||
+
+ (PIX (0, 0) == PIX (1, 0)
+ && PIX (0, 0) == PIX (-1, 1)
+ && PIX (0, 0) == PIX (2, -1) &&
+
+ ((!BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (-1, 0))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (0, -1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (1, -1))) ||
+ (!BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (-1, 2))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (1, 1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (2, 0))))) )
+ {
+ SCALE_SETPIX (dst_p + dlen, PIX (0, 0));
+ }
+ // detect a 2:1 line going across the pixel below current
+ else if ( (PIX (0, 1) == PIX (-1, 0)
+ && PIX (0, 1) == PIX (1, 1)
+ && PIX (0, 1) == PIX (2, 2) &&
+
+ ((!BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (-1, -1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (1, 0))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (2, 1))) ||
+ (!BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (-1, 1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (0, 2))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (1, 2))))) ||
+
+ (PIX (0, 1) == PIX (1, 0)
+ && PIX (0, 1) == PIX (-1, 1)
+ && PIX (0, 1) == PIX (2, 0) &&
+
+ ((!BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (-1, 0))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (1, -1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (2, -1))) ||
+ (!BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (-1, 2))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (0, 2))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (1, 1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (2, 1))))) )
+ {
+ SCALE_SETPIX (dst_p + dlen, PIX (0, 1));
+ }
+ else
+ SCALE_SETPIX (dst_p + dlen, Scale_Blend_11 (
+ PIX (0, 0), PIX (0, 1))
+ );
+ }
+
+ dst_p++;
+
+ // check pixel to the right from the current one
+ if (!(cmatch & 2))
+ {
+ if (SCALE_CMPYUV (PIX (0, 0), PIX (1, 0), BIADAPT_YUVLOW))
+ {
+ SCALE_SETPIX (dst_p, Scale_Blend_11 (
+ PIX (0, 0), PIX (1, 0))
+ );
+ cmatch |= 2;
+ }
+ // detect a 1:2 line going across the current pixel
+ else if ( (PIX (0, 0) == PIX (1, -1)
+ && PIX (0, 0) == PIX (0, 1)
+ && PIX (0, 0) == PIX (-1, 2) &&
+
+ ((!BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (0, -1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (-1, 0))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (-1, 1))) ||
+ (!BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (2, -1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (1, 1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (0, 2))))) ||
+
+ (PIX (0, 0) == PIX (0, -1)
+ && PIX (0, 0) == PIX (1, 1)
+ && PIX (0, 0) == PIX (1, 2) &&
+
+ ((!BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (-1, -1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (-1, 0))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (0, 1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (0, 2))) ||
+ (!BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (1, -1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (2, 1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (2, 2))))) )
+
+ {
+ SCALE_SETPIX (dst_p, PIX (0, 0));
+ }
+ // detect a 1:2 line going across the pixel to the right
+ else if ( (PIX (1, 0) == PIX (1, -1)
+ && PIX (1, 0) == PIX (0, 1)
+ && PIX (1, 0) == PIX (0, 2) &&
+
+ ((!BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (0, -1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (-1, 1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (-1, 2))) ||
+ (!BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (2, -1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (2, 0))
+ && !BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (1, 1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (1, 2))))) ||
+
+ (PIX (1, 0) == PIX (0, -1)
+ && PIX (1, 0) == PIX (1, 1)
+ && PIX (1, 0) == PIX (2, 2) &&
+
+ ((!BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (-1, -1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (0, 1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (1, 2))) ||
+ (!BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (1, -1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (2, 0))
+ && !BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (2, 1))))) )
+ {
+ SCALE_SETPIX (dst_p, PIX (1, 0));
+ }
+ else
+ SCALE_SETPIX (dst_p, Scale_Blend_11 (
+ PIX (0, 0), PIX (1, 0))
+ );
+ }
+
+ if (PIX (0, 0) == PIX (1, 1) && PIX (1, 0) == PIX (0, 1))
+ {
+ // diagonals are equal
+ int *coord;
+ int cl, cr;
+ Uint32 clr;
+
+ // both pairs are equal, have to resolve the pixel
+ // race; we try detecting which color is
+ // the background by looking for a line or an edge
+ // examine 8 pixels surrounding the current quad
+
+ cl = cr = 2;
+ for (coord = resolve_coord[0]; *coord < 100; coord += 2)
+ {
+ clr = PIX (coord[0], coord[1]);
+
+ if (BIADAPT_CMPYUV_MED (clr, PIX (0, 0)))
+ cl++;
+ else if (BIADAPT_CMPYUV_MED (clr, PIX (1, 0)))
+ cr++;
+ }
+
+ // least count wins
+ if (cl > cr)
+ clr = PIX (1, 0);
+ else if (cr > cl)
+ clr = PIX (0, 0);
+ else
+ clr = Scale_Blend_11 (PIX (0, 0), PIX (1, 0));
+
+ SCALE_SETPIX (dst_p + dlen, clr);
+ continue;
+ }
+
+ if (cmatch == 3
+ || (BIADAPT_CMPYUV_LOW (PIX (1, 0), PIX (0, 1))
+ && BIADAPT_CMPYUV_LOW (PIX (1, 0), PIX (1, 1))))
+ {
+ SCALE_SETPIX (dst_p + dlen, Scale_Blend_11 (
+ PIX (0, 1), PIX (1, 0))
+ );
+ continue;
+ }
+ else if (cmatch && BIADAPT_CMPYUV_LOW (PIX (0, 0), PIX (1, 1)))
+ {
+ SCALE_SETPIX (dst_p + dlen, Scale_Blend_11 (
+ PIX (0, 0), PIX (1, 1))
+ );
+ continue;
+ }
+
+ // check pixel to the bottom-right
+ if (BIADAPT_CMPYUV_HIGH (PIX (0, 0), PIX (1, 1))
+ && BIADAPT_CMPYUV_HIGH (PIX (1, 0), PIX (0, 1)))
+ {
+ if (SCALE_GETY (PIX (0, 0)) > SCALE_GETY (PIX (1, 0)))
+ {
+ SCALE_SETPIX (dst_p + dlen, Scale_Blend_11 (
+ PIX (0, 0), PIX (1, 1))
+ );
+ }
+ else
+ {
+ SCALE_SETPIX (dst_p + dlen, Scale_Blend_11 (
+ PIX (1, 0), PIX (0, 1))
+ );
+ }
+ }
+ else if (BIADAPT_CMPYUV_HIGH (PIX (0, 0), PIX (1, 1)))
+ {
+ // main diagonal is same color
+ // use its value
+ SCALE_SETPIX (dst_p + dlen, Scale_Blend_11 (
+ PIX (0, 0), PIX (1, 1))
+ );
+ }
+ else if (BIADAPT_CMPYUV_HIGH (PIX (1, 0), PIX (0, 1)))
+ {
+ // 2nd diagonal is same color
+ // use its value
+ SCALE_SETPIX (dst_p + dlen, Scale_Blend_11 (
+ PIX (1, 0), PIX (0, 1))
+ );
+ }
+ else
+ {
+ // blend all 4
+ SCALE_SETPIX (dst_p + dlen, Scale_Blend_1111 (
+ PIX (0, 0), PIX (0, 1),
+ PIX (1, 0), PIX (1, 1)
+ ));
+ }
+ }
+ }
+
+ SCALE_(PlatDone) ();
+}
+
diff --git a/src/libs/graphics/sdl/bilinear2x.c b/src/libs/graphics/sdl/bilinear2x.c
new file mode 100644
index 0000000..a12eb93
--- /dev/null
+++ b/src/libs/graphics/sdl/bilinear2x.c
@@ -0,0 +1,112 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+// Core algorithm of the BiLinear screen scaler
+// Template
+// When this file is built standalone is produces a plain C version
+// Also #included by 2xscalers_mmx.c for an MMX version
+
+#include "libs/graphics/sdl/sdl_common.h"
+#include "types.h"
+#include "scalers.h"
+#include "scaleint.h"
+#include "2xscalers.h"
+
+
+// Bilinear scaling to 2x
+// The name expands to either
+// Scale_BilinearFilter (for plain C) or
+// Scale_MMX_BilinearFilter (for MMX)
+// Scale_SSE_BilinearFilter (for SSE)
+// [others when platforms are added]
+void
+SCALE_(BilinearFilter) (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r)
+{
+ int x, y;
+ const int w = src->w, h = src->h;
+ int xend, yend;
+ int dsrc, ddst;
+ SDL_Rect *region = r;
+ SDL_Rect limits;
+ SDL_PixelFormat *fmt = dst->format;
+ const int pitch = src->pitch, dp = dst->pitch;
+ const int bpp = fmt->BytesPerPixel;
+ const int len = pitch / bpp, dlen = dp / bpp;
+ Uint32 p[4]; // influential pixels array
+ Uint32 *srow0 = (Uint32 *) src->pixels;
+ Uint32 *dst_p = (Uint32 *) dst->pixels;
+
+ SCALE_(PlatInit) ();
+
+ // expand updated region if necessary
+ // pixels neighbooring the updated region may
+ // change as a result of updates
+ limits.x = 0;
+ limits.y = 0;
+ limits.w = w;
+ limits.h = h;
+ Scale_ExpandRect (region, 1, &limits);
+
+ xend = region->x + region->w;
+ yend = region->y + region->h;
+ dsrc = len - region->w;
+ ddst = (dlen - region->w) * 2;
+
+ // move ptrs to the first updated pixel
+ srow0 += len * region->y + region->x;
+ dst_p += (dlen * region->y + region->x) * 2;
+
+ for (y = region->y; y < yend; ++y, dst_p += ddst, srow0 += dsrc)
+ {
+ Uint32 *srow1;
+
+ SCALE_(Prefetch) (srow0 + 16);
+ SCALE_(Prefetch) (srow0 + 32);
+
+ if (y < h - 1)
+ srow1 = srow0 + len;
+ else
+ srow1 = srow0;
+
+ SCALE_(Prefetch) (srow1 + 16);
+ SCALE_(Prefetch) (srow1 + 32);
+
+ for (x = region->x; x < xend; ++x, ++srow0, ++srow1, dst_p += 2)
+ {
+ if (x < w - 1)
+ { // can blend directly from pixels
+ SCALE_BILINEAR_BLEND4 (srow0, srow1, dst_p, dlen);
+ }
+ else
+ { // need to make temp pixel rows
+ p[0] = srow0[0];
+ p[1] = p[0];
+ p[2] = srow1[0];
+ p[3] = p[2];
+
+ SCALE_BILINEAR_BLEND4 (&p[0], &p[2], dst_p, dlen);
+ }
+ }
+
+ SCALE_(Prefetch) (srow0 + dsrc);
+ SCALE_(Prefetch) (srow0 + dsrc + 16);
+ SCALE_(Prefetch) (srow1 + dsrc);
+ SCALE_(Prefetch) (srow1 + dsrc + 16);
+ }
+
+ SCALE_(PlatDone) ();
+}
+
diff --git a/src/libs/graphics/sdl/canvas.c b/src/libs/graphics/sdl/canvas.c
new file mode 100644
index 0000000..ad7024d
--- /dev/null
+++ b/src/libs/graphics/sdl/canvas.c
@@ -0,0 +1,2176 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "port.h"
+#include <string.h>
+ // for memcpy()
+
+#include SDL_INCLUDE(SDL.h)
+#include "sdl_common.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/graphics/tfb_draw.h"
+#include "libs/graphics/cmap.h"
+#include "libs/log.h"
+#include "libs/memlib.h"
+#include "primitives.h"
+#include "palette.h"
+#include "sdluio.h"
+#include "rotozoom.h"
+#include "options.h"
+#include "types.h"
+
+typedef SDL_Surface *NativeCanvas;
+
+// BYTE x BYTE weight (mult >> 8) table
+static Uint8 btable[256][256];
+
+void
+TFB_DrawCanvas_Initialize (void)
+{
+ int i, j;
+ for (i = 0; i < 256; ++i)
+ for (j = 0; j < 256; ++j)
+ btable[j][i] = (j * i + 0x80) >> 8;
+ // need error correction here
+}
+
+const char *
+TFB_DrawCanvas_GetError (void)
+{
+ const char *err = SDL_GetError ();
+ // TODO: Should we call SDL_ClearError() here so that it is not
+ // returned again later?
+ return err;
+}
+
+static void
+checkPrimitiveMode (SDL_Surface *surf, Color *color, DrawMode *mode)
+{
+ const SDL_PixelFormat *fmt = surf->format;
+ // Special case: We support DRAW_ALPHA mode to non-alpha surfaces
+ // for primitives via Color.a
+ if (mode->kind == DRAW_REPLACE && fmt->Amask == 0 && color->a != 0xff)
+ {
+ mode->kind = DRAW_ALPHA;
+ mode->factor = color->a;
+ color->a = 0xff;
+ }
+}
+
+void
+TFB_DrawCanvas_Line (int x1, int y1, int x2, int y2, Color color,
+ DrawMode mode, TFB_Canvas target)
+{
+ SDL_Surface *dst = target;
+ SDL_PixelFormat *fmt = dst->format;
+ Uint32 sdlColor;
+ RenderPixelFn plotFn;
+
+ checkPrimitiveMode (dst, &color, &mode);
+ sdlColor = SDL_MapRGBA (fmt, color.r, color.g, color.b, color.a);
+
+ plotFn = renderpixel_for (target, mode.kind);
+ if (!plotFn)
+ {
+ log_add (log_Warning, "ERROR: TFB_DrawCanvas_Line "
+ "unsupported draw mode (%d)", (int)mode.kind);
+ return;
+ }
+
+ SDL_LockSurface (dst);
+ line_prim (x1, y1, x2, y2, sdlColor, plotFn, mode.factor, dst);
+ SDL_UnlockSurface (dst);
+}
+
+void
+TFB_DrawCanvas_Rect (RECT *rect, Color color, DrawMode mode, TFB_Canvas target)
+{
+ SDL_Surface *dst = target;
+ SDL_PixelFormat *fmt = dst->format;
+ Uint32 sdlColor;
+ SDL_Rect sr;
+ sr.x = rect->corner.x;
+ sr.y = rect->corner.y;
+ sr.w = rect->extent.width;
+ sr.h = rect->extent.height;
+
+ checkPrimitiveMode (dst, &color, &mode);
+ sdlColor = SDL_MapRGBA (fmt, color.r, color.g, color.b, color.a);
+
+ if (mode.kind == DRAW_REPLACE)
+ { // Standard SDL fillrect rendering
+ Uint32 colorkey;
+ if (fmt->Amask && (TFB_GetColorKey (dst, &colorkey) == 0))
+ { // special case -- alpha surface with colorkey
+ // colorkey rects are transparent
+ if ((sdlColor & ~fmt->Amask) == (colorkey & ~fmt->Amask))
+ sdlColor &= ~fmt->Amask; // make transparent
+ }
+ SDL_FillRect (dst, &sr, sdlColor);
+ }
+ else
+ { // Custom fillrect rendering
+ RenderPixelFn plotFn = renderpixel_for (target, mode.kind);
+ if (!plotFn)
+ {
+ log_add (log_Warning, "ERROR: TFB_DrawCanvas_Rect "
+ "unsupported draw mode (%d)", (int)mode.kind);
+ return;
+ }
+
+ SDL_LockSurface (dst);
+ fillrect_prim (sr, sdlColor, plotFn, mode.factor, dst);
+ SDL_UnlockSurface (dst);
+ }
+}
+
+static void
+TFB_DrawCanvas_Blit (SDL_Surface *src, SDL_Rect *src_r,
+ SDL_Surface *dst, SDL_Rect *dst_r, DrawMode mode)
+{
+ SDL_PixelFormat *srcfmt = src->format;
+
+ if (mode.kind == DRAW_REPLACE)
+ { // Standard SDL simple blit
+ SDL_BlitSurface (src, src_r, dst, dst_r);
+ }
+ else if (mode.kind == DRAW_ALPHA && srcfmt->Amask == 0)
+ { // Standard SDL surface-alpha blit
+ // Note that surface alpha and per-pixel alpha cannot work
+ // at the same time, which is why the Amask test
+ int hasAlpha = TFB_HasSurfaceAlphaMod (src);
+ assert (!hasAlpha);
+ // Set surface alpha temporarily
+ TFB_SetSurfaceAlphaMod (src, mode.factor);
+ SDL_BlitSurface (src, src_r, dst, dst_r);
+ TFB_DisableSurfaceAlphaMod (src);
+ }
+ else
+ { // Custom blit
+ SDL_Rect loc_src_r, loc_dst_r;
+ RenderPixelFn plotFn = renderpixel_for (dst, mode.kind);
+ if (!plotFn)
+ {
+ log_add (log_Warning, "ERROR: TFB_DrawCanvas_Blit "
+ "unsupported draw mode (%d)", (int)mode.kind);
+ return;
+ }
+
+ if (!src_r)
+ { // blit whole image; generate rect
+ loc_src_r.x = 0;
+ loc_src_r.y = 0;
+ loc_src_r.w = src->w;
+ loc_src_r.h = src->h;
+ src_r = &loc_src_r;
+ }
+
+ if (!dst_r)
+ { // blit to 0,0; generate rect
+ loc_dst_r.x = 0;
+ loc_dst_r.y = 0;
+ loc_dst_r.w = dst->w;
+ loc_dst_r.h = dst->h;
+ dst_r = &loc_dst_r;
+ }
+
+ SDL_LockSurface (dst);
+ blt_prim (src, *src_r, plotFn, mode.factor, dst, *dst_r);
+ SDL_UnlockSurface (dst);
+ }
+}
+
+// XXX: If a colormap is passed in, it has to have been acquired via
+// TFB_GetColorMap(). We release the colormap at the end.
+void
+TFB_DrawCanvas_Image (TFB_Image *img, int x, int y, int scale,
+ int scaleMode, TFB_ColorMap *cmap, DrawMode mode, TFB_Canvas target)
+{
+ SDL_Rect srcRect, targetRect, *pSrcRect;
+ SDL_Surface *surf;
+ SDL_Palette *NormalPal;
+
+ if (img == 0)
+ {
+ log_add (log_Warning,
+ "ERROR: TFB_DrawCanvas_Image passed null image ptr");
+ return;
+ }
+
+ LockMutex (img->mutex);
+
+ NormalPal = ((SDL_Surface *)img->NormalImg)->format->palette;
+ // only set the new palette if it changed
+ if (NormalPal && cmap && img->colormap_version != cmap->version)
+ TFB_SetColors (img->NormalImg, cmap->palette->colors, 0, 256);
+
+ if (scale != 0 && scale != GSCALE_IDENTITY)
+ {
+ if (scaleMode == TFB_SCALE_TRILINEAR && img->MipmapImg)
+ {
+ // only set the new palette if it changed
+ if (TFB_DrawCanvas_IsPaletted (img->MipmapImg)
+ && cmap && img->colormap_version != cmap->version)
+ TFB_SetColors (img->MipmapImg, cmap->palette->colors, 0, 256);
+ }
+ else if (scaleMode == TFB_SCALE_TRILINEAR && !img->MipmapImg)
+ { // Do bilinear scaling instead when mipmap is unavailable
+ scaleMode = TFB_SCALE_BILINEAR;
+ }
+
+ TFB_DrawImage_FixScaling (img, scale, scaleMode);
+ surf = img->ScaledImg;
+ if (TFB_DrawCanvas_IsPaletted (surf))
+ {
+ // We may only get a paletted scaled image if the source is
+ // paletted. Currently, all scaling targets are truecolor.
+ assert (NormalPal && NormalPal->colors);
+ TFB_SetColors (surf, NormalPal->colors, 0, NormalPal->ncolors);
+ }
+
+ srcRect.x = 0;
+ srcRect.y = 0;
+ srcRect.w = img->extent.width;
+ srcRect.h = img->extent.height;
+ pSrcRect = &srcRect;
+
+ targetRect.x = x - img->last_scale_hs.x;
+ targetRect.y = y - img->last_scale_hs.y;
+ }
+ else
+ {
+ surf = img->NormalImg;
+ pSrcRect = NULL;
+
+ targetRect.x = x - img->NormalHs.x;
+ targetRect.y = y - img->NormalHs.y;
+ }
+
+ if (cmap)
+ {
+ img->colormap_version = cmap->version;
+ // TODO: Technically, this is not a proper place to release a
+ // colormap. As it stands now, the colormap must have been
+ // addrefed when passed to us.
+ TFB_ReturnColorMap (cmap);
+ }
+
+ TFB_DrawCanvas_Blit (surf, pSrcRect, target, &targetRect, mode);
+ UnlockMutex (img->mutex);
+}
+
+// Assumes the source and destination surfaces are in the same format
+static void
+TFB_DrawCanvas_Fill (SDL_Surface *src, Uint32 fillcolor, SDL_Surface *dst)
+{
+ const SDL_PixelFormat *srcfmt = src->format;
+ SDL_PixelFormat *dstfmt = dst->format;
+ const int width = src->w;
+ const int height = src->h;
+ const int bpp = dstfmt->BytesPerPixel;
+ const int sp = src->pitch, dp = dst->pitch;
+ const int slen = sp / bpp, dlen = dp / bpp;
+ const int dsrc = slen - width, ddst = dlen - width;
+ Uint32 *src_p;
+ Uint32 *dst_p;
+ int x, y;
+ Uint32 srckey = 0, dstkey = 0; // 0 means alpha=0 too
+ Uint32 amask = srcfmt->Amask;
+ int alpha = (fillcolor & amask) >> srcfmt->Ashift;
+
+ if (srcfmt->BytesPerPixel != 4 || dstfmt->BytesPerPixel != 4)
+ {
+ log_add (log_Warning, "TFB_DrawCanvas_Fill: Unsupported surface "
+ "formats: %d bytes/pixel source, %d bytes/pixel destination",
+ (int)srcfmt->BytesPerPixel, (int)dstfmt->BytesPerPixel);
+ return;
+ }
+
+ // Strip the alpha channel from fillcolor because we process the
+ // alpha separately
+ fillcolor &= ~amask;
+
+ SDL_LockSurface(src);
+ SDL_LockSurface(dst);
+
+ src_p = (Uint32 *)src->pixels;
+ dst_p = (Uint32 *)dst->pixels;
+
+ if (dstkey == fillcolor)
+ { // color collision, must switch colorkey
+ // new colorkey is grey (1/2,1/2,1/2)
+ dstkey = SDL_MapRGBA (dstfmt, 127, 127, 127, 0);
+ }
+
+ if (srcfmt->Amask)
+ { // alpha-based fill
+ for (y = 0; y < height; ++y, dst_p += ddst, src_p += dsrc)
+ {
+ for (x = 0; x < width; ++x, ++src_p, ++dst_p)
+ {
+ Uint32 p = *src_p & amask;
+
+ if (p == 0)
+ { // fully transparent pixel
+ *dst_p = dstkey;
+ }
+ else if (alpha == 0xff)
+ { // not for DRAW_ALPHA; use alpha chan directly
+ *dst_p = p | fillcolor;
+ }
+ else
+ { // for DRAW_ALPHA; modulate the alpha channel
+ p >>= srcfmt->Ashift;
+ p = (p * alpha) >> 8;
+ p <<= srcfmt->Ashift;
+ *dst_p = p | fillcolor;
+ }
+ }
+ }
+ }
+ else if (TFB_GetColorKey (src, &srckey) == 0)
+ { // colorkey-based fill
+
+ for (y = 0; y < height; ++y, dst_p += ddst, src_p += dsrc)
+ {
+ for (x = 0; x < width; ++x, ++src_p, ++dst_p)
+ {
+ Uint32 p = *src_p;
+
+ *dst_p = (p == srckey) ? dstkey : fillcolor;
+ }
+ }
+ }
+ else
+ {
+ log_add (log_Warning, "TFB_DrawCanvas_Fill: Unsupported source"
+ "surface format\n");
+ }
+
+ SDL_UnlockSurface(dst);
+ SDL_UnlockSurface(src);
+
+ // save the colorkey (dynamic image -- not using RLE coding here)
+ TFB_SetColorKey (dst, dstkey, 0);
+ // if the filled surface is RGBA, colorkey will only be used
+ // when SDL_SRCALPHA flag is cleared. this allows us to blit
+ // the surface in different ways to diff targets
+}
+
+void
+TFB_DrawCanvas_FilledImage (TFB_Image *img, int x, int y, int scale,
+ int scaleMode, Color color, DrawMode mode, TFB_Canvas target)
+{
+ SDL_Surface *dst = target;
+ SDL_Rect srcRect, targetRect, *pSrcRect;
+ SDL_Surface *surf;
+ SDL_Palette *palette;
+ int i;
+ bool force_fill = false;
+
+ if (img == 0)
+ {
+ log_add (log_Warning,
+ "ERROR: TFB_DrawCanvas_FilledImage passed null image ptr");
+ return;
+ }
+
+ checkPrimitiveMode (dst, &color, &mode);
+
+ LockMutex (img->mutex);
+
+ if (scale != 0 && scale != GSCALE_IDENTITY)
+ {
+ if (scaleMode == TFB_SCALE_TRILINEAR)
+ scaleMode = TFB_SCALE_BILINEAR;
+ // no point in trilinear for filled images
+
+ if (scale != img->last_scale || scaleMode != img->last_scale_type)
+ force_fill = true;
+
+ TFB_DrawImage_FixScaling (img, scale, scaleMode);
+ surf = img->ScaledImg;
+ srcRect.x = 0;
+ srcRect.y = 0;
+ srcRect.w = img->extent.width;
+ srcRect.h = img->extent.height;
+ pSrcRect = &srcRect;
+
+ targetRect.x = x - img->last_scale_hs.x;
+ targetRect.y = y - img->last_scale_hs.y;
+ }
+ else
+ {
+ if (img->last_scale != 0)
+ {
+ // Make sure we remember that the last fill was from
+ // an unscaled image
+ force_fill = true;
+ img->last_scale = 0;
+ }
+
+ surf = img->NormalImg;
+ pSrcRect = NULL;
+
+ targetRect.x = x - img->NormalHs.x;
+ targetRect.y = y - img->NormalHs.y;
+ }
+
+ palette = surf->format->palette;
+ if (palette)
+ { // set palette for fill-stamp
+ // Calling TFB_SetColors() results in an expensive src -> dst
+ // color-mapping operation for an SDL blit, following the call.
+ // We want to avoid that as much as possible.
+
+ // TODO: generate a 32bpp filled image?
+
+ SDL_Color colors[256];
+
+ colors[0] = ColorToNative (color);
+ for (i = 1; i < palette->ncolors; i++)
+ colors[i] = colors[0];
+
+ TFB_SetColors (surf, colors, 0, palette->ncolors);
+ // reflect the change in *actual* image palette
+ img->colormap_version--;
+ }
+ else
+ { // fill the non-transparent parts of the image with fillcolor
+ SDL_Surface *newfill = img->FilledImg;
+ SDL_PixelFormat *fillfmt;
+
+ if (newfill && (newfill->w < surf->w || newfill->h < surf->h))
+ {
+ TFB_DrawCanvas_Delete (newfill);
+ newfill = NULL;
+ }
+
+ // prepare the filled image
+ if (!newfill)
+ {
+ newfill = SDL_CreateRGBSurface (SDL_SWSURFACE,
+ surf->w, surf->h,
+ surf->format->BitsPerPixel,
+ surf->format->Rmask,
+ surf->format->Gmask,
+ surf->format->Bmask,
+ surf->format->Amask);
+ force_fill = true;
+ }
+ fillfmt = newfill->format;
+
+ if (force_fill || !sameColor (img->last_fill, color))
+ { // image or fillcolor changed - regenerate
+ Uint32 fillColor;
+
+ if (mode.kind == DRAW_ALPHA && fillfmt->Amask)
+ { // Per-pixel alpha and surface alpha will not work together
+ // We have to handle DRAW_ALPHA differently by modulating
+ // the surface alpha channel ourselves.
+ color.a = mode.factor;
+ mode.kind = DRAW_REPLACE;
+ }
+ else
+ { // Make sure we do not modulate the alpha channel
+ color.a = 0xff;
+ }
+ fillColor = SDL_MapRGBA (newfill->format, color.r, color.g,
+ color.b, color.a);
+ TFB_DrawCanvas_Fill (surf, fillColor, newfill);
+ // cache filled image if possible
+ img->last_fill = color;
+ }
+
+ img->FilledImg = newfill;
+ surf = newfill;
+ }
+
+ TFB_DrawCanvas_Blit (surf, pSrcRect, dst, &targetRect, mode);
+ UnlockMutex (img->mutex);
+}
+
+void
+TFB_DrawCanvas_FontChar (TFB_Char *fontChar, TFB_Image *backing,
+ int x, int y, DrawMode mode, TFB_Canvas target)
+{
+ SDL_Surface *dst = target;
+ SDL_Rect srcRect, targetRect;
+ SDL_Surface *surf;
+ int w, h;
+
+ if (fontChar == 0)
+ {
+ log_add (log_Warning, "ERROR: "
+ "TFB_DrawCanvas_FontChar passed null char ptr");
+ return;
+ }
+ if (backing == 0)
+ {
+ log_add (log_Warning, "ERROR: "
+ "TFB_DrawCanvas_FontChar passed null backing ptr");
+ return;
+ }
+
+ w = fontChar->extent.width;
+ h = fontChar->extent.height;
+
+ LockMutex (backing->mutex);
+
+ surf = backing->NormalImg;
+ if (surf->format->BytesPerPixel != 4
+ || surf->w < w || surf->h < h)
+ {
+ log_add (log_Warning, "ERROR: "
+ "TFB_DrawCanvas_FontChar bad backing surface: %dx%dx%d; "
+ "char: %dx%d",
+ surf->w, surf->h, (int)surf->format->BytesPerPixel, w, h);
+ UnlockMutex (backing->mutex);
+ return;
+ }
+
+ srcRect.x = 0;
+ srcRect.y = 0;
+ srcRect.w = fontChar->extent.width;
+ srcRect.h = fontChar->extent.height;
+
+ targetRect.x = x - fontChar->HotSpot.x;
+ targetRect.y = y - fontChar->HotSpot.y;
+
+ // transfer the alpha channel to the backing surface
+ SDL_LockSurface (surf);
+ {
+ int x, y;
+ const int sskip = fontChar->pitch - w;
+ const int dskip = (surf->pitch / 4) - w;
+ const Uint32 dmask = ~surf->format->Amask;
+ const int ashift = surf->format->Ashift;
+ Uint8 *src_p = fontChar->data;
+ Uint32 *dst_p = (Uint32 *)surf->pixels;
+
+ if (mode.kind == DRAW_ALPHA)
+ { // Per-pixel alpha and surface alpha will not work together
+ // We have to handle DRAW_ALPHA differently by modulating
+ // the backing surface alpha channel ourselves.
+ // The existing backing surface alpha channel is ignored.
+ int alpha = mode.factor;
+ mode.kind = DRAW_REPLACE;
+
+ for (y = 0; y < h; ++y, src_p += sskip, dst_p += dskip)
+ {
+ for (x = 0; x < w; ++x, ++src_p, ++dst_p)
+ {
+ Uint32 p = *dst_p & dmask;
+ Uint32 a = *src_p;
+
+ // we use >> 8 instead of / 255, and it does not handle
+ // alpha == 255 correctly
+ if (alpha != 0xff)
+ { // modulate the alpha channel
+ a = (a * alpha) >> 8;
+ }
+ *dst_p = p | (a << ashift);
+ }
+ }
+ }
+ else /* if (mode.kind != DRAW_ALPHA) */
+ { // Transfer the alpha channel to the backing surface
+ // DRAW_REPLACE + Color.a is NOT supported right now
+ for (y = 0; y < h; ++y, src_p += sskip, dst_p += dskip)
+ {
+ for (x = 0; x < w; ++x, ++src_p, ++dst_p)
+ {
+ *dst_p = (*dst_p & dmask) | ((Uint32)*src_p << ashift);
+ }
+ }
+ }
+ }
+ SDL_UnlockSurface (surf);
+
+ TFB_DrawCanvas_Blit (surf, &srcRect, dst, &targetRect, mode);
+ UnlockMutex (backing->mutex);
+}
+
+TFB_Canvas
+TFB_DrawCanvas_New_TrueColor (int w, int h, BOOLEAN hasalpha)
+{
+ SDL_Surface *new_surf;
+ SDL_PixelFormat* fmt = format_conv_surf->format;
+
+ new_surf = SDL_CreateRGBSurface (SDL_SWSURFACE, w, h,
+ fmt->BitsPerPixel, fmt->Rmask, fmt->Gmask, fmt->Bmask,
+ hasalpha ? fmt->Amask : 0);
+ if (!new_surf)
+ {
+ log_add (log_Fatal, "INTERNAL PANIC: Failed to create TFB_Canvas: %s",
+ SDL_GetError());
+ exit (EXIT_FAILURE);
+ }
+ return new_surf;
+}
+
+TFB_Canvas
+TFB_DrawCanvas_New_ForScreen (int w, int h, BOOLEAN withalpha)
+{
+ SDL_Surface *new_surf;
+ SDL_PixelFormat* fmt = SDL_Screen->format;
+
+ if (fmt->palette)
+ {
+ log_add (log_Warning, "TFB_DrawCanvas_New_ForScreen() WARNING:"
+ "Paletted display format will be slow");
+
+ new_surf = TFB_DrawCanvas_New_TrueColor (w, h, withalpha);
+ }
+ else
+ {
+ if (withalpha && fmt->Amask == 0)
+ fmt = format_conv_surf->format; // Screen has no alpha and we need it
+
+ new_surf = SDL_CreateRGBSurface (SDL_SWSURFACE, w, h,
+ fmt->BitsPerPixel, fmt->Rmask, fmt->Gmask, fmt->Bmask,
+ withalpha ? fmt->Amask : 0);
+ }
+
+ if (!new_surf)
+ {
+ log_add (log_Fatal, "TFB_DrawCanvas_New_ForScreen() INTERNAL PANIC:"
+ "Failed to create TFB_Canvas: %s", SDL_GetError());
+ exit (EXIT_FAILURE);
+ }
+ return new_surf;
+}
+
+TFB_Canvas
+TFB_DrawCanvas_New_Paletted (int w, int h, Color palette[256],
+ int transparent_index)
+{
+ SDL_Surface *new_surf;
+ new_surf = SDL_CreateRGBSurface (SDL_SWSURFACE, w, h, 8, 0, 0, 0, 0);
+ if (!new_surf)
+ {
+ log_add (log_Fatal, "INTERNAL PANIC: Failed to create TFB_Canvas: %s",
+ SDL_GetError());
+ exit (EXIT_FAILURE);
+ }
+ if (palette != NULL)
+ {
+ TFB_DrawCanvas_SetPalette (new_surf, palette);
+ }
+ if (transparent_index >= 0)
+ {
+ TFB_SetColorKey (new_surf, transparent_index, 0);
+ }
+ else
+ {
+ TFB_DisableColorKey (new_surf);
+ }
+ return new_surf;
+}
+
+TFB_Canvas
+TFB_DrawCanvas_New_ScaleTarget (TFB_Canvas canvas, TFB_Canvas oldcanvas, int type, int last_type)
+{
+ SDL_Surface *src = canvas;
+ SDL_Surface *old = oldcanvas;
+ SDL_Surface *newsurf = NULL;
+
+ // For the purposes of this function, bilinear == trilinear
+ if (type == TFB_SCALE_TRILINEAR)
+ type = TFB_SCALE_BILINEAR;
+ if (last_type == TFB_SCALE_TRILINEAR)
+ last_type = TFB_SCALE_BILINEAR;
+
+ if (old && type != last_type)
+ {
+ TFB_DrawCanvas_Delete (old);
+ old = NULL;
+ }
+ if (old)
+ return old; /* can just reuse the old one */
+
+ if (type == TFB_SCALE_NEAREST)
+ {
+ newsurf = SDL_CreateRGBSurface (SDL_SWSURFACE, src->w,
+ src->h,
+ src->format->BitsPerPixel,
+ src->format->Rmask,
+ src->format->Gmask,
+ src->format->Bmask,
+ src->format->Amask);
+ TFB_DrawCanvas_CopyTransparencyInfo (src, newsurf);
+ }
+ else
+ {
+ // The scaled image may in fact be larger by 1 pixel than the source
+ // because of hotspot alignment and fractional edge pixels
+ if (SDL_Screen->format->BitsPerPixel == 32)
+ newsurf = TFB_DrawCanvas_New_ForScreen (src->w + 1, src->h + 1, TRUE);
+ else
+ newsurf = TFB_DrawCanvas_New_TrueColor (src->w + 1, src->h + 1, TRUE);
+ }
+
+ return newsurf;
+}
+
+TFB_Canvas
+TFB_DrawCanvas_New_RotationTarget (TFB_Canvas src_canvas, int angle)
+{
+ SDL_Surface *src = src_canvas;
+ SDL_Surface *newsurf;
+ EXTENT size;
+
+ TFB_DrawCanvas_GetRotatedExtent (src_canvas, angle, &size);
+
+ newsurf = SDL_CreateRGBSurface (SDL_SWSURFACE,
+ size.width, size.height,
+ src->format->BitsPerPixel,
+ src->format->Rmask,
+ src->format->Gmask,
+ src->format->Bmask,
+ src->format->Amask);
+ if (!newsurf)
+ {
+ log_add (log_Fatal, "TFB_DrawCanvas_New_RotationTarget()"
+ " INTERNAL PANIC: Failed to create TFB_Canvas: %s",
+ SDL_GetError());
+ exit (EXIT_FAILURE);
+ }
+ TFB_DrawCanvas_CopyTransparencyInfo (src, newsurf);
+
+ return newsurf;
+}
+
+TFB_Canvas
+TFB_DrawCanvas_LoadFromFile (void *dir, const char *fileName)
+{
+ SDL_Surface *surf = sdluio_loadImage (dir, fileName);
+ if (!surf)
+ return NULL;
+
+ if (surf->format->BitsPerPixel < 8)
+ {
+ SDL_SetError ("unsupported image format (min 8bpp)");
+ SDL_FreeSurface (surf);
+ surf = NULL;
+ }
+
+ return surf;
+}
+
+void
+TFB_DrawCanvas_Delete (TFB_Canvas canvas)
+{
+ if (!canvas)
+ {
+ log_add (log_Warning, "INTERNAL PANIC: Attempted"
+ " to delete a NULL canvas!");
+ /* Should we actually die here? */
+ }
+ else
+ {
+ SDL_FreeSurface (canvas);
+ }
+}
+
+BOOLEAN
+TFB_DrawCanvas_GetFontCharData (TFB_Canvas canvas, BYTE *outData,
+ unsigned dataPitch)
+{
+ SDL_Surface *surf = canvas;
+ int x, y;
+ Uint8 r, g, b, a;
+ Uint32 p;
+ SDL_PixelFormat *fmt = surf->format;
+ GetPixelFn getpix;
+
+ if (!surf || !outData)
+ return FALSE;
+
+ SDL_LockSurface (surf);
+
+ getpix = getpixel_for (surf);
+
+ // produce an alpha-only image in internal BYTE[] format
+ // from the SDL surface
+ for (y = 0; y < surf->h; ++y)
+ {
+ BYTE *dst = outData + dataPitch * y;
+
+ for (x = 0; x < surf->w; ++x, ++dst)
+ {
+ p = getpix (surf, x, y);
+ SDL_GetRGBA (p, fmt, &r, &g, &b, &a);
+
+ if (!fmt->Amask)
+ { // produce alpha from intensity (Y component)
+ // using a fast approximation
+ // contributions to Y are: R=2, G=4, B=1
+ a = ((r * 2) + (g * 4) + b) / 7;
+ }
+
+ *dst = a;
+ }
+ }
+
+ SDL_UnlockSurface (surf);
+
+ return TRUE;
+}
+
+Color *
+TFB_DrawCanvas_ExtractPalette (TFB_Canvas canvas)
+{
+ int i;
+ Color *result;
+ SDL_Surface *surf = canvas;
+ SDL_Palette *palette = surf->format->palette;
+
+ if (!palette)
+ return NULL;
+
+ // There may be less colors in the surface than 256. Init to 0 first.
+ result = HCalloc (sizeof (Color) * 256);
+ assert (palette->ncolors <= 256);
+ for (i = 0; i < palette->ncolors; ++i)
+ result[i] = NativeToColor (palette->colors[i]);
+
+ return result;
+}
+
+TFB_Canvas
+TFB_DrawCanvas_ToScreenFormat (TFB_Canvas canvas)
+{
+ SDL_Surface *result = TFB_DisplayFormatAlpha (canvas);
+ if (result == NULL)
+ {
+ log_add (log_Debug, "WARNING: Could not convert"
+ " sprite-canvas to display format.");
+ return canvas;
+ }
+ else if (result == canvas)
+ { // no conversion was necessary
+ return canvas;
+ }
+ else
+ { // converted
+ TFB_DrawCanvas_Delete (canvas);
+ return result;
+ }
+
+ return canvas;
+}
+
+BOOLEAN
+TFB_DrawCanvas_IsPaletted (TFB_Canvas canvas)
+{
+ return ((SDL_Surface *)canvas)->format->palette != NULL;
+}
+
+void
+TFB_DrawCanvas_SetPalette (TFB_Canvas target, Color palette[256])
+{
+ SDL_Color colors[256];
+ int i;
+
+ for (i = 0; i < 256; ++i)
+ colors[i] = ColorToNative (palette[i]);
+
+ TFB_SetColors (target, colors, 0, 256);
+}
+
+int
+TFB_DrawCanvas_GetTransparentIndex (TFB_Canvas canvas)
+{
+ Uint32 colorkey;
+ if (TFB_GetColorKey (canvas, &colorkey))
+ {
+ return colorkey;
+ }
+ return -1;
+}
+
+void
+TFB_DrawCanvas_SetTransparentIndex (TFB_Canvas canvas, int index, BOOLEAN rleaccel)
+{
+ if (index >= 0)
+ {
+ TFB_SetColorKey (canvas, index, rleaccel);
+
+ if (!TFB_DrawCanvas_IsPaletted (canvas))
+ {
+ // disables surface alpha so color key transparency actually works
+ TFB_DisableSurfaceAlphaMod (canvas);
+ }
+ }
+ else
+ {
+ TFB_DisableColorKey (canvas);
+ }
+}
+
+void
+TFB_DrawCanvas_CopyTransparencyInfo (TFB_Canvas src_canvas,
+ TFB_Canvas dst_canvas)
+{
+ SDL_Surface* src = src_canvas;
+
+ if (src->format->palette)
+ {
+ int index;
+ index = TFB_DrawCanvas_GetTransparentIndex (src_canvas);
+ TFB_DrawCanvas_SetTransparentIndex (dst_canvas, index, FALSE);
+ }
+ else
+ {
+ Color color;
+ if (TFB_DrawCanvas_GetTransparentColor (src_canvas, &color))
+ TFB_DrawCanvas_SetTransparentColor (dst_canvas, color, FALSE);
+ }
+}
+
+BOOLEAN
+TFB_DrawCanvas_GetTransparentColor (TFB_Canvas canvas, Color *color)
+{
+ Uint32 colorkey;
+ if (!TFB_DrawCanvas_IsPaletted (canvas)
+ && (TFB_GetColorKey ((SDL_Surface *)canvas, &colorkey) == 0))
+ {
+ Uint8 ur, ug, ub;
+ SDL_GetRGB (colorkey, ((SDL_Surface *)canvas)->format, &ur, &ug, &ub);
+ color->r = ur;
+ color->g = ug;
+ color->b = ub;
+ color->a = 0xff;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void
+TFB_DrawCanvas_SetTransparentColor (TFB_Canvas canvas, Color color,
+ BOOLEAN rleaccel)
+{
+ Uint32 sdlColor;
+ sdlColor = SDL_MapRGBA (((SDL_Surface *)canvas)->format,
+ color.r, color.g, color.b, 0);
+ TFB_SetColorKey (canvas, sdlColor, rleaccel);
+
+ if (!TFB_DrawCanvas_IsPaletted (canvas))
+ {
+ // disables surface alpha so color key transparency actually works
+ TFB_DisableSurfaceAlphaMod (canvas);
+ }
+}
+
+void
+TFB_DrawCanvas_GetScaledExtent (TFB_Canvas src_canvas, HOT_SPOT* src_hs,
+ TFB_Canvas src_mipmap, HOT_SPOT* mm_hs,
+ int scale, int type, EXTENT *size, HOT_SPOT *hs)
+{
+ SDL_Surface *src = src_canvas;
+ sint32 x, y, w, h;
+ int frac;
+
+ if (!src_mipmap)
+ {
+ w = src->w * scale;
+ h = src->h * scale;
+ x = src_hs->x * scale;
+ y = src_hs->y * scale;
+ }
+ else
+ {
+ // interpolates extents between src and mipmap to get smoother
+ // transition when surface changes
+ SDL_Surface *mipmap = src_mipmap;
+ int ratio = scale * 2 - GSCALE_IDENTITY;
+
+ assert (scale >= GSCALE_IDENTITY / 2);
+
+ w = mipmap->w * GSCALE_IDENTITY + (src->w - mipmap->w) * ratio;
+ h = mipmap->h * GSCALE_IDENTITY + (src->h - mipmap->h) * ratio;
+
+ // Seems it is better to use mipmap hotspot because some
+ // source and mipmap images have the same dimensions!
+ x = mm_hs->x * GSCALE_IDENTITY + (src_hs->x - mm_hs->x) * ratio;
+ y = mm_hs->y * GSCALE_IDENTITY + (src_hs->y - mm_hs->y) * ratio;
+ }
+
+ if (type != TFB_SCALE_NEAREST)
+ {
+ // align hotspot on an whole pixel
+ if (x & (GSCALE_IDENTITY - 1))
+ {
+ frac = GSCALE_IDENTITY - (x & (GSCALE_IDENTITY - 1));
+ x += frac;
+ w += frac;
+ }
+ if (y & (GSCALE_IDENTITY - 1))
+ {
+ frac = GSCALE_IDENTITY - (y & (GSCALE_IDENTITY - 1));
+ y += frac;
+ h += frac;
+ }
+ // pad the extent to accomodate fractional edge pixels
+ w += (GSCALE_IDENTITY - 1);
+ h += (GSCALE_IDENTITY - 1);
+ }
+
+ size->width = w / GSCALE_IDENTITY;
+ size->height = h / GSCALE_IDENTITY;
+ hs->x = x / GSCALE_IDENTITY;
+ hs->y = y / GSCALE_IDENTITY;
+
+ // Scaled image can be larger than the source by 1 pixel
+ // because of hotspot alignment and fractional edge pixels
+ assert (size->width <= src->w + 1 && size->height <= src->h + 1);
+
+ if (!size->width && src->w)
+ size->width = 1;
+ if (!size->height && src->h)
+ size->height = 1;
+}
+
+void
+TFB_DrawCanvas_GetExtent (TFB_Canvas canvas, EXTENT *size)
+{
+ SDL_Surface *src = canvas;
+
+ size->width = src->w;
+ size->height = src->h;
+}
+
+void
+TFB_DrawCanvas_Rescale_Nearest (TFB_Canvas src_canvas, TFB_Canvas dst_canvas,
+ int scale, HOT_SPOT* src_hs, EXTENT* size, HOT_SPOT* dst_hs)
+{
+ SDL_Surface *src = src_canvas;
+ SDL_Surface *dst = dst_canvas;
+ int x, y;
+ int fsx = 0, fsy = 0; // source fractional dx and dy increments
+ int ssx = 0, ssy = 0; // source fractional x and y starting points
+ int w, h;
+
+ if (scale > 0)
+ {
+ TFB_DrawCanvas_GetScaledExtent (src, src_hs, NULL, NULL, scale,
+ TFB_SCALE_NEAREST, size, dst_hs);
+
+ w = size->width;
+ h = size->height;
+ }
+ else
+ {
+ // Just go with the dst surface dimensions
+ w = dst->w;
+ h = dst->h;
+ }
+
+ if (w > dst->w || h > dst->h)
+ {
+ log_add (log_Warning, "TFB_DrawCanvas_Rescale_Nearest: Tried to scale"
+ " image to size %d %d when dest_canvas has only"
+ " dimensions of %d %d! Failing.",
+ w, h, dst->w, dst->h);
+ return;
+ }
+
+ if (w > 1)
+ fsx = ((src->w - 1) << 16) / (w - 1);
+ if (h > 1)
+ fsy = ((src->h - 1) << 16) / (h - 1);
+ // We start with a value in 0..0.5 range to shift the bigger
+ // jumps towards the center of the image
+ ssx = 0x6000;
+ ssy = 0x6000;
+
+ SDL_LockSurface (src);
+ SDL_LockSurface (dst);
+
+ if (src->format->BytesPerPixel == 1 && dst->format->BytesPerPixel == 1)
+ {
+ Uint8 *sp, *csp, *dp, *cdp;
+ int sx, sy; // source fractional x and y positions
+
+ sp = csp = (Uint8 *) src->pixels;
+ dp = cdp = (Uint8 *) dst->pixels;
+
+ for (y = 0, sy = ssy; y < h; ++y)
+ {
+ csp += (sy >> 16) * src->pitch;
+ sp = csp;
+ dp = cdp;
+ for (x = 0, sx = ssx; x < w; ++x)
+ {
+ sp += (sx >> 16);
+ *dp = *sp;
+ sx &= 0xffff;
+ sx += fsx;
+ ++dp;
+ }
+ sy &= 0xffff;
+ sy += fsy;
+ cdp += dst->pitch;
+ }
+ }
+ else if (src->format->BytesPerPixel == 4 && dst->format->BytesPerPixel == 4)
+ {
+ Uint32 *sp, *csp, *dp, *cdp;
+ int sx, sy; // source fractional x and y positions
+ int sgap, dgap;
+
+ sgap = src->pitch >> 2;
+ dgap = dst->pitch >> 2;
+
+ sp = csp = (Uint32 *) src->pixels;
+ dp = cdp = (Uint32 *) dst->pixels;
+
+ for (y = 0, sy = ssy; y < h; ++y)
+ {
+ csp += (sy >> 16) * sgap;
+ sp = csp;
+ dp = cdp;
+ for (x = 0, sx = ssx; x < w; ++x)
+ {
+ sp += (sx >> 16);
+ *dp = *sp;
+ sx &= 0xffff;
+ sx += fsx;
+ ++dp;
+ }
+ sy &= 0xffff;
+ sy += fsy;
+ cdp += dgap;
+ }
+ }
+ else
+ {
+ log_add (log_Warning, "Tried to deal with unknown BPP: %d -> %d",
+ src->format->BitsPerPixel, dst->format->BitsPerPixel);
+ }
+ SDL_UnlockSurface (dst);
+ SDL_UnlockSurface (src);
+}
+
+typedef union
+{
+ Uint32 value;
+ Uint8 chan[4];
+ struct
+ {
+ Uint8 r, g, b, a;
+ } c;
+} pixel_t;
+
+static inline Uint8
+dot_product_8_4 (pixel_t* p, int c, Uint8* v)
+{ // math expanded for speed
+#if 0
+ return (
+ (Uint32)p[0].chan[c] * v[0] + (Uint32)p[1].chan[c] * v[1] +
+ (Uint32)p[2].chan[c] * v[2] + (Uint32)p[3].chan[c] * v[3]
+ ) >> 8;
+#else
+ // mult-table driven version
+ return
+ btable[p[0].chan[c]][v[0]] + btable[p[1].chan[c]][v[1]] +
+ btable[p[2].chan[c]][v[2]] + btable[p[3].chan[c]][v[3]];
+#endif
+}
+
+static inline Uint8
+weight_product_8_4 (pixel_t* p, int c, Uint8* w)
+{ // math expanded for speed
+ return (
+ (Uint32)p[0].chan[c] * w[0] + (Uint32)p[1].chan[c] * w[1] +
+ (Uint32)p[2].chan[c] * w[2] + (Uint32)p[3].chan[c] * w[3]
+ ) / (w[0] + w[1] + w[2] + w[3]);
+}
+
+static inline Uint8
+blend_ratio_2 (Uint8 c1, Uint8 c2, int ratio)
+{ // blend 2 color values according to ratio (0..256)
+ // identical to proper alpha blending
+ return (((c1 - c2) * ratio) >> 8) + c2;
+}
+
+static inline Uint32
+scale_read_pixel (void* ppix, SDL_PixelFormat *fmt, SDL_Color *pal,
+ Uint32 mask, Uint32 key)
+{
+ pixel_t p;
+
+ // start off with non-present pixel
+ p.value = 0;
+
+ if (pal)
+ { // paletted pixel; mask not used
+ Uint32 c = *(Uint8 *)ppix;
+
+ if (c != key)
+ {
+ p.c.r = pal[c].r;
+ p.c.g = pal[c].g;
+ p.c.b = pal[c].b;
+ p.c.a = SDL_ALPHA_OPAQUE;
+ }
+ }
+ else
+ { // RGB(A) pixel; (pix & mask) != key
+ Uint32 c = *(Uint32 *)ppix;
+
+ if ((c & mask) != key)
+ {
+#if 0
+ SDL_GetRGBA (c, fmt, &p.c.r, &p.c.g, &p.c.b, &p.c.a);
+#else
+ // Assume 8 bits/channel; a safe assumption with 32bpp surfaces
+ p.c.r = (c >> fmt->Rshift) & 0xff;
+ p.c.g = (c >> fmt->Gshift) & 0xff;
+ p.c.b = (c >> fmt->Bshift) & 0xff;
+ if (fmt->Amask)
+ p.c.a = (c >> fmt->Ashift) & 0xff;
+ else
+ p.c.a = SDL_ALPHA_OPAQUE;
+ }
+#endif
+ }
+
+ return p.value;
+}
+
+static inline Uint32
+scale_get_pixel (SDL_Surface *src, Uint32 mask, Uint32 key, int x, int y)
+{
+ SDL_Color *pal = src->format->palette? src->format->palette->colors : 0;
+
+ if (x < 0 || x >= src->w || y < 0 || y >= src->h)
+ return 0;
+
+ return scale_read_pixel ((Uint8*)src->pixels + y * src->pitch +
+ x * src->format->BytesPerPixel, src->format, pal, mask, key);
+}
+
+void
+TFB_DrawCanvas_Rescale_Trilinear (TFB_Canvas src_canvas, TFB_Canvas src_mipmap,
+ TFB_Canvas dst_canvas, int scale, HOT_SPOT* src_hs, HOT_SPOT* mm_hs,
+ EXTENT* size, HOT_SPOT* dst_hs)
+{
+ SDL_Surface *src = src_canvas;
+ SDL_Surface *dst = dst_canvas;
+ SDL_Surface *mm = src_mipmap;
+ SDL_PixelFormat *srcfmt = src->format;
+ SDL_PixelFormat *mmfmt = mm->format;
+ SDL_PixelFormat *dstfmt = dst->format;
+ SDL_Color *srcpal = srcfmt->palette? srcfmt->palette->colors : 0;
+ const int sbpp = srcfmt->BytesPerPixel;
+ const int mmbpp = mmfmt->BytesPerPixel;
+ const int slen = src->pitch;
+ const int mmlen = mm->pitch;
+ const int dst_has_alpha = (dstfmt->Amask != 0);
+ Uint32 transparent = 0;
+ const int alpha_threshold = dst_has_alpha ? 0 : 127;
+ // src v. mipmap importance factor
+ int ratio = scale * 2 - GSCALE_IDENTITY;
+ // source masks and keys
+ Uint32 mk0 = 0, ck0 = ~0, mk1 = 0, ck1 = ~0;
+ // source fractional x and y positions
+ int sx0, sy0, sx1, sy1;
+ // source fractional dx and dy increments
+ int fsx0 = 0, fsy0 = 0, fsx1 = 0, fsy1 = 0;
+ // source fractional x and y starting points
+ int ssx0 = 0, ssy0 = 0, ssx1 = 0, ssy1 = 0;
+ int x, y, w, h;
+
+ TFB_GetColorKey (dst, &transparent);
+
+ if (mmfmt->palette && !srcpal)
+ {
+ log_add (log_Warning, "TFB_DrawCanvas_Rescale_Trilinear: "
+ "Mipmap is paletted, but source is not! Failing.");
+ return;
+ }
+
+ if (scale > 0)
+ {
+ int fw, fh;
+
+ // Use (scale / GSCALE_IDENTITY) sizing factor
+ TFB_DrawCanvas_GetScaledExtent (src, src_hs, mm, mm_hs, scale,
+ TFB_SCALE_TRILINEAR, size, dst_hs);
+
+ w = size->width;
+ h = size->height;
+
+ fw = mm->w * GSCALE_IDENTITY + (src->w - mm->w) * ratio;
+ fh = mm->h * GSCALE_IDENTITY + (src->h - mm->h) * ratio;
+
+ // This limits the effective source dimensions to 2048x2048,
+ // and we also lose 4 bits of precision out of 16 (no problem)
+ fsx0 = (src->w << 20) / fw;
+ fsx0 <<= 4;
+ fsy0 = (src->h << 20) / fh;
+ fsy0 <<= 4;
+
+ fsx1 = (mm->w << 20) / fw;
+ fsx1 <<= 4;
+ fsy1 = (mm->h << 20) / fh;
+ fsy1 <<= 4;
+
+ // position the hotspots directly over each other
+ ssx0 = (src_hs->x << 16) - fsx0 * dst_hs->x;
+ ssy0 = (src_hs->y << 16) - fsy0 * dst_hs->y;
+
+ ssx1 = (mm_hs->x << 16) - fsx1 * dst_hs->x;
+ ssy1 = (mm_hs->y << 16) - fsy1 * dst_hs->y;
+ }
+ else
+ {
+ // Just go with the dst surface dimensions
+ w = dst->w;
+ h = dst->h;
+
+ fsx0 = (src->w << 16) / w;
+ fsy0 = (src->h << 16) / h;
+
+ fsx1 = (mm->w << 16) / w;
+ fsy1 = (mm->h << 16) / h;
+
+ // give equal importance to both edges
+ ssx0 = (((src->w - 1) << 16) - fsx0 * (w - 1)) >> 1;
+ ssy0 = (((src->h - 1) << 16) - fsy0 * (h - 1)) >> 1;
+
+ ssx1 = (((mm->w - 1) << 16) - fsx1 * (w - 1)) >> 1;
+ ssy1 = (((mm->h - 1) << 16) - fsy1 * (h - 1)) >> 1;
+ }
+
+ if (w > dst->w || h > dst->h)
+ {
+ log_add (log_Warning, "TFB_DrawCanvas_Rescale_Trilinear: "
+ "Tried to scale image to size %d %d when dest_canvas"
+ " has only dimensions of %d %d! Failing.",
+ w, h, dst->w, dst->h);
+ return;
+ }
+
+ if ((srcfmt->BytesPerPixel != 1 && srcfmt->BytesPerPixel != 4) ||
+ (mmfmt->BytesPerPixel != 1 && mmfmt->BytesPerPixel != 4) ||
+ (dst->format->BytesPerPixel != 4))
+ {
+ log_add (log_Warning, "TFB_DrawCanvas_Rescale_Trilinear: "
+ "Tried to deal with unknown BPP: %d -> %d, mipmap %d",
+ srcfmt->BitsPerPixel, dst->format->BitsPerPixel,
+ mmfmt->BitsPerPixel);
+ return;
+ }
+
+ // use colorkeys where appropriate
+ if (srcfmt->Amask)
+ { // alpha transparency
+ mk0 = srcfmt->Amask;
+ ck0 = 0;
+ }
+ else if (TFB_GetColorKey (src, &ck0) == 0)
+ { // colorkey transparency
+ mk0 = ~srcfmt->Amask;
+ ck0 &= mk0;
+ }
+
+ if (mmfmt->Amask)
+ { // alpha transparency
+ mk1 = mmfmt->Amask;
+ ck1 = 0;
+ }
+ else if (TFB_GetColorKey (mm, &ck1) == 0)
+ { // colorkey transparency
+ mk1 = ~mmfmt->Amask;
+ ck1 &= mk1;
+ }
+
+ SDL_LockSurface(src);
+ SDL_LockSurface(dst);
+ SDL_LockSurface(mm);
+
+ for (y = 0, sy0 = ssy0, sy1 = ssy1;
+ y < h;
+ ++y, sy0 += fsy0, sy1 += fsy1)
+ {
+ Uint32 *dst_p = (Uint32 *) ((Uint8*)dst->pixels + y * dst->pitch);
+ const int py0 = (sy0 >> 16);
+ const int py1 = (sy1 >> 16);
+ Uint8 *src_a0 = (Uint8*)src->pixels + py0 * slen;
+ Uint8 *src_a1 = (Uint8*)mm->pixels + py1 * mmlen;
+ // retrieve the fractional portions of y
+ const Uint8 v0 = (sy0 >> 8) & 0xff;
+ const Uint8 v1 = (sy1 >> 8) & 0xff;
+ Uint8 w0[4], w1[4]; // pixel weight vectors
+
+ for (x = 0, sx0 = ssx0, sx1 = ssx1;
+ x < w;
+ ++x, ++dst_p, sx0 += fsx0, sx1 += fsx1)
+ {
+ const int px0 = (sx0 >> 16);
+ const int px1 = (sx1 >> 16);
+ // retrieve the fractional portions of x
+ const Uint8 u0 = (sx0 >> 8) & 0xff;
+ const Uint8 u1 = (sx1 >> 8) & 0xff;
+ // pixels are examined and numbered in pattern
+ // 0 1
+ // 2 3
+ // the ideal pixel (4) is somewhere between these four
+ // and is calculated from these using weight vector (w)
+ // with a dot product
+ pixel_t p0[5], p1[5];
+ Uint8 res_a;
+
+ w0[0] = btable[255 - u0][255 - v0];
+ w0[1] = btable[u0][255 - v0];
+ w0[2] = btable[255 - u0][v0];
+ w0[3] = btable[u0][v0];
+
+ w1[0] = btable[255 - u1][255 - v1];
+ w1[1] = btable[u1][255 - v1];
+ w1[2] = btable[255 - u1][v1];
+ w1[3] = btable[u1][v1];
+
+ // Collect interesting pixels from src image
+ // Optimization: speed is criticial on larger images;
+ // most pixel reads fall completely inside the image
+ if (px0 >= 0 && px0 + 1 < src->w && py0 >= 0 && py0 + 1 < src->h)
+ {
+ Uint8 *src_p = src_a0 + px0 * sbpp;
+
+ p0[0].value = scale_read_pixel (src_p, srcfmt,
+ srcpal, mk0, ck0);
+ p0[1].value = scale_read_pixel (src_p + sbpp, srcfmt,
+ srcpal, mk0, ck0);
+ p0[2].value = scale_read_pixel (src_p + slen, srcfmt,
+ srcpal, mk0, ck0);
+ p0[3].value = scale_read_pixel (src_p + sbpp + slen, srcfmt,
+ srcpal, mk0, ck0);
+ }
+ else
+ {
+ p0[0].value = scale_get_pixel (src, mk0, ck0, px0, py0);
+ p0[1].value = scale_get_pixel (src, mk0, ck0, px0 + 1, py0);
+ p0[2].value = scale_get_pixel (src, mk0, ck0, px0, py0 + 1);
+ p0[3].value = scale_get_pixel (src, mk0, ck0,
+ px0 + 1, py0 + 1);
+ }
+
+ // Collect interesting pixels from mipmap image
+ if (px1 >= 0 && px1 + 1 < mm->w && py1 >= 0 && py1 + 1 < mm->h)
+ {
+ Uint8 *mm_p = src_a1 + px1 * mmbpp;
+
+ p1[0].value = scale_read_pixel (mm_p, mmfmt,
+ srcpal, mk1, ck1);
+ p1[1].value = scale_read_pixel (mm_p + mmbpp, mmfmt,
+ srcpal, mk1, ck1);
+ p1[2].value = scale_read_pixel (mm_p + mmlen, mmfmt,
+ srcpal, mk1, ck1);
+ p1[3].value = scale_read_pixel (mm_p + mmbpp + mmlen, mmfmt,
+ srcpal, mk1, ck1);
+ }
+ else
+ {
+ p1[0].value = scale_get_pixel (mm, mk1, ck1, px1, py1);
+ p1[1].value = scale_get_pixel (mm, mk1, ck1, px1 + 1, py1);
+ p1[2].value = scale_get_pixel (mm, mk1, ck1, px1, py1 + 1);
+ p1[3].value = scale_get_pixel (mm, mk1, ck1,
+ px1 + 1, py1 + 1);
+ }
+
+ p0[4].c.a = dot_product_8_4 (p0, 3, w0);
+ p1[4].c.a = dot_product_8_4 (p1, 3, w1);
+
+ res_a = blend_ratio_2 (p0[4].c.a, p1[4].c.a, ratio);
+
+ if (res_a <= alpha_threshold)
+ {
+ *dst_p = transparent;
+ }
+ else if (!dst_has_alpha)
+ { // RGB surface handling
+ p0[4].c.r = dot_product_8_4 (p0, 0, w0);
+ p0[4].c.g = dot_product_8_4 (p0, 1, w0);
+ p0[4].c.b = dot_product_8_4 (p0, 2, w0);
+
+ p1[4].c.r = dot_product_8_4 (p1, 0, w1);
+ p1[4].c.g = dot_product_8_4 (p1, 1, w1);
+ p1[4].c.b = dot_product_8_4 (p1, 2, w1);
+
+ p0[4].c.r = blend_ratio_2 (p0[4].c.r, p1[4].c.r, ratio);
+ p0[4].c.g = blend_ratio_2 (p0[4].c.g, p1[4].c.g, ratio);
+ p0[4].c.b = blend_ratio_2 (p0[4].c.b, p1[4].c.b, ratio);
+
+ // TODO: we should handle alpha-blending here, but we do
+ // not know the destination color for blending!
+
+ *dst_p =
+ (p0[4].c.r << dstfmt->Rshift) |
+ (p0[4].c.g << dstfmt->Gshift) |
+ (p0[4].c.b << dstfmt->Bshift);
+ }
+ else
+ { // RGBA surface handling
+
+ // we do not want to blend with non-present pixels
+ // (pixels that have alpha == 0) as these will
+ // skew the result and make resulting alpha useless
+ if (p0[4].c.a != 0)
+ {
+ int i;
+ for (i = 0; i < 4; ++i)
+ if (p0[i].c.a == 0)
+ w0[i] = 0;
+
+ p0[4].c.r = weight_product_8_4 (p0, 0, w0);
+ p0[4].c.g = weight_product_8_4 (p0, 1, w0);
+ p0[4].c.b = weight_product_8_4 (p0, 2, w0);
+ }
+ if (p1[4].c.a != 0)
+ {
+ int i;
+ for (i = 0; i < 4; ++i)
+ if (p1[i].c.a == 0)
+ w1[i] = 0;
+
+ p1[4].c.r = weight_product_8_4 (p1, 0, w1);
+ p1[4].c.g = weight_product_8_4 (p1, 1, w1);
+ p1[4].c.b = weight_product_8_4 (p1, 2, w1);
+ }
+
+ if (p0[4].c.a != 0 && p1[4].c.a != 0)
+ { // blend if both present
+ p0[4].c.r = blend_ratio_2 (p0[4].c.r, p1[4].c.r, ratio);
+ p0[4].c.g = blend_ratio_2 (p0[4].c.g, p1[4].c.g, ratio);
+ p0[4].c.b = blend_ratio_2 (p0[4].c.b, p1[4].c.b, ratio);
+ }
+ else if (p1[4].c.a != 0)
+ { // other pixel is present
+ p0[4].value = p1[4].value;
+ }
+
+ // error-correct alpha to fully opaque to remove
+ // the often unwanted and unnecessary blending
+ if (res_a > 0xf8)
+ res_a = 0xff;
+
+ *dst_p =
+ (p0[4].c.r << dstfmt->Rshift) |
+ (p0[4].c.g << dstfmt->Gshift) |
+ (p0[4].c.b << dstfmt->Bshift) |
+ (res_a << dstfmt->Ashift);
+ }
+ }
+ }
+
+ SDL_UnlockSurface(mm);
+ SDL_UnlockSurface(dst);
+ SDL_UnlockSurface(src);
+}
+
+void
+TFB_DrawCanvas_Rescale_Bilinear (TFB_Canvas src_canvas, TFB_Canvas dst_canvas,
+ int scale, HOT_SPOT* src_hs, EXTENT* size, HOT_SPOT* dst_hs)
+{
+ SDL_Surface *src = src_canvas;
+ SDL_Surface *dst = dst_canvas;
+ SDL_PixelFormat *srcfmt = src->format;
+ SDL_PixelFormat *dstfmt = dst->format;
+ SDL_Color *srcpal = srcfmt->palette? srcfmt->palette->colors : 0;
+ const int sbpp = srcfmt->BytesPerPixel;
+ const int slen = src->pitch;
+ const int dst_has_alpha = (dstfmt->Amask != 0);
+ Uint32 srckey = 0, transparent = 0;
+ const int alpha_threshold = dst_has_alpha ? 0 : 127;
+ // source masks and keys
+ Uint32 mk = 0, ck = ~0;
+ // source fractional x and y positions
+ int sx, sy;
+ // source fractional dx and dy increments
+ int fsx = 0, fsy = 0;
+ // source fractional x and y starting points
+ int ssx = 0, ssy = 0;
+ int x, y, w, h;
+
+ // Get destination transparent color if it exists
+ TFB_GetColorKey (dst, &transparent);
+
+ if (scale > 0)
+ {
+ // Use (scale / GSCALE_IDENTITY) sizing factor
+ TFB_DrawCanvas_GetScaledExtent (src, src_hs, NULL, NULL, scale,
+ TFB_SCALE_BILINEAR, size, dst_hs);
+
+ w = size->width;
+ h = size->height;
+ fsx = (GSCALE_IDENTITY << 16) / scale;
+ fsy = (GSCALE_IDENTITY << 16) / scale;
+
+ // position the hotspots directly over each other
+ ssx = (src_hs->x << 16) - fsx * dst_hs->x;
+ ssy = (src_hs->y << 16) - fsy * dst_hs->y;
+ }
+ else
+ {
+ // Just go with the dst surface dimensions
+ w = dst->w;
+ h = dst->h;
+ fsx = (src->w << 16) / w;
+ fsy = (src->h << 16) / h;
+
+ // give equal importance to both edges
+ ssx = (((src->w - 1) << 16) - fsx * (w - 1)) >> 1;
+ ssy = (((src->h - 1) << 16) - fsy * (h - 1)) >> 1;
+ }
+
+ if (w > dst->w || h > dst->h)
+ {
+ log_add (log_Warning, "TFB_DrawCanvas_Rescale_Bilinear: "
+ "Tried to scale image to size %d %d when dest_canvas"
+ " has only dimensions of %d %d! Failing.",
+ w, h, dst->w, dst->h);
+ return;
+ }
+
+ if ((srcfmt->BytesPerPixel != 1 && srcfmt->BytesPerPixel != 4) ||
+ (dst->format->BytesPerPixel != 4))
+ {
+ log_add (log_Warning, "TFB_DrawCanvas_Rescale_Bilinear: "
+ "Tried to deal with unknown BPP: %d -> %d",
+ srcfmt->BitsPerPixel, dst->format->BitsPerPixel);
+ return;
+ }
+
+ // use colorkeys where appropriate
+ if (srcfmt->Amask)
+ { // alpha transparency
+ mk = srcfmt->Amask;
+ ck = 0;
+ }
+ else if (TFB_GetColorKey (src, &srckey) == 0)
+ { // colorkey transparency
+ mk = ~srcfmt->Amask;
+ ck = srckey & mk;
+ }
+
+ SDL_LockSurface(src);
+ SDL_LockSurface(dst);
+
+ for (y = 0, sy = ssy; y < h; ++y, sy += fsy)
+ {
+ Uint32 *dst_p = (Uint32 *) ((Uint8*)dst->pixels + y * dst->pitch);
+ const int py = (sy >> 16);
+ Uint8 *src_a = (Uint8*)src->pixels + py * slen;
+ // retrieve the fractional portions of y
+ const Uint8 v = (sy >> 8) & 0xff;
+ Uint8 weight[4]; // pixel weight vectors
+
+ for (x = 0, sx = ssx; x < w; ++x, ++dst_p, sx += fsx)
+ {
+ const int px = (sx >> 16);
+ // retrieve the fractional portions of x
+ const Uint8 u = (sx >> 8) & 0xff;
+ // pixels are examined and numbered in pattern
+ // 0 1
+ // 2 3
+ // the ideal pixel (4) is somewhere between these four
+ // and is calculated from these using weight vector (weight)
+ // with a dot product
+ pixel_t p[5];
+
+ weight[0] = btable[255 - u][255 - v];
+ weight[1] = btable[u][255 - v];
+ weight[2] = btable[255 - u][v];
+ weight[3] = btable[u][v];
+
+ // Collect interesting pixels from src image
+ // Optimization: speed is criticial on larger images;
+ // most pixel reads fall completely inside the image
+ if (px >= 0 && px + 1 < src->w && py >= 0 && py + 1 < src->h)
+ {
+ Uint8 *src_p = src_a + px * sbpp;
+
+ p[0].value = scale_read_pixel (src_p, srcfmt, srcpal, mk, ck);
+ p[1].value = scale_read_pixel (src_p + sbpp, srcfmt,
+ srcpal, mk, ck);
+ p[2].value = scale_read_pixel (src_p + slen, srcfmt,
+ srcpal, mk, ck);
+ p[3].value = scale_read_pixel (src_p + sbpp + slen, srcfmt,
+ srcpal, mk, ck);
+ }
+ else
+ {
+ p[0].value = scale_get_pixel (src, mk, ck, px, py);
+ p[1].value = scale_get_pixel (src, mk, ck, px + 1, py);
+ p[2].value = scale_get_pixel (src, mk, ck, px, py + 1);
+ p[3].value = scale_get_pixel (src, mk, ck, px + 1, py + 1);
+ }
+
+ p[4].c.a = dot_product_8_4 (p, 3, weight);
+
+ if (p[4].c.a <= alpha_threshold)
+ {
+ *dst_p = transparent;
+ }
+ else if (!dst_has_alpha)
+ { // RGB surface handling
+ p[4].c.r = dot_product_8_4 (p, 0, weight);
+ p[4].c.g = dot_product_8_4 (p, 1, weight);
+ p[4].c.b = dot_product_8_4 (p, 2, weight);
+
+ // TODO: we should handle alpha-blending here, but we do
+ // not know the destination color for blending!
+
+ *dst_p =
+ (p[4].c.r << dstfmt->Rshift) |
+ (p[4].c.g << dstfmt->Gshift) |
+ (p[4].c.b << dstfmt->Bshift);
+ }
+ else
+ { // RGBA surface handling
+
+ // we do not want to blend with non-present pixels
+ // (pixels that have alpha == 0) as these will
+ // skew the result and make resulting alpha useless
+ int i;
+ for (i = 0; i < 4; ++i)
+ if (p[i].c.a == 0)
+ weight[i] = 0;
+
+ p[4].c.r = weight_product_8_4 (p, 0, weight);
+ p[4].c.g = weight_product_8_4 (p, 1, weight);
+ p[4].c.b = weight_product_8_4 (p, 2, weight);
+
+ // error-correct alpha to fully opaque to remove
+ // the often unwanted and unnecessary blending
+ if (p[4].c.a > 0xf8)
+ p[4].c.a = 0xff;
+
+ *dst_p =
+ (p[4].c.r << dstfmt->Rshift) |
+ (p[4].c.g << dstfmt->Gshift) |
+ (p[4].c.b << dstfmt->Bshift) |
+ (p[4].c.a << dstfmt->Ashift);
+ }
+ }
+ }
+
+ SDL_UnlockSurface(dst);
+ SDL_UnlockSurface(src);
+}
+
+void
+TFB_DrawCanvas_Lock (TFB_Canvas canvas)
+{
+ SDL_Surface *surf = canvas;
+ SDL_LockSurface (surf);
+}
+
+void
+TFB_DrawCanvas_Unlock (TFB_Canvas canvas)
+{
+ SDL_Surface *surf = canvas;
+ SDL_UnlockSurface (surf);
+}
+
+void
+TFB_DrawCanvas_GetScreenFormat (TFB_PixelFormat *fmt)
+{
+ SDL_PixelFormat *sdl = SDL_Screen->format;
+
+ if (sdl->palette)
+ {
+ log_add (log_Warning, "TFB_DrawCanvas_GetScreenFormat() WARNING:"
+ "Paletted display format will be slow");
+
+ fmt->BitsPerPixel = 32;
+ fmt->Rmask = 0x000000ff;
+ fmt->Gmask = 0x0000ff00;
+ fmt->Bmask = 0x00ff0000;
+ fmt->Amask = 0xff000000;
+ }
+ else
+ {
+ fmt->BitsPerPixel = sdl->BitsPerPixel;
+ fmt->Rmask = sdl->Rmask;
+ fmt->Gmask = sdl->Gmask;
+ fmt->Bmask = sdl->Bmask;
+ fmt->Amask = sdl->Amask;
+ }
+}
+
+int
+TFB_DrawCanvas_GetStride (TFB_Canvas canvas)
+{
+ SDL_Surface *surf = canvas;
+ return surf->pitch;
+}
+
+void*
+TFB_DrawCanvas_GetLine (TFB_Canvas canvas, int line)
+{
+ SDL_Surface *surf = canvas;
+ return (uint8 *)surf->pixels + surf->pitch * line;
+}
+
+Color
+TFB_DrawCanvas_GetPixel (TFB_Canvas canvas, int x, int y)
+{
+ SDL_Surface* surf = canvas;
+ Uint32 pixel;
+ GetPixelFn getpixel;
+ Color c = {0, 0, 0, 0};
+
+ if (x < 0 || x >= surf->w || y < 0 || y >= surf->h)
+ { // outside bounds, return 0
+ return c;
+ }
+
+ SDL_LockSurface (surf);
+
+ getpixel = getpixel_for(surf);
+ pixel = (*getpixel)(surf, x, y);
+ SDL_GetRGBA (pixel, surf->format, &c.r, &c.g, &c.b, &c.a);
+
+ SDL_UnlockSurface (surf);
+
+ return c;
+}
+
+void
+TFB_DrawCanvas_Rotate (TFB_Canvas src_canvas, TFB_Canvas dst_canvas,
+ int angle, EXTENT size)
+{
+ SDL_Surface *src = src_canvas;
+ SDL_Surface *dst = dst_canvas;
+ int ret;
+ Color color;
+
+ if (size.width > dst->w || size.height > dst->h)
+ {
+ log_add (log_Warning, "TFB_DrawCanvas_Rotate: Tried to rotate"
+ " image to size %d %d when dst_canvas has only dimensions"
+ " of %d %d! Failing.",
+ size.width, size.height, dst->w, dst->h);
+ return;
+ }
+
+ if (TFB_DrawCanvas_GetTransparentColor (src, &color))
+ {
+ TFB_DrawCanvas_SetTransparentColor (dst, color, FALSE);
+ /* fill destination with transparent color before rotating */
+ SDL_FillRect(dst, NULL, SDL_MapRGBA (dst->format,
+ color.r, color.g, color.b, 0));
+ }
+
+ ret = rotateSurface (src, dst, angle, 0);
+ if (ret != 0)
+ {
+ log_add (log_Warning, "TFB_DrawCanvas_Rotate: WARNING:"
+ " actual rotation func returned failure\n");
+ }
+}
+
+void
+TFB_DrawCanvas_GetRotatedExtent (TFB_Canvas src_canvas, int angle, EXTENT *size)
+{
+ int dstw, dsth;
+ SDL_Surface *src = src_canvas;
+
+ rotozoomSurfaceSize (src->w, src->h, angle, 1, &dstw, &dsth);
+ size->height = dsth;
+ size->width = dstw;
+}
+
+void
+TFB_DrawCanvas_CopyRect (TFB_Canvas source, const RECT *srcRect,
+ TFB_Canvas target, POINT dstPt)
+{
+ SDL_Rect sourceRect, targetRect;
+
+ if (source == 0 || target == 0)
+ {
+ log_add (log_Warning,
+ "ERROR: TFB_DrawCanvas_CopyRect passed null canvas ptr");
+ return;
+ }
+
+ sourceRect.x = srcRect->corner.x;
+ sourceRect.y = srcRect->corner.y;
+ sourceRect.w = srcRect->extent.width;
+ sourceRect.h = srcRect->extent.height;
+
+ targetRect.x = dstPt.x;
+ targetRect.y = dstPt.y;
+ // According to SDL docs, width and height are ignored, but
+ // we'll set them anyway, just in case.
+ targetRect.w = srcRect->extent.width;
+ targetRect.h = srcRect->extent.height;
+
+ SDL_BlitSurface (source, &sourceRect, target, &targetRect);
+}
+
+void
+TFB_DrawCanvas_SetClipRect (TFB_Canvas canvas, const RECT *clipRect)
+{
+ if (canvas == 0)
+ {
+ log_add (log_Warning,
+ "ERROR: TFB_DrawCanvas_SetClipRect passed null canvas ptr");
+ return;
+ }
+
+ if (!clipRect)
+ { // clipping disabled
+ SDL_SetClipRect (canvas, NULL);
+ }
+ else
+ {
+ SDL_Rect r;
+ r.x = clipRect->corner.x;
+ r.y = clipRect->corner.y;
+ r.w = clipRect->extent.width;
+ r.h = clipRect->extent.height;
+ SDL_SetClipRect (canvas, &r);
+ }
+}
+
+BOOLEAN
+TFB_DrawCanvas_Intersect (TFB_Canvas canvas1, POINT c1org,
+ TFB_Canvas canvas2, POINT c2org, const RECT *interRect)
+{
+ BOOLEAN ret = FALSE;
+ SDL_Surface *surf1 = canvas1;
+ SDL_Surface *surf2 = canvas2;
+ int x, y;
+ Uint32 s1key, s2key;
+ Uint32 s1mask, s2mask;
+ GetPixelFn getpixel1, getpixel2;
+
+ SDL_LockSurface (surf1);
+ SDL_LockSurface (surf2);
+
+ getpixel1 = getpixel_for (surf1);
+ getpixel2 = getpixel_for (surf2);
+
+ if (surf1->format->Amask)
+ { // use alpha transparency info
+ s1mask = surf1->format->Amask;
+ // consider any not fully transparent pixel collidable
+ s1key = 0;
+ }
+ else
+ { // colorkey transparency
+ Uint32 colorkey = 0;
+ TFB_GetColorKey(surf1, &colorkey);
+ s1mask = ~surf1->format->Amask;
+ s1key = colorkey & s1mask;
+ }
+
+ if (surf2->format->Amask)
+ { // use alpha transparency info
+ s2mask = surf2->format->Amask;
+ // consider any not fully transparent pixel collidable
+ s2key = 0;
+ }
+ else
+ { // colorkey transparency
+ Uint32 colorkey = 0;
+ TFB_GetColorKey(surf2, &colorkey);
+ s2mask = ~surf2->format->Amask;
+ s2key = colorkey & s2mask;
+ }
+
+ // convert surface origins to pixel offsets within
+ c1org.x = interRect->corner.x - c1org.x;
+ c1org.y = interRect->corner.y - c1org.y;
+ c2org.x = interRect->corner.x - c2org.x;
+ c2org.y = interRect->corner.y - c2org.y;
+
+ for (y = 0; y < interRect->extent.height; ++y)
+ {
+ for (x = 0; x < interRect->extent.width; ++x)
+ {
+ Uint32 p1 = getpixel1 (surf1, x + c1org.x, y + c1org.y) & s1mask;
+ Uint32 p2 = getpixel2 (surf2, x + c2org.x, y + c2org.y) & s2mask;
+
+ if (p1 != s1key && p2 != s2key)
+ { // pixel collision
+ ret = TRUE;
+ break;
+ }
+ }
+ }
+
+ SDL_UnlockSurface (surf2);
+ SDL_UnlockSurface (surf1);
+
+ return ret;
+}
+
+// Read/write the canvas pixels in a Color format understood by the core.
+// The pixels array is assumed to be at least width * height large.
+// The pixels array can be wider/narrower or taller/shorter than the canvas,
+// and in that case, only the relevant pixels will be transfered.
+static BOOLEAN
+TFB_DrawCanvas_TransferColors (TFB_Canvas canvas, BOOLEAN write,
+ Color *pixels, int width, int height)
+{
+ SDL_Surface *surf = canvas;
+ SDL_PixelFormat *fmt;
+ GetPixelFn getpix;
+ PutPixelFn putpix;
+ int x, y, w, h;
+
+ if (canvas == 0)
+ {
+ log_add (log_Warning, "ERROR: TFB_DrawCanvas_TransferColors "
+ "passed null canvas");
+ return FALSE;
+ }
+
+ fmt = surf->format;
+ getpix = getpixel_for (surf);
+ putpix = putpixel_for (surf);
+
+ w = width < surf->w ? width : surf->w;
+ h = height < surf->h ? height : surf->h;
+
+ SDL_LockSurface (surf);
+
+ // This could be done faster if we assumed 32bpp surfaces
+ for (y = 0; y < h; ++y)
+ {
+ // pixels array pitch is width so as not to violate the interface
+ Color *c = pixels + y * width;
+
+ for (x = 0; x < w; ++x, ++c)
+ {
+ if (write)
+ { // writing from data to surface
+ Uint32 p = SDL_MapRGBA (fmt, c->r, c->g, c->b, c->a);
+ putpix (surf, x, y, p);
+ }
+ else
+ { // reading from surface to data
+ Uint32 p = getpix (surf, x, y);
+ SDL_GetRGBA (p, fmt, &c->r, &c->g, &c->b, &c->a);
+ }
+ }
+ }
+
+ SDL_UnlockSurface (surf);
+
+ return TRUE;
+}
+
+// Read the canvas pixels in a Color format understood by the core.
+// See TFB_DrawCanvas_TransferColors() for pixels array info
+BOOLEAN
+TFB_DrawCanvas_GetPixelColors (TFB_Canvas canvas, Color *pixels,
+ int width, int height)
+{
+ return TFB_DrawCanvas_TransferColors (canvas, FALSE, pixels,
+ width, height);
+}
+
+// Write the canvas pixels from a Color format understood by the core.
+// See TFB_DrawCanvas_TransferColors() for pixels array info
+BOOLEAN
+TFB_DrawCanvas_SetPixelColors (TFB_Canvas canvas, const Color *pixels,
+ int width, int height)
+{
+ // unconst pixels, but it is safe -- it will not be written to
+ return TFB_DrawCanvas_TransferColors (canvas, TRUE, (Color *)pixels,
+ width, height);
+}
+
+// Read/write the indexed canvas pixels as palette indexes.
+// The data array is assumed to be at least width * height large.
+// The data array can be wider/narrower or taller/shorter than the canvas,
+// and in that case, only the relevant pixels will be transfered.
+static BOOLEAN
+TFB_DrawCanvas_TransferIndexes (TFB_Canvas canvas, BOOLEAN write,
+ BYTE *data, int width, int height)
+{
+ SDL_Surface *surf = canvas;
+ const SDL_PixelFormat *fmt;
+ int y, w, h;
+
+ if (canvas == 0)
+ {
+ log_add (log_Warning, "ERROR: TFB_DrawCanvas_TransferIndexes "
+ "passed null canvas");
+ return FALSE;
+ }
+ fmt = surf->format;
+ if (!TFB_DrawCanvas_IsPaletted (canvas) || fmt->BitsPerPixel != 8)
+ {
+ log_add (log_Warning, "ERROR: TFB_DrawCanvas_TransferIndexes "
+ "unimplemeted function: not an 8bpp indexed canvas");
+ return FALSE;
+ }
+
+ w = width < surf->w ? width : surf->w;
+ h = height < surf->h ? height : surf->h;
+
+ SDL_LockSurface (surf);
+
+ for (y = 0; y < h; ++y)
+ {
+ Uint8 *surf_p = (Uint8 *)surf->pixels + y * surf->pitch;
+ // pixels array pitch is width so as not to violate the interface
+ BYTE *data_p = data + y * width;
+
+ if (write)
+ { // writing from data to surface
+ memcpy (surf_p, data_p, w * sizeof (BYTE));
+ }
+ else
+ { // reading from surface to data
+ memcpy (data_p, surf_p, w * sizeof (BYTE));
+ }
+ }
+
+ SDL_UnlockSurface (surf);
+
+ return TRUE;
+}
+
+// Read the indexed canvas pixels as palette indexes.
+// See TFB_DrawCanvas_TransferIndexes() for data array info.
+BOOLEAN
+TFB_DrawCanvas_GetPixelIndexes (TFB_Canvas canvas, BYTE *data,
+ int width, int height)
+{
+ return TFB_DrawCanvas_TransferIndexes (canvas, FALSE, data,
+ width, height);
+}
+
+// Write the indexed canvas pixels as palette indexes.
+// See TFB_DrawCanvas_TransferIndexes() for data array info.
+BOOLEAN
+TFB_DrawCanvas_SetPixelIndexes (TFB_Canvas canvas, const BYTE *data,
+ int width, int height)
+{
+ // unconst data, but it is safe -- it will not be written to
+ return TFB_DrawCanvas_TransferIndexes (canvas, TRUE, (BYTE *)data,
+ width, height);
+}
diff --git a/src/libs/graphics/sdl/hq2x.c b/src/libs/graphics/sdl/hq2x.c
new file mode 100644
index 0000000..02dc806
--- /dev/null
+++ b/src/libs/graphics/sdl/hq2x.c
@@ -0,0 +1,2888 @@
+//hq2x filter
+//--------------------------------------------------------------------------
+//Copyright (C) 2003 MaxSt ( maxst@hiend3d.com ) - Original version
+//
+//Portions Copyright (C) 2005 Alex Volkov ( codepro@usa.net )
+// Modified Oct-2-2005
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+// Core algorithm of the HQ screen scaler
+// adapted from hq2x -- www.hiend3d.com/hq2x.html
+// Template
+// When this file is built standalone is produces a plain C version
+// Also #included by 2xscalers_mmx.c for an MMX version
+
+#include "libs/graphics/sdl/sdl_common.h"
+#include "types.h"
+#include "scalers.h"
+#include "scaleint.h"
+#include "2xscalers.h"
+
+
+// Pixel blending/manipulation instructions
+#define PIXEL00_0 dst_p[0] = pix[5];
+#define PIXEL00_10 dst_p[0] = Scale_Blend_31(pix[5], pix[1]);
+#define PIXEL00_11 dst_p[0] = Scale_Blend_31(pix[5], pix[4]);
+#define PIXEL00_12 dst_p[0] = Scale_Blend_31(pix[5], pix[2]);
+#define PIXEL00_20 dst_p[0] = Scale_Blend_211(pix[5], pix[4], pix[2]);
+#define PIXEL00_21 dst_p[0] = Scale_Blend_211(pix[5], pix[1], pix[2]);
+#define PIXEL00_22 dst_p[0] = Scale_Blend_211(pix[5], pix[1], pix[4]);
+#define PIXEL00_60 dst_p[0] = Scale_Blend_521(pix[5], pix[2], pix[4]);
+#define PIXEL00_61 dst_p[0] = Scale_Blend_521(pix[5], pix[4], pix[2]);
+#define PIXEL00_70 dst_p[0] = Scale_Blend_611(pix[5], pix[4], pix[2]);
+#define PIXEL00_90 dst_p[0] = Scale_Blend_233(pix[5], pix[4], pix[2]);
+#define PIXEL00_100 dst_p[0] = Scale_Blend_e11(pix[5], pix[4], pix[2]);
+#define PIXEL01_0 dst_p[1] = pix[5];
+#define PIXEL01_10 dst_p[1] = Scale_Blend_31(pix[5], pix[3]);
+#define PIXEL01_11 dst_p[1] = Scale_Blend_31(pix[5], pix[2]);
+#define PIXEL01_12 dst_p[1] = Scale_Blend_31(pix[5], pix[6]);
+#define PIXEL01_20 dst_p[1] = Scale_Blend_211(pix[5], pix[2], pix[6]);
+#define PIXEL01_21 dst_p[1] = Scale_Blend_211(pix[5], pix[3], pix[6]);
+#define PIXEL01_22 dst_p[1] = Scale_Blend_211(pix[5], pix[3], pix[2]);
+#define PIXEL01_60 dst_p[1] = Scale_Blend_521(pix[5], pix[6], pix[2]);
+#define PIXEL01_61 dst_p[1] = Scale_Blend_521(pix[5], pix[2], pix[6]);
+#define PIXEL01_70 dst_p[1] = Scale_Blend_611(pix[5], pix[2], pix[6]);
+#define PIXEL01_90 dst_p[1] = Scale_Blend_233(pix[5], pix[2], pix[6]);
+#define PIXEL01_100 dst_p[1] = Scale_Blend_e11(pix[5], pix[2], pix[6]);
+#define PIXEL10_0 dst_p[dlen] = pix[5];
+#define PIXEL10_10 dst_p[dlen] = Scale_Blend_31(pix[5], pix[7]);
+#define PIXEL10_11 dst_p[dlen] = Scale_Blend_31(pix[5], pix[8]);
+#define PIXEL10_12 dst_p[dlen] = Scale_Blend_31(pix[5], pix[4]);
+#define PIXEL10_20 dst_p[dlen] = Scale_Blend_211(pix[5], pix[8], pix[4]);
+#define PIXEL10_21 dst_p[dlen] = Scale_Blend_211(pix[5], pix[7], pix[4]);
+#define PIXEL10_22 dst_p[dlen] = Scale_Blend_211(pix[5], pix[7], pix[8]);
+#define PIXEL10_60 dst_p[dlen] = Scale_Blend_521(pix[5], pix[4], pix[8]);
+#define PIXEL10_61 dst_p[dlen] = Scale_Blend_521(pix[5], pix[8], pix[4]);
+#define PIXEL10_70 dst_p[dlen] = Scale_Blend_611(pix[5], pix[8], pix[4]);
+#define PIXEL10_90 dst_p[dlen] = Scale_Blend_233(pix[5], pix[8], pix[4]);
+#define PIXEL10_100 dst_p[dlen] = Scale_Blend_e11(pix[5], pix[8], pix[4]);
+#define PIXEL11_0 dst_p[dlen + 1] = pix[5];
+#define PIXEL11_10 dst_p[dlen + 1] = Scale_Blend_31(pix[5], pix[9]);
+#define PIXEL11_11 dst_p[dlen + 1] = Scale_Blend_31(pix[5], pix[6]);
+#define PIXEL11_12 dst_p[dlen + 1] = Scale_Blend_31(pix[5], pix[8]);
+#define PIXEL11_20 dst_p[dlen + 1] = Scale_Blend_211(pix[5], pix[6], pix[8]);
+#define PIXEL11_21 dst_p[dlen + 1] = Scale_Blend_211(pix[5], pix[9], pix[8]);
+#define PIXEL11_22 dst_p[dlen + 1] = Scale_Blend_211(pix[5], pix[9], pix[6]);
+#define PIXEL11_60 dst_p[dlen + 1] = Scale_Blend_521(pix[5], pix[8], pix[6]);
+#define PIXEL11_61 dst_p[dlen + 1] = Scale_Blend_521(pix[5], pix[6], pix[8]);
+#define PIXEL11_70 dst_p[dlen + 1] = Scale_Blend_611(pix[5], pix[6], pix[8]);
+#define PIXEL11_90 dst_p[dlen + 1] = Scale_Blend_233(pix[5], pix[6], pix[8]);
+#define PIXEL11_100 dst_p[dlen + 1] = Scale_Blend_e11(pix[5], pix[6], pix[8]);
+
+
+// HQ scaling to 2x
+// The name expands to
+// Scale_HqFilter (for plain C)
+// Scale_MMX_HqFilter (for MMX)
+// [others when platforms are added]
+void
+SCALE_(HqFilter) (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r)
+{
+ int x, y;
+ const int w = src->w, h = src->h;
+ int xend, yend;
+ int dsrc, ddst;
+ SDL_Rect *region = r;
+ SDL_Rect limits;
+ SDL_PixelFormat *fmt = dst->format;
+ const int sp = src->pitch, dp = dst->pitch;
+ const int bpp = fmt->BytesPerPixel;
+ const int slen = sp / bpp, dlen = dp / bpp;
+ Uint32 *src_p = (Uint32 *)src->pixels;
+ Uint32 *dst_p = (Uint32 *)dst->pixels;
+
+ int prevline, nextline;
+ Uint32 pix[10];
+ Uint32 yuv[10];
+// +----+----+----+
+// | | | |
+// | p1 | p2 | p3 |
+// +----+----+----+
+// | | | |
+// | p4 | p5 | p6 |
+// +----+----+----+
+// | | | |
+// | p7 | p8 | p9 |
+// +----+----+----+
+
+ // MMX code runs faster w/o branching
+ #define HQXX_DIFFYUV(p1, p2) \
+ SCALE_DIFFYUV (p1, p2)
+
+ SCALE_(PlatInit) ();
+
+ // expand updated region if necessary
+ // pixels neighbooring the updated region may
+ // change as a result of updates
+ limits.x = 0;
+ limits.y = 0;
+ limits.w = src->w;
+ limits.h = src->h;
+ Scale_ExpandRect (region, 1, &limits);
+
+ xend = region->x + region->w;
+ yend = region->y + region->h;
+ dsrc = slen - region->w;
+ ddst = (dlen - region->w) * 2;
+
+ // move ptrs to the first updated pixel
+ src_p += slen * region->y + region->x;
+ dst_p += (dlen * region->y + region->x) * 2;
+
+ for (y = region->y; y < yend; ++y, dst_p += ddst, src_p += dsrc)
+ {
+ if (y > 0)
+ prevline = -slen;
+ else
+ prevline = 0;
+
+ if (y < h - 1)
+ nextline = slen;
+ else
+ nextline = 0;
+
+ // prime the (tiny) sliding-window pixel arrays
+ pix[3] = src_p[prevline];
+ pix[6] = src_p[0];
+ pix[9] = src_p[nextline];
+
+ yuv[3] = SCALE_TOYUV (pix[3]);
+ yuv[6] = SCALE_TOYUV (pix[6]);
+ yuv[9] = SCALE_TOYUV (pix[9]);
+
+ if (region->x > 0)
+ {
+ pix[2] = src_p[prevline - 1];
+ pix[5] = src_p[-1];
+ pix[8] = src_p[nextline - 1];
+
+ yuv[2] = SCALE_TOYUV (pix[2]);
+ yuv[5] = SCALE_TOYUV (pix[5]);
+ yuv[8] = SCALE_TOYUV (pix[8]);
+ }
+ else
+ {
+ pix[2] = pix[3];
+ pix[5] = pix[6];
+ pix[8] = pix[9];
+
+ yuv[2] = yuv[3];
+ yuv[5] = yuv[6];
+ yuv[8] = yuv[9];
+ }
+
+ for (x = region->x; x < xend; ++x, ++src_p, dst_p += 2)
+ {
+ int pattern = 0;
+
+ // slide the window
+ pix[1] = pix[2];
+ pix[4] = pix[5];
+ pix[7] = pix[8];
+
+ yuv[1] = yuv[2];
+ yuv[4] = yuv[5];
+ yuv[7] = yuv[8];
+
+ pix[2] = pix[3];
+ pix[5] = pix[6];
+ pix[8] = pix[9];
+
+ yuv[2] = yuv[3];
+ yuv[5] = yuv[6];
+ yuv[8] = yuv[9];
+
+ if (x < w - 1)
+ {
+ pix[3] = src_p[prevline + 1];
+ pix[6] = src_p[1];
+ pix[9] = src_p[nextline + 1];
+
+ yuv[3] = SCALE_TOYUV (pix[3]);
+ yuv[6] = SCALE_TOYUV (pix[6]);
+ yuv[9] = SCALE_TOYUV (pix[9]);
+ }
+ else
+ {
+ pix[3] = pix[2];
+ pix[6] = pix[5];
+ pix[9] = pix[8];
+
+ yuv[3] = yuv[2];
+ yuv[6] = yuv[5];
+ yuv[9] = yuv[8];
+ }
+
+ // this runs much faster with branching removed
+ pattern |= HQXX_DIFFYUV (yuv[5], yuv[1]) & 0x0001;
+ pattern |= HQXX_DIFFYUV (yuv[5], yuv[2]) & 0x0002;
+ pattern |= HQXX_DIFFYUV (yuv[5], yuv[3]) & 0x0004;
+ pattern |= HQXX_DIFFYUV (yuv[5], yuv[4]) & 0x0008;
+ pattern |= HQXX_DIFFYUV (yuv[5], yuv[6]) & 0x0010;
+ pattern |= HQXX_DIFFYUV (yuv[5], yuv[7]) & 0x0020;
+ pattern |= HQXX_DIFFYUV (yuv[5], yuv[8]) & 0x0040;
+ pattern |= HQXX_DIFFYUV (yuv[5], yuv[9]) & 0x0080;
+
+ switch (pattern)
+ {
+ case 0:
+ case 1:
+ case 4:
+ case 32:
+ case 128:
+ case 5:
+ case 132:
+ case 160:
+ case 33:
+ case 129:
+ case 36:
+ case 133:
+ case 164:
+ case 161:
+ case 37:
+ case 165:
+ {
+ PIXEL00_20
+ PIXEL01_20
+ PIXEL10_20
+ PIXEL11_20
+ break;
+ }
+ case 2:
+ case 34:
+ case 130:
+ case 162:
+ {
+ PIXEL00_22
+ PIXEL01_21
+ PIXEL10_20
+ PIXEL11_20
+ break;
+ }
+ case 16:
+ case 17:
+ case 48:
+ case 49:
+ {
+ PIXEL00_20
+ PIXEL01_22
+ PIXEL10_20
+ PIXEL11_21
+ break;
+ }
+ case 64:
+ case 65:
+ case 68:
+ case 69:
+ {
+ PIXEL00_20
+ PIXEL01_20
+ PIXEL10_21
+ PIXEL11_22
+ break;
+ }
+ case 8:
+ case 12:
+ case 136:
+ case 140:
+ {
+ PIXEL00_21
+ PIXEL01_20
+ PIXEL10_22
+ PIXEL11_20
+ break;
+ }
+ case 3:
+ case 35:
+ case 131:
+ case 163:
+ {
+ PIXEL00_11
+ PIXEL01_21
+ PIXEL10_20
+ PIXEL11_20
+ break;
+ }
+ case 6:
+ case 38:
+ case 134:
+ case 166:
+ {
+ PIXEL00_22
+ PIXEL01_12
+ PIXEL10_20
+ PIXEL11_20
+ break;
+ }
+ case 20:
+ case 21:
+ case 52:
+ case 53:
+ {
+ PIXEL00_20
+ PIXEL01_11
+ PIXEL10_20
+ PIXEL11_21
+ break;
+ }
+ case 144:
+ case 145:
+ case 176:
+ case 177:
+ {
+ PIXEL00_20
+ PIXEL01_22
+ PIXEL10_20
+ PIXEL11_12
+ break;
+ }
+ case 192:
+ case 193:
+ case 196:
+ case 197:
+ {
+ PIXEL00_20
+ PIXEL01_20
+ PIXEL10_21
+ PIXEL11_11
+ break;
+ }
+ case 96:
+ case 97:
+ case 100:
+ case 101:
+ {
+ PIXEL00_20
+ PIXEL01_20
+ PIXEL10_12
+ PIXEL11_22
+ break;
+ }
+ case 40:
+ case 44:
+ case 168:
+ case 172:
+ {
+ PIXEL00_21
+ PIXEL01_20
+ PIXEL10_11
+ PIXEL11_20
+ break;
+ }
+ case 9:
+ case 13:
+ case 137:
+ case 141:
+ {
+ PIXEL00_12
+ PIXEL01_20
+ PIXEL10_22
+ PIXEL11_20
+ break;
+ }
+ case 18:
+ case 50:
+ {
+ PIXEL00_22
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_10
+ }
+ else
+ {
+ PIXEL01_20
+ }
+ PIXEL10_20
+ PIXEL11_21
+ break;
+ }
+ case 80:
+ case 81:
+ {
+ PIXEL00_20
+ PIXEL01_22
+ PIXEL10_21
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_10
+ }
+ else
+ {
+ PIXEL11_20
+ }
+ break;
+ }
+ case 72:
+ case 76:
+ {
+ PIXEL00_21
+ PIXEL01_20
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_10
+ }
+ else
+ {
+ PIXEL10_20
+ }
+ PIXEL11_22
+ break;
+ }
+ case 10:
+ case 138:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_10
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ PIXEL01_21
+ PIXEL10_22
+ PIXEL11_20
+ break;
+ }
+ case 66:
+ {
+ PIXEL00_22
+ PIXEL01_21
+ PIXEL10_21
+ PIXEL11_22
+ break;
+ }
+ case 24:
+ {
+ PIXEL00_21
+ PIXEL01_22
+ PIXEL10_22
+ PIXEL11_21
+ break;
+ }
+ case 7:
+ case 39:
+ case 135:
+ {
+ PIXEL00_11
+ PIXEL01_12
+ PIXEL10_20
+ PIXEL11_20
+ break;
+ }
+ case 148:
+ case 149:
+ case 180:
+ {
+ PIXEL00_20
+ PIXEL01_11
+ PIXEL10_20
+ PIXEL11_12
+ break;
+ }
+ case 224:
+ case 228:
+ case 225:
+ {
+ PIXEL00_20
+ PIXEL01_20
+ PIXEL10_12
+ PIXEL11_11
+ break;
+ }
+ case 41:
+ case 169:
+ case 45:
+ {
+ PIXEL00_12
+ PIXEL01_20
+ PIXEL10_11
+ PIXEL11_20
+ break;
+ }
+ case 22:
+ case 54:
+ {
+ PIXEL00_22
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_20
+ }
+ PIXEL10_20
+ PIXEL11_21
+ break;
+ }
+ case 208:
+ case 209:
+ {
+ PIXEL00_20
+ PIXEL01_22
+ PIXEL10_21
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_20
+ }
+ break;
+ }
+ case 104:
+ case 108:
+ {
+ PIXEL00_21
+ PIXEL01_20
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_20
+ }
+ PIXEL11_22
+ break;
+ }
+ case 11:
+ case 139:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ PIXEL01_21
+ PIXEL10_22
+ PIXEL11_20
+ break;
+ }
+ case 19:
+ case 51:
+ {
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL00_11
+ PIXEL01_10
+ }
+ else
+ {
+ PIXEL00_60
+ PIXEL01_90
+ }
+ PIXEL10_20
+ PIXEL11_21
+ break;
+ }
+ case 146:
+ case 178:
+ {
+ PIXEL00_22
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_10
+ PIXEL11_12
+ }
+ else
+ {
+ PIXEL01_90
+ PIXEL11_61
+ }
+ PIXEL10_20
+ break;
+ }
+ case 84:
+ case 85:
+ {
+ PIXEL00_20
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL01_11
+ PIXEL11_10
+ }
+ else
+ {
+ PIXEL01_60
+ PIXEL11_90
+ }
+ PIXEL10_21
+ break;
+ }
+ case 112:
+ case 113:
+ {
+ PIXEL00_20
+ PIXEL01_22
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL10_12
+ PIXEL11_10
+ }
+ else
+ {
+ PIXEL10_61
+ PIXEL11_90
+ }
+ break;
+ }
+ case 200:
+ case 204:
+ {
+ PIXEL00_21
+ PIXEL01_20
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_10
+ PIXEL11_11
+ }
+ else
+ {
+ PIXEL10_90
+ PIXEL11_60
+ }
+ break;
+ }
+ case 73:
+ case 77:
+ {
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL00_12
+ PIXEL10_10
+ }
+ else
+ {
+ PIXEL00_61
+ PIXEL10_90
+ }
+ PIXEL01_20
+ PIXEL11_22
+ break;
+ }
+ case 42:
+ case 170:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_10
+ PIXEL10_11
+ }
+ else
+ {
+ PIXEL00_90
+ PIXEL10_60
+ }
+ PIXEL01_21
+ PIXEL11_20
+ break;
+ }
+ case 14:
+ case 142:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_10
+ PIXEL01_12
+ }
+ else
+ {
+ PIXEL00_90
+ PIXEL01_61
+ }
+ PIXEL10_22
+ PIXEL11_20
+ break;
+ }
+ case 67:
+ {
+ PIXEL00_11
+ PIXEL01_21
+ PIXEL10_21
+ PIXEL11_22
+ break;
+ }
+ case 70:
+ {
+ PIXEL00_22
+ PIXEL01_12
+ PIXEL10_21
+ PIXEL11_22
+ break;
+ }
+ case 28:
+ {
+ PIXEL00_21
+ PIXEL01_11
+ PIXEL10_22
+ PIXEL11_21
+ break;
+ }
+ case 152:
+ {
+ PIXEL00_21
+ PIXEL01_22
+ PIXEL10_22
+ PIXEL11_12
+ break;
+ }
+ case 194:
+ {
+ PIXEL00_22
+ PIXEL01_21
+ PIXEL10_21
+ PIXEL11_11
+ break;
+ }
+ case 98:
+ {
+ PIXEL00_22
+ PIXEL01_21
+ PIXEL10_12
+ PIXEL11_22
+ break;
+ }
+ case 56:
+ {
+ PIXEL00_21
+ PIXEL01_22
+ PIXEL10_11
+ PIXEL11_21
+ break;
+ }
+ case 25:
+ {
+ PIXEL00_12
+ PIXEL01_22
+ PIXEL10_22
+ PIXEL11_21
+ break;
+ }
+ case 26:
+ case 31:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_20
+ }
+ PIXEL10_22
+ PIXEL11_21
+ break;
+ }
+ case 82:
+ case 214:
+ {
+ PIXEL00_22
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_20
+ }
+ PIXEL10_21
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_20
+ }
+ break;
+ }
+ case 88:
+ case 248:
+ {
+ PIXEL00_21
+ PIXEL01_22
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_20
+ }
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_20
+ }
+ break;
+ }
+ case 74:
+ case 107:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ PIXEL01_21
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_20
+ }
+ PIXEL11_22
+ break;
+ }
+ case 27:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ PIXEL01_10
+ PIXEL10_22
+ PIXEL11_21
+ break;
+ }
+ case 86:
+ {
+ PIXEL00_22
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_20
+ }
+ PIXEL10_21
+ PIXEL11_10
+ break;
+ }
+ case 216:
+ {
+ PIXEL00_21
+ PIXEL01_22
+ PIXEL10_10
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_20
+ }
+ break;
+ }
+ case 106:
+ {
+ PIXEL00_10
+ PIXEL01_21
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_20
+ }
+ PIXEL11_22
+ break;
+ }
+ case 30:
+ {
+ PIXEL00_10
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_20
+ }
+ PIXEL10_22
+ PIXEL11_21
+ break;
+ }
+ case 210:
+ {
+ PIXEL00_22
+ PIXEL01_10
+ PIXEL10_21
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_20
+ }
+ break;
+ }
+ case 120:
+ {
+ PIXEL00_21
+ PIXEL01_22
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_20
+ }
+ PIXEL11_10
+ break;
+ }
+ case 75:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ PIXEL01_21
+ PIXEL10_10
+ PIXEL11_22
+ break;
+ }
+ case 29:
+ {
+ PIXEL00_12
+ PIXEL01_11
+ PIXEL10_22
+ PIXEL11_21
+ break;
+ }
+ case 198:
+ {
+ PIXEL00_22
+ PIXEL01_12
+ PIXEL10_21
+ PIXEL11_11
+ break;
+ }
+ case 184:
+ {
+ PIXEL00_21
+ PIXEL01_22
+ PIXEL10_11
+ PIXEL11_12
+ break;
+ }
+ case 99:
+ {
+ PIXEL00_11
+ PIXEL01_21
+ PIXEL10_12
+ PIXEL11_22
+ break;
+ }
+ case 57:
+ {
+ PIXEL00_12
+ PIXEL01_22
+ PIXEL10_11
+ PIXEL11_21
+ break;
+ }
+ case 71:
+ {
+ PIXEL00_11
+ PIXEL01_12
+ PIXEL10_21
+ PIXEL11_22
+ break;
+ }
+ case 156:
+ {
+ PIXEL00_21
+ PIXEL01_11
+ PIXEL10_22
+ PIXEL11_12
+ break;
+ }
+ case 226:
+ {
+ PIXEL00_22
+ PIXEL01_21
+ PIXEL10_12
+ PIXEL11_11
+ break;
+ }
+ case 60:
+ {
+ PIXEL00_21
+ PIXEL01_11
+ PIXEL10_11
+ PIXEL11_21
+ break;
+ }
+ case 195:
+ {
+ PIXEL00_11
+ PIXEL01_21
+ PIXEL10_21
+ PIXEL11_11
+ break;
+ }
+ case 102:
+ {
+ PIXEL00_22
+ PIXEL01_12
+ PIXEL10_12
+ PIXEL11_22
+ break;
+ }
+ case 153:
+ {
+ PIXEL00_12
+ PIXEL01_22
+ PIXEL10_22
+ PIXEL11_12
+ break;
+ }
+ case 58:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_10
+ }
+ else
+ {
+ PIXEL00_70
+ }
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_10
+ }
+ else
+ {
+ PIXEL01_70
+ }
+ PIXEL10_11
+ PIXEL11_21
+ break;
+ }
+ case 83:
+ {
+ PIXEL00_11
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_10
+ }
+ else
+ {
+ PIXEL01_70
+ }
+ PIXEL10_21
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_10
+ }
+ else
+ {
+ PIXEL11_70
+ }
+ break;
+ }
+ case 92:
+ {
+ PIXEL00_21
+ PIXEL01_11
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_10
+ }
+ else
+ {
+ PIXEL10_70
+ }
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_10
+ }
+ else
+ {
+ PIXEL11_70
+ }
+ break;
+ }
+ case 202:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_10
+ }
+ else
+ {
+ PIXEL00_70
+ }
+ PIXEL01_21
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_10
+ }
+ else
+ {
+ PIXEL10_70
+ }
+ PIXEL11_11
+ break;
+ }
+ case 78:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_10
+ }
+ else
+ {
+ PIXEL00_70
+ }
+ PIXEL01_12
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_10
+ }
+ else
+ {
+ PIXEL10_70
+ }
+ PIXEL11_22
+ break;
+ }
+ case 154:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_10
+ }
+ else
+ {
+ PIXEL00_70
+ }
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_10
+ }
+ else
+ {
+ PIXEL01_70
+ }
+ PIXEL10_22
+ PIXEL11_12
+ break;
+ }
+ case 114:
+ {
+ PIXEL00_22
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_10
+ }
+ else
+ {
+ PIXEL01_70
+ }
+ PIXEL10_12
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_10
+ }
+ else
+ {
+ PIXEL11_70
+ }
+ break;
+ }
+ case 89:
+ {
+ PIXEL00_12
+ PIXEL01_22
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_10
+ }
+ else
+ {
+ PIXEL10_70
+ }
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_10
+ }
+ else
+ {
+ PIXEL11_70
+ }
+ break;
+ }
+ case 90:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_10
+ }
+ else
+ {
+ PIXEL00_70
+ }
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_10
+ }
+ else
+ {
+ PIXEL01_70
+ }
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_10
+ }
+ else
+ {
+ PIXEL10_70
+ }
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_10
+ }
+ else
+ {
+ PIXEL11_70
+ }
+ break;
+ }
+ case 55:
+ case 23:
+ {
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL00_11
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL00_60
+ PIXEL01_90
+ }
+ PIXEL10_20
+ PIXEL11_21
+ break;
+ }
+ case 182:
+ case 150:
+ {
+ PIXEL00_22
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ PIXEL11_12
+ }
+ else
+ {
+ PIXEL01_90
+ PIXEL11_61
+ }
+ PIXEL10_20
+ break;
+ }
+ case 213:
+ case 212:
+ {
+ PIXEL00_20
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL01_11
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL01_60
+ PIXEL11_90
+ }
+ PIXEL10_21
+ break;
+ }
+ case 241:
+ case 240:
+ {
+ PIXEL00_20
+ PIXEL01_22
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL10_12
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL10_61
+ PIXEL11_90
+ }
+ break;
+ }
+ case 236:
+ case 232:
+ {
+ PIXEL00_21
+ PIXEL01_20
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ PIXEL11_11
+ }
+ else
+ {
+ PIXEL10_90
+ PIXEL11_60
+ }
+ break;
+ }
+ case 109:
+ case 105:
+ {
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL00_12
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL00_61
+ PIXEL10_90
+ }
+ PIXEL01_20
+ PIXEL11_22
+ break;
+ }
+ case 171:
+ case 43:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ PIXEL10_11
+ }
+ else
+ {
+ PIXEL00_90
+ PIXEL10_60
+ }
+ PIXEL01_21
+ PIXEL11_20
+ break;
+ }
+ case 143:
+ case 15:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ PIXEL01_12
+ }
+ else
+ {
+ PIXEL00_90
+ PIXEL01_61
+ }
+ PIXEL10_22
+ PIXEL11_20
+ break;
+ }
+ case 124:
+ {
+ PIXEL00_21
+ PIXEL01_11
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_20
+ }
+ PIXEL11_10
+ break;
+ }
+ case 203:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ PIXEL01_21
+ PIXEL10_10
+ PIXEL11_11
+ break;
+ }
+ case 62:
+ {
+ PIXEL00_10
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_20
+ }
+ PIXEL10_11
+ PIXEL11_21
+ break;
+ }
+ case 211:
+ {
+ PIXEL00_11
+ PIXEL01_10
+ PIXEL10_21
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_20
+ }
+ break;
+ }
+ case 118:
+ {
+ PIXEL00_22
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_20
+ }
+ PIXEL10_12
+ PIXEL11_10
+ break;
+ }
+ case 217:
+ {
+ PIXEL00_12
+ PIXEL01_22
+ PIXEL10_10
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_20
+ }
+ break;
+ }
+ case 110:
+ {
+ PIXEL00_10
+ PIXEL01_12
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_20
+ }
+ PIXEL11_22
+ break;
+ }
+ case 155:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ PIXEL01_10
+ PIXEL10_22
+ PIXEL11_12
+ break;
+ }
+ case 188:
+ {
+ PIXEL00_21
+ PIXEL01_11
+ PIXEL10_11
+ PIXEL11_12
+ break;
+ }
+ case 185:
+ {
+ PIXEL00_12
+ PIXEL01_22
+ PIXEL10_11
+ PIXEL11_12
+ break;
+ }
+ case 61:
+ {
+ PIXEL00_12
+ PIXEL01_11
+ PIXEL10_11
+ PIXEL11_21
+ break;
+ }
+ case 157:
+ {
+ PIXEL00_12
+ PIXEL01_11
+ PIXEL10_22
+ PIXEL11_12
+ break;
+ }
+ case 103:
+ {
+ PIXEL00_11
+ PIXEL01_12
+ PIXEL10_12
+ PIXEL11_22
+ break;
+ }
+ case 227:
+ {
+ PIXEL00_11
+ PIXEL01_21
+ PIXEL10_12
+ PIXEL11_11
+ break;
+ }
+ case 230:
+ {
+ PIXEL00_22
+ PIXEL01_12
+ PIXEL10_12
+ PIXEL11_11
+ break;
+ }
+ case 199:
+ {
+ PIXEL00_11
+ PIXEL01_12
+ PIXEL10_21
+ PIXEL11_11
+ break;
+ }
+ case 220:
+ {
+ PIXEL00_21
+ PIXEL01_11
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_10
+ }
+ else
+ {
+ PIXEL10_70
+ }
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_20
+ }
+ break;
+ }
+ case 158:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_10
+ }
+ else
+ {
+ PIXEL00_70
+ }
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_20
+ }
+ PIXEL10_22
+ PIXEL11_12
+ break;
+ }
+ case 234:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_10
+ }
+ else
+ {
+ PIXEL00_70
+ }
+ PIXEL01_21
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_20
+ }
+ PIXEL11_11
+ break;
+ }
+ case 242:
+ {
+ PIXEL00_22
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_10
+ }
+ else
+ {
+ PIXEL01_70
+ }
+ PIXEL10_12
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_20
+ }
+ break;
+ }
+ case 59:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_10
+ }
+ else
+ {
+ PIXEL01_70
+ }
+ PIXEL10_11
+ PIXEL11_21
+ break;
+ }
+ case 121:
+ {
+ PIXEL00_12
+ PIXEL01_22
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_20
+ }
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_10
+ }
+ else
+ {
+ PIXEL11_70
+ }
+ break;
+ }
+ case 87:
+ {
+ PIXEL00_11
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_20
+ }
+ PIXEL10_21
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_10
+ }
+ else
+ {
+ PIXEL11_70
+ }
+ break;
+ }
+ case 79:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ PIXEL01_12
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_10
+ }
+ else
+ {
+ PIXEL10_70
+ }
+ PIXEL11_22
+ break;
+ }
+ case 122:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_10
+ }
+ else
+ {
+ PIXEL00_70
+ }
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_10
+ }
+ else
+ {
+ PIXEL01_70
+ }
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_20
+ }
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_10
+ }
+ else
+ {
+ PIXEL11_70
+ }
+ break;
+ }
+ case 94:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_10
+ }
+ else
+ {
+ PIXEL00_70
+ }
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_20
+ }
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_10
+ }
+ else
+ {
+ PIXEL10_70
+ }
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_10
+ }
+ else
+ {
+ PIXEL11_70
+ }
+ break;
+ }
+ case 218:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_10
+ }
+ else
+ {
+ PIXEL00_70
+ }
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_10
+ }
+ else
+ {
+ PIXEL01_70
+ }
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_10
+ }
+ else
+ {
+ PIXEL10_70
+ }
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_20
+ }
+ break;
+ }
+ case 91:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_10
+ }
+ else
+ {
+ PIXEL01_70
+ }
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_10
+ }
+ else
+ {
+ PIXEL10_70
+ }
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_10
+ }
+ else
+ {
+ PIXEL11_70
+ }
+ break;
+ }
+ case 229:
+ {
+ PIXEL00_20
+ PIXEL01_20
+ PIXEL10_12
+ PIXEL11_11
+ break;
+ }
+ case 167:
+ {
+ PIXEL00_11
+ PIXEL01_12
+ PIXEL10_20
+ PIXEL11_20
+ break;
+ }
+ case 173:
+ {
+ PIXEL00_12
+ PIXEL01_20
+ PIXEL10_11
+ PIXEL11_20
+ break;
+ }
+ case 181:
+ {
+ PIXEL00_20
+ PIXEL01_11
+ PIXEL10_20
+ PIXEL11_12
+ break;
+ }
+ case 186:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_10
+ }
+ else
+ {
+ PIXEL00_70
+ }
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_10
+ }
+ else
+ {
+ PIXEL01_70
+ }
+ PIXEL10_11
+ PIXEL11_12
+ break;
+ }
+ case 115:
+ {
+ PIXEL00_11
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_10
+ }
+ else
+ {
+ PIXEL01_70
+ }
+ PIXEL10_12
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_10
+ }
+ else
+ {
+ PIXEL11_70
+ }
+ break;
+ }
+ case 93:
+ {
+ PIXEL00_12
+ PIXEL01_11
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_10
+ }
+ else
+ {
+ PIXEL10_70
+ }
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_10
+ }
+ else
+ {
+ PIXEL11_70
+ }
+ break;
+ }
+ case 206:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_10
+ }
+ else
+ {
+ PIXEL00_70
+ }
+ PIXEL01_12
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_10
+ }
+ else
+ {
+ PIXEL10_70
+ }
+ PIXEL11_11
+ break;
+ }
+ case 205:
+ case 201:
+ {
+ PIXEL00_12
+ PIXEL01_20
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_10
+ }
+ else
+ {
+ PIXEL10_70
+ }
+ PIXEL11_11
+ break;
+ }
+ case 174:
+ case 46:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_10
+ }
+ else
+ {
+ PIXEL00_70
+ }
+ PIXEL01_12
+ PIXEL10_11
+ PIXEL11_20
+ break;
+ }
+ case 179:
+ case 147:
+ {
+ PIXEL00_11
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_10
+ }
+ else
+ {
+ PIXEL01_70
+ }
+ PIXEL10_20
+ PIXEL11_12
+ break;
+ }
+ case 117:
+ case 116:
+ {
+ PIXEL00_20
+ PIXEL01_11
+ PIXEL10_12
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_10
+ }
+ else
+ {
+ PIXEL11_70
+ }
+ break;
+ }
+ case 189:
+ {
+ PIXEL00_12
+ PIXEL01_11
+ PIXEL10_11
+ PIXEL11_12
+ break;
+ }
+ case 231:
+ {
+ PIXEL00_11
+ PIXEL01_12
+ PIXEL10_12
+ PIXEL11_11
+ break;
+ }
+ case 126:
+ {
+ PIXEL00_10
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_20
+ }
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_20
+ }
+ PIXEL11_10
+ break;
+ }
+ case 219:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ PIXEL01_10
+ PIXEL10_10
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_20
+ }
+ break;
+ }
+ case 125:
+ {
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL00_12
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL00_61
+ PIXEL10_90
+ }
+ PIXEL01_11
+ PIXEL11_10
+ break;
+ }
+ case 221:
+ {
+ PIXEL00_12
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL01_11
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL01_60
+ PIXEL11_90
+ }
+ PIXEL10_10
+ break;
+ }
+ case 207:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ PIXEL01_12
+ }
+ else
+ {
+ PIXEL00_90
+ PIXEL01_61
+ }
+ PIXEL10_10
+ PIXEL11_11
+ break;
+ }
+ case 238:
+ {
+ PIXEL00_10
+ PIXEL01_12
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ PIXEL11_11
+ }
+ else
+ {
+ PIXEL10_90
+ PIXEL11_60
+ }
+ break;
+ }
+ case 190:
+ {
+ PIXEL00_10
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ PIXEL11_12
+ }
+ else
+ {
+ PIXEL01_90
+ PIXEL11_61
+ }
+ PIXEL10_11
+ break;
+ }
+ case 187:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ PIXEL10_11
+ }
+ else
+ {
+ PIXEL00_90
+ PIXEL10_60
+ }
+ PIXEL01_10
+ PIXEL11_12
+ break;
+ }
+ case 243:
+ {
+ PIXEL00_11
+ PIXEL01_10
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL10_12
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL10_61
+ PIXEL11_90
+ }
+ break;
+ }
+ case 119:
+ {
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL00_11
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL00_60
+ PIXEL01_90
+ }
+ PIXEL10_12
+ PIXEL11_10
+ break;
+ }
+ case 237:
+ case 233:
+ {
+ PIXEL00_12
+ PIXEL01_20
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_100
+ }
+ PIXEL11_11
+ break;
+ }
+ case 175:
+ case 47:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_100
+ }
+ PIXEL01_12
+ PIXEL10_11
+ PIXEL11_20
+ break;
+ }
+ case 183:
+ case 151:
+ {
+ PIXEL00_11
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_100
+ }
+ PIXEL10_20
+ PIXEL11_12
+ break;
+ }
+ case 245:
+ case 244:
+ {
+ PIXEL00_20
+ PIXEL01_11
+ PIXEL10_12
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_100
+ }
+ break;
+ }
+ case 250:
+ {
+ PIXEL00_10
+ PIXEL01_10
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_20
+ }
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_20
+ }
+ break;
+ }
+ case 123:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ PIXEL01_10
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_20
+ }
+ PIXEL11_10
+ break;
+ }
+ case 95:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_20
+ }
+ PIXEL10_10
+ PIXEL11_10
+ break;
+ }
+ case 222:
+ {
+ PIXEL00_10
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_20
+ }
+ PIXEL10_10
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_20
+ }
+ break;
+ }
+ case 252:
+ {
+ PIXEL00_21
+ PIXEL01_11
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_20
+ }
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_100
+ }
+ break;
+ }
+ case 249:
+ {
+ PIXEL00_12
+ PIXEL01_22
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_100
+ }
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_20
+ }
+ break;
+ }
+ case 235:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ PIXEL01_21
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_100
+ }
+ PIXEL11_11
+ break;
+ }
+ case 111:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_100
+ }
+ PIXEL01_12
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_20
+ }
+ PIXEL11_22
+ break;
+ }
+ case 63:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_100
+ }
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_20
+ }
+ PIXEL10_11
+ PIXEL11_21
+ break;
+ }
+ case 159:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_100
+ }
+ PIXEL10_22
+ PIXEL11_12
+ break;
+ }
+ case 215:
+ {
+ PIXEL00_11
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_100
+ }
+ PIXEL10_21
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_20
+ }
+ break;
+ }
+ case 246:
+ {
+ PIXEL00_22
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_20
+ }
+ PIXEL10_12
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_100
+ }
+ break;
+ }
+ case 254:
+ {
+ PIXEL00_10
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_20
+ }
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_20
+ }
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_100
+ }
+ break;
+ }
+ case 253:
+ {
+ PIXEL00_12
+ PIXEL01_11
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_100
+ }
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_100
+ }
+ break;
+ }
+ case 251:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ PIXEL01_10
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_100
+ }
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_20
+ }
+ break;
+ }
+ case 239:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_100
+ }
+ PIXEL01_12
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_100
+ }
+ PIXEL11_11
+ break;
+ }
+ case 127:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_100
+ }
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_20
+ }
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_20
+ }
+ PIXEL11_10
+ break;
+ }
+ case 191:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_100
+ }
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_100
+ }
+ PIXEL10_11
+ PIXEL11_12
+ break;
+ }
+ case 223:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_100
+ }
+ PIXEL10_10
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_20
+ }
+ break;
+ }
+ case 247:
+ {
+ PIXEL00_11
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_100
+ }
+ PIXEL10_12
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_100
+ }
+ break;
+ }
+ case 255:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_100
+ }
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_100
+ }
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_100
+ }
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_100
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ SCALE_(PlatDone) ();
+}
+
diff --git a/src/libs/graphics/sdl/nearest2x.c b/src/libs/graphics/sdl/nearest2x.c
new file mode 100644
index 0000000..42e6813
--- /dev/null
+++ b/src/libs/graphics/sdl/nearest2x.c
@@ -0,0 +1,207 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+// Core algorithm of the BiLinear screen scaler
+// Template
+// When this file is built standalone is produces a plain C version
+// Also #included by 2xscalers_mmx.c for an MMX version
+
+#include "libs/graphics/sdl/sdl_common.h"
+#include "types.h"
+#include "scalers.h"
+#include "scaleint.h"
+#include "2xscalers.h"
+
+// Nearest Neighbor scaling to 2x
+// The name expands to
+// Scale_Nearest (for plain C)
+// Scale_MMX_Nearest (for MMX)
+// Scale_SSE_Nearest (for SSE)
+// [others when platforms are added]
+void
+SCALE_(Nearest) (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r)
+{
+ int y;
+ const int rw = r->w, rh = r->h;
+ const int sp = src->pitch, dp = dst->pitch;
+ const int bpp = dst->format->BytesPerPixel;
+ const int slen = sp / bpp, dlen = dp / bpp;
+ const int dsrc = slen-rw, ddst = (dlen-rw) * 2;
+
+ Uint32 *src_p = (Uint32 *)src->pixels;
+ Uint32 *dst_p = (Uint32 *)dst->pixels;
+
+ // guard asm code against such atrocities
+ if (rw == 0 || rh == 0)
+ return;
+
+ SCALE_(PlatInit) ();
+
+ // move ptrs to the first updated pixel
+ src_p += slen * r->y + r->x;
+ dst_p += (dlen * r->y + r->x) * 2;
+
+#if defined(MMX_ASM) && defined(MSVC_ASM)
+ // Just about everything has to be done in asm for MSVC
+ // to actually take advantage of asm here
+ // MSVC does not support beautiful GCC-like asm templates
+
+ y = rh;
+ __asm
+ {
+ // setup vars
+ mov esi, src_p
+ mov edi, dst_p
+
+ PREFETCH (esi + 0x40)
+ PREFETCH (esi + 0x80)
+ PREFETCH (esi + 0xc0)
+
+ mov edx, dlen
+ lea edx, [edx * 4]
+ mov eax, dsrc
+ lea eax, [eax * 4]
+ mov ebx, ddst
+ lea ebx, [ebx * 4]
+
+ mov ecx, rw
+ loop_y:
+ test ecx, 1
+ jz even_x
+
+ // one-pixel transfer
+ movd mm1, [esi]
+ punpckldq mm1, mm1 // pix1 | pix1 -> mm1
+ add esi, 4
+ MOVNTQ (edi, mm1)
+ add edi, 8
+ MOVNTQ (edi - 8 + edx, mm1)
+
+ even_x:
+ shr ecx, 1 // x = rw / 2
+ jz end_x // rw was 1
+
+ loop_x:
+ // two-pixel transfer
+ movq mm1, [esi]
+ movq mm2, mm1
+ PREFETCH (esi + 0x100)
+ punpckldq mm1, mm1 // pix1 | pix1 -> mm1
+ add esi, 8
+ MOVNTQ (edi, mm1)
+ punpckhdq mm2, mm2 // pix2 | pix2 -> mm2
+ MOVNTQ (edi + edx, mm1)
+ add edi, 16
+ MOVNTQ (edi - 8, mm2)
+ MOVNTQ (edi - 8 + edx, mm2)
+
+ dec ecx
+ jnz loop_x
+
+ end_x:
+ // try to prefetch as early as possible to have it on time
+ PREFETCH (esi + eax)
+
+ mov ecx, rw
+ add esi, eax
+
+ PREFETCH (esi + 0x40)
+ PREFETCH (esi + 0x80)
+ PREFETCH (esi + 0xc0)
+
+ add edi, ebx
+
+ dec y
+ jnz loop_y
+ }
+
+#elif defined(MMX_ASM) && defined(GCC_ASM)
+
+ SCALE_(Prefetch) (src_p + 16);
+ SCALE_(Prefetch) (src_p + 32);
+ SCALE_(Prefetch) (src_p + 48);
+
+ for (y = rh; y; --y)
+ {
+ int x = rw;
+
+ if (x & 1)
+ { // one-pixel transfer
+ __asm__ (
+ "movd (%0), %%mm1 \n\t"
+ "punpckldq %%mm1, %%mm1 \n\t"
+ MOVNTQ (%%mm1, (%1)) "\n\t"
+ MOVNTQ (%%mm1, (%1,%2)) "\n\t"
+
+ : /* nothing */
+ : /*0*/"r" (src_p), /*1*/"r" (dst_p), /*2*/"r" (dlen*sizeof(Uint32))
+ );
+
+ ++src_p;
+ dst_p += 2;
+ --x;
+ }
+
+ for (x >>= 1; x; --x, src_p += 2, dst_p += 4)
+ { // two-pixel transfer
+ __asm__ (
+ "movq (%0), %%mm1 \n\t"
+ "movq %%mm1, %%mm2 \n\t"
+ PREFETCH (0x100(%0)) "\n\t"
+ "punpckldq %%mm1, %%mm1 \n\t"
+ MOVNTQ (%%mm1, (%1)) "\n\t"
+ MOVNTQ (%%mm1, (%1,%2)) "\n\t"
+ "punpckhdq %%mm2, %%mm2 \n\t"
+ MOVNTQ (%%mm2, 8(%1)) "\n\t"
+ MOVNTQ (%%mm2, 8(%1,%2)) "\n\t"
+
+ : /* nothing */
+ : /*0*/"r" (src_p), /*1*/"r" (dst_p), /*2*/"r" (dlen*sizeof(Uint32))
+ );
+ }
+
+ src_p += dsrc;
+ // try to prefetch as early as possible to have it on time
+ SCALE_(Prefetch) (src_p);
+
+ dst_p += ddst;
+
+ SCALE_(Prefetch) (src_p + 16);
+ SCALE_(Prefetch) (src_p + 32);
+ SCALE_(Prefetch) (src_p + 48);
+ }
+
+#else
+ // Plain C version
+ for (y = 0; y < rh; ++y)
+ {
+ int x;
+ for (x = 0; x < rw; ++x, ++src_p, dst_p += 2)
+ {
+ Uint32 pix = *src_p;
+ dst_p[0] = pix;
+ dst_p[1] = pix;
+ dst_p[dlen] = pix;
+ dst_p[dlen + 1] = pix;
+ }
+ dst_p += ddst;
+ src_p += dsrc;
+ }
+#endif
+
+ SCALE_(PlatDone) ();
+}
+
diff --git a/src/libs/graphics/sdl/opengl.c b/src/libs/graphics/sdl/opengl.c
new file mode 100644
index 0000000..d8fe33f
--- /dev/null
+++ b/src/libs/graphics/sdl/opengl.c
@@ -0,0 +1,575 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_OPENGL
+
+#include "libs/graphics/sdl/opengl.h"
+#include "libs/graphics/bbox.h"
+#include "scalers.h"
+#include "options.h"
+#include "libs/log.h"
+
+#if SDL_MAJOR_VERSION == 1
+
+typedef struct _gl_screeninfo {
+ SDL_Surface *scaled;
+ GLuint texture;
+ BOOLEAN dirty, active;
+ SDL_Rect updated;
+} TFB_GL_SCREENINFO;
+
+static TFB_GL_SCREENINFO GL_Screens[TFB_GFX_NUMSCREENS];
+
+static int ScreenFilterMode;
+
+static TFB_ScaleFunc scaler = NULL;
+static BOOLEAN first_init = TRUE;
+
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+#define R_MASK 0xff000000
+#define G_MASK 0x00ff0000
+#define B_MASK 0x0000ff00
+#define A_MASK 0x000000ff
+#else
+#define R_MASK 0x000000ff
+#define G_MASK 0x0000ff00
+#define B_MASK 0x00ff0000
+#define A_MASK 0xff000000
+#endif
+
+static void TFB_GL_Preprocess (int force_full_redraw, int transition_amount, int fade_amount);
+static void TFB_GL_Postprocess (void);
+static void TFB_GL_UploadTransitionScreen (void);
+static void TFB_GL_Scaled_ScreenLayer (SCREEN screen, Uint8 a, SDL_Rect *rect);
+static void TFB_GL_Unscaled_ScreenLayer (SCREEN screen, Uint8 a, SDL_Rect *rect);
+static void TFB_GL_ColorLayer (Uint8 r, Uint8 g, Uint8 b, Uint8 a, SDL_Rect *rect);
+
+static TFB_GRAPHICS_BACKEND opengl_scaled_backend = {
+ TFB_GL_Preprocess,
+ TFB_GL_Postprocess,
+ TFB_GL_UploadTransitionScreen,
+ TFB_GL_Scaled_ScreenLayer,
+ TFB_GL_ColorLayer };
+
+static TFB_GRAPHICS_BACKEND opengl_unscaled_backend = {
+ TFB_GL_Preprocess,
+ TFB_GL_Postprocess,
+ TFB_GL_UploadTransitionScreen,
+ TFB_GL_Unscaled_ScreenLayer,
+ TFB_GL_ColorLayer };
+
+
+static int
+AttemptColorDepth (int flags, int width, int height, int bpp)
+{
+ SDL_Surface *SDL_Video;
+ int videomode_flags;
+ ScreenColorDepth = bpp;
+ ScreenWidthActual = width;
+ ScreenHeightActual = height;
+
+ switch (bpp) {
+ case 15:
+ SDL_GL_SetAttribute (SDL_GL_RED_SIZE, 5);
+ SDL_GL_SetAttribute (SDL_GL_GREEN_SIZE, 5);
+ SDL_GL_SetAttribute (SDL_GL_BLUE_SIZE, 5);
+ break;
+
+ case 16:
+ SDL_GL_SetAttribute (SDL_GL_RED_SIZE, 5);
+ SDL_GL_SetAttribute (SDL_GL_GREEN_SIZE, 6);
+ SDL_GL_SetAttribute (SDL_GL_BLUE_SIZE, 5);
+ break;
+
+ case 24:
+ SDL_GL_SetAttribute (SDL_GL_RED_SIZE, 8);
+ SDL_GL_SetAttribute (SDL_GL_GREEN_SIZE, 8);
+ SDL_GL_SetAttribute (SDL_GL_BLUE_SIZE, 8);
+ break;
+
+ case 32:
+ SDL_GL_SetAttribute (SDL_GL_RED_SIZE, 8);
+ SDL_GL_SetAttribute (SDL_GL_GREEN_SIZE, 8);
+ SDL_GL_SetAttribute (SDL_GL_BLUE_SIZE, 8);
+ break;
+ default:
+ break;
+ }
+
+ SDL_GL_SetAttribute (SDL_GL_DEPTH_SIZE, 0);
+ SDL_GL_SetAttribute (SDL_GL_DOUBLEBUFFER, 1);
+
+ videomode_flags = SDL_OPENGL;
+ if (flags & TFB_GFXFLAGS_FULLSCREEN)
+ videomode_flags |= SDL_FULLSCREEN;
+ videomode_flags |= SDL_ANYFORMAT;
+
+ SDL_Video = SDL_SetVideoMode (ScreenWidthActual, ScreenHeightActual,
+ bpp, videomode_flags);
+ if (SDL_Video == NULL)
+ {
+ log_add (log_Error, "Couldn't set OpenGL %ix%ix%i video mode: %s",
+ ScreenWidthActual, ScreenHeightActual, bpp,
+ SDL_GetError ());
+ return -1;
+ }
+ else
+ {
+ log_add (log_Info, "Set the resolution to: %ix%ix%i"
+ " (surface reports %ix%ix%i)",
+ width, height, bpp,
+ SDL_GetVideoSurface()->w, SDL_GetVideoSurface()->h,
+ SDL_GetVideoSurface()->format->BitsPerPixel);
+
+ log_add (log_Info, "OpenGL renderer: %s version: %s",
+ glGetString (GL_RENDERER), glGetString (GL_VERSION));
+ }
+ return 0;
+}
+
+int
+TFB_GL_ConfigureVideo (int driver, int flags, int width, int height, int togglefullscreen)
+{
+ int i, texture_width, texture_height;
+ GraphicsDriver = driver;
+
+ if (AttemptColorDepth (flags, width, height, 32) &&
+ AttemptColorDepth (flags, width, height, 24) &&
+ AttemptColorDepth (flags, width, height, 16))
+ {
+ log_add (log_Error, "Couldn't set any OpenGL %ix%i video mode!",
+ width, height);
+ return -1;
+ }
+
+ if (!togglefullscreen)
+ {
+ if (format_conv_surf)
+ SDL_FreeSurface (format_conv_surf);
+ format_conv_surf = SDL_CreateRGBSurface (SDL_SWSURFACE, 0, 0, 32,
+ R_MASK, G_MASK, B_MASK, A_MASK);
+ if (format_conv_surf == NULL)
+ {
+ log_add (log_Error, "Couldn't create format_conv_surf: %s",
+ SDL_GetError());
+ return -1;
+ }
+
+ for (i = 0; i < TFB_GFX_NUMSCREENS; i++)
+ {
+ if (0 != SDL1_ReInit_Screen (&SDL_Screens[i], format_conv_surf,
+ ScreenWidth, ScreenHeight))
+ return -1;
+ }
+
+ SDL_Screen = SDL_Screens[0];
+ TransitionScreen = SDL_Screens[2];
+
+ if (first_init)
+ {
+ for (i = 0; i < TFB_GFX_NUMSCREENS; i++)
+ {
+ GL_Screens[i].scaled = NULL;
+ GL_Screens[i].dirty = TRUE;
+ GL_Screens[i].active = TRUE;
+ }
+ GL_Screens[1].active = FALSE;
+ first_init = FALSE;
+ }
+ }
+
+ if (GfxFlags & TFB_GFXFLAGS_SCALE_SOFT_ONLY)
+ {
+ if (!togglefullscreen)
+ {
+ for (i = 0; i < TFB_GFX_NUMSCREENS; i++)
+ {
+ if (!GL_Screens[i].active)
+ continue;
+ if (0 != SDL1_ReInit_Screen (&GL_Screens[i].scaled, format_conv_surf,
+ ScreenWidth * 2, ScreenHeight * 2))
+ return -1;
+ }
+ scaler = Scale_PrepPlatform (flags, SDL_Screen->format);
+ }
+
+ texture_width = 1024;
+ texture_height = 512;
+
+ graphics_backend = &opengl_scaled_backend;
+ }
+ else
+ {
+ texture_width = 512;
+ texture_height = 256;
+
+ scaler = NULL;
+ graphics_backend = &opengl_unscaled_backend;
+ }
+
+
+ if (GfxFlags & TFB_GFXFLAGS_SCALE_ANY)
+ ScreenFilterMode = GL_LINEAR;
+ else
+ ScreenFilterMode = GL_NEAREST;
+
+ glViewport (0, 0, ScreenWidthActual, ScreenHeightActual);
+ glClearColor (0,0,0,0);
+ glClear (GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+ SDL_GL_SwapBuffers ();
+ glClear (GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+ glDisable (GL_DITHER);
+ glDepthMask(GL_FALSE);
+
+ for (i = 0; i < TFB_GFX_NUMSCREENS; i++)
+ {
+ if (!GL_Screens[i].active)
+ continue;
+ glGenTextures (1, &GL_Screens[i].texture);
+ glBindTexture (GL_TEXTURE_2D, GL_Screens[i].texture);
+ glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
+ glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+ glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB, texture_width, texture_height,
+ 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+ }
+
+ return 0;
+}
+
+int
+TFB_GL_InitGraphics (int driver, int flags, int width, int height)
+{
+ char VideoName[256];
+
+ log_add (log_Info, "Initializing SDL with OpenGL support.");
+
+ SDL_VideoDriverName (VideoName, sizeof (VideoName));
+ log_add (log_Info, "SDL driver used: %s", VideoName);
+ log_add (log_Info, "SDL initialized.");
+ log_add (log_Info, "Initializing Screen.");
+
+ ScreenWidth = 320;
+ ScreenHeight = 240;
+
+ if (TFB_GL_ConfigureVideo (driver, flags, width, height, 0))
+ {
+ log_add (log_Fatal, "Could not initialize video: "
+ "no fallback at start of program!");
+ exit (EXIT_FAILURE);
+ }
+
+ // Initialize scalers (let them precompute whatever)
+ Scale_Init ();
+
+ return 0;
+}
+
+void
+TFB_GL_UninitGraphics (void)
+{
+ int i;
+
+ for (i = 0; i < TFB_GFX_NUMSCREENS; i++) {
+ UnInit_Screen (&GL_Screens[i].scaled);
+ }
+}
+
+static void
+TFB_GL_UploadTransitionScreen (void)
+{
+ GL_Screens[TFB_SCREEN_TRANSITION].updated.x = 0;
+ GL_Screens[TFB_SCREEN_TRANSITION].updated.y = 0;
+ GL_Screens[TFB_SCREEN_TRANSITION].updated.w = ScreenWidth;
+ GL_Screens[TFB_SCREEN_TRANSITION].updated.h = ScreenHeight;
+ GL_Screens[TFB_SCREEN_TRANSITION].dirty = TRUE;
+}
+
+static void
+TFB_GL_ScanLines (void)
+{
+ int y;
+
+ glDisable (GL_TEXTURE_2D);
+ glEnable (GL_BLEND);
+ glBlendFunc (GL_DST_COLOR, GL_ZERO);
+ glColor3f (0.85f, 0.85f, 0.85f);
+ for (y = 0; y < ScreenHeightActual; y += 2)
+ {
+ glBegin (GL_LINES);
+ glVertex2i (0, y);
+ glVertex2i (ScreenWidthActual, y);
+ glEnd ();
+ }
+
+ glBlendFunc (GL_DST_COLOR, GL_ONE);
+ glColor3f (0.2f, 0.2f, 0.2f);
+ for (y = 1; y < ScreenHeightActual; y += 2)
+ {
+ glBegin (GL_LINES);
+ glVertex2i (0, y);
+ glVertex2i (ScreenWidthActual, y);
+ glEnd ();
+ }
+}
+
+static void
+TFB_GL_DrawQuad (SDL_Rect *r)
+{
+ BOOLEAN keep_aspect_ratio = optKeepAspectRatio;
+ int x1 = 0, y1 = 0, x2 = ScreenWidthActual, y2 = ScreenHeightActual;
+ int sx = 0, sy = 0;
+ int sw, sh;
+ float sx_multiplier = 1;
+ float sy_multiplier = 1;
+
+ if (keep_aspect_ratio)
+ {
+ float threshold = 0.75f;
+ float ratio = ScreenHeightActual / (float)ScreenWidthActual;
+
+ if (ratio > threshold)
+ {
+ // screen is narrower than 4:3
+ int height = (int)(ScreenWidthActual * threshold);
+ y1 = (ScreenHeightActual - height) / 2;
+ y2 = ScreenHeightActual - y1;
+
+ if (r != NULL)
+ {
+ sx_multiplier = ScreenWidthActual / (float)ScreenWidth;
+ sy_multiplier = height / (float)ScreenHeight;
+ sx = (int)(r->x * sx_multiplier);
+ sy = (int)(((ScreenHeight - (r->y + r->h)) * sy_multiplier) + y1);
+ }
+ }
+ else if (ratio < threshold)
+ {
+ // screen is wider than 4:3
+ int width = (int)(ScreenHeightActual / threshold);
+ x1 = (ScreenWidthActual - width) / 2;
+ x2 = ScreenWidthActual - x1;
+
+ if (r != NULL)
+ {
+ sx_multiplier = width / (float)ScreenWidth;
+ sy_multiplier = ScreenHeightActual / (float)ScreenHeight;
+ sx = (int)((r->x * sx_multiplier) + x1);
+ sy = (int)((ScreenHeight - (r->y + r->h)) * sy_multiplier);
+ }
+ }
+ else
+ {
+ // screen is 4:3
+ keep_aspect_ratio = 0;
+ }
+ }
+
+ if (r != NULL)
+ {
+ if (!keep_aspect_ratio)
+ {
+ sx_multiplier = ScreenWidthActual / (float)ScreenWidth;
+ sy_multiplier = ScreenHeightActual / (float)ScreenHeight;
+ sx = (int)(r->x * sx_multiplier);
+ sy = (int)((ScreenHeight - (r->y + r->h)) * sy_multiplier);
+ }
+ sw = (int)(r->w * sx_multiplier);
+ sh = (int)(r->h * sy_multiplier);
+ glScissor (sx, sy, sw, sh);
+ glEnable (GL_SCISSOR_TEST);
+ }
+
+ glBegin (GL_TRIANGLE_FAN);
+ glTexCoord2f (0, 0);
+ glVertex2i (x1, y1);
+ glTexCoord2f (ScreenWidth / 512.0f, 0);
+ glVertex2i (x2, y1);
+ glTexCoord2f (ScreenWidth / 512.0f, ScreenHeight / 256.0f);
+ glVertex2i (x2, y2);
+ glTexCoord2f (0, ScreenHeight / 256.0f);
+ glVertex2i (x1, y2);
+ glEnd ();
+ if (r != NULL)
+ {
+ glDisable (GL_SCISSOR_TEST);
+ }
+}
+
+static void
+TFB_GL_Preprocess (int force_full_redraw, int transition_amount, int fade_amount)
+{
+ glMatrixMode (GL_PROJECTION);
+ glLoadIdentity ();
+ glOrtho (0,ScreenWidthActual,ScreenHeightActual, 0, -1, 1);
+ glMatrixMode (GL_MODELVIEW);
+ glLoadIdentity ();
+ if (optKeepAspectRatio)
+ glClear (GL_COLOR_BUFFER_BIT);
+
+ (void) transition_amount;
+ (void) fade_amount;
+
+ if (force_full_redraw == TFB_REDRAW_YES)
+ {
+ GL_Screens[TFB_SCREEN_MAIN].updated.x = 0;
+ GL_Screens[TFB_SCREEN_MAIN].updated.y = 0;
+ GL_Screens[TFB_SCREEN_MAIN].updated.w = ScreenWidth;
+ GL_Screens[TFB_SCREEN_MAIN].updated.h = ScreenHeight;
+ GL_Screens[TFB_SCREEN_MAIN].dirty = TRUE;
+ }
+ else if (TFB_BBox.valid)
+ {
+ GL_Screens[TFB_SCREEN_MAIN].updated.x = TFB_BBox.region.corner.x;
+ GL_Screens[TFB_SCREEN_MAIN].updated.y = TFB_BBox.region.corner.y;
+ GL_Screens[TFB_SCREEN_MAIN].updated.w = TFB_BBox.region.extent.width;
+ GL_Screens[TFB_SCREEN_MAIN].updated.h = TFB_BBox.region.extent.height;
+ GL_Screens[TFB_SCREEN_MAIN].dirty = TRUE;
+ }
+}
+
+static void
+TFB_GL_Unscaled_ScreenLayer (SCREEN screen, Uint8 a, SDL_Rect *rect)
+{
+ glBindTexture (GL_TEXTURE_2D, GL_Screens[screen].texture);
+ glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ if (GL_Screens[screen].dirty)
+ {
+ int PitchWords = SDL_Screens[screen]->pitch / 4;
+ glPixelStorei (GL_UNPACK_ROW_LENGTH, PitchWords);
+ /* Matrox OpenGL drivers do not handle GL_UNPACK_SKIP_*
+ correctly */
+ glPixelStorei (GL_UNPACK_SKIP_ROWS, 0);
+ glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0);
+ SDL_LockSurface (SDL_Screens[screen]);
+ glTexSubImage2D (GL_TEXTURE_2D, 0, GL_Screens[screen].updated.x,
+ GL_Screens[screen].updated.y,
+ GL_Screens[screen].updated.w,
+ GL_Screens[screen].updated.h,
+ GL_RGBA, GL_UNSIGNED_BYTE,
+ (Uint32 *)SDL_Screens[screen]->pixels +
+ (GL_Screens[screen].updated.y * PitchWords +
+ GL_Screens[screen].updated.x));
+ SDL_UnlockSurface (SDL_Screens[screen]);
+ GL_Screens[screen].dirty = FALSE;
+ }
+
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, ScreenFilterMode);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, ScreenFilterMode);
+ glEnable (GL_TEXTURE_2D);
+
+ if (a == 255)
+ {
+ glDisable (GL_BLEND);
+ glColor4f (1, 1, 1, 1);
+ }
+ else
+ {
+ float a_f = a / 255.0f;
+ glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable (GL_BLEND);
+ glColor4f (1, 1, 1, a_f);
+ }
+
+ TFB_GL_DrawQuad (rect);
+}
+
+static void
+TFB_GL_Scaled_ScreenLayer (SCREEN screen, Uint8 a, SDL_Rect *rect)
+{
+ glBindTexture (GL_TEXTURE_2D, GL_Screens[screen].texture);
+ glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ if (GL_Screens[screen].dirty)
+ {
+ int PitchWords = GL_Screens[screen].scaled->pitch / 4;
+ scaler (SDL_Screens[screen], GL_Screens[screen].scaled, &GL_Screens[screen].updated);
+ glPixelStorei (GL_UNPACK_ROW_LENGTH, PitchWords);
+
+ /* Matrox OpenGL drivers do not handle GL_UNPACK_SKIP_*
+ correctly */
+ glPixelStorei (GL_UNPACK_SKIP_ROWS, 0);
+ glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0);
+ SDL_LockSurface (GL_Screens[screen].scaled);
+ glTexSubImage2D (GL_TEXTURE_2D, 0, GL_Screens[screen].updated.x * 2,
+ GL_Screens[screen].updated.y * 2,
+ GL_Screens[screen].updated.w * 2,
+ GL_Screens[screen].updated.h * 2,
+ GL_RGBA, GL_UNSIGNED_BYTE,
+ (Uint32 *)GL_Screens[screen].scaled->pixels +
+ (GL_Screens[screen].updated.y * 2 * PitchWords +
+ GL_Screens[screen].updated.x * 2));
+ SDL_UnlockSurface (GL_Screens[screen].scaled);
+ GL_Screens[screen].dirty = FALSE;
+ }
+
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, ScreenFilterMode);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, ScreenFilterMode);
+ glEnable (GL_TEXTURE_2D);
+
+ if (a == 255)
+ {
+ glDisable (GL_BLEND);
+ glColor4f (1, 1, 1, 1);
+ }
+ else
+ {
+ float a_f = a / 255.0f;
+ glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable (GL_BLEND);
+ glColor4f (1, 1, 1, a_f);
+ }
+
+ TFB_GL_DrawQuad (rect);
+}
+
+static void
+TFB_GL_ColorLayer (Uint8 r, Uint8 g, Uint8 b, Uint8 a, SDL_Rect *rect)
+{
+ float r_f = r / 255.0f;
+ float g_f = g / 255.0f;
+ float b_f = b / 255.0f;
+ float a_f = a / 255.0f;
+ glColor4f(r_f, g_f, b_f, a_f);
+
+ glDisable (GL_TEXTURE_2D);
+ if (a != 255)
+ {
+ glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable (GL_BLEND);
+ }
+ else
+ {
+ glDisable (GL_BLEND);
+ }
+
+ TFB_GL_DrawQuad (rect);
+}
+
+static void
+TFB_GL_Postprocess (void)
+{
+ if (GfxFlags & TFB_GFXFLAGS_SCANLINES)
+ TFB_GL_ScanLines ();
+
+ SDL_GL_SwapBuffers ();
+}
+
+#endif
+#endif
diff --git a/src/libs/graphics/sdl/opengl.h b/src/libs/graphics/sdl/opengl.h
new file mode 100644
index 0000000..6550bca
--- /dev/null
+++ b/src/libs/graphics/sdl/opengl.h
@@ -0,0 +1,89 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef OPENGL_H
+#define OPENGL_H
+
+#include "libs/graphics/sdl/sdl_common.h"
+#if SDL_MAJOR_VERSION == 1
+
+int TFB_GL_InitGraphics (int driver, int flags, int width, int height);
+void TFB_GL_UninitGraphics (void);
+int TFB_GL_ConfigureVideo (int driver, int flags, int width, int height, int togglefullscreen);
+
+#ifdef HAVE_OPENGL
+#ifdef WIN32
+
+#ifdef _MSC_VER
+#pragma comment (lib, "opengl32.lib")
+#pragma comment (lib, "glu32.lib")
+#endif
+
+/* To avoid including windows.h,
+ Win32's <GL/gl.h> needs APIENTRY and WINGDIAPI defined properly. */
+
+#ifndef APIENTRY
+#define GLUT_APIENTRY_DEFINED
+#if __MINGW32__ || (_MSC_VER >= 800) || defined(_STDCALL_SUPPORTED)
+#define APIENTRY __stdcall
+#else
+#define APIENTRY
+#endif
+#endif
+
+#ifndef WINAPI
+#define GLUT_WINAPI_DEFINED
+#if __MINGW32__ || (_MSC_VER >= 800) || defined(_STDCALL_SUPPORTED)
+#define WINAPI __stdcall
+#else
+#define WINAPI
+#endif
+#endif
+
+/* This is from Win32's <winnt.h> */
+#ifndef CALLBACK
+#if (defined(_M_MRX000) || defined(_M_IX86) || defined(_M_ALPHA) || defined(_M_PPC)) && !defined(MIDL_PASS)
+#define CALLBACK __stdcall
+#else
+#define CALLBACK
+#endif
+#endif
+
+/* This is from Win32's <wingdi.h> and <winnt.h> */
+#ifndef WINGDIAPI
+#define GLUT_WINGDIAPI_DEFINED
+#define WINGDIAPI __declspec(dllimport)
+#endif
+
+/* This is from Win32's <ctype.h> */
+#ifndef LIBS_GRAPHICS_SDL_OPENGL_H_
+typedef unsigned short wchar_t;
+#define LIBS_GRAPHICS_SDL_OPENGL_H_
+#endif
+
+#include "GL/glu.h"
+
+#else /* !defined(WIN32) */
+
+#include "port.h"
+#include SDL_INCLUDE(SDL_opengl.h)
+
+#endif /* WIN32 */
+#endif /* SDL_MAJOR_VERSION == 1 */
+#endif /* HAVE_OPENGL */
+#endif
diff --git a/src/libs/graphics/sdl/palette.c b/src/libs/graphics/sdl/palette.c
new file mode 100644
index 0000000..18f4418
--- /dev/null
+++ b/src/libs/graphics/sdl/palette.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2009 Alex Volkov <codepro@usa.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "palette.h"
+#include "libs/memlib.h"
+#include "libs/log.h"
+
+NativePalette *
+AllocNativePalette (void)
+{
+ return HCalloc (sizeof (NativePalette));
+}
+
+void
+FreeNativePalette (NativePalette *palette)
+{
+ HFree (palette);
+}
+
+void
+SetNativePaletteColor (NativePalette *palette, int index, Color color)
+{
+ assert (index < NUMBER_OF_PLUTVALS);
+ palette->colors[index] = ColorToNative (color);
+}
+
+Color
+GetNativePaletteColor (NativePalette *palette, int index)
+{
+ assert (index < NUMBER_OF_PLUTVALS);
+ return NativeToColor (palette->colors[index]);
+}
diff --git a/src/libs/graphics/sdl/palette.h b/src/libs/graphics/sdl/palette.h
new file mode 100644
index 0000000..2cefe7c
--- /dev/null
+++ b/src/libs/graphics/sdl/palette.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2009 Alex Volkov <codepro@usa.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef PALETTE_H_INCL__
+#define PALETTE_H_INCL__
+
+#include "port.h"
+#include SDL_INCLUDE(SDL.h)
+#include "libs/graphics/cmap.h"
+
+struct NativePalette
+{
+ SDL_Color colors[NUMBER_OF_PLUTVALS];
+};
+
+static inline Color
+NativeToColor (SDL_Color native)
+{
+ Color color;
+ color.r = native.r;
+ color.g = native.g;
+ color.b = native.b;
+ color.a = 0xff; // fully opaque
+ return color;
+}
+
+static inline SDL_Color
+ColorToNative (Color color)
+{
+ SDL_Color native;
+ native.r = color.r;
+ native.g = color.g;
+ native.b = color.b;
+#if SDL_MAJOR_VERSION == 1
+ native.unused = 0;
+#else
+ native.a = color.a;
+#endif
+ return native;
+}
+
+#endif /* PALETTE_H_INCL__ */
diff --git a/src/libs/graphics/sdl/png2sdl.c b/src/libs/graphics/sdl/png2sdl.c
new file mode 100644
index 0000000..1717886
--- /dev/null
+++ b/src/libs/graphics/sdl/png2sdl.c
@@ -0,0 +1,300 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * This code is adapted from IMG_png.c in the SDL2_image library, which is
+ * available under the zlib license and is (c) 1997-2019 Sam Lantinga. The
+ * code itself is credited to Philippe Lavoie as the original author. It
+ * also shares some heritage with libpng's own example.c file, and
+ * ultimately inherits the GPL2+ license from the rest of UQM.
+ *
+ * Differences from SDL2_image are:
+ * - It is PNG-only
+ * - It directly links the relevant version of libpng at compile time
+ * - It always uses libpng and will never forward to alternative
+ * libraries such as ImageIO.framework. This means that palette
+ * information will always be preserved, as UQM requires.
+ * - It locks the surface as the API demands rather than using
+ * volatility markers
+ * - Palette assignment is done through the API rather than by
+ * directly editing the format contents
+ */
+
+#include "png2sdl.h"
+#include <png.h>
+
+/* Link function between SDL_RWops and PNG's data source */
+static void
+png_read_data(png_structp ctx, png_bytep area, png_size_t size)
+{
+ SDL_RWops *src = (SDL_RWops *)png_get_io_ptr (ctx);
+ SDL_RWread (src, area, size, 1);
+}
+
+SDL_Surface *
+TFB_png_to_sdl (SDL_RWops *src)
+{
+ Sint64 start;
+ const char *error;
+ SDL_Surface *surface;
+ png_structp png_ptr;
+ png_infop info_ptr;
+ png_uint_32 width, height;
+ int bit_depth, color_type, interlace_type, num_channels;
+ Uint32 Rmask, Gmask, Bmask, Amask;
+ png_bytep *row_pointers;
+ int row, i;
+ int ckey = -1;
+ png_color_16 *transv;
+
+ if (!src)
+ {
+ /* The error message has been set in SDL_RWFromFile */
+ return NULL;
+ }
+ start = SDL_RWtell (src);
+
+ /* Initialize the data we will clean up when we're done */
+ error = NULL;
+ png_ptr = NULL;
+ info_ptr = NULL;
+ row_pointers = NULL;
+ surface = NULL;
+
+ /* Create the PNG loading context structure */
+ png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING,
+ NULL, NULL, NULL);
+ if (png_ptr == NULL)
+ {
+ error = "Couldn't allocate memory for PNG file";
+ goto done;
+ }
+
+ /* Allocate/initialize the memory for image information */
+ info_ptr = png_create_info_struct (png_ptr);
+ if (info_ptr == NULL)
+ {
+ error = "Couldn't create image information for PNG file";
+ goto done;
+ }
+
+ /* Set error handling */
+ if (setjmp (png_jmpbuf (png_ptr)))
+ {
+ error = "Error reading the PNG file.";
+ goto done;
+ }
+
+ /* Set up the input control */
+ png_set_read_fn (png_ptr, src, png_read_data);
+
+ /* Read PNG header info */
+ png_read_info (png_ptr, info_ptr);
+ png_get_IHDR (png_ptr, info_ptr, &width, &height, &bit_depth,
+ &color_type, &interlace_type, NULL, NULL);
+
+
+ /* Configure the decode based on what we know of the image
+ * already: strip 16 bit color down to 8 bit, automatically
+ * deinterlace, expand grayscale images or those with more
+ * than one transparent color or any translucent colors into
+ * full RGB or RGBA, and expand 1, 2, or 4-bpp paletted
+ * images to 8bpp. */
+ png_set_strip_16 (png_ptr);
+ png_set_interlace_handling (png_ptr);
+ png_set_packing (png_ptr);
+ if (color_type == PNG_COLOR_TYPE_GRAY)
+ {
+ png_set_expand (png_ptr);
+ }
+ if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS))
+ {
+ int num_trans;
+ Uint8 *trans;
+ png_get_tRNS (png_ptr, info_ptr, &trans, &num_trans, &transv);
+ if (color_type == PNG_COLOR_TYPE_PALETTE)
+ {
+ /* Check if all tRNS entries are opaque except one */
+ int j, t = -1;
+ for (j = 0; j < num_trans; j++)
+ {
+ if (trans[j] == 0)
+ {
+ if (t >= 0)
+ {
+ break;
+ }
+ t = j;
+ }
+ else if (trans[j] != 255)
+ {
+ break;
+ }
+ }
+ if (j == num_trans)
+ {
+ /* exactly one transparent index */
+ ckey = t;
+ }
+ else
+ {
+ /* more than one transparent index, or translucency */
+ png_set_expand (png_ptr);
+ }
+ }
+ else
+ {
+ ckey = 0; /* actual value will be set later */
+ }
+ }
+
+ if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+ {
+ png_set_gray_to_rgb (png_ptr);
+ }
+
+ /* Register our changes with the reading machinery and refresh
+ * our ancillary data about the image */
+ png_read_update_info (png_ptr, info_ptr);
+ png_get_IHDR (png_ptr, info_ptr, &width, &height, &bit_depth,
+ &color_type, &interlace_type, NULL, NULL);
+
+ /* Allocate the SDL surface to hold the image */
+ Rmask = Gmask = Bmask = Amask = 0;
+ num_channels = png_get_channels (png_ptr, info_ptr);
+ if (num_channels >= 3)
+ {
+#if SDL_BYTEORDER == SDL_LIL_ENDIAN
+ Rmask = 0x000000FF;
+ Gmask = 0x0000FF00;
+ Bmask = 0x00FF0000;
+ Amask = (num_channels == 4) ? 0xFF000000 : 0;
+#else
+ int s = (num_channels == 4) ? 0 : 8;
+ Rmask = 0xFF000000 >> s;
+ Gmask = 0x00FF0000 >> s;
+ Bmask = 0x0000FF00 >> s;
+ Amask = 0x000000FF >> s;
+#endif
+ }
+ surface = SDL_CreateRGBSurface (SDL_SWSURFACE, width, height,
+ bit_depth * num_channels,
+ Rmask, Gmask, Bmask, Amask);
+ if (surface == NULL)
+ {
+ error = SDL_GetError ();
+ goto done;
+ }
+
+ if (ckey != -1)
+ {
+ if (color_type != PNG_COLOR_TYPE_PALETTE)
+ {
+ /* FIXME: Should these be truncated or shifted down? */
+ ckey = SDL_MapRGB(surface->format,
+ (Uint8)transv->red,
+ (Uint8)transv->green,
+ (Uint8)transv->blue);
+ }
+#if SDL_MAJOR_VERSION >= 2
+ SDL_SetColorKey (surface, SDL_TRUE, ckey);
+#else
+ SDL_SetColorKey (surface, SDL_SRCCOLORKEY, ckey);
+#endif
+ }
+
+ SDL_LockSurface (surface);
+ /* Create the array of pointers to image data */
+ row_pointers = (png_bytep *)SDL_malloc (sizeof (png_bytep) * height);
+ if (!row_pointers)
+ {
+ error = "Out of memory";
+ goto done;
+ }
+ for (row = 0; row < (int)height; row++)
+ {
+ row_pointers[row] = (png_bytep)
+ (Uint8 *)surface->pixels + row*surface->pitch;
+ }
+
+ /* Read the entire image in one go */
+ png_read_image (png_ptr, row_pointers);
+ SDL_UnlockSurface (surface);
+
+ /* and we're done! (png_read_end() can be omitted if no
+ * processing of post-IDAT text/time/etc. is desired)
+ * In some cases it can't read PNGs created by some popular
+ * programs (ACDSEE), we do not want to process comments, so
+ * we omit png_read_end */
+
+ /* Load the palette, if any */
+ if (surface->format->palette)
+ {
+ SDL_Color palette[256];
+ int png_num_palette;
+ png_colorp png_palette;
+ png_get_PLTE (png_ptr, info_ptr, &png_palette, &png_num_palette);
+ if (color_type == PNG_COLOR_TYPE_GRAY)
+ {
+ png_num_palette = 256;
+ for (i = 0; i < 256; i++)
+ {
+ palette[i].r = (Uint8)i;
+ palette[i].g = (Uint8)i;
+ palette[i].b = (Uint8)i;
+ }
+ }
+ else if (png_num_palette > 0)
+ {
+ for (i = 0; i < png_num_palette; ++i)
+ {
+ palette[i].b = png_palette[i].blue;
+ palette[i].g = png_palette[i].green;
+ palette[i].r = png_palette[i].red;
+ }
+ }
+#if SDL_MAJOR_VERSION >= 2
+ SDL_SetPaletteColors (surface->format->palette, palette,
+ 0, png_num_palette);
+#else
+ SDL_SetPalette (surface, SDL_LOGPAL, palette,
+ 0, png_num_palette);
+#endif
+ }
+
+done: /* Clean up and return */
+ if (png_ptr)
+ {
+ png_destroy_read_struct (&png_ptr,
+ info_ptr ? &info_ptr : (png_infopp)0,
+ (png_infopp)0);
+ }
+ if (row_pointers)
+ {
+ SDL_free (row_pointers);
+ }
+ if (error)
+ {
+ SDL_RWseek(src, start, RW_SEEK_SET);
+ if (surface)
+ {
+ SDL_FreeSurface (surface);
+ surface = NULL;
+ }
+ fprintf (stderr, "%s", error);
+ }
+ return surface;
+}
diff --git a/src/libs/graphics/sdl/png2sdl.h b/src/libs/graphics/sdl/png2sdl.h
new file mode 100644
index 0000000..c0a9ec3
--- /dev/null
+++ b/src/libs/graphics/sdl/png2sdl.h
@@ -0,0 +1,24 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PNG2SDL_H_
+#define PNG2SDL_H_
+
+#include <SDL.h>
+
+SDL_Surface *TFB_png_to_sdl (SDL_RWops *src);
+
+#endif
diff --git a/src/libs/graphics/sdl/primitives.c b/src/libs/graphics/sdl/primitives.c
new file mode 100644
index 0000000..6dd539a
--- /dev/null
+++ b/src/libs/graphics/sdl/primitives.c
@@ -0,0 +1,633 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "port.h"
+#include "sdl_common.h"
+#include "primitives.h"
+
+
+// Pixel drawing routines
+
+static Uint32
+getpixel_8(SDL_Surface *surface, int x, int y)
+{
+ /* Here p is the address to the pixel we want to retrieve */
+ Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x;
+ return *p;
+}
+
+static void
+putpixel_8(SDL_Surface *surface, int x, int y, Uint32 pixel)
+{
+ /* Here p is the address to the pixel we want to set */
+ Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * 1;
+ *p = pixel;
+}
+
+static Uint32
+getpixel_16(SDL_Surface *surface, int x, int y)
+{
+ /* Here p is the address to the pixel we want to retrieve */
+ Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * 2;
+ return *(Uint16 *)p;
+}
+
+static void
+putpixel_16(SDL_Surface *surface, int x, int y, Uint32 pixel)
+{
+ /* Here p is the address to the pixel we want to set */
+ Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * 2;
+ *(Uint16 *)p = pixel;
+}
+
+static Uint32
+getpixel_24_be(SDL_Surface *surface, int x, int y)
+{
+ /* Here p is the address to the pixel we want to retrieve */
+ Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
+ return p[0] << 16 | p[1] << 8 | p[2];
+}
+
+static void
+putpixel_24_be(SDL_Surface *surface, int x, int y, Uint32 pixel)
+{
+ /* Here p is the address to the pixel we want to set */
+ Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
+ p[0] = (pixel >> 16) & 0xff;
+ p[1] = (pixel >> 8) & 0xff;
+ p[2] = pixel & 0xff;
+}
+
+static Uint32
+getpixel_24_le(SDL_Surface *surface, int x, int y)
+{
+ /* Here p is the address to the pixel we want to retrieve */
+ Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
+ return p[0] | p[1] << 8 | p[2] << 16;
+}
+
+static void
+putpixel_24_le(SDL_Surface *surface, int x, int y, Uint32 pixel)
+{
+ /* Here p is the address to the pixel we want to set */
+ Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
+ p[0] = pixel & 0xff;
+ p[1] = (pixel >> 8) & 0xff;
+ p[2] = (pixel >> 16) & 0xff;
+}
+
+static Uint32
+getpixel_32(SDL_Surface *surface, int x, int y)
+{
+ /* Here p is the address to the pixel we want to retrieve */
+ Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * 4;
+ return *(Uint32 *)p;
+}
+
+static void
+putpixel_32(SDL_Surface *surface, int x, int y, Uint32 pixel)
+{
+ /* Here p is the address to the pixel we want to set */
+ Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * 4;
+ *(Uint32 *)p = pixel;
+}
+
+GetPixelFn
+getpixel_for(SDL_Surface *surface)
+{
+ int bpp = surface->format->BytesPerPixel;
+ switch (bpp) {
+ case 1:
+ return &getpixel_8;
+ case 2:
+ return &getpixel_16;
+ case 3:
+ if (SDL_BYTEORDER == SDL_BIG_ENDIAN) {
+ return &getpixel_24_be;
+ } else {
+ return &getpixel_24_le;
+ }
+ case 4:
+ return &getpixel_32;
+ }
+ return NULL;
+}
+
+PutPixelFn
+putpixel_for(SDL_Surface *surface)
+{
+ int bpp = surface->format->BytesPerPixel;
+ switch (bpp) {
+ case 1:
+ return &putpixel_8;
+ case 2:
+ return &putpixel_16;
+ case 3:
+ if (SDL_BYTEORDER == SDL_BIG_ENDIAN) {
+ return &putpixel_24_be;
+ } else {
+ return &putpixel_24_le;
+ }
+ case 4:
+ return &putpixel_32;
+ }
+ return NULL;
+}
+
+static void
+renderpixel_replace(SDL_Surface *surface, int x, int y, Uint32 pixel,
+ int factor)
+{
+ (void) factor; // ignored
+ putpixel_32(surface, x, y, pixel);
+}
+
+static inline Uint8
+clip_channel(int c)
+{
+ if (c < 0)
+ c = 0;
+ else if (c > 255)
+ c = 255;
+ return c;
+}
+
+static inline Uint8
+modulated_sum(Uint8 dc, Uint8 sc, int factor)
+{
+ // We use >> 8 instead of / 255 because it is faster, but it does
+ // not work 100% correctly. It should be safe because this should
+ // not be called for factor==255
+ int b = dc + ((sc * factor) >> 8);
+ return clip_channel(b);
+}
+
+static inline Uint8
+alpha_blend(Uint8 dc, Uint8 sc, int alpha)
+{
+ // We use >> 8 instead of / 255 because it is faster, but it does
+ // not work 100% correctly. It should be safe because this should
+ // not be called for alpha==255
+ // No need to clip since we should never get values outside of 0..255
+ // range, unless alpha is over 255, which is not supported.
+ return (((sc - dc) * alpha) >> 8) + dc;
+}
+
+// Assumes 8 bits/channel, a safe assumption for 32bpp surfaces
+#define UNPACK_PIXEL_32(p, fmt, r, g, b) \
+ do { \
+ (r) = ((p) >> (fmt)->Rshift) & 0xff; \
+ (g) = ((p) >> (fmt)->Gshift) & 0xff; \
+ (b) = ((p) >> (fmt)->Bshift) & 0xff; \
+ } while (0)
+
+// Assumes the channels already clipped to 8 bits
+static inline Uint32
+PACK_PIXEL_32(const SDL_PixelFormat *fmt,
+ Uint8 r, Uint8 g, Uint8 b)
+{
+ return ((Uint32)r << fmt->Rshift) | ((Uint32)g << fmt->Gshift)
+ | ((Uint32)b << fmt->Bshift);
+}
+
+static void
+renderpixel_additive(SDL_Surface *surface, int x, int y, Uint32 pixel,
+ int factor)
+{
+ const SDL_PixelFormat *fmt = surface->format;
+ Uint32 *p;
+ Uint32 sp;
+ Uint8 sr, sg, sb;
+ int r, g, b;
+
+ p = (Uint32 *) ((Uint8 *)surface->pixels + y * surface->pitch + x * 4);
+ sp = *p;
+ UNPACK_PIXEL_32(sp, fmt, sr, sg, sb);
+ UNPACK_PIXEL_32(pixel, fmt, r, g, b);
+
+ // TODO: We may need a special case for factor == -ADDITIVE_FACTOR_1 too,
+ // but it is not important enough right now to care ;)
+ if (factor == ADDITIVE_FACTOR_1)
+ { // no need to modulate the 'pixel', and modulation does not
+ // work correctly with factor==255 anyway
+ sr = clip_channel(sr + r);
+ sg = clip_channel(sg + g);
+ sb = clip_channel(sb + b);
+ }
+ else
+ {
+ sr = modulated_sum(sr, r, factor);
+ sg = modulated_sum(sg, g, factor);
+ sb = modulated_sum(sb, b, factor);
+ }
+
+ *p = PACK_PIXEL_32(fmt, sr, sg, sb);
+}
+
+static void
+renderpixel_alpha(SDL_Surface *surface, int x, int y, Uint32 pixel,
+ int factor)
+{
+ const SDL_PixelFormat *fmt = surface->format;
+ Uint32 *p;
+ Uint32 sp;
+ Uint8 sr, sg, sb;
+ int r, g, b;
+
+ if (factor == FULLY_OPAQUE_ALPHA)
+ { // alpha == 255 is equivalent to 'replace' and blending does not
+ // work correctly anyway because we use >> 8 instead of / 255
+ putpixel_32(surface, x, y, pixel);
+ return;
+ }
+
+ p = (Uint32 *) ((Uint8 *)surface->pixels + y * surface->pitch + x * 4);
+ sp = *p;
+ UNPACK_PIXEL_32(sp, fmt, sr, sg, sb);
+ UNPACK_PIXEL_32(pixel, fmt, r, g, b);
+ sr = alpha_blend(sr, r, factor);
+ sg = alpha_blend(sg, g, factor);
+ sb = alpha_blend(sb, b, factor);
+ *p = PACK_PIXEL_32(fmt, sr, sg, sb);
+}
+
+RenderPixelFn
+renderpixel_for(SDL_Surface *surface, RenderKind kind)
+{
+ const SDL_PixelFormat *fmt = surface->format;
+
+ // The only supported rendering is to 32bpp surfaces
+ if (fmt->BytesPerPixel != 4)
+ return NULL;
+
+ // Rendering other than REPLACE is not supported on RGBA surfaces
+ if (fmt->Amask != 0 && kind != renderReplace)
+ return NULL;
+
+ switch (kind)
+ {
+ case renderReplace:
+ return &renderpixel_replace;
+ case renderAdditive:
+ return &renderpixel_additive;
+ case renderAlpha:
+ return &renderpixel_alpha;
+ }
+ // should not ever get here
+ return NULL;
+}
+
+/* Line drawing routine
+ * Adapted from Paul Heckbert's implementation of Bresenham's algorithm,
+ * 3 Sep 85; taken from Graphics Gems I */
+
+void
+line_prim(int x1, int y1, int x2, int y2, Uint32 color, RenderPixelFn plot,
+ int factor, SDL_Surface *dst)
+{
+ int d, x, y, ax, ay, sx, sy, dx, dy;
+ SDL_Rect clip_r;
+
+ SDL_GetClipRect (dst, &clip_r);
+ if (!clip_line (&x1, &y1, &x2, &y2, &clip_r))
+ return; // line is completely outside clipping rectangle
+
+ dx = x2-x1;
+ ax = ((dx < 0) ? -dx : dx) << 1;
+ sx = (dx < 0) ? -1 : 1;
+ dy = y2-y1;
+ ay = ((dy < 0) ? -dy : dy) << 1;
+ sy = (dy < 0) ? -1 : 1;
+
+ x = x1;
+ y = y1;
+ if (ax > ay) {
+ d = ay - (ax >> 1);
+ for (;;) {
+ (*plot)(dst, x, y, color, factor);
+ if (x == x2)
+ return;
+ if (d >= 0) {
+ y += sy;
+ d -= ax;
+ }
+ x += sx;
+ d += ay;
+ }
+ } else {
+ d = ax - (ay >> 1);
+ for (;;) {
+ (*plot)(dst, x, y, color, factor);
+ if (y == y2)
+ return;
+ if (d >= 0) {
+ x += sx;
+ d -= ay;
+ }
+ y += sy;
+ d += ax;
+ }
+ }
+}
+
+
+// Clips line against rectangle using Cohen-Sutherland algorithm
+
+enum {C_TOP = 0x1, C_BOTTOM = 0x2, C_RIGHT = 0x4, C_LEFT = 0x8};
+
+static int
+compute_code (float x, float y, float xmin, float ymin, float xmax, float ymax)
+{
+ int c = 0;
+ if (y > ymax)
+ c |= C_TOP;
+ else if (y < ymin)
+ c |= C_BOTTOM;
+ if (x > xmax)
+ c |= C_RIGHT;
+ else if (x < xmin)
+ c |= C_LEFT;
+ return c;
+}
+
+int
+clip_line (int *lx1, int *ly1, int *lx2, int *ly2, const SDL_Rect *r)
+{
+ int C0, C1, C;
+ float x, y, x0, y0, x1, y1, xmin, ymin, xmax, ymax;
+
+ x0 = (float)*lx1;
+ y0 = (float)*ly1;
+ x1 = (float)*lx2;
+ y1 = (float)*ly2;
+
+ xmin = (float)r->x;
+ ymin = (float)r->y;
+ xmax = (float)r->x + r->w - 1;
+ ymax = (float)r->y + r->h - 1;
+
+ C0 = compute_code (x0, y0, xmin, ymin, xmax, ymax);
+ C1 = compute_code (x1, y1, xmin, ymin, xmax, ymax);
+
+ for (;;) {
+ /* trivial accept: both ends in rectangle */
+ if ((C0 | C1) == 0)
+ {
+ *lx1 = (int)x0;
+ *ly1 = (int)y0;
+ *lx2 = (int)x1;
+ *ly2 = (int)y1;
+ return 1;
+ }
+
+ /* trivial reject: both ends on the external side of the rectangle */
+ if ((C0 & C1) != 0)
+ return 0;
+
+ /* normal case: clip end outside rectangle */
+ C = C0 ? C0 : C1;
+ if (C & C_TOP)
+ {
+ x = x0 + (x1 - x0) * (ymax - y0) / (y1 - y0);
+ y = ymax;
+ }
+ else if (C & C_BOTTOM)
+ {
+ x = x0 + (x1 - x0) * (ymin - y0) / (y1 - y0);
+ y = ymin;
+ }
+ else if (C & C_RIGHT)
+ {
+ x = xmax;
+ y = y0 + (y1 - y0) * (xmax - x0) / (x1 - x0);
+ }
+ else
+ {
+ x = xmin;
+ y = y0 + (y1 - y0) * (xmin - x0) / (x1 - x0);
+ }
+
+ /* set new end point and iterate */
+ if (C == C0)
+ {
+ x0 = x; y0 = y;
+ C0 = compute_code (x0, y0, xmin, ymin, xmax, ymax);
+ }
+ else
+ {
+ x1 = x; y1 = y;
+ C1 = compute_code (x1, y1, xmin, ymin, xmax, ymax);
+ }
+ }
+}
+
+void
+fillrect_prim(SDL_Rect r, Uint32 color, RenderPixelFn plot, int factor,
+ SDL_Surface *dst)
+{
+ int x, y;
+ int x1, y1;
+ SDL_Rect clip_r;
+
+ SDL_GetClipRect (dst, &clip_r);
+ if (!clip_rect (&r, &clip_r))
+ return; // rect is completely outside clipping rectangle
+
+ // TODO: calculate destination pointer directly instead of
+ // using the plot(x,y) version
+ x1 = r.x + r.w;
+ y1 = r.y + r.h;
+ for (y = r.y; y < y1; ++y)
+ {
+ for (x = r.x; x < x1; ++x)
+ plot(dst, x, y, color, factor);
+ }
+}
+
+// clip the rectangle against the clip rectangle
+int
+clip_rect(SDL_Rect *r, const SDL_Rect *clip_r)
+{
+ // NOTE: the following clipping code is copied in part
+ // from SDL-1.2.4 sources
+ int dx, dy;
+ int w = r->w;
+ int h = r->h;
+ // SDL_Rect.w and .h are unsigned, we need signed
+
+ dx = clip_r->x - r->x;
+ if (dx > 0)
+ {
+ w -= dx;
+ r->x += dx;
+ }
+ dx = r->x + w - clip_r->x - clip_r->w;
+ if (dx > 0)
+ w -= dx;
+
+ dy = clip_r->y - r->y;
+ if (dy > 0)
+ {
+ h -= dy;
+ r->y += dy;
+ }
+ dy = r->y + h - clip_r->y - clip_r->h;
+ if (dy > 0)
+ h -= dy;
+
+ if (w <= 0 || h <= 0)
+ {
+ r->w = 0;
+ r->h = 0;
+ return 0;
+ }
+
+ r->w = w;
+ r->h = h;
+ return 1;
+}
+
+void
+blt_prim(SDL_Surface *src, SDL_Rect src_r, RenderPixelFn plot, int factor,
+ SDL_Surface *dst, SDL_Rect dst_r)
+{
+ SDL_PixelFormat *srcfmt = src->format;
+ SDL_Palette *srcpal = srcfmt->palette;
+ SDL_PixelFormat *dstfmt = dst->format;
+ Uint32 mask = 0;
+ Uint32 key = ~0;
+ GetPixelFn getpix = getpixel_for(src);
+ SDL_Rect clip_r;
+ int x, y;
+
+ SDL_GetClipRect (dst, &clip_r);
+ if (!clip_blt_rects (&src_r, &dst_r, &clip_r))
+ return; // rect is completely outside clipping rectangle
+
+ if (src_r.x >= src->w || src_r.y >= src->h)
+ return; // rect is completely outside source bounds
+
+ if (src_r.x + src_r.w > src->w)
+ src_r.w = src->w - src_r.x;
+ if (src_r.y + src_r.h > src->h)
+ src_r.h = src->h - src_r.y;
+
+ // use colorkeys where appropriate
+ if (srcfmt->Amask)
+ { // alpha transparency
+ mask = srcfmt->Amask;
+ key = 0;
+ }
+ else if (TFB_GetColorKey (src, &key) == 0)
+ {
+ mask = ~0;
+ }
+ // TODO: calculate the source and destination pointers directly
+ // instead of using the plot(x,y) version
+ for (y = 0; y < src_r.h; ++y)
+ {
+ for (x = 0; x < src_r.w; ++x)
+ {
+ Uint8 r, g, b, a;
+ Uint32 p;
+
+ p = getpix(src, src_r.x + x, src_r.y + y);
+ if (srcpal)
+ { // source is paletted, colorkey does not use mask
+ if (p == key)
+ continue; // transparent pixel
+ }
+ else
+ { // source is RGB(A), colorkey uses mask
+ if ((p & mask) == key)
+ continue; // transparent pixel
+ }
+
+ // convert pixel format to destination
+ SDL_GetRGBA(p, srcfmt, &r, &g, &b, &a);
+ // TODO: handle source pixel alpha; plot() should probably
+ // get a source alpha parameter
+ p = SDL_MapRGBA(dstfmt, r, g, b, a);
+
+ plot(dst, dst_r.x + x, dst_r.y + y, p, factor);
+ }
+ }
+}
+
+// clip the source and destination rectangles against the clip rectangle
+int
+clip_blt_rects(SDL_Rect *src_r, SDL_Rect *dst_r, const SDL_Rect *clip_r)
+{
+ // NOTE: the following clipping code is copied in part
+ // from SDL-1.2.4 sources
+ int w, h;
+ int dx, dy;
+
+ // clip the source rectangle to the source surface
+ w = src_r->w;
+ if (src_r->x < 0)
+ {
+ w += src_r->x;
+ dst_r->x -= src_r->x;
+ src_r->x = 0;
+ }
+
+ h = src_r->h;
+ if (src_r->y < 0)
+ {
+ h += src_r->y;
+ dst_r->y -= src_r->y;
+ src_r->y = 0;
+ }
+
+ // clip the destination rectangle against the clip rectangle,
+ // minding the source rectangle in the process
+ dx = clip_r->x - dst_r->x;
+ if (dx > 0)
+ {
+ w -= dx;
+ dst_r->x += dx;
+ src_r->x += dx;
+ }
+ dx = dst_r->x + w - clip_r->x - clip_r->w;
+ if (dx > 0)
+ w -= dx;
+
+ dy = clip_r->y - dst_r->y;
+ if (dy > 0)
+ {
+ h -= dy;
+ dst_r->y += dy;
+ src_r->y += dy;
+ }
+ dy = dst_r->y + h - clip_r->y - clip_r->h;
+ if (dy > 0)
+ h -= dy;
+
+ if (w <= 0 || h <= 0)
+ {
+ src_r->w = 0;
+ src_r->h = 0;
+ return 0;
+ }
+
+ src_r->w = w;
+ src_r->h = h;
+ return 1;
+}
+
diff --git a/src/libs/graphics/sdl/primitives.h b/src/libs/graphics/sdl/primitives.h
new file mode 100644
index 0000000..9d8aff2
--- /dev/null
+++ b/src/libs/graphics/sdl/primitives.h
@@ -0,0 +1,62 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef PRIMITIVES_H
+#define PRIMITIVES_H
+
+/* Function types for the pixel functions */
+
+typedef Uint32 (*GetPixelFn)(SDL_Surface *, int x, int y);
+// 'pixel' is in destination surface format
+typedef void (*PutPixelFn)(SDL_Surface *, int x, int y, Uint32 pixel);
+
+GetPixelFn getpixel_for(SDL_Surface *surface);
+PutPixelFn putpixel_for(SDL_Surface *surface);
+
+// This currently matches gfxlib.h:DrawKind for simplicity
+typedef enum
+{
+ renderReplace = 0,
+ renderAdditive,
+ renderAlpha,
+} RenderKind;
+
+#define FULLY_OPAQUE_ALPHA 255
+#define ADDITIVE_FACTOR_1 255
+
+// 'pixel' is in destination surface format
+// See gfxlib.h:DrawKind for 'factor' spec
+typedef void (*RenderPixelFn)(SDL_Surface *, int x, int y, Uint32 pixel,
+ int factor);
+
+RenderPixelFn renderpixel_for(SDL_Surface *surface, RenderKind);
+
+void line_prim(int x1, int y1, int x2, int y2, Uint32 color,
+ RenderPixelFn plot, int factor, SDL_Surface *dst);
+void fillrect_prim(SDL_Rect r, Uint32 color,
+ RenderPixelFn plot, int factor, SDL_Surface *dst);
+void blt_prim(SDL_Surface *src, SDL_Rect src_r,
+ RenderPixelFn plot, int factor,
+ SDL_Surface *dst, SDL_Rect dst_r);
+
+int clip_line(int *lx1, int *ly1, int *lx2, int *ly2, const SDL_Rect *clip_r);
+int clip_rect(SDL_Rect *r, const SDL_Rect *clip_r);
+int clip_blt_rects(SDL_Rect *src_r, SDL_Rect *dst_r, const SDL_Rect *clip_r);
+
+
+#endif /* PRIMITIVES_H */
diff --git a/src/libs/graphics/sdl/pure.c b/src/libs/graphics/sdl/pure.c
new file mode 100644
index 0000000..df4a329
--- /dev/null
+++ b/src/libs/graphics/sdl/pure.c
@@ -0,0 +1,474 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "pure.h"
+#include "libs/graphics/bbox.h"
+#include "scalers.h"
+#include "libs/log.h"
+
+#if SDL_MAJOR_VERSION == 1
+
+static SDL_Surface *SDL_Video = NULL;
+static SDL_Surface *fade_color_surface = NULL;
+static SDL_Surface *fade_temp = NULL;
+static SDL_Surface *scaled_display = NULL;
+
+static TFB_ScaleFunc scaler = NULL;
+
+static Uint32 fade_color;
+
+static void TFB_Pure_Scaled_Preprocess (int force_full_redraw, int transition_amount, int fade_amount);
+static void TFB_Pure_Scaled_Postprocess (void);
+static void TFB_Pure_Unscaled_Preprocess (int force_full_redraw, int transition_amount, int fade_amount);
+static void TFB_Pure_Unscaled_Postprocess (void);
+static void TFB_Pure_UploadTransitionScreen (void);
+static void TFB_Pure_ScreenLayer (SCREEN screen, Uint8 a, SDL_Rect *rect);
+static void TFB_Pure_ColorLayer (Uint8 r, Uint8 g, Uint8 b, Uint8 a, SDL_Rect *rect);
+
+static TFB_GRAPHICS_BACKEND pure_scaled_backend = {
+ TFB_Pure_Scaled_Preprocess,
+ TFB_Pure_Scaled_Postprocess,
+ TFB_Pure_UploadTransitionScreen,
+ TFB_Pure_ScreenLayer,
+ TFB_Pure_ColorLayer };
+
+static TFB_GRAPHICS_BACKEND pure_unscaled_backend = {
+ TFB_Pure_Unscaled_Preprocess,
+ TFB_Pure_Unscaled_Postprocess,
+ TFB_Pure_UploadTransitionScreen,
+ TFB_Pure_ScreenLayer,
+ TFB_Pure_ColorLayer };
+
+// We cannot rely on SDL_DisplayFormatAlpha() anymore. It can return
+// formats that we do not expect (SDL v1.2.14 on Mac OSX). Mac likes
+// ARGB surfaces, but SDL_DisplayFormatAlpha thinks that only RGBA are fast.
+// This is a generic replacement that gives what we want.
+static void
+CalcAlphaFormat (const SDL_PixelFormat* video, SDL_PixelFormat* ours)
+{
+ int valid = 0;
+
+ // We use 32-bit surfaces internally
+ ours->BitsPerPixel = 32;
+
+ // Try to get as close to the video format as possible
+ if (video->BitsPerPixel == 15 || video->BitsPerPixel == 16)
+ { // At least match the channel order
+ ours->Rshift = video->Rshift / 5 * 8;
+ ours->Gshift = video->Gshift / 5 * 8;
+ ours->Bshift = video->Bshift / 5 * 8;
+ valid = 1;
+ }
+ else if (video->BitsPerPixel == 24 || video->BitsPerPixel == 32)
+ {
+ // We can only use channels aligned on byte boundary
+ if (video->Rshift % 8 == 0 && video->Gshift % 8 == 0
+ && video->Bshift % 8 == 0)
+ { // Match RGB in video
+ ours->Rshift = video->Rshift;
+ ours->Gshift = video->Gshift;
+ ours->Bshift = video->Bshift;
+ valid = 1;
+ }
+ }
+
+ if (valid)
+ { // For alpha, use the unoccupied byte
+ ours->Ashift = 48 - (ours->Rshift + ours->Gshift + ours->Bshift);
+ // Set channels according to byte positions
+ ours->Rmask = 0xff << ours->Rshift;
+ ours->Gmask = 0xff << ours->Gshift;
+ ours->Bmask = 0xff << ours->Bshift;
+ ours->Amask = 0xff << ours->Ashift;
+ return;
+ }
+
+ // Fallback case. It does not matter what we set, but SDL likes
+ // Alpha to be the highest.
+ ours->Rmask = 0x000000ff;
+ ours->Gmask = 0x0000ff00;
+ ours->Bmask = 0x00ff0000;
+ ours->Amask = 0xff000000;
+}
+
+int
+TFB_Pure_ConfigureVideo (int driver, int flags, int width, int height, int togglefullscreen)
+{
+ int i, videomode_flags;
+ SDL_PixelFormat conv_fmt;
+
+ GraphicsDriver = driver;
+
+ // must use SDL_SWSURFACE, HWSURFACE doesn't work properly
+ // with fades/scaling
+ if (width == 320 && height == 240)
+ {
+ videomode_flags = SDL_SWSURFACE;
+ ScreenWidthActual = 320;
+ ScreenHeightActual = 240;
+ graphics_backend = &pure_unscaled_backend;
+ }
+ else
+ {
+ videomode_flags = SDL_SWSURFACE;
+ ScreenWidthActual = 640;
+ ScreenHeightActual = 480;
+ graphics_backend = &pure_scaled_backend;
+
+ if (width != 640 || height != 480)
+ log_add (log_Error, "Screen resolution of %dx%d not supported "
+ "under pure SDL, using 640x480", width, height);
+ }
+
+ videomode_flags |= SDL_ANYFORMAT;
+ if (flags & TFB_GFXFLAGS_FULLSCREEN)
+ videomode_flags |= SDL_FULLSCREEN;
+
+ /* We'll ask for a 32bpp frame, but it doesn't really matter, because we've set
+ SDL_ANYFORMAT */
+ SDL_Video = SDL_SetVideoMode (ScreenWidthActual, ScreenHeightActual,
+ 32, videomode_flags);
+
+ if (SDL_Video == NULL)
+ {
+ log_add (log_Error, "Couldn't set %ix%i video mode: %s",
+ ScreenWidthActual, ScreenHeightActual,
+ SDL_GetError ());
+ return -1;
+ }
+ else
+ {
+ const SDL_Surface *video = SDL_GetVideoSurface ();
+ const SDL_PixelFormat* fmt = video->format;
+
+ ScreenColorDepth = fmt->BitsPerPixel;
+ log_add (log_Info, "Set the resolution to: %ix%ix%i",
+ video->w, video->h, ScreenColorDepth);
+ log_add (log_Info, " Video: R %08x, G %08x, B %08x, A %08x",
+ fmt->Rmask, fmt->Gmask, fmt->Bmask, fmt->Amask);
+
+ if (togglefullscreen)
+ {
+ // NOTE: We cannot change the format_conv_surf now because we
+ // have already loaded lots of graphics and changing it now
+ // will only lead to chaos.
+ // Just check if channel order has changed significantly
+ CalcAlphaFormat (fmt, &conv_fmt);
+ fmt = format_conv_surf->format;
+ if (conv_fmt.Rmask != fmt->Rmask || conv_fmt.Bmask != fmt->Bmask)
+ log_add (log_Warning, "Warning: pixel format has changed "
+ "significantly. Rendering will be slow.");
+ return 0;
+ }
+ }
+
+ // Create a 32bpp surface in a compatible format which will supply
+ // the format information to all other surfaces used in the game
+ if (format_conv_surf)
+ {
+ SDL_FreeSurface (format_conv_surf);
+ format_conv_surf = NULL;
+ }
+ CalcAlphaFormat (SDL_Video->format, &conv_fmt);
+ format_conv_surf = SDL_CreateRGBSurface (SDL_SWSURFACE, 0, 0,
+ conv_fmt.BitsPerPixel, conv_fmt.Rmask, conv_fmt.Gmask,
+ conv_fmt.Bmask, conv_fmt.Amask);
+ if (!format_conv_surf)
+ {
+ log_add (log_Error, "Couldn't create format_conv_surf: %s",
+ SDL_GetError());
+ return -1;
+ }
+ else
+ {
+ const SDL_PixelFormat* fmt = format_conv_surf->format;
+ log_add (log_Info, " Internal: R %08x, G %08x, B %08x, A %08x",
+ fmt->Rmask, fmt->Gmask, fmt->Bmask, fmt->Amask);
+ }
+
+ for (i = 0; i < TFB_GFX_NUMSCREENS; i++)
+ {
+ if (0 != SDL1_ReInit_Screen (&SDL_Screens[i], format_conv_surf,
+ ScreenWidth, ScreenHeight))
+ return -1;
+ }
+
+ SDL_Screen = SDL_Screens[0];
+ TransitionScreen = SDL_Screens[2];
+
+ if (0 != SDL1_ReInit_Screen (&fade_color_surface, format_conv_surf,
+ ScreenWidth, ScreenHeight))
+ return -1;
+ fade_color = SDL_MapRGB (fade_color_surface->format, 0, 0, 0);
+ SDL_FillRect (fade_color_surface, NULL, fade_color);
+
+ if (0 != SDL1_ReInit_Screen (&fade_temp, format_conv_surf,
+ ScreenWidth, ScreenHeight))
+ return -1;
+
+ if (ScreenWidthActual > ScreenWidth || ScreenHeightActual > ScreenHeight)
+ {
+ if (0 != SDL1_ReInit_Screen (&scaled_display, format_conv_surf,
+ ScreenWidthActual, ScreenHeightActual))
+ return -1;
+
+ scaler = Scale_PrepPlatform (flags, SDL_Screen->format);
+ }
+ else
+ { // no need to scale
+ scaler = NULL;
+ }
+
+ return 0;
+}
+
+int
+TFB_Pure_InitGraphics (int driver, int flags, const char *renderer, int width, int height)
+{
+ char VideoName[256];
+
+ log_add (log_Info, "Initializing Pure-SDL graphics.");
+
+ SDL_VideoDriverName (VideoName, sizeof (VideoName));
+ log_add (log_Info, "SDL driver used: %s", VideoName);
+ (void) renderer;
+ // The "renderer" argument is ignored by SDL1. To control how SDL1
+ // gets its pixmap, set the environment variable SDL_VIDEODRIVER.
+ // For Linux: x11 (default), dga, fbcon, directfb, svgalib,
+ // ggi, aalib
+ // For Windows: directx (default), windib
+
+ log_add (log_Info, "SDL initialized.");
+ log_add (log_Info, "Initializing Screen.");
+
+ ScreenWidth = 320;
+ ScreenHeight = 240;
+
+ if (TFB_Pure_ConfigureVideo (driver, flags, width, height, 0))
+ {
+ log_add (log_Fatal, "Could not initialize video: "
+ "no fallback at start of program!");
+ exit (EXIT_FAILURE);
+ }
+
+ // Initialize scalers (let them precompute whatever)
+ Scale_Init ();
+
+ return 0;
+}
+
+void
+TFB_Pure_UninitGraphics (void)
+{
+ UnInit_Screen (&scaled_display);
+ UnInit_Screen (&fade_color_surface);
+ UnInit_Screen (&fade_temp);
+}
+
+static void
+ScanLines (SDL_Surface *dst, SDL_Rect *r)
+{
+ const int rw = r->w * 2;
+ const int rh = r->h * 2;
+ SDL_PixelFormat *fmt = dst->format;
+ const int pitch = dst->pitch;
+ const int len = pitch / fmt->BytesPerPixel;
+ int ddst;
+ Uint32 *p = (Uint32 *) dst->pixels;
+ int x, y;
+
+ p += len * (r->y * 2) + (r->x * 2);
+ ddst = len + len - rw;
+
+ for (y = rh; y; y -= 2, p += ddst)
+ {
+ for (x = rw; x; --x, ++p)
+ {
+ // we ignore the lower bits as the difference
+ // of 1 in 255 is negligible
+ *p = ((*p >> 1) & 0x7f7f7f7f) + ((*p >> 2) & 0x3f3f3f3f);
+ }
+ }
+}
+
+static SDL_Surface *backbuffer = NULL, *scalebuffer = NULL;
+static SDL_Rect updated;
+
+static void
+TFB_Pure_Scaled_Preprocess (int force_full_redraw, int transition_amount, int fade_amount)
+{
+ if (force_full_redraw != TFB_REDRAW_NO)
+ {
+ updated.x = updated.y = 0;
+ updated.w = ScreenWidth;
+ updated.h = ScreenHeight;
+ }
+ else
+ {
+ updated.x = TFB_BBox.region.corner.x;
+ updated.y = TFB_BBox.region.corner.y;
+ updated.w = TFB_BBox.region.extent.width;
+ updated.h = TFB_BBox.region.extent.height;
+ }
+
+ if (transition_amount == 255 && fade_amount == 255)
+ backbuffer = SDL_Screens[TFB_SCREEN_MAIN];
+ else
+ backbuffer = fade_temp;
+
+ // we can scale directly onto SDL_Video if video is compatible
+ if (SDL_Video->format->BitsPerPixel == SDL_Screen->format->BitsPerPixel
+ && SDL_Video->format->Rmask == SDL_Screen->format->Rmask
+ && SDL_Video->format->Bmask == SDL_Screen->format->Bmask)
+ scalebuffer = SDL_Video;
+ else
+ scalebuffer = scaled_display;
+
+}
+
+static void
+TFB_Pure_Unscaled_Preprocess (int force_full_redraw, int transition_amount, int fade_amount)
+{
+ if (force_full_redraw != TFB_REDRAW_NO)
+ {
+ updated.x = updated.y = 0;
+ updated.w = ScreenWidth;
+ updated.h = ScreenHeight;
+ }
+ else
+ {
+ updated.x = TFB_BBox.region.corner.x;
+ updated.y = TFB_BBox.region.corner.y;
+ updated.w = TFB_BBox.region.extent.width;
+ updated.h = TFB_BBox.region.extent.height;
+ }
+
+ backbuffer = SDL_Video;
+ (void)transition_amount;
+ (void)fade_amount;
+}
+
+static void
+TFB_Pure_Scaled_Postprocess (void)
+{
+ SDL_LockSurface (scalebuffer);
+ SDL_LockSurface (backbuffer);
+
+ if (scaler)
+ scaler (backbuffer, scalebuffer, &updated);
+
+ if (GfxFlags & TFB_GFXFLAGS_SCANLINES)
+ ScanLines (scalebuffer, &updated);
+
+ SDL_UnlockSurface (backbuffer);
+ SDL_UnlockSurface (scalebuffer);
+
+ updated.x *= 2;
+ updated.y *= 2;
+ updated.w *= 2;
+ updated.h *= 2;
+ if (scalebuffer != SDL_Video)
+ SDL_BlitSurface (scalebuffer, &updated, SDL_Video, &updated);
+
+ SDL_UpdateRects (SDL_Video, 1, &updated);
+}
+
+static void
+TFB_Pure_Unscaled_Postprocess (void)
+{
+ SDL_UpdateRect (SDL_Video, updated.x, updated.y,
+ updated.w, updated.h);
+}
+
+static void
+TFB_Pure_UploadTransitionScreen (void)
+{
+ /* This is a no-op in SDL1 Pure mode */
+}
+
+static void
+TFB_Pure_ScreenLayer (SCREEN screen, Uint8 a, SDL_Rect *rect)
+{
+ if (SDL_Screens[screen] == backbuffer)
+ return;
+ SDL_SetAlpha (SDL_Screens[screen], SDL_SRCALPHA, a);
+ SDL_BlitSurface (SDL_Screens[screen], rect, backbuffer, rect);
+}
+
+static void
+TFB_Pure_ColorLayer (Uint8 r, Uint8 g, Uint8 b, Uint8 a, SDL_Rect *rect)
+{
+ Uint32 col = SDL_MapRGB (fade_color_surface->format, r, g, b);
+ if (col != fade_color)
+ {
+ fade_color = col;
+ SDL_FillRect (fade_color_surface, NULL, fade_color);
+ }
+ SDL_SetAlpha (fade_color_surface, SDL_SRCALPHA, a);
+ SDL_BlitSurface (fade_color_surface, rect, backbuffer, rect);
+}
+
+void
+Scale_PerfTest (void)
+{
+ TimeCount TimeStart, TimeIn;
+ TimeCount Now = 0;
+ SDL_Rect updated = {0, 0, ScreenWidth, ScreenHeight};
+ int i;
+
+ if (!scaler)
+ {
+ log_add (log_Error, "No scaler configured! "
+ "Run with larger resolution, please");
+ return;
+ }
+ if (!scaled_display)
+ {
+ log_add (log_Error, "Run scaler performance tests "
+ "in Pure mode, please");
+ return;
+ }
+
+ SDL_LockSurface (SDL_Screen);
+ SDL_LockSurface (scaled_display);
+
+ TimeStart = TimeIn = SDL_GetTicks ();
+
+ for (i = 1; i < 1001; ++i) // run for 1000 frames
+ {
+ scaler (SDL_Screen, scaled_display, &updated);
+
+ if (GfxFlags & TFB_GFXFLAGS_SCANLINES)
+ ScanLines (scaled_display, &updated);
+
+ if (i % 100 == 0)
+ {
+ Now = SDL_GetTicks ();
+ log_add (log_Debug, "%03d(%04u) ", 100*1000 / (Now - TimeIn),
+ Now - TimeIn);
+ TimeIn = Now;
+ }
+ }
+
+ log_add (log_Debug, "Full frames scaled: %d; over %u ms; %d fps\n",
+ (i - 1), Now - TimeStart, i * 1000 / (Now - TimeStart));
+
+ SDL_UnlockSurface (scaled_display);
+ SDL_UnlockSurface (SDL_Screen);
+}
+
+#endif
diff --git a/src/libs/graphics/sdl/pure.h b/src/libs/graphics/sdl/pure.h
new file mode 100644
index 0000000..50cf06f
--- /dev/null
+++ b/src/libs/graphics/sdl/pure.h
@@ -0,0 +1,29 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef PURE_H
+#define PURE_H
+
+#include "libs/graphics/sdl/sdl_common.h"
+
+int TFB_Pure_InitGraphics (int driver, int flags, const char *renderer, int width, int height);
+void TFB_Pure_UninitGraphics (void);
+int TFB_Pure_ConfigureVideo (int driver, int flags, int width, int height, int togglefullscreen);
+void Scale_PerfTest (void);
+
+#endif
diff --git a/src/libs/graphics/sdl/rotozoom.c b/src/libs/graphics/sdl/rotozoom.c
new file mode 100644
index 0000000..9f22769
--- /dev/null
+++ b/src/libs/graphics/sdl/rotozoom.c
@@ -0,0 +1,1038 @@
+/*
+
+ rotozoom.c - rotozoomer for 32bit or 8bit surfaces
+ LGPL (c) A. Schiffler
+
+ Note by sc2 developers:
+ Taken from SDL_gfx library and modified, original code can be downloaded
+ from http://www.ferzkopp.net/Software/SDL_gfx-2.0/
+
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include "sdl_common.h"
+#include "libs/memlib.h"
+#include "port.h"
+#include "rotozoom.h"
+
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+
+
+
+/*
+
+ 32bit Zoomer with optional anti-aliasing by bilinear interpolation.
+
+ Zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
+
+*/
+
+int zoomSurfaceRGBA(SDL_Surface * src, SDL_Surface * dst, int smooth)
+{
+ int x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy, ex, ey, t1, t2, sstep;
+ tColorRGBA *c00, *c01, *c10, *c11;
+ tColorRGBA *sp, *csp, *dp;
+ int sgap, dgap;
+
+ /*
+ * Variable setup
+ */
+ if (smooth) {
+ /*
+ * For interpolation: assume source dimension is one pixel
+ */
+ /*
+ * smaller to avoid overflow on right and bottom edge.
+ */
+ sx = (int) (65536.0 * (float) (src->w - 1) / (float) dst->w);
+ sy = (int) (65536.0 * (float) (src->h - 1) / (float) dst->h);
+ } else {
+ sx = (int) (65536.0 * (float) src->w / (float) dst->w);
+ sy = (int) (65536.0 * (float) src->h / (float) dst->h);
+ }
+
+ /*
+ * Allocate memory for row increments
+ */
+
+#ifndef __SYMBIAN32__
+ if ((sax = (int *) alloca((dst->w + 1) * sizeof(Uint32))) == NULL)
+ return (-1);
+ if ((say = (int *) alloca((dst->h + 1) * sizeof(Uint32))) == NULL)
+ return (-1);
+#else
+ if ((sax = (int *) HMalloc((dst->w + 1) * sizeof(Uint32))) == NULL)
+ return (-1);
+ if ((say = (int *) HMalloc((dst->h + 1) * sizeof(Uint32))) == NULL)
+ {
+ HFree(sax);
+ return (-1);
+ }
+#endif
+
+
+ /*
+ * Precalculate row increments
+ */
+ csx = 0;
+ csax = sax;
+ for (x = 0; x <= dst->w; x++) {
+ *csax = csx;
+ csax++;
+ csx &= 0xffff;
+ csx += sx;
+ }
+ csy = 0;
+ csay = say;
+ for (y = 0; y <= dst->h; y++) {
+ *csay = csy;
+ csay++;
+ csy &= 0xffff;
+ csy += sy;
+ }
+
+ /*
+ * Pointer setup
+ */
+ sp = csp = (tColorRGBA *) src->pixels;
+ dp = (tColorRGBA *) dst->pixels;
+ sgap = src->pitch - src->w * 4;
+ dgap = dst->pitch - dst->w * 4;
+
+ /*
+ * Switch between interpolating and non-interpolating code
+ */
+ if (smooth) {
+
+ /*
+ * Interpolating Zoom
+ */
+
+ /*
+ * Scan destination
+ */
+ csay = say;
+ for (y = 0; y < dst->h; y++) {
+ /*
+ * Setup color source pointers
+ */
+ c00 = csp;
+ c01 = csp;
+ c01++;
+ c10 = (tColorRGBA *) ((Uint8 *) csp + src->pitch);
+ c11 = c10;
+ c11++;
+ csax = sax;
+ for (x = 0; x < dst->w; x++) {
+
+ /*
+ * Interpolate colors
+ */
+ ex = (*csax & 0xffff);
+ ey = (*csay & 0xffff);
+ t1 = ((((c01->r - c00->r) * ex) >> 16) + c00->r) & 0xff;
+ t2 = ((((c11->r - c10->r) * ex) >> 16) + c10->r) & 0xff;
+ dp->r = (((t2 - t1) * ey) >> 16) + t1;
+ t1 = ((((c01->g - c00->g) * ex) >> 16) + c00->g) & 0xff;
+ t2 = ((((c11->g - c10->g) * ex) >> 16) + c10->g) & 0xff;
+ dp->g = (((t2 - t1) * ey) >> 16) + t1;
+ t1 = ((((c01->b - c00->b) * ex) >> 16) + c00->b) & 0xff;
+ t2 = ((((c11->b - c10->b) * ex) >> 16) + c10->b) & 0xff;
+ dp->b = (((t2 - t1) * ey) >> 16) + t1;
+ t1 = ((((c01->a - c00->a) * ex) >> 16) + c00->a) & 0xff;
+ t2 = ((((c11->a - c10->a) * ex) >> 16) + c10->a) & 0xff;
+ dp->a = (((t2 - t1) * ey) >> 16) + t1;
+
+ /*
+ * Advance source pointers
+ */
+ csax++;
+ sstep = (*csax >> 16);
+ c00 += sstep;
+ c01 += sstep;
+ c10 += sstep;
+ c11 += sstep;
+ /*
+ * Advance destination pointer
+ */
+ dp++;
+ }
+ /*
+ * Advance source pointer
+ */
+ csay++;
+ csp = (tColorRGBA *) ((Uint8 *) csp + (*csay >> 16) * src->pitch);
+ /*
+ * Advance destination pointers
+ */
+ dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
+ }
+
+ } else {
+
+ /*
+ * Non-Interpolating Zoom
+ */
+
+ csay = say;
+ for (y = 0; y < dst->h; y++) {
+ sp = csp;
+ csax = sax;
+ for (x = 0; x < dst->w; x++) {
+ /*
+ * Draw
+ */
+ *dp = *sp;
+ /*
+ * Advance source pointers
+ */
+ csax++;
+ sp += (*csax >> 16);
+ /*
+ * Advance destination pointer
+ */
+ dp++;
+ }
+ /*
+ * Advance source pointer
+ */
+ csay++;
+ csp = (tColorRGBA *) ((Uint8 *) csp + (*csay >> 16) * src->pitch);
+ /*
+ * Advance destination pointers
+ */
+ dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
+ }
+
+ }
+
+#ifdef __SYMBIAN32__
+ HFree(sax);
+ HFree(say);
+#endif
+
+ return (0);
+}
+
+/*
+
+ 8bit Zoomer without smoothing.
+
+ Zoomes 8bit palette/Y 'src' surface to 'dst' surface.
+
+*/
+
+static
+int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
+{
+ Uint32 sx, sy, *sax, *say, *csax, *csay, csx, csy;
+ int x, y;
+ Uint8 *sp, *dp, *csp;
+ int dgap;
+
+ /*
+ * Variable setup
+ */
+ sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
+ sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
+
+ /*
+ * Allocate memory for row increments
+ */
+#ifndef __SYMBIAN32__
+ if ((sax = (Uint32 *) alloca(dst->w * sizeof(Uint32))) == NULL)
+ return (-1);
+ if ((say = (Uint32 *) alloca(dst->h * sizeof(Uint32))) == NULL)
+ return (-1);
+#else
+ if ((sax = (Uint32 *) HMalloc(dst->w * sizeof(Uint32))) == NULL)
+ return (-1);
+ if ((say = (Uint32 *) HMalloc(dst->h * sizeof(Uint32))) == NULL)
+ {
+ HFree(sax);
+ return (-1);
+ }
+#endif
+
+ /*
+ * Precalculate row increments
+ */
+ csx = 0;
+ csax = sax;
+ for (x = 0; x < dst->w; x++) {
+ csx += sx;
+ *csax = (csx >> 16);
+ csx &= 0xffff;
+ csax++;
+ }
+ csy = 0;
+ csay = say;
+ for (y = 0; y < dst->h; y++) {
+ csy += sy;
+ *csay = (csy >> 16);
+ csy &= 0xffff;
+ csay++;
+ }
+
+ csx = 0;
+ csax = sax;
+ for (x = 0; x < dst->w; x++) {
+ csx += (*csax);
+ csax++;
+ }
+ csy = 0;
+ csay = say;
+ for (y = 0; y < dst->h; y++) {
+ csy += (*csay);
+ csay++;
+ }
+
+ /*
+ * Pointer setup
+ */
+ sp = csp = (Uint8 *) src->pixels;
+ dp = (Uint8 *) dst->pixels;
+ dgap = dst->pitch - dst->w;
+
+ /*
+ * Draw
+ */
+ csay = say;
+ for (y = 0; y < dst->h; y++) {
+ csax = sax;
+ sp = csp;
+ for (x = 0; x < dst->w; x++) {
+ /*
+ * Draw
+ */
+ *dp = *sp;
+ /*
+ * Advance source pointers
+ */
+ sp += (*csax);
+ csax++;
+ /*
+ * Advance destination pointer
+ */
+ dp++;
+ }
+ /*
+ * Advance source pointer (for row)
+ */
+ csp += ((*csay) * src->pitch);
+ csay++;
+ /*
+ * Advance destination pointers
+ */
+ dp += dgap;
+ }
+
+#ifdef __SYMBIAN32__
+ HFree(sax);
+ HFree(say);
+#endif
+
+ return (0);
+}
+
+/*
+
+ 32bit Rotozoomer with optional anti-aliasing by bilinear interpolation.
+
+ Rotates and zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
+
+*/
+
+static
+void transformSurfaceRGBA(SDL_Surface * src, SDL_Surface * dst, int cx, int cy, int isin, int icos, int smooth)
+{
+ int x, y, t1, t2, dx, dy, xd, yd, sdx, sdy, ax, ay, ex, ey, sw, sh;
+ tColorRGBA c00, c01, c10, c11;
+ tColorRGBA *pc, *sp;
+ int gap;
+
+ /*
+ * Variable setup
+ */
+ xd = ((src->w - dst->w) << 15);
+ yd = ((src->h - dst->h) << 15);
+ ax = (cx << 16) - (icos * cx);
+ ay = (cy << 16) - (isin * cx);
+ sw = src->w - 1;
+ sh = src->h - 1;
+ pc = dst->pixels;
+ gap = dst->pitch - dst->w * 4;
+
+ /*
+ * Switch between interpolating and non-interpolating code
+ */
+ if (smooth) {
+ for (y = 0; y < dst->h; y++) {
+ dy = cy - y;
+ sdx = (ax + (isin * dy)) + xd;
+ sdy = (ay - (icos * dy)) + yd;
+ for (x = 0; x < dst->w; x++) {
+ dx = (sdx >> 16);
+ dy = (sdy >> 16);
+ if ((dx >= -1) && (dy >= -1) && (dx < src->w) && (dy < src->h)) {
+ if ((dx >= 0) && (dy >= 0) && (dx < sw) && (dy < sh)) {
+ sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy);
+ sp += dx;
+ c00 = *sp;
+ sp += 1;
+ c01 = *sp;
+ sp = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
+ sp -= 1;
+ c10 = *sp;
+ sp += 1;
+ c11 = *sp;
+ } else if ((dx == sw) && (dy == sh)) {
+ sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy);
+ sp += dx;
+ c00 = *sp;
+ c01 = *sp;
+ c10 = *sp;
+ c11 = *sp;
+ } else if ((dx == -1) && (dy == -1)) {
+ sp = (tColorRGBA *) (src->pixels);
+ c00 = *sp;
+ c01 = *sp;
+ c10 = *sp;
+ c11 = *sp;
+ } else if ((dx == -1) && (dy == sh)) {
+ sp = (tColorRGBA *) (src->pixels);
+ sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy);
+ c00 = *sp;
+ c01 = *sp;
+ c10 = *sp;
+ c11 = *sp;
+ } else if ((dx == sw) && (dy == -1)) {
+ sp = (tColorRGBA *) (src->pixels);
+ sp += dx;
+ c00 = *sp;
+ c01 = *sp;
+ c10 = *sp;
+ c11 = *sp;
+ } else if (dx == -1) {
+ sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy);
+ c00 = *sp;
+ c01 = *sp;
+ c10 = *sp;
+ sp = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
+ c11 = *sp;
+ } else if (dy == -1) {
+ sp = (tColorRGBA *) (src->pixels);
+ sp += dx;
+ c00 = *sp;
+ c01 = *sp;
+ c10 = *sp;
+ sp += 1;
+ c11 = *sp;
+ } else if (dx == sw) {
+ sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy);
+ sp += dx;
+ c00 = *sp;
+ c01 = *sp;
+ sp = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
+ c10 = *sp;
+ c11 = *sp;
+ } else if (dy == sh) {
+ sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy);
+ sp += dx;
+ c00 = *sp;
+ sp += 1;
+ c01 = *sp;
+ c10 = *sp;
+ c11 = *sp;
+ }
+ /*
+ * Interpolate colors
+ */
+ ex = (sdx & 0xffff);
+ ey = (sdy & 0xffff);
+ t1 = ((((c01.r - c00.r) * ex) >> 16) + c00.r) & 0xff;
+ t2 = ((((c11.r - c10.r) * ex) >> 16) + c10.r) & 0xff;
+ pc->r = (((t2 - t1) * ey) >> 16) + t1;
+ t1 = ((((c01.g - c00.g) * ex) >> 16) + c00.g) & 0xff;
+ t2 = ((((c11.g - c10.g) * ex) >> 16) + c10.g) & 0xff;
+ pc->g = (((t2 - t1) * ey) >> 16) + t1;
+ t1 = ((((c01.b - c00.b) * ex) >> 16) + c00.b) & 0xff;
+ t2 = ((((c11.b - c10.b) * ex) >> 16) + c10.b) & 0xff;
+ pc->b = (((t2 - t1) * ey) >> 16) + t1;
+ t1 = ((((c01.a - c00.a) * ex) >> 16) + c00.a) & 0xff;
+ t2 = ((((c11.a - c10.a) * ex) >> 16) + c10.a) & 0xff;
+ pc->a = (((t2 - t1) * ey) >> 16) + t1;
+ }
+ sdx += icos;
+ sdy += isin;
+ pc++;
+ }
+ pc = (tColorRGBA *) ((Uint8 *) pc + gap);
+ }
+ } else {
+ for (y = 0; y < dst->h; y++) {
+ dy = cy - y;
+ sdx = (ax + (isin * dy)) + xd;
+ sdy = (ay - (icos * dy)) + yd;
+ for (x = 0; x < dst->w; x++) {
+ dx = (short) (sdx >> 16);
+ dy = (short) (sdy >> 16);
+ if ((dx >= 0) && (dy >= 0) && (dx < src->w) && (dy < src->h)) {
+ sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy);
+ sp += dx;
+ *pc = *sp;
+ }
+ sdx += icos;
+ sdy += isin;
+ pc++;
+ }
+ pc = (tColorRGBA *) ((Uint8 *) pc + gap);
+ }
+ }
+}
+
+/*
+
+ 8bit Rotozoomer without smoothing
+
+ Rotates and zoomes 8bit palette/Y 'src' surface to 'dst' surface.
+
+*/
+
+static
+void transformSurfaceY(SDL_Surface * src, SDL_Surface * dst, int cx, int cy, int isin, int icos)
+{
+ int x, y, dx, dy, xd, yd, sdx, sdy, ax, ay, sw, sh;
+ tColorY *pc, *sp;
+ int gap;
+ Uint32 colorkey = 0;
+
+ /*
+ * Variable setup
+ */
+ xd = ((src->w - dst->w) << 15);
+ yd = ((src->h - dst->h) << 15);
+ ax = (cx << 16) - (icos * cx);
+ ay = (cy << 16) - (isin * cx);
+ sw = src->w - 1;
+ sh = src->h - 1;
+ pc = dst->pixels;
+ gap = dst->pitch - dst->w;
+ /*
+ * Clear surface to colorkey
+ */
+ TFB_GetColorKey (src, &colorkey);
+ memset(pc, (unsigned char) (colorkey & 0xff), dst->pitch * dst->h);
+ /*
+ * Iterate through destination surface
+ */
+ for (y = 0; y < dst->h; y++) {
+ dy = cy - y;
+ sdx = (ax + (isin * dy)) + xd;
+ sdy = (ay - (icos * dy)) + yd;
+ for (x = 0; x < dst->w; x++) {
+ dx = (short) (sdx >> 16);
+ dy = (short) (sdy >> 16);
+ if ((dx >= 0) && (dy >= 0) && (dx < src->w) && (dy < src->h)) {
+ sp = (tColorY *) (src->pixels);
+ sp += (src->pitch * dy + dx);
+ *pc = *sp;
+ }
+ sdx += icos;
+ sdy += isin;
+ pc++;
+ }
+ pc += gap;
+ }
+}
+
+/*
+
+ rotozoomSurface()
+
+ Rotates and zoomes a 32bit or 8bit 'src' surface to newly created 'dst' surface.
+ 'angle' is the rotation in degrees. 'zoom' a scaling factor. If 'smooth' is 1
+ then the destination 32bit surface is anti-aliased. If the surface is not 8bit
+ or 32bit RGBA/ABGR it will be converted into a 32bit RGBA format on the fly.
+
+*/
+
+#define VALUE_LIMIT 0.001
+
+
+/* Local rotozoom-size function with trig result return */
+
+static
+void rotozoomSurfaceSizeTrig(int width, int height, double angle, double zoom, int *dstwidth, int *dstheight,
+ double *canglezoom, double *sanglezoom)
+{
+ double x, y, cx, cy, sx, sy;
+ double radangle;
+ int dstwidthhalf, dstheighthalf;
+
+ /*
+ * Determine destination width and height by rotating a centered source box
+ */
+ radangle = angle * (M_PI / 180.0);
+ *sanglezoom = sin(radangle);
+ *canglezoom = cos(radangle);
+ *sanglezoom *= zoom;
+ *canglezoom *= zoom;
+ x = width / 2;
+ y = height / 2;
+ cx = *canglezoom * x;
+ cy = *canglezoom * y;
+ sx = *sanglezoom * x;
+ sy = *sanglezoom * y;
+ dstwidthhalf = MAX(ceil(fabs(cx) + fabs(sy)), 1);
+ dstheighthalf = MAX(ceil(fabs(sx) + fabs(cy)), 1);
+ *dstwidth = 2 * dstwidthhalf;
+ *dstheight = 2 * dstheighthalf;
+}
+
+
+/* Publically available rotozoom-size function */
+
+void rotozoomSurfaceSize(int width, int height, double angle, double zoom, int *dstwidth, int *dstheight)
+{
+ double dummy_sanglezoom, dummy_canglezoom;
+
+ rotozoomSurfaceSizeTrig(width, height, angle, zoom, dstwidth, dstheight, &dummy_sanglezoom, &dummy_canglezoom);
+}
+
+
+/* Publically available rotozoom function */
+
+SDL_Surface *rotozoomSurface(SDL_Surface * src, double angle, double zoom, int smooth)
+{
+ SDL_Surface *rz_src;
+ SDL_Surface *rz_dst;
+ double zoominv;
+ double sanglezoom, canglezoom, sanglezoominv, canglezoominv;
+ int dstwidthhalf, dstwidth, dstheighthalf, dstheight;
+ int is32bit;
+ int i, src_converted;
+
+ /*
+ * Sanity check
+ */
+ if (src == NULL)
+ return (NULL);
+
+ /*
+ * Determine if source surface is 32bit or 8bit
+ */
+ is32bit = (src->format->BitsPerPixel == 32);
+ if ((is32bit) || (src->format->BitsPerPixel == 8)) {
+ /*
+ * Use source surface 'as is'
+ */
+ rz_src = src;
+ src_converted = 0;
+ } else {
+ /*
+ * New source surface is 32bit with a defined RGBA ordering
+ */
+ rz_src =
+ SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
+ SDL_BlitSurface(src, NULL, rz_src, NULL);
+ src_converted = 1;
+ is32bit = 1;
+ }
+
+ /*
+ * Sanity check zoom factor
+ */
+ if (zoom < VALUE_LIMIT) {
+ zoom = VALUE_LIMIT;
+ }
+ zoominv = 65536.0 / (zoom * zoom);
+
+ /*
+ * Check if we have a rotozoom or just a zoom
+ */
+ if (fabs(angle) > VALUE_LIMIT) {
+
+ /*
+ * Angle!=0: full rotozoom
+ */
+ /*
+ * -----------------------
+ */
+
+ /* Determine target size */
+ rotozoomSurfaceSizeTrig(rz_src->w, rz_src->h, angle, zoom, &dstwidth, &dstheight, &canglezoom, &sanglezoom);
+
+ /*
+ * Calculate target factors from sin/cos and zoom
+ */
+ sanglezoominv = sanglezoom;
+ canglezoominv = canglezoom;
+ sanglezoominv *= zoominv;
+ canglezoominv *= zoominv;
+
+ /* Calculate half size */
+ dstwidthhalf = dstwidth / 2;
+ dstheighthalf = dstheight / 2;
+
+ /*
+ * Alloc space to completely contain the rotated surface
+ */
+ rz_dst = NULL;
+ if (is32bit) {
+ /*
+ * Target surface is 32bit with source RGBA/ABGR ordering
+ */
+ rz_dst =
+ SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 32,
+ rz_src->format->Rmask, rz_src->format->Gmask,
+ rz_src->format->Bmask, rz_src->format->Amask);
+ } else {
+ /*
+ * Target surface is 8bit
+ */
+ rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 8, 0, 0, 0, 0);
+ }
+
+ /*
+ * Lock source surface
+ */
+ SDL_LockSurface(rz_src);
+ /*
+ * Check which kind of surface we have
+ */
+ if (is32bit) {
+ /*
+ * Call the 32bit transformation routine to do the rotation (using alpha)
+ */
+ transformSurfaceRGBA(rz_src, rz_dst, dstwidthhalf, dstheighthalf,
+ (int) (sanglezoominv), (int) (canglezoominv), smooth);
+ /*
+ * Turn on source-alpha support
+ */
+ TFB_SetSurfaceAlphaMod (rz_dst, 255);
+ } else {
+ /*
+ * Copy palette and colorkey info
+ */
+ Uint32 srckey = 0;
+ TFB_GetColorKey (rz_src, &srckey);
+ for (i = 0; i < rz_src->format->palette->ncolors; i++) {
+ rz_dst->format->palette->colors[i] = rz_src->format->palette->colors[i];
+ }
+ rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors;
+ /*
+ * Call the 8bit transformation routine to do the rotation
+ */
+ transformSurfaceY(rz_src, rz_dst, dstwidthhalf, dstheighthalf,
+ (int) (sanglezoominv), (int) (canglezoominv));
+ TFB_SetColorKey(rz_dst, srckey, 1);
+ }
+ /*
+ * Unlock source surface
+ */
+ SDL_UnlockSurface(rz_src);
+
+ } else {
+
+ /*
+ * Angle=0: Just a zoom
+ */
+ /*
+ * --------------------
+ */
+
+ /*
+ * Calculate target size
+ */
+ zoomSurfaceSize(rz_src->w, rz_src->h, zoom, zoom, &dstwidth, &dstheight);
+
+ /*
+ * Alloc space to completely contain the zoomed surface
+ */
+ rz_dst = NULL;
+ if (is32bit) {
+ /*
+ * Target surface is 32bit with source RGBA/ABGR ordering
+ */
+ rz_dst =
+ SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 32,
+ rz_src->format->Rmask, rz_src->format->Gmask,
+ rz_src->format->Bmask, rz_src->format->Amask);
+ } else {
+ /*
+ * Target surface is 8bit
+ */
+ rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 8, 0, 0, 0, 0);
+ }
+
+ /*
+ * Lock source surface
+ */
+ SDL_LockSurface(rz_src);
+ /*
+ * Check which kind of surface we have
+ */
+ if (is32bit) {
+ /*
+ * Call the 32bit transformation routine to do the zooming (using alpha)
+ */
+ zoomSurfaceRGBA(rz_src, rz_dst, smooth);
+ /*
+ * Turn on source-alpha support
+ */
+ TFB_SetSurfaceAlphaMod (rz_dst, 255);
+ } else {
+ /*
+ * Copy palette and colorkey info
+ */
+ Uint32 srckey = 0;
+ TFB_GetColorKey (rz_src, &srckey);
+ for (i = 0; i < rz_src->format->palette->ncolors; i++) {
+ rz_dst->format->palette->colors[i] = rz_src->format->palette->colors[i];
+ }
+ rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors;
+ /*
+ * Call the 8bit transformation routine to do the zooming
+ */
+ zoomSurfaceY(rz_src, rz_dst);
+ TFB_SetColorKey(rz_dst, srckey, 1);
+ }
+ /*
+ * Unlock source surface
+ */
+ SDL_UnlockSurface(rz_src);
+ }
+
+ /*
+ * Cleanup temp surface
+ */
+ if (src_converted) {
+ SDL_FreeSurface(rz_src);
+ }
+
+ /*
+ * Return destination surface
+ */
+ return (rz_dst);
+}
+
+/* Publically available rotate function */
+
+int rotateSurface(SDL_Surface *src, SDL_Surface *dst, double angle, int smooth)
+{
+ double zoominv;
+ double sanglezoom, canglezoom, sanglezoominv, canglezoominv;
+ int dstwidthhalf, dstwidth, dstheighthalf, dstheight;
+ int is32bit;
+ int i;
+
+ /* Sanity check */
+ if (!src || !dst)
+ return -1;
+ if (src->format->BitsPerPixel != dst->format->BitsPerPixel)
+ return -1;
+
+ /* Determine if source surface is 32bit or 8bit */
+ is32bit = (src->format->BitsPerPixel == 32);
+
+ zoominv = 65536.0;
+
+ /* Check if we have to rotate anything */
+ if (fabs(angle) <= VALUE_LIMIT)
+ {
+ SDL_BlitSurface(src, NULL, dst, NULL);
+ return 0;
+ }
+
+ /* Determine target size */
+ rotozoomSurfaceSizeTrig(src->w, src->h, angle, 1, &dstwidth, &dstheight, &canglezoom, &sanglezoom);
+
+ /*
+ * Calculate target factors from sin/cos and zoom
+ */
+ sanglezoominv = sanglezoom;
+ canglezoominv = canglezoom;
+ sanglezoominv *= zoominv;
+ canglezoominv *= zoominv;
+
+ /* Calculate half size */
+ dstwidthhalf = dstwidth / 2;
+ dstheighthalf = dstheight / 2;
+
+ /* Check if the rotated surface will fit destination */
+ if (dst->w < dstwidth || dst->h < dstheight)
+ return -1;
+
+ /* Lock source surface */
+ SDL_LockSurface(src);
+ SDL_LockSurface(dst);
+ /* Check which kind of surface we have */
+ if (is32bit)
+ { /* Call the 32bit transformation routine to do the rotation (using alpha) */
+ transformSurfaceRGBA(src, dst, dstwidthhalf, dstheighthalf,
+ (int) (sanglezoominv), (int) (canglezoominv), smooth);
+ }
+ else
+ {
+ /* Copy palette info */
+ for (i = 0; i < src->format->palette->ncolors; i++)
+ dst->format->palette->colors[i] = src->format->palette->colors[i];
+ dst->format->palette->ncolors = src->format->palette->ncolors;
+ /* Call the 8bit transformation routine to do the rotation */
+ transformSurfaceY(src, dst, dstwidthhalf, dstheighthalf,
+ (int) (sanglezoominv), (int) (canglezoominv));
+ }
+ /* Unlock source surface */
+ SDL_UnlockSurface(dst);
+ SDL_UnlockSurface(src);
+
+ return 0;
+}
+
+/*
+
+ zoomSurface()
+
+ Zoomes a 32bit or 8bit 'src' surface to newly created 'dst' surface.
+ 'zoomx' and 'zoomy' are scaling factors for width and height. If 'smooth' is 1
+ then the destination 32bit surface is anti-aliased. If the surface is not 8bit
+ or 32bit RGBA/ABGR it will be converted into a 32bit RGBA format on the fly.
+
+*/
+
+#define VALUE_LIMIT 0.001
+
+void zoomSurfaceSize(int width, int height, double zoomx, double zoomy, int *dstwidth, int *dstheight)
+{
+ /*
+ * Sanity check zoom factors
+ */
+ if (zoomx < VALUE_LIMIT) {
+ zoomx = VALUE_LIMIT;
+ }
+ if (zoomy < VALUE_LIMIT) {
+ zoomy = VALUE_LIMIT;
+ }
+
+ /*
+ * Calculate target size
+ */
+ *dstwidth = (int) ((double) width * zoomx);
+ *dstheight = (int) ((double) height * zoomy);
+ if (*dstwidth < 1) {
+ *dstwidth = 1;
+ }
+ if (*dstheight < 1) {
+ *dstheight = 1;
+ }
+}
+
+SDL_Surface *zoomSurface(SDL_Surface * src, double zoomx, double zoomy, int smooth)
+{
+ SDL_Surface *rz_src;
+ SDL_Surface *rz_dst;
+ int dstwidth, dstheight;
+ int is32bit;
+ int i, src_converted;
+
+ /*
+ * Sanity check
+ */
+ if (src == NULL)
+ return (NULL);
+
+ /*
+ * Determine if source surface is 32bit or 8bit
+ */
+ is32bit = (src->format->BitsPerPixel == 32);
+ if ((is32bit) || (src->format->BitsPerPixel == 8)) {
+ /*
+ * Use source surface 'as is'
+ */
+ rz_src = src;
+ src_converted = 0;
+ } else {
+ /*
+ * New source surface is 32bit with a defined RGBA ordering
+ */
+ rz_src =
+ SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
+ SDL_BlitSurface(src, NULL, rz_src, NULL);
+ src_converted = 1;
+ is32bit = 1;
+ }
+
+ /* Get size if target */
+ zoomSurfaceSize(rz_src->w, rz_src->h, zoomx, zoomy, &dstwidth, &dstheight);
+
+ /*
+ * Alloc space to completely contain the zoomed surface
+ */
+ rz_dst = NULL;
+ if (is32bit) {
+ /*
+ * Target surface is 32bit with source RGBA/ABGR ordering
+ */
+ rz_dst =
+ SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 32,
+ rz_src->format->Rmask, rz_src->format->Gmask,
+ rz_src->format->Bmask, rz_src->format->Amask);
+ } else {
+ /*
+ * Target surface is 8bit
+ */
+ rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 8, 0, 0, 0, 0);
+ }
+
+ /*
+ * Lock source surface
+ */
+ SDL_LockSurface(rz_src);
+ /*
+ * Check which kind of surface we have
+ */
+ if (is32bit) {
+ /*
+ * Call the 32bit transformation routine to do the zooming (using alpha)
+ */
+ zoomSurfaceRGBA(rz_src, rz_dst, smooth);
+ /*
+ * Turn on source-alpha support
+ */
+ TFB_SetSurfaceAlphaMod (rz_dst, 255);
+ } else {
+ /*
+ * Copy palette and colorkey info
+ */
+ Uint32 srckey = 0;
+ TFB_GetColorKey (rz_src, &srckey);
+ for (i = 0; i < rz_src->format->palette->ncolors; i++) {
+ rz_dst->format->palette->colors[i] = rz_src->format->palette->colors[i];
+ }
+ rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors;
+ /*
+ * Call the 8bit transformation routine to do the zooming
+ */
+ zoomSurfaceY(rz_src, rz_dst);
+ TFB_SetColorKey(rz_dst, srckey, 0);
+ }
+ /*
+ * Unlock source surface
+ */
+ SDL_UnlockSurface(rz_src);
+
+ /*
+ * Cleanup temp surface
+ */
+ if (src_converted) {
+ SDL_FreeSurface(rz_src);
+ }
+
+ /*
+ * Return destination surface
+ */
+ return (rz_dst);
+}
+
diff --git a/src/libs/graphics/sdl/rotozoom.h b/src/libs/graphics/sdl/rotozoom.h
new file mode 100644
index 0000000..728fd42
--- /dev/null
+++ b/src/libs/graphics/sdl/rotozoom.h
@@ -0,0 +1,96 @@
+/*
+
+ rotozoom.h - rotozoomer for 32bit or 8bit surfaces
+ LGPL (c) A. Schiffler
+
+ Note by sc2 developers:
+ Taken from SDL_gfx library and modified, original code can be downloaded
+ from http://www.ferzkopp.net/Software/SDL_gfx-2.0/
+
+*/
+
+
+#ifndef ROTOZOOM_H
+#define ROTOZOOM_H
+
+#include <math.h>
+#ifndef M_PI
+#define M_PI 3.141592654
+#endif
+
+#include "port.h"
+#include SDL_INCLUDE(SDL.h)
+
+
+/* ---- Defines */
+
+#define SMOOTHING_OFF 0
+#define SMOOTHING_ON 1
+
+/* ---- Structures */
+
+typedef struct tColorRGBA {
+ Uint8 r;
+ Uint8 g;
+ Uint8 b;
+ Uint8 a;
+} tColorRGBA;
+
+typedef struct tColorY {
+ Uint8 y;
+} tColorY;
+
+
+/* ---- Prototypes */
+
+/*
+ zoomSurfaceRGBA()
+
+ Zoom the src surface into dst. The zoom amount is determined
+ by the dimensions of src and dst
+
+*/
+int zoomSurfaceRGBA(SDL_Surface * src, SDL_Surface * dst, int smooth);
+
+/*
+
+ rotozoomSurface()
+
+ Rotates and zoomes a 32bit or 8bit 'src' surface to newly created 'dst' surface.
+ 'angle' is the rotation in degrees. 'zoom' a scaling factor. If 'smooth' is 1
+ then the destination 32bit surface is anti-aliased. If the surface is not 8bit
+ or 32bit RGBA/ABGR it will be converted into a 32bit RGBA format on the fly.
+
+*/
+
+SDL_Surface *rotozoomSurface(SDL_Surface * src, double angle, double zoom,
+ int smooth);
+
+int rotateSurface(SDL_Surface * src, SDL_Surface * dst, double angle,
+ int smooth);
+
+/* Returns the size of the target surface for a rotozoomSurface() call */
+
+void rotozoomSurfaceSize(int width, int height, double angle, double zoom,
+ int *dstwidth, int *dstheight);
+
+/*
+
+ zoomSurface()
+
+ Zoomes a 32bit or 8bit 'src' surface to newly created 'dst' surface.
+ 'zoomx' and 'zoomy' are scaling factors for width and height. If 'smooth' is 1
+ then the destination 32bit surface is anti-aliased. If the surface is not 8bit
+ or 32bit RGBA/ABGR it will be converted into a 32bit RGBA format on the fly.
+
+*/
+
+SDL_Surface *zoomSurface(SDL_Surface * src, double zoomx, double zoomy,
+ int smooth);
+
+/* Returns the size of the target surface for a zoomSurface() call */
+
+void zoomSurfaceSize(int width, int height, double zoomx, double zoomy,
+ int *dstwidth, int *dstheight);
+
+#endif
diff --git a/src/libs/graphics/sdl/scaleint.h b/src/libs/graphics/sdl/scaleint.h
new file mode 100644
index 0000000..e54de80
--- /dev/null
+++ b/src/libs/graphics/sdl/scaleint.h
@@ -0,0 +1,433 @@
+/*
+ * Copyright (C) 2005 Alex Volkov (codepro@usa.net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+// Scalers Internals
+
+#ifndef SCALEINT_H_
+#define SCALEINT_H_
+
+#include "libs/graphics/sdl/sdl_common.h"
+#include "types.h"
+
+
+// Plain C names
+#define SCALE_(name) Scale ## _ ## name
+
+// These are defaults
+#define SCALE_GETPIX(p) ( *(Uint32 *)(p) )
+#define SCALE_SETPIX(p, c) ( *(Uint32 *)(p) = (c) )
+
+// Plain C defaults
+#define SCALE_CMPRGB(p1, p2) \
+ SCALE_(GetRGBDelta) (fmt, p1, p2)
+
+#define SCALE_TOYUV(p) \
+ SCALE_(RGBtoYUV) (fmt, p)
+
+#define SCALE_CMPYUV(p1, p2, toler) \
+ SCALE_(CmpYUV) (fmt, p1, p2, toler)
+
+#define SCALE_DIFFYUV(p1, p2) \
+ SCALE_(DiffYUV) (p1, p2)
+#define SCALE_DIFFYUV_TY 0x40
+#define SCALE_DIFFYUV_TU 0x12
+#define SCALE_DIFFYUV_TV 0x0c
+
+#define SCALE_GETY(p) \
+ SCALE_(GetPixY) (fmt, p)
+
+#define SCALE_BILINEAR_BLEND4(r0, r1, dst, dlen) \
+ SCALE_(Blend_bilinear) (r0, r1, dst, dlen)
+
+#define NO_PREFETCH 0
+#define INTEL_PREFETCH 1
+#define AMD_PREFETCH 2
+
+typedef enum
+{
+ YUV_XFORM_R = 0,
+ YUV_XFORM_G = 1,
+ YUV_XFORM_B = 2,
+ YUV_XFORM_Y = 0,
+ YUV_XFORM_U = 1,
+ YUV_XFORM_V = 2
+} RGB_YUV_INDEX;
+
+extern const int YUV_matrix[3][3];
+
+// pre-computed transformations for 8 bits per channel
+extern int RGB_to_YUV[/*RGB*/ 3][/*YUV*/ 3][ /*mult-res*/ 256];
+extern sint16 dRGB_to_dYUV[/*RGB*/ 3][/*YUV*/ 3][ /*mult-res*/ 512];
+
+typedef Uint32 YUV_VECTOR;
+// pre-computed transformations for RGB555
+extern YUV_VECTOR RGB15_to_YUV[0x8000];
+
+
+// Platform+Scaler function lookups
+//
+typedef struct
+{
+ int flag;
+ TFB_ScaleFunc func;
+} Scale_FuncDef_t;
+
+
+// expands the given rectangle in all directions by 'expansion'
+// guarded by 'limits'
+extern void Scale_ExpandRect (SDL_Rect* rect, int expansion,
+ const SDL_Rect* limits);
+
+
+// Standard plain C versions of support functions
+
+// Initialize various platform-specific features
+static inline void
+SCALE_(PlatInit) (void)
+{
+}
+
+// Finish with various platform-specific features
+static inline void
+SCALE_(PlatDone) (void)
+{
+}
+
+#if 0
+static inline void
+SCALE_(Prefetch) (const void* p)
+{
+ /* no-op in pure C */
+ (void)p;
+}
+#else
+# define Scale_Prefetch(p)
+#endif
+
+// compute the RGB distance squared between 2 pixels
+// Plain C version
+static inline int
+SCALE_(GetRGBDelta) (const SDL_PixelFormat* fmt, Uint32 pix1, Uint32 pix2)
+{
+ int c;
+ int delta;
+
+ c = ((pix1 >> fmt->Rshift) & 0xff) - ((pix2 >> fmt->Rshift) & 0xff);
+ delta = c * c;
+
+ c = ((pix1 >> fmt->Gshift) & 0xff) - ((pix2 >> fmt->Gshift) & 0xff);
+ delta += c * c;
+
+ c = ((pix1 >> fmt->Bshift) & 0xff) - ((pix2 >> fmt->Bshift) & 0xff);
+ delta += c * c;
+
+ return delta;
+}
+
+// retrieve the Y (intensity) component of pixel's YUV
+// Plain C version
+static inline int
+SCALE_(GetPixY) (const SDL_PixelFormat* fmt, Uint32 pix)
+{
+ Uint32 r, g, b;
+
+ r = (pix >> fmt->Rshift) & 0xff;
+ g = (pix >> fmt->Gshift) & 0xff;
+ b = (pix >> fmt->Bshift) & 0xff;
+
+ return RGB_to_YUV [YUV_XFORM_R][YUV_XFORM_Y][r]
+ + RGB_to_YUV [YUV_XFORM_G][YUV_XFORM_Y][g]
+ + RGB_to_YUV [YUV_XFORM_B][YUV_XFORM_Y][b];
+}
+
+static inline YUV_VECTOR
+SCALE_(RGBtoYUV) (const SDL_PixelFormat* fmt, Uint32 pix)
+{
+ return RGB15_to_YUV[
+ (((pix >> (fmt->Rshift + 3)) & 0x1f) << 10) |
+ (((pix >> (fmt->Gshift + 3)) & 0x1f) << 5) |
+ (((pix >> (fmt->Bshift + 3)) & 0x1f) )
+ ];
+}
+
+// compare 2 pixels with respect to their YUV representations
+// tolerance set by toler arg
+// returns true: close; false: distant (-gt toler)
+// Plain C version
+static inline bool
+SCALE_(CmpYUV) (const SDL_PixelFormat* fmt, Uint32 pix1, Uint32 pix2, int toler)
+#if 1
+{
+ int dr, dg, db;
+ int delta;
+
+ dr = ((pix1 >> fmt->Rshift) & 0xff) - ((pix2 >> fmt->Rshift) & 0xff) + 255;
+ dg = ((pix1 >> fmt->Gshift) & 0xff) - ((pix2 >> fmt->Gshift) & 0xff) + 255;
+ db = ((pix1 >> fmt->Bshift) & 0xff) - ((pix2 >> fmt->Bshift) & 0xff) + 255;
+
+ // compute Y delta
+ delta = abs (dRGB_to_dYUV [YUV_XFORM_R][YUV_XFORM_Y][dr]
+ + dRGB_to_dYUV [YUV_XFORM_G][YUV_XFORM_Y][dg]
+ + dRGB_to_dYUV [YUV_XFORM_B][YUV_XFORM_Y][db]);
+ if (delta > toler)
+ return false;
+
+ // compute U delta
+ delta += abs (dRGB_to_dYUV [YUV_XFORM_R][YUV_XFORM_U][dr]
+ + dRGB_to_dYUV [YUV_XFORM_G][YUV_XFORM_U][dg]
+ + dRGB_to_dYUV [YUV_XFORM_B][YUV_XFORM_U][db]);
+ if (delta > toler)
+ return false;
+
+ // compute V delta
+ delta += abs (dRGB_to_dYUV [YUV_XFORM_R][YUV_XFORM_V][dr]
+ + dRGB_to_dYUV [YUV_XFORM_G][YUV_XFORM_V][dg]
+ + dRGB_to_dYUV [YUV_XFORM_B][YUV_XFORM_V][db]);
+
+ return delta <= toler;
+}
+#else
+{
+ int delta;
+ Uint32 yuv1, yuv2;
+
+ yuv1 = RGB15_to_YUV[
+ (((pix1 >> (fmt->Rshift + 3)) & 0x1f) << 10) |
+ (((pix1 >> (fmt->Gshift + 3)) & 0x1f) << 5) |
+ (((pix1 >> (fmt->Bshift + 3)) & 0x1f) )
+ ];
+
+ yuv2 = RGB15_to_YUV[
+ (((pix2 >> (fmt->Rshift + 3)) & 0x1f) << 10) |
+ (((pix2 >> (fmt->Gshift + 3)) & 0x1f) << 5) |
+ (((pix2 >> (fmt->Bshift + 3)) & 0x1f) )
+ ];
+
+ // compute Y delta
+ delta = abs ((yuv1 & 0xff0000) - (yuv2 & 0xff0000)) >> 16;
+ if (delta > toler)
+ return false;
+
+ // compute U delta
+ delta += abs ((yuv1 & 0x00ff00) - (yuv2 & 0x00ff00)) >> 8;
+ if (delta > toler)
+ return false;
+
+ // compute V delta
+ delta += abs ((yuv1 & 0x0000ff) - (yuv2 & 0x0000ff));
+
+ return delta <= toler;
+}
+#endif
+
+// Check if 2 pixels are different with respect to their
+// YUV representations
+// returns 0: close; ~0: distant
+static inline int
+SCALE_(DiffYUV) (Uint32 yuv1, Uint32 yuv2)
+{
+ // non-branching version -- assumes 2's complement integers
+ // delta math only needs 25 bits and we have 32 available;
+ // only interested in the sign bits after subtraction
+ sint32 delta, ret;
+
+ if (yuv1 == yuv2)
+ return 0;
+
+ // compute Y delta
+ delta = abs ((yuv1 & 0xff0000) - (yuv2 & 0xff0000));
+ ret = (SCALE_DIFFYUV_TY << 16) - delta; // save sign bit
+
+ // compute U delta
+ delta = abs ((yuv1 & 0x00ff00) - (yuv2 & 0x00ff00));
+ ret |= (SCALE_DIFFYUV_TU << 8) - delta; // save sign bit
+
+ // compute V delta
+ delta = abs ((yuv1 & 0x0000ff) - (yuv2 & 0x0000ff));
+ ret |= SCALE_DIFFYUV_TV - delta; // save sign bit
+
+ return (ret >> 31);
+}
+
+// blends two pixels with 1:1 ratio
+static inline Uint32
+SCALE_(Blend_11) (Uint32 pix1, Uint32 pix2)
+{
+ /* (pix1 + pix2) >> 1 */
+ return
+ /* lower bits can be safely ignored - the error is minimal
+ expression that calcs them is left for posterity
+ (pix1 & pix2 & low_mask) +
+ */
+ ((pix1 & 0xfefefefe) >> 1) + ((pix2 & 0xfefefefe) >> 1);
+}
+
+// blends four pixels with 1:1:1:1 ratio
+static inline Uint32
+SCALE_(Blend_1111) (Uint32 pix1, Uint32 pix2,
+ Uint32 pix3, Uint32 pix4)
+{
+ /* (pix1 + pix2 + pix3 + pix4) >> 2 */
+ return
+ /* lower bits can be safely ignored - the error is minimal
+ expression that calcs them is left for posterity
+ ((((pix1 & low_mask) + (pix2 & low_mask) +
+ (pix3 & low_mask) + (pix4 & low_mask)
+ ) >> 2) & low_mask) +
+ */
+ ((pix1 & 0xfcfcfcfc) >> 2) + ((pix2 & 0xfcfcfcfc) >> 2) +
+ ((pix3 & 0xfcfcfcfc) >> 2) + ((pix4 & 0xfcfcfcfc) >> 2);
+}
+
+// blends pixels with 3:1 ratio
+static inline Uint32
+Scale_Blend_31 (Uint32 pix1, Uint32 pix2)
+{
+ /* (pix1 * 3 + pix2) / 4 */
+ /* lower bits can be safely ignored - the error is minimal */
+ return ((pix1 & 0xfefefefe) >> 1) + ((pix1 & 0xfcfcfcfc) >> 2) +
+ ((pix2 & 0xfcfcfcfc) >> 2);
+}
+
+// blends pixels with 2:1:1 ratio
+static inline Uint32
+Scale_Blend_211 (Uint32 pix1, Uint32 pix2, Uint32 pix3)
+{
+ /* (pix1 * 2 + pix2 + pix3) / 4 */
+ /* lower bits can be safely ignored - the error is minimal */
+ return ((pix1 & 0xfefefefe) >> 1) +
+ ((pix2 & 0xfcfcfcfc) >> 2) +
+ ((pix3 & 0xfcfcfcfc) >> 2);
+}
+
+// blends pixels with 5:2:1 ratio
+static inline Uint32
+Scale_Blend_521 (Uint32 pix1, Uint32 pix2, Uint32 pix3)
+{
+ /* (pix1 * 5 + pix2 * 2 + pix3) / 8 */
+ /* lower bits can be safely ignored - the error is minimal */
+ return ((pix1 & 0xfefefefe) >> 1) + ((pix1 & 0xf8f8f8f8) >> 3) +
+ ((pix2 & 0xfcfcfcfc) >> 2) +
+ ((pix3 & 0xf8f8f8f8) >> 3) +
+ 0x02020202 /* half-error */;
+}
+
+// blends pixels with 6:1:1 ratio
+static inline Uint32
+Scale_Blend_611 (Uint32 pix1, Uint32 pix2, Uint32 pix3)
+{
+ /* (pix1 * 6 + pix2 + pix3) / 8 */
+ /* lower bits can be safely ignored - the error is minimal */
+ return ((pix1 & 0xfefefefe) >> 1) + ((pix1 & 0xfcfcfcfc) >> 2) +
+ ((pix2 & 0xf8f8f8f8) >> 3) +
+ ((pix3 & 0xf8f8f8f8) >> 3) +
+ 0x02020202 /* half-error */;
+}
+
+// blends pixels with 2:3:3 ratio
+static inline Uint32
+Scale_Blend_233 (Uint32 pix1, Uint32 pix2, Uint32 pix3)
+{
+ /* (pix1 * 2 + pix2 * 3 + pix3 * 3) / 8 */
+ /* lower bits can be safely ignored - the error is minimal */
+ return ((pix1 & 0xfcfcfcfc) >> 2) +
+ ((pix2 & 0xfcfcfcfc) >> 2) + ((pix2 & 0xf8f8f8f8) >> 3) +
+ ((pix3 & 0xfcfcfcfc) >> 2) + ((pix3 & 0xf8f8f8f8) >> 3) +
+ 0x02020202 /* half-error */;
+}
+
+// blends pixels with 14:1:1 ratio
+static inline Uint32
+Scale_Blend_e11 (Uint32 pix1, Uint32 pix2, Uint32 pix3)
+{
+ /* (pix1 * 14 + pix2 + pix3) >> 4 */
+ /* lower bits can be safely ignored - the error is minimal */
+ return ((pix1 & 0xfefefefe) >> 1) + ((pix1 & 0xfcfcfcfc) >> 2) +
+ ((pix1 & 0xf8f8f8f8) >> 3) +
+ ((pix2 & 0xf0f0f0f0) >> 4) +
+ ((pix3 & 0xf0f0f0f0) >> 4) +
+ 0x03030303 /* half-error */;
+}
+
+// Halfs the pixel's intensity
+static inline Uint32
+SCALE_(HalfPixel) (Uint32 pix)
+{
+ return ((pix & 0xfefefefe) >> 1);
+}
+
+
+// Bilinear weighted blend of four pixels
+// Function produces 4 blended pixels and writes them
+// out to the surface (in 2x2 matrix)
+// Pixels are computed using expanded weight matrix like so:
+// ('sp' - source pixel, 'dp' - destination pixel)
+// dp[0] = (9*sp[0] + 3*sp[1] + 3*sp[2] + 1*sp[3]) / 16
+// dp[1] = (3*sp[0] + 9*sp[1] + 1*sp[2] + 3*sp[3]) / 16
+// dp[2] = (3*sp[0] + 1*sp[1] + 9*sp[2] + 3*sp[3]) / 16
+// dp[3] = (1*sp[0] + 3*sp[1] + 3*sp[2] + 9*sp[3]) / 16
+static inline void
+SCALE_(Blend_bilinear) (const Uint32* row0, const Uint32* row1,
+ Uint32* dst_p, Uint32 dlen)
+{
+ // We loose some lower bits here and try to compensate for
+ // that by adding half-error values.
+ // In general, the error is minimal (+-7)
+ // The >>4 reduction is achieved gradually
+# define BL_PACKED_HALF(p) \
+ (((p) & 0xfefefefe) >> 1)
+# define BL_SUM(p1, p2) \
+ (BL_PACKED_HALF(p1) + BL_PACKED_HALF(p2))
+# define BL_HALF_ERR 0x01010101
+# define BL_SUM_WERR(p1, p2) \
+ (BL_PACKED_HALF(p1) + BL_PACKED_HALF(p2) + BL_HALF_ERR)
+
+ Uint32 sum1111, sum1331, sum3113;
+
+ // cache p[0] + 3*(p[1] + p[2]) + p[3] in sum1331
+ // cache p[1] + 3*(p[0] + p[3]) + p[2] in sum3113
+ sum1331 = BL_SUM (row0[1], row1[0]);
+ sum3113 = BL_SUM (row0[0], row1[1]);
+
+ // cache p[0] + p[1] + p[2] + p[3] in sum1111
+ sum1111 = BL_SUM_WERR (sum1331, sum3113);
+
+ sum1331 = BL_SUM_WERR (sum1331, sum1111);
+ sum1331 = BL_PACKED_HALF (sum1331);
+ sum3113 = BL_SUM_WERR (sum3113, sum1111);
+ sum3113 = BL_PACKED_HALF (sum3113);
+
+ // pixel 0 math -- (9*p[0] + 3*(p[1] + p[2]) + p[3]) / 16
+ dst_p[0] = BL_PACKED_HALF (row0[0]) + sum1331;
+
+ // pixel 1 math -- (9*p[1] + 3*(p[0] + p[3]) + p[2]) / 16
+ dst_p[1] = BL_PACKED_HALF (row0[1]) + sum3113;
+
+ // pixel 2 math -- (9*p[2] + 3*(p[0] + p[3]) + p[1]) / 16
+ dst_p[dlen] = BL_PACKED_HALF (row1[0]) + sum3113;
+
+ // pixel 3 math -- (9*p[3] + 3*(p[1] + p[2]) + p[0]) / 16
+ dst_p[dlen + 1] = BL_PACKED_HALF (row1[1]) + sum1331;
+
+# undef BL_PACKED_HALF
+# undef BL_SUM
+# undef BL_HALF_ERR
+# undef BL_SUM_WERR
+}
+
+#endif /* SCALEINT_H_ */
diff --git a/src/libs/graphics/sdl/scalemmx.h b/src/libs/graphics/sdl/scalemmx.h
new file mode 100644
index 0000000..69c83fe
--- /dev/null
+++ b/src/libs/graphics/sdl/scalemmx.h
@@ -0,0 +1,793 @@
+/*
+ * Copyright (C) 2005 Alex Volkov (codepro@usa.net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef SCALEMMX_H_
+#define SCALEMMX_H_
+
+#if !defined(SCALE_)
+# error Please define SCALE_(name) before including scalemmx.h
+#endif
+
+#if !defined(MSVC_ASM) && !defined(GCC_ASM)
+# error Please define target assembler (MSVC_ASM, GCC_ASM) before including scalemmx.h
+#endif
+
+// MMX defaults (no Format param)
+#undef SCALE_CMPRGB
+#define SCALE_CMPRGB(p1, p2) \
+ SCALE_(GetRGBDelta) (p1, p2)
+
+#undef SCALE_TOYUV
+#define SCALE_TOYUV(p) \
+ SCALE_(RGBtoYUV) (p)
+
+#undef SCALE_CMPYUV
+#define SCALE_CMPYUV(p1, p2, toler) \
+ SCALE_(CmpYUV) (p1, p2, toler)
+
+#undef SCALE_GETY
+#define SCALE_GETY(p) \
+ SCALE_(GetPixY) (p)
+
+// MMX transformation multipliers
+extern Uint64 mmx_888to555_mult;
+extern Uint64 mmx_Y_mult;
+extern Uint64 mmx_U_mult;
+extern Uint64 mmx_V_mult;
+extern Uint64 mmx_YUV_threshold;
+
+#define USE_YUV_LOOKUP
+
+#if defined(MSVC_ASM)
+// MSVC inline assembly versions
+
+#if defined(USE_MOVNTQ)
+# define MOVNTQ(addr, val) movntq [addr], val
+#else
+# define MOVNTQ(addr, val) movq [addr], val
+#endif
+
+#if USE_PREFETCH == INTEL_PREFETCH
+// using Intel SSE non-temporal prefetch
+# define PREFETCH(addr) prefetchnta [addr]
+# define HAVE_PREFETCH
+#elif USE_PREFETCH == AMD_PREFETCH
+// using AMD 3DNOW! prefetch
+# define PREFETCH(addr) prefetch [addr]
+# define HAVE_PREFETCH
+#else
+// no prefetch -- too bad for poor MMX-only souls
+# define PREFETCH(addr)
+# undef HAVE_PREFETCH
+#endif
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1300)
+# pragma warning( disable : 4799 )
+#endif
+
+static inline void
+SCALE_(PlatInit) (void)
+{
+ __asm
+ {
+ // mm0 will be kept == 0 throughout
+ // 0 is needed for bytes->words unpack instructions
+ pxor mm0, mm0
+ }
+}
+
+static inline void
+SCALE_(PlatDone) (void)
+{
+ // finish with MMX registers and yield them to FPU
+ __asm
+ {
+ emms
+ }
+}
+
+#if defined(HAVE_PREFETCH)
+static inline void
+SCALE_(Prefetch) (const void* p)
+{
+ __asm
+ {
+ mov eax, p
+ PREFETCH (eax)
+ }
+}
+
+#else /* Not HAVE_PREFETCH */
+
+static inline void
+SCALE_(Prefetch) (const void* p)
+{
+ (void)p; // silence compiler
+ /* no-op */
+}
+
+#endif /* HAVE_PREFETCH */
+
+// compute the RGB distance squared between 2 pixels
+static inline int
+SCALE_(GetRGBDelta) (Uint32 pix1, Uint32 pix2)
+{
+ __asm
+ {
+ // load pixels
+ movd mm1, pix1
+ punpcklbw mm1, mm0
+ movd mm2, pix2
+ punpcklbw mm2, mm0
+ // get the difference between RGBA components
+ psubw mm1, mm2
+ // squared and sumed
+ pmaddwd mm1, mm1
+ // finish suming the squares
+ movq mm2, mm1
+ punpckhdq mm2, mm0
+ paddd mm1, mm2
+ // store result
+ movd eax, mm1
+ }
+}
+
+// retrieve the Y (intensity) component of pixel's YUV
+static inline int
+SCALE_(GetPixY) (Uint32 pix)
+{
+ __asm
+ {
+ // load pixel
+ movd mm1, pix
+ punpcklbw mm1, mm0
+ // process
+ pmaddwd mm1, mmx_Y_mult // RGB * Yvec
+ movq mm2, mm1 // finish suming
+ punpckhdq mm2, mm0 // ditto
+ paddd mm1, mm2 // ditto
+ // store result
+ movd eax, mm1
+ shr eax, 14
+ }
+}
+
+#ifdef USE_YUV_LOOKUP
+
+// convert pixel RGB vector into YUV representation vector
+static inline YUV_VECTOR
+SCALE_(RGBtoYUV) (Uint32 pix)
+{
+ __asm
+ {
+ // convert RGB888 to 555
+ movd mm1, pix
+ punpcklbw mm1, mm0
+ psrlw mm1, 3 // 8->5 bit
+ pmaddwd mm1, mmx_888to555_mult // shuffle into the right channel order
+ movq mm2, mm1 // finish shuffling
+ punpckhdq mm2, mm0 // ditto
+ por mm1, mm2 // ditto
+
+ // lookup the YUV vector
+ movd eax, mm1
+ mov eax, [RGB15_to_YUV + eax * 4]
+ }
+}
+
+// compare 2 pixels with respect to their YUV representations
+// tolerance set by toler arg
+// returns true: close; false: distant (-gt toler)
+static inline bool
+SCALE_(CmpYUV) (Uint32 pix1, Uint32 pix2, int toler)
+{
+ __asm
+ {
+ // convert RGB888 to 555
+ movd mm1, pix1
+ punpcklbw mm1, mm0
+ psrlw mm1, 3 // 8->5 bit
+ movd mm3, pix2
+ punpcklbw mm3, mm0
+ psrlw mm3, 3 // 8->5 bit
+ pmaddwd mm1, mmx_888to555_mult // shuffle into the right channel order
+ movq mm2, mm1 // finish shuffling
+ pmaddwd mm3, mmx_888to555_mult // shuffle into the right channel order
+ movq mm4, mm3 // finish shuffling
+ punpckhdq mm2, mm0 // ditto
+ por mm1, mm2 // ditto
+ punpckhdq mm4, mm0 // ditto
+ por mm3, mm4 // ditto
+
+ // lookup the YUV vector
+ movd eax, mm1
+ movd edx, mm3
+ movd mm1, [RGB15_to_YUV + eax * 4]
+ movq mm4, mm1
+ movd mm2, [RGB15_to_YUV + edx * 4]
+
+ // get abs difference between YUV components
+#ifdef USE_PSADBW
+ // we can use PSADBW and save us some grief
+ psadbw mm1, mm2
+ movd edx, mm1
+#else
+ // no PSADBW -- have to do it the hard way
+ psubusb mm1, mm2
+ psubusb mm2, mm4
+ por mm1, mm2
+
+ // sum the differences
+ // XXX: technically, this produces a MAX diff of 510
+ // but we do not need anything bigger, currently
+ movq mm2, mm1
+ psrlq mm2, 8
+ paddusb mm1, mm2
+ psrlq mm2, 8
+ paddusb mm1, mm2
+ movd edx, mm1
+ and edx, 0xff
+#endif /* USE_PSADBW */
+ xor eax, eax
+ shl edx, 1
+ cmp edx, toler
+ // store result
+ setle al
+ }
+}
+
+#else /* Not USE_YUV_LOOKUP */
+
+// convert pixel RGB vector into YUV representation vector
+static inline YUV_VECTOR
+SCALE_(RGBtoYUV) (Uint32 pix)
+{
+ __asm
+ {
+ movd mm1, pix
+ punpcklbw mm1, mm0
+
+ movq mm2, mm1
+
+ // Y vector multiply
+ pmaddwd mm1, mmx_Y_mult
+ movq mm4, mm1
+ punpckhdq mm4, mm0
+ punpckldq mm1, mm0 // clear out the high dword
+ paddd mm1, mm4
+ psrad mm1, 15
+
+ movq mm3, mm2
+
+ // U vector multiply
+ pmaddwd mm2, mmx_U_mult
+ psrad mm2, 10
+
+ // V vector multiply
+ pmaddwd mm3, mmx_V_mult
+ psrad mm3, 10
+
+ // load (1|1|1|1) into mm4
+ pcmpeqw mm4, mm4
+ psrlw mm4, 15
+
+ packssdw mm3, mm2
+ pmaddwd mm3, mm4
+ psrad mm3, 5
+
+ // load (64|64) into mm4
+ punpcklwd mm4, mm0
+ pslld mm4, 6
+ paddd mm3, mm4
+
+ packssdw mm3, mm1
+ packuswb mm3, mm0
+
+ movd eax, mm3
+ }
+}
+
+// compare 2 pixels with respect to their YUV representations
+// tolerance set by toler arg
+// returns true: close; false: distant (-gt toler)
+static inline bool
+SCALE_(CmpYUV) (Uint32 pix1, Uint32 pix2, int toler)
+{
+ __asm
+ {
+ movd mm1, pix1
+ punpcklbw mm1, mm0
+ movd mm2, pix2
+ punpcklbw mm2, mm0
+
+ psubw mm1, mm2
+ movq mm2, mm1
+
+ // Y vector multiply
+ pmaddwd mm1, mmx_Y_mult
+ movq mm4, mm1
+ punpckhdq mm4, mm0
+ paddd mm1, mm4
+ // abs()
+ movq mm4, mm1
+ psrad mm4, 31
+ pxor mm4, mm1
+ psubd mm1, mm4
+
+ movq mm3, mm2
+
+ // U vector multiply
+ pmaddwd mm2, mmx_U_mult
+ movq mm4, mm2
+ punpckhdq mm4, mm0
+ paddd mm2, mm4
+ // abs()
+ movq mm4, mm2
+ psrad mm4, 31
+ pxor mm4, mm2
+ psubd mm2, mm4
+
+ paddd mm1, mm2
+
+ // V vector multiply
+ pmaddwd mm3, mmx_V_mult
+ movq mm4, mm3
+ punpckhdq mm3, mm0
+ paddd mm3, mm4
+ // abs()
+ movq mm4, mm3
+ psrad mm4, 31
+ pxor mm4, mm3
+ psubd mm3, mm4
+
+ paddd mm1, mm3
+
+ movd edx, mm1
+ xor eax, eax
+ shr edx, 14
+ cmp edx, toler
+ // store result
+ setle al
+ }
+}
+
+#endif /* USE_YUV_LOOKUP */
+
+// Check if 2 pixels are different with respect to their
+// YUV representations
+// returns 0: close; ~0: distant
+static inline int
+SCALE_(DiffYUV) (Uint32 yuv1, Uint32 yuv2)
+{
+ __asm
+ {
+ // load YUV pixels
+ movd mm1, yuv1
+ movq mm4, mm1
+ movd mm2, yuv2
+ // abs difference between channels
+ psubusb mm1, mm2
+ psubusb mm2, mm4
+ por mm1, mm2
+ // compare to threshold
+ psubusb mm1, mmx_YUV_threshold
+
+ movd edx, mm1
+ // transform eax to 0 or ~0
+ xor eax, eax
+ or edx, edx
+ setz al
+ dec eax
+ }
+}
+
+// bilinear weighted blend of four pixels
+// MSVC asm version
+static inline void
+SCALE_(Blend_bilinear) (const Uint32* row0, const Uint32* row1,
+ Uint32* dst_p, Uint32 dlen)
+{
+ __asm
+ {
+ // EL0: setup vars
+ mov ebx, row0 // EL0
+
+ // EL0: load pixels
+ movq mm1, [ebx] // EL0
+ movq mm2, mm1 // EL0: p[1] -> mm2
+ PREFETCH (ebx + 0x80)
+ punpckhbw mm2, mm0 // EL0: p[1] -> mm2
+ mov ebx, row1
+ punpcklbw mm1, mm0 // EL0: p[0] -> mm1
+ movq mm3, [ebx]
+ movq mm4, mm3 // EL0: p[3] -> mm4
+ movq mm6, mm2 // EL1.1: p[1] -> mm6
+ PREFETCH (ebx + 0x80)
+ punpcklbw mm3, mm0 // EL0: p[2] -> mm3
+ movq mm5, mm1 // EL1.1: p[0] -> mm5
+ punpckhbw mm4, mm0 // EL0: p[3] -> mm4
+
+ mov edi, dst_p // EL0
+
+ // EL1: cache p[0] + 3*(p[1] + p[2]) + p[3] in mm6
+ paddw mm6, mm3 // EL1.2: p[1] + p[2] -> mm6
+ // EL1: cache p[0] + p[1] + p[2] + p[3] in mm7
+ movq mm7, mm6 // EL1.3: p[1] + p[2] -> mm7
+ // EL1: cache p[1] + 3*(p[0] + p[3]) + p[2] in mm5
+ paddw mm5, mm4 // EL1.2: p[0] + p[3] -> mm5
+ psllw mm6, 1 // EL1.4: 2*(p[1] + p[2]) -> mm6
+ paddw mm7, mm5 // EL1.4: sum(p[]) -> mm7
+ psllw mm5, 1 // EL1.5: 2*(p[0] + p[3]) -> mm5
+ paddw mm6, mm7 // EL1.5: p[0] + 3*(p[1] + p[2]) + p[3] -> mm6
+ paddw mm5, mm7 // EL1.6: p[1] + 3*(p[0] + p[3]) + p[2] -> mm5
+
+ // EL2: pixel 0 math -- (9*p[0] + 3*(p[1] + p[2]) + p[3]) / 16
+ psllw mm1, 3 // EL2.1: 8*p[0] -> mm1
+ paddw mm1, mm6 // EL2.2: 9*p[0] + 3*(p[1] + p[2]) + p[3] -> mm1
+ psrlw mm1, 4 // EL2.3: sum[0]/16 -> mm1
+
+ mov edx, dlen // EL0
+
+ // EL3: pixel 1 math -- (9*p[1] + 3*(p[0] + p[3]) + p[2]) / 16
+ psllw mm2, 3 // EL3.1: 8*p[1] -> mm2
+ paddw mm2, mm5 // EL3.2: 9*p[1] + 3*(p[0] + p[3]) + p[2] -> mm2
+ psrlw mm2, 4 // EL3.3: sum[1]/16 -> mm5
+
+ // EL2/3: store pixels 0 & 1
+ packuswb mm1, mm2 // EL2/3: pack into bytes
+ MOVNTQ (edi, mm1) // EL2/3: store 2 pixels
+
+ // EL4: pixel 2 math -- (9*p[2] + 3*(p[0] + p[3]) + p[1]) / 16
+ psllw mm3, 3 // EL4.1: 8*p[2] -> mm3
+ paddw mm3, mm5 // EL4.2: 9*p[2] + 3*(p[0] + p[3]) + p[1] -> mm3
+ psrlw mm3, 4 // EL4.3: sum[2]/16 -> mm3
+
+ // EL5: pixel 3 math -- (9*p[3] + 3*(p[1] + p[2]) + p[0]) / 16
+ psllw mm4, 3 // EL5.1: 8*p[3] -> mm4
+ paddw mm4, mm6 // EL5.2: 9*p[3] + 3*(p[1] + p[2]) + p[0] -> mm4
+ psrlw mm4, 4 // EL5.3: sum[3]/16 -> mm4
+
+ // EL4/5: store pixels 2 & 3
+ packuswb mm3, mm4 // EL4/5: pack into bytes
+ MOVNTQ (edi + edx*4, mm3) // EL4/5: store 2 pixels
+ }
+}
+// End MSVC_ASM
+
+#elif defined(GCC_ASM)
+// GCC inline assembly versions
+
+#if defined(USE_MOVNTQ)
+# define MOVNTQ(val, addr) "movntq " #val "," #addr
+#else
+# define MOVNTQ(val, addr) "movq " #val "," #addr
+#endif
+
+#if USE_PREFETCH == INTEL_PREFETCH
+// using Intel SSE non-temporal prefetch
+# define PREFETCH(addr) "prefetchnta " #addr
+#elif USE_PREFETCH == AMD_PREFETCH
+// using AMD 3DNOW! prefetch
+# define PREFETCH(addr) "prefetch " #addr
+#else
+// no prefetch -- too bad for poor MMX-only souls
+# define PREFETCH(addr)
+#endif
+
+#if defined(__x86_64__)
+# define A_REG "rax"
+# define D_REG "rdx"
+# define CLR_UPPER32(r) "xor " "%%" r "," "%%" r
+#else
+# define A_REG "eax"
+# define D_REG "edx"
+# define CLR_UPPER32(r)
+#endif
+
+static inline void
+SCALE_(PlatInit) (void)
+{
+ __asm__ (
+ // mm0 will be kept == 0 throughout
+ // 0 is needed for bytes->words unpack instructions
+ "pxor %%mm0, %%mm0 \n\t"
+
+ : /* nothing */
+ : /* nothing */
+ );
+}
+
+static inline void
+SCALE_(PlatDone) (void)
+{
+ // finish with MMX registers and yield them to FPU
+ __asm__ (
+ "emms \n\t"
+ : /* nothing */ : /* nothing */
+ );
+}
+
+static inline void
+SCALE_(Prefetch) (const void* p)
+{
+ __asm__ __volatile__ ("" PREFETCH (%0) : /*nothing*/ : "m" (p) );
+}
+
+// compute the RGB distance squared between 2 pixels
+static inline int
+SCALE_(GetRGBDelta) (Uint32 pix1, Uint32 pix2)
+{
+ int res;
+
+ __asm__ (
+ // load pixels
+ "movd %1, %%mm1 \n\t"
+ "punpcklbw %%mm0, %%mm1 \n\t"
+ "movd %2, %%mm2 \n\t"
+ "punpcklbw %%mm0, %%mm2 \n\t"
+ // get the difference between RGBA components
+ "psubw %%mm2, %%mm1 \n\t"
+ // squared and sumed
+ "pmaddwd %%mm1, %%mm1 \n\t"
+ // finish suming the squares
+ "movq %%mm1, %%mm2 \n\t"
+ "punpckhdq %%mm0, %%mm2 \n\t"
+ "paddd %%mm2, %%mm1 \n\t"
+ // store result
+ "movd %%mm1, %0 \n\t"
+
+ : /*0*/"=rm" (res)
+ : /*1*/"rm" (pix1), /*2*/"rm" (pix2)
+ );
+
+ return res;
+}
+
+// retrieve the Y (intensity) component of pixel's YUV
+static inline int
+SCALE_(GetPixY) (Uint32 pix)
+{
+ int ret;
+
+ __asm__ (
+ // load pixel
+ "movd %1, %%mm1 \n\t"
+ "punpcklbw %%mm0, %%mm1 \n\t"
+ // process
+ "pmaddwd %2, %%mm1 \n\t" // R,G,B * Yvec
+ "movq %%mm1, %%mm2 \n\t" // finish suming
+ "punpckhdq %%mm0, %%mm2 \n\t" // ditto
+ "paddd %%mm2, %%mm1 \n\t" // ditto
+ // store index
+ "movd %%mm1, %0 \n\t"
+
+ : /*0*/"=r" (ret)
+ : /*1*/"rm" (pix), /*2*/"m" (mmx_Y_mult)
+ );
+ return ret >> 14;
+}
+
+#ifdef USE_YUV_LOOKUP
+
+// convert pixel RGB vector into YUV representation vector
+static inline YUV_VECTOR
+SCALE_(RGBtoYUV) (Uint32 pix)
+{
+ int i;
+
+ __asm__ (
+ // convert RGB888 to 555
+ "movd %1, %%mm1 \n\t"
+ "punpcklbw %%mm0, %%mm1 \n\t"
+ "psrlw $3, %%mm1 \n\t" // 8->5 bit
+ "pmaddwd %2, %%mm1 \n\t" // shuffle into the right channel order
+ "movq %%mm1, %%mm2 \n\t" // finish shuffling
+ "punpckhdq %%mm0, %%mm2 \n\t" // ditto
+ "por %%mm2, %%mm1 \n\t" // ditto
+ "movd %%mm1, %0 \n\t"
+
+ : /*0*/"=rm" (i)
+ : /*1*/"rm" (pix), /*2*/"m" (mmx_888to555_mult)
+ );
+ return RGB15_to_YUV[i];
+}
+
+// compare 2 pixels with respect to their YUV representations
+// tolerance set by toler arg
+// returns true: close; false: distant (-gt toler)
+static inline bool
+SCALE_(CmpYUV) (Uint32 pix1, Uint32 pix2, int toler)
+{
+ int delta;
+
+ __asm__ (
+ "movd %1, %%mm1 \n\t"
+ "movd %2, %%mm3 \n\t"
+
+ // convert RGB888 to 555
+ // this is somewhat parallelized
+ "punpcklbw %%mm0, %%mm1 \n\t"
+ CLR_UPPER32 (A_REG) "\n\t"
+ "psrlw $3, %%mm1 \n\t" // 8->5 bit
+ "punpcklbw %%mm0, %%mm3 \n\t"
+ "psrlw $3, %%mm3 \n\t" // 8->5 bit
+ "pmaddwd %4, %%mm1 \n\t" // shuffle into the right channel order
+ "movq %%mm1, %%mm2 \n\t" // finish shuffling
+ "pmaddwd %4, %%mm3 \n\t" // shuffle into the right channel order
+ CLR_UPPER32 (D_REG) "\n\t"
+ "movq %%mm3, %%mm4 \n\t" // finish shuffling
+ "punpckhdq %%mm0, %%mm2 \n\t" // ditto
+ "por %%mm2, %%mm1 \n\t" // ditto
+ "punpckhdq %%mm0, %%mm4 \n\t" // ditto
+ "por %%mm4, %%mm3 \n\t" // ditto
+
+ // lookup the YUV vector
+ "movd %%mm1, %%eax \n\t"
+ "movd %%mm3, %%edx \n\t"
+ "movd (%3, %%" A_REG ", 4), %%mm1 \n\t"
+ "movq %%mm1, %%mm4 \n\t"
+ "movd (%3, %%" D_REG ", 4), %%mm2 \n\t"
+
+ // get abs difference between YUV components
+#ifdef USE_PSADBW
+ // we can use PSADBW and save us some grief
+ "psadbw %%mm2, %%mm1 \n\t"
+ "movd %%mm1, %0 \n\t"
+#else
+ // no PSADBW -- have to do it the hard way
+ "psubusb %%mm2, %%mm1 \n\t"
+ "psubusb %%mm4, %%mm2 \n\t"
+ "por %%mm2, %%mm1 \n\t"
+
+ // sum the differences
+ // technically, this produces a MAX diff of 510
+ // but we do not need anything bigger, currently
+ "movq %%mm1, %%mm2 \n\t"
+ "psrlq $8, %%mm2 \n\t"
+ "paddusb %%mm2, %%mm1 \n\t"
+ "psrlq $8, %%mm2 \n\t"
+ "paddusb %%mm2, %%mm1 \n\t"
+ // store intermediate delta
+ "movd %%mm1, %0 \n\t"
+ "andl $0xff, %0 \n\t"
+#endif /* USE_PSADBW */
+ : /*0*/"=rm" (delta)
+ : /*1*/"rm" (pix1), /*2*/"rm" (pix2),
+ /*3*/ "r" (RGB15_to_YUV),
+ /*4*/"m" (mmx_888to555_mult)
+ : "%" A_REG, "%" D_REG, "cc"
+ );
+
+ return (delta << 1) <= toler;
+}
+
+#endif /* USE_YUV_LOOKUP */
+
+// Check if 2 pixels are different with respect to their
+// YUV representations
+// returns 0: close; ~0: distant
+static inline int
+SCALE_(DiffYUV) (Uint32 yuv1, Uint32 yuv2)
+{
+ sint32 ret;
+
+ __asm__ (
+ // load YUV pixels
+ "movd %1, %%mm1 \n\t"
+ "movq %%mm1, %%mm4 \n\t"
+ "movd %2, %%mm2 \n\t"
+ // abs difference between channels
+ "psubusb %%mm2, %%mm1 \n\t"
+ "psubusb %%mm4, %%mm2 \n\t"
+ CLR_UPPER32(D_REG) "\n\t"
+ "por %%mm2, %%mm1 \n\t"
+ // compare to threshold
+ "psubusb %3, %%mm1 \n\t"
+
+ "movd %%mm1, %%edx \n\t"
+ // transform eax to 0 or ~0
+ "xor %%" A_REG ", %%" A_REG "\n\t"
+ "or %%" D_REG ", %%" D_REG "\n\t"
+ "setz %%al \n\t"
+ "dec %%" A_REG " \n\t"
+
+ : /*0*/"=a" (ret)
+ : /*1*/"rm" (yuv1), /*2*/"rm" (yuv2),
+ /*3*/"m" (mmx_YUV_threshold)
+ : "%" D_REG, "cc"
+ );
+ return ret;
+}
+
+// Bilinear weighted blend of four pixels
+// Function produces 4 blended pixels (in 2x2 matrix) and writes them
+// out to the surface
+// Last version
+static inline void
+SCALE_(Blend_bilinear) (const Uint32* row0, const Uint32* row1,
+ Uint32* dst_p, Uint32 dlen)
+{
+ __asm__ (
+ // EL0: load pixels
+ "movq %0, %%mm1 \n\t" // EL0
+ "movq %%mm1, %%mm2 \n\t" // EL0: p[1] -> mm2
+ PREFETCH (0x80%0) "\n\t"
+ "punpckhbw %%mm0, %%mm2 \n\t" // EL0: p[1] -> mm2
+ "punpcklbw %%mm0, %%mm1 \n\t" // EL0: p[0] -> mm1
+ "movq %1, %%mm3 \n\t"
+ "movq %%mm3, %%mm4 \n\t" // EL0: p[3] -> mm4
+ "movq %%mm2, %%mm6 \n\t" // EL1.1: p[1] -> mm6
+ PREFETCH (0x80%1) "\n\t"
+ "punpcklbw %%mm0, %%mm3 \n\t" // EL0: p[2] -> mm3
+ "movq %%mm1, %%mm5 \n\t" // EL1.1: p[0] -> mm5
+ "punpckhbw %%mm0, %%mm4 \n\t" // EL0: p[3] -> mm4
+
+ // EL1: cache p[0] + 3*(p[1] + p[2]) + p[3] in mm6
+ "paddw %%mm3, %%mm6 \n\t" // EL1.2: p[1] + p[2] -> mm6
+ // EL1: cache p[0] + p[1] + p[2] + p[3] in mm7
+ "movq %%mm6, %%mm7 \n\t" // EL1.3: p[1] + p[2] -> mm7
+ // EL1: cache p[1] + 3*(p[0] + p[3]) + p[2] in mm5
+ "paddw %%mm4, %%mm5 \n\t" // EL1.2: p[0] + p[3] -> mm5
+ "psllw $1, %%mm6 \n\t" // EL1.4: 2*(p[1] + p[2]) -> mm6
+ "paddw %%mm5, %%mm7 \n\t" // EL1.4: sum(p[]) -> mm7
+ "psllw $1, %%mm5 \n\t" // EL1.5: 2*(p[0] + p[3]) -> mm5
+ "paddw %%mm7, %%mm6 \n\t" // EL1.5: p[0] + 3*(p[1] + p[2]) + p[3] -> mm6
+ "paddw %%mm7, %%mm5 \n\t" // EL1.6: p[1] + 3*(p[0] + p[3]) + p[2] -> mm5
+
+ // EL2: pixel 0 math -- (9*p[0] + 3*(p[1] + p[2]) + p[3]) / 16
+ "psllw $3, %%mm1 \n\t" // EL2.1: 8*p[0] -> mm1
+ "paddw %%mm6, %%mm1 \n\t" // EL2.2: 9*p[0] + 3*(p[1] + p[2]) + p[3] -> mm1
+ "psrlw $4, %%mm1 \n\t" // EL2.3: sum[0]/16 -> mm1
+
+ // EL3: pixel 1 math -- (9*p[1] + 3*(p[0] + p[3]) + p[2]) / 16
+ "psllw $3, %%mm2 \n\t" // EL3.1: 8*p[1] -> mm2
+ "paddw %%mm5, %%mm2 \n\t" // EL3.2: 9*p[1] + 3*(p[0] + p[3]) + p[2] -> mm5
+ "psrlw $4, %%mm2 \n\t" // EL3.3: sum[1]/16 -> mm5
+
+ // EL2/4: store pixels 0 & 1
+ "packuswb %%mm2, %%mm1 \n\t" // EL2/4: pack into bytes
+ MOVNTQ (%%mm1, (%2)) "\n\t" // EL2/4: store 2 pixels
+
+ // EL4: pixel 2 math -- (9*p[2] + 3*(p[0] + p[3]) + p[1]) / 16
+ "psllw $3, %%mm3 \n\t" // EL4.1: 8*p[2] -> mm3
+ "paddw %%mm5, %%mm3 \n\t" // EL4.2: 9*p[2] + 3*(p[0] + p[3]) + p[1] -> mm3
+ "psrlw $4, %%mm3 \n\t" // EL4.3: sum[2]/16 -> mm3
+
+ // EL5: pixel 3 math -- (9*p[3] + 3*(p[1] + p[2]) + p[0]) / 16
+ "psllw $3, %%mm4 \n\t" // EL5.1: 8*p[3] -> mm4
+ "paddw %%mm6, %%mm4 \n\t" // EL5.2: 9*p[3] + 3*(p[1] + p[2]) + p[0] -> mm4
+ "psrlw $4, %%mm4 \n\t" // EL5.3: sum[3]/16 -> mm4
+
+ // EL4/5: store pixels 2 & 3
+ "packuswb %%mm4, %%mm3 \n\t" // EL4/5: pack into bytes
+ MOVNTQ (%%mm3, (%2,%3,4)) "\n\t" // EL4/5: store 2 pixels
+
+ : /* nothing */
+ : /*0*/"m" (*row0), /*1*/"m" (*row1), /*2*/"r" (dst_p),
+ /*3*/"r" ((unsigned long)dlen) /* 'long' is for proper reg alloc on amd64 */
+ : "memory"
+ );
+}
+
+#undef A_REG
+#undef D_REG
+#undef CLR_UPPER32
+
+#endif // GCC_ASM
+
+#endif /* SCALEMMX_H_ */
diff --git a/src/libs/graphics/sdl/scalers.c b/src/libs/graphics/sdl/scalers.c
new file mode 100644
index 0000000..751dae3
--- /dev/null
+++ b/src/libs/graphics/sdl/scalers.c
@@ -0,0 +1,289 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "types.h"
+#include "libs/graphics/sdl/sdl_common.h"
+#include "libs/platform.h"
+#include "libs/log.h"
+#include "scalers.h"
+#include "scaleint.h"
+#include "2xscalers.h"
+#ifdef USE_PLATFORM_ACCEL
+# ifndef __APPLE__
+ // MacOS X framework has no SDL_cpuinfo.h for some reason
+# include SDL_INCLUDE(SDL_cpuinfo.h)
+# endif
+# ifdef MMX_ASM
+# include "2xscalers_mmx.h"
+# endif /* MMX_ASM */
+#endif /* USE_PLATFORM_ACCEL */
+
+#if SDL_MAJOR_VERSION == 1
+#define SDL_HasMMX SDL_HasMMXExt
+#endif
+
+typedef enum
+{
+ SCALEPLAT_NULL = PLATFORM_NULL,
+ SCALEPLAT_C = PLATFORM_C,
+ SCALEPLAT_MMX = PLATFORM_MMX,
+ SCALEPLAT_SSE = PLATFORM_SSE,
+ SCALEPLAT_3DNOW = PLATFORM_3DNOW,
+ SCALEPLAT_ALTIVEC = PLATFORM_ALTIVEC,
+
+ SCALEPLAT_C_RGBA,
+ SCALEPLAT_C_BGRA,
+ SCALEPLAT_C_ARGB,
+ SCALEPLAT_C_ABGR,
+
+} Scale_PlatType_t;
+
+
+// RGB -> YUV transformation
+// the RGB vector is multiplied by the transformation matrix
+// to get the YUV vector
+#if 0
+// original table -- not used
+const int YUV_matrix[3][3] =
+{
+ /* Y U V */
+ /* R */ {0.2989, -0.1687, 0.5000},
+ /* G */ {0.5867, -0.3312, -0.4183},
+ /* B */ {0.1144, 0.5000, -0.0816}
+};
+#else
+// scaled up by a 2^14 factor, with Y doubled
+const int YUV_matrix[3][3] =
+{
+ /* Y U V */
+ /* R */ { 9794, -2764, 8192},
+ /* G */ {19224, -5428, -6853},
+ /* B */ { 3749, 8192, -1339}
+};
+#endif
+
+// pre-computed transformations for 8 bits per channel
+int RGB_to_YUV[/*RGB*/ 3][/*YUV*/ 3][ /*mult-res*/ 256];
+sint16 dRGB_to_dYUV[/*RGB*/ 3][/*YUV*/ 3][ /*mult-res*/ 512];
+
+// pre-computed transformations for RGB555
+YUV_VECTOR RGB15_to_YUV[0x8000];
+
+PLATFORM_TYPE force_platform = PLATFORM_NULL;
+Scale_PlatType_t Scale_Platform = SCALEPLAT_NULL;
+
+
+// pre-compute the RGB->YUV transformations
+void
+Scale_Init (void)
+{
+ int i1, i2, i3;
+
+ for (i1 = 0; i1 < 3; i1++) // enum R,G,B
+ for (i2 = 0; i2 < 3; i2++) // enum Y,U,V
+ for (i3 = 0; i3 < 256; i3++) // enum possible channel vals
+ {
+ RGB_to_YUV[i1][i2][i3] =
+ (YUV_matrix[i1][i2] * i3) >> 14;
+ }
+
+ for (i1 = 0; i1 < 3; i1++) // enum R,G,B
+ for (i2 = 0; i2 < 3; i2++) // enum Y,U,V
+ for (i3 = -255; i3 < 256; i3++) // enum possible channel delta vals
+ {
+ dRGB_to_dYUV[i1][i2][i3 + 255] =
+ (YUV_matrix[i1][i2] * i3) >> 14;
+ }
+
+ for (i1 = 0; i1 < 32; ++i1)
+ for (i2 = 0; i2 < 32; ++i2)
+ for (i3 = 0; i3 < 32; ++i3)
+ {
+ int y, u, v;
+ // adding upper bits halved for error correction
+ int r = (i1 << 3) | (i1 >> 3);
+ int g = (i2 << 3) | (i2 >> 3);
+ int b = (i3 << 3) | (i3 >> 3);
+
+ y = ( r * YUV_matrix[YUV_XFORM_R][YUV_XFORM_Y]
+ + g * YUV_matrix[YUV_XFORM_G][YUV_XFORM_Y]
+ + b * YUV_matrix[YUV_XFORM_B][YUV_XFORM_Y]
+ ) >> 15; // we dont need Y doubled, need Y to fit 8 bits
+
+ // U and V are half the importance of Y
+ u = 64+(( r * YUV_matrix[YUV_XFORM_R][YUV_XFORM_U]
+ + g * YUV_matrix[YUV_XFORM_G][YUV_XFORM_U]
+ + b * YUV_matrix[YUV_XFORM_B][YUV_XFORM_U]
+ ) >> 15); // halved
+
+ v = 64+(( r * YUV_matrix[YUV_XFORM_R][YUV_XFORM_V]
+ + g * YUV_matrix[YUV_XFORM_G][YUV_XFORM_V]
+ + b * YUV_matrix[YUV_XFORM_B][YUV_XFORM_V]
+ ) >> 15); // halved
+
+ RGB15_to_YUV[(i1 << 10) | (i2 << 5) | i3] = (y << 16) | (u << 8) | v;
+ }
+}
+
+
+// expands the given rectangle in all directions by 'expansion'
+// guarded by 'limits'
+void
+Scale_ExpandRect (SDL_Rect* rect, int expansion, const SDL_Rect* limits)
+{
+ if (rect->x - expansion >= limits->x)
+ {
+ rect->w += expansion;
+ rect->x -= expansion;
+ }
+ else
+ {
+ rect->w += rect->x - limits->x;
+ rect->x = limits->x;
+ }
+
+ if (rect->y - expansion >= limits->y)
+ {
+ rect->h += expansion;
+ rect->y -= expansion;
+ }
+ else
+ {
+ rect->h += rect->y - limits->y;
+ rect->y = limits->y;
+ }
+
+ if (rect->x + rect->w + expansion <= limits->w)
+ rect->w += expansion;
+ else
+ rect->w = limits->w - rect->x;
+
+ if (rect->y + rect->h + expansion <= limits->h)
+ rect->h += expansion;
+ else
+ rect->h = limits->h - rect->y;
+}
+
+
+// Platform+Scaler function lookups
+
+typedef struct
+{
+ Scale_PlatType_t platform;
+ const Scale_FuncDef_t* funcdefs;
+} Scale_PlatDef_t;
+
+
+static const Scale_PlatDef_t
+Scale_PlatDefs[] =
+{
+#if defined(MMX_ASM)
+ {SCALEPLAT_SSE, Scale_SSE_Functions},
+ {SCALEPLAT_3DNOW, Scale_3DNow_Functions},
+ {SCALEPLAT_MMX, Scale_MMX_Functions},
+#endif /* MMX_ASM */
+ // Default
+ {SCALEPLAT_NULL, Scale_C_Functions}
+};
+
+
+TFB_ScaleFunc
+Scale_PrepPlatform (int flags, const SDL_PixelFormat* fmt)
+{
+ const Scale_PlatDef_t* pdef;
+ const Scale_FuncDef_t* fdef;
+
+ (void)flags;
+
+ Scale_Platform = SCALEPLAT_NULL;
+
+ // first match wins
+ // add better platform techs to the top
+#ifdef MMX_ASM
+ if ( (!force_platform && (SDL_HasSSE () || SDL_HasMMX ()))
+ || force_platform == PLATFORM_SSE)
+ {
+ log_add (log_Info, "Screen scalers are using SSE/MMX-Ext/MMX code");
+ Scale_Platform = SCALEPLAT_SSE;
+
+ Scale_SSE_PrepPlatform (fmt);
+ }
+ else
+ if ( (!force_platform && SDL_HasAltiVec ())
+ || force_platform == PLATFORM_ALTIVEC)
+ {
+ log_add (log_Info, "Screen scalers would use AltiVec code "
+ "if someone actually wrote it");
+ //Scale_Platform = SCALEPLAT_ALTIVEC;
+ }
+ else
+ if ( (!force_platform && SDL_Has3DNow ())
+ || force_platform == PLATFORM_3DNOW)
+ {
+ log_add (log_Info, "Screen scalers are using 3DNow/MMX code");
+ Scale_Platform = SCALEPLAT_3DNOW;
+
+ Scale_3DNow_PrepPlatform (fmt);
+ }
+ else
+ if ( (!force_platform && SDL_HasMMX ())
+ || force_platform == PLATFORM_MMX)
+ {
+ log_add (log_Info, "Screen scalers are using MMX code");
+ Scale_Platform = SCALEPLAT_MMX;
+
+ Scale_MMX_PrepPlatform (fmt);
+ }
+#endif
+
+ if (Scale_Platform == SCALEPLAT_NULL)
+ { // Plain C versions
+ if (fmt->Rmask == 0xff000000 && fmt->Bmask == 0x0000ff00)
+ Scale_Platform = SCALEPLAT_C_RGBA;
+ else if (fmt->Rmask == 0x00ff0000 && fmt->Bmask == 0x000000ff)
+ Scale_Platform = SCALEPLAT_C_ARGB;
+ else if (fmt->Rmask == 0x0000ff00 && fmt->Bmask == 0xff000000)
+ Scale_Platform = SCALEPLAT_C_BGRA;
+ else if (fmt->Rmask == 0x000000ff && fmt->Bmask == 0x00ff0000)
+ Scale_Platform = SCALEPLAT_C_ABGR;
+ else
+ { // use slowest default
+ log_add (log_Warning, "Scale_PrepPlatform(): unknown masks "
+ "(Red %08x, Blue %08x)", fmt->Rmask, fmt->Bmask);
+ Scale_Platform = SCALEPLAT_C;
+ }
+
+ if (Scale_Platform == SCALEPLAT_C)
+ log_add (log_Info, "Screen scalers are using slow generic C code");
+ else
+ log_add (log_Info, "Screen scalers are using optimized C code");
+ }
+
+ // Lookup the scaling function
+ // First find the right platform
+ for (pdef = Scale_PlatDefs;
+ pdef->platform != Scale_Platform && pdef->platform != SCALEPLAT_NULL;
+ ++pdef)
+ ;
+ // Next find the right function
+ for (fdef = pdef->funcdefs;
+ (flags & fdef->flag) != fdef->flag;
+ ++fdef)
+ ;
+
+ return fdef->func;
+}
+
diff --git a/src/libs/graphics/sdl/scalers.h b/src/libs/graphics/sdl/scalers.h
new file mode 100644
index 0000000..cd36fe5
--- /dev/null
+++ b/src/libs/graphics/sdl/scalers.h
@@ -0,0 +1,27 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef SCALERS_H_
+#define SCALERS_H_
+
+void Scale_Init (void);
+
+typedef void (* TFB_ScaleFunc) (SDL_Surface *src, SDL_Surface *dst,
+ SDL_Rect *r);
+
+TFB_ScaleFunc Scale_PrepPlatform (int flags, const SDL_PixelFormat* fmt);
+
+#endif /* SCALERS_H_ */
diff --git a/src/libs/graphics/sdl/sdl1_common.c b/src/libs/graphics/sdl/sdl1_common.c
new file mode 100644
index 0000000..5c675e1
--- /dev/null
+++ b/src/libs/graphics/sdl/sdl1_common.c
@@ -0,0 +1,247 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "sdl_common.h"
+#include "opengl.h"
+#include "pure.h"
+#include "primitives.h"
+#include "options.h"
+#include "uqmversion.h"
+#include "libs/graphics/drawcmd.h"
+#include "libs/graphics/dcqueue.h"
+#include "libs/graphics/cmap.h"
+#include "libs/input/sdl/input.h"
+ // for ProcessInputEvent()
+#include "libs/graphics/bbox.h"
+#include "port.h"
+#include "libs/uio.h"
+#include "libs/log.h"
+#include "libs/memlib.h"
+#include "libs/vidlib.h"
+
+#if SDL_MAJOR_VERSION == 1
+
+static void TFB_PreQuit (void);
+
+void
+TFB_PreInit (void)
+{
+ log_add (log_Info, "Initializing base SDL functionality.");
+ log_add (log_Info, "Using SDL version %d.%d.%d (compiled with "
+ "%d.%d.%d)", SDL_Linked_Version ()->major,
+ SDL_Linked_Version ()->minor, SDL_Linked_Version ()->patch,
+ SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
+#if 0
+ if (SDL_Linked_Version ()->major != SDL_MAJOR_VERSION ||
+ SDL_Linked_Version ()->minor != SDL_MINOR_VERSION ||
+ SDL_Linked_Version ()->patch != SDL_PATCHLEVEL) {
+ log_add (log_Warning, "The used SDL library is not the same version "
+ "as the one used to compile The Ur-Quan Masters with! "
+ "If you experience any crashes, this would be an excellent "
+ "suspect.");
+ }
+#endif
+
+ if ((SDL_Init (SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE) == -1))
+ {
+ log_add (log_Fatal, "Could not initialize SDL: %s.", SDL_GetError ());
+ exit (EXIT_FAILURE);
+ }
+
+ atexit (TFB_PreQuit);
+}
+
+static void
+TFB_PreQuit (void)
+{
+ SDL_Quit ();
+}
+
+int
+TFB_ReInitGraphics (int driver, int flags, int width, int height)
+{
+ int result;
+ int togglefullscreen = 0;
+ char caption[200];
+
+ if (GfxFlags == (flags ^ TFB_GFXFLAGS_FULLSCREEN) &&
+ driver == GraphicsDriver &&
+ width == ScreenWidthActual && height == ScreenHeightActual)
+ {
+ togglefullscreen = 1;
+ }
+
+ GfxFlags = flags;
+
+ if (driver == TFB_GFXDRIVER_SDL_OPENGL)
+ {
+#ifdef HAVE_OPENGL
+ result = TFB_GL_ConfigureVideo (driver, flags, width, height,
+ togglefullscreen);
+#else
+ driver = TFB_GFXDRIVER_SDL_PURE;
+ log_add (log_Warning, "OpenGL support not compiled in,"
+ " so using pure SDL driver");
+ result = TFB_Pure_ConfigureVideo (driver, flags, width, height,
+ togglefullscreen);
+#endif
+ }
+ else
+ {
+ result = TFB_Pure_ConfigureVideo (driver, flags, width, height,
+ togglefullscreen);
+ }
+
+ sprintf (caption, "The Ur-Quan Masters v%d.%d.%d%s",
+ UQM_MAJOR_VERSION, UQM_MINOR_VERSION,
+ UQM_PATCH_VERSION, UQM_EXTRA_VERSION);
+ SDL_WM_SetCaption (caption, NULL);
+
+ if (flags & TFB_GFXFLAGS_FULLSCREEN)
+ SDL_ShowCursor (SDL_DISABLE);
+ else
+ SDL_ShowCursor (SDL_ENABLE);
+
+ return result;
+}
+
+bool
+TFB_SetGamma (float gamma)
+{
+ return (SDL_SetGamma (gamma, gamma, gamma) == 0);
+}
+
+int
+TFB_HasSurfaceAlphaMod (SDL_Surface *surface)
+{
+ if (!surface)
+ {
+ return 0;
+ }
+ return (surface->flags & SDL_SRCALPHA) ? 1 : 0;
+}
+
+int
+TFB_GetSurfaceAlphaMod (SDL_Surface *surface, Uint8 *alpha)
+{
+ if (!surface || !surface->format || !alpha)
+ {
+ return -1;
+ }
+ if (surface->flags & SDL_SRCALPHA)
+ {
+ *alpha = surface->format->alpha;
+ }
+ else
+ {
+ *alpha = 255;
+ }
+ return 0;
+}
+
+int
+TFB_SetSurfaceAlphaMod (SDL_Surface *surface, Uint8 alpha)
+{
+ if (!surface)
+ {
+ return -1;
+ }
+ return SDL_SetAlpha (surface, SDL_SRCALPHA, alpha);
+}
+
+int
+TFB_DisableSurfaceAlphaMod (SDL_Surface *surface)
+{
+ if (!surface)
+ {
+ return -1;
+ }
+ return SDL_SetAlpha (surface, 0, 255);
+}
+
+int
+TFB_GetColorKey (SDL_Surface *surface, Uint32 *key)
+{
+ if (surface && surface->format && key &&
+ (surface->flags & SDL_SRCCOLORKEY))
+ {
+ *key = surface->format->colorkey;
+ return 0;
+ }
+ return -1;
+}
+
+int
+TFB_SetColorKey (SDL_Surface *surface, Uint32 key, int rleaccel)
+{
+ if (!surface)
+ {
+ return -1;
+ }
+ return SDL_SetColorKey (surface, SDL_SRCCOLORKEY | (rleaccel ? SDL_RLEACCEL : 0), key);
+}
+
+int
+TFB_DisableColorKey (SDL_Surface *surface)
+{
+ if (!surface)
+ {
+ return -1;
+ }
+ return SDL_SetColorKey (surface, 0, 0);
+}
+
+int
+TFB_SetColors (SDL_Surface *surface, SDL_Color *colors, int firstcolor, int ncolors)
+{
+ return SDL_SetColors (surface, colors, firstcolor, ncolors);
+}
+
+int
+TFB_SupportsHardwareScaling (void)
+{
+#ifdef HAVE_OPENGL
+ return 1;
+#else
+ return 0;
+#endif
+}
+
+static SDL_Surface *
+Create_Screen (SDL_Surface *templat, int w, int h)
+{
+ SDL_Surface *newsurf = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h,
+ templat->format->BitsPerPixel,
+ templat->format->Rmask, templat->format->Gmask,
+ templat->format->Bmask, 0);
+ if (newsurf == 0) {
+ log_add (log_Error, "Couldn't create screen buffes: %s",
+ SDL_GetError());
+ }
+ return newsurf;
+}
+
+int
+SDL1_ReInit_Screen (SDL_Surface **screen, SDL_Surface *templat, int w, int h)
+{
+ UnInit_Screen (screen);
+ *screen = Create_Screen (templat, w, h);
+
+ return *screen == 0 ? -1 : 0;
+}
+#endif
diff --git a/src/libs/graphics/sdl/sdl2_common.c b/src/libs/graphics/sdl/sdl2_common.c
new file mode 100644
index 0000000..3eaf7af
--- /dev/null
+++ b/src/libs/graphics/sdl/sdl2_common.c
@@ -0,0 +1,222 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "sdl_common.h"
+#include "opengl.h"
+#include "pure.h"
+#include "primitives.h"
+#include "options.h"
+#include "uqmversion.h"
+#include "libs/graphics/drawcmd.h"
+#include "libs/graphics/dcqueue.h"
+#include "libs/graphics/cmap.h"
+#include "libs/input/sdl/input.h"
+ // for ProcessInputEvent()
+#include "libs/graphics/bbox.h"
+#include "port.h"
+#include "libs/uio.h"
+#include "libs/log.h"
+#include "libs/memlib.h"
+#include "libs/vidlib.h"
+
+#if SDL_MAJOR_VERSION > 1
+
+static void TFB_PreQuit (void);
+
+void
+TFB_PreInit (void)
+{
+ SDL_version compiled, linked;
+ SDL_VERSION(&compiled);
+ SDL_GetVersion(&linked);
+ log_add (log_Info, "Initializing base SDL functionality.");
+ log_add (log_Info, "Using SDL version %d.%d.%d (compiled with "
+ "%d.%d.%d)", linked.major, linked.minor, linked.patch,
+ compiled.major, compiled.minor, compiled.patch);
+#if 0
+ if (compiled.major != linked.major || compiled.minor != linked.minor ||
+ compiled.patch != linked.patch)
+ {
+ log_add (log_Warning, "The used SDL library is not the same version "
+ "as the one used to compile The Ur-Quan Masters with! "
+ "If you experience any crashes, this would be an excellent "
+ "suspect.");
+ }
+#endif
+
+ if ((SDL_Init (SDL_INIT_VIDEO) == -1))
+ {
+ log_add (log_Fatal, "Could not initialize SDL: %s.", SDL_GetError ());
+ exit (EXIT_FAILURE);
+ }
+
+ atexit (TFB_PreQuit);
+}
+
+static void
+TFB_PreQuit (void)
+{
+ SDL_Quit ();
+}
+
+int
+TFB_ReInitGraphics (int driver, int flags, int width, int height)
+{
+ int result;
+ int togglefullscreen = 0;
+
+ if (GfxFlags == (flags ^ TFB_GFXFLAGS_FULLSCREEN) &&
+ driver == GraphicsDriver &&
+ width == ScreenWidthActual && height == ScreenHeightActual)
+ {
+ togglefullscreen = 1;
+ }
+
+ GfxFlags = flags;
+
+ result = TFB_Pure_ConfigureVideo (TFB_GFXDRIVER_SDL_PURE, flags,
+ width, height, togglefullscreen);
+
+ if (flags & TFB_GFXFLAGS_FULLSCREEN)
+ SDL_ShowCursor (SDL_DISABLE);
+ else
+ SDL_ShowCursor (SDL_ENABLE);
+
+ return result;
+}
+
+bool
+TFB_SetGamma (float gamma)
+{
+ log_add (log_Warning, "Custom gamma correction is not available in the SDL2 engine.");
+ return 0;
+}
+
+int
+TFB_HasSurfaceAlphaMod (SDL_Surface *surface)
+{
+ SDL_BlendMode blend_mode;
+ if (!surface)
+ {
+ return 0;
+ }
+ if (SDL_GetSurfaceBlendMode (surface, &blend_mode) != 0)
+ {
+ return 0;
+ }
+ return blend_mode == SDL_BLENDMODE_BLEND;
+}
+
+int
+TFB_GetSurfaceAlphaMod (SDL_Surface *surface, Uint8 *alpha)
+{
+ SDL_BlendMode blend_mode;
+ if (!surface || !alpha)
+ {
+ return -1;
+ }
+ if (SDL_GetSurfaceBlendMode (surface, &blend_mode) == 0)
+ {
+ if (blend_mode == SDL_BLENDMODE_BLEND)
+ {
+ return SDL_GetSurfaceAlphaMod (surface, alpha);
+ }
+ }
+ *alpha = 255;
+ return 0;
+}
+
+int
+TFB_SetSurfaceAlphaMod (SDL_Surface *surface, Uint8 alpha)
+{
+ int result;
+ if (!surface)
+ {
+ return -1;
+ }
+ result = SDL_SetSurfaceBlendMode (surface, SDL_BLENDMODE_BLEND);
+ if (result == 0)
+ {
+ result = SDL_SetSurfaceAlphaMod (surface, alpha);
+ }
+ return result;
+}
+
+int
+TFB_DisableSurfaceAlphaMod (SDL_Surface *surface)
+{
+ if (!surface)
+ {
+ return -1;
+ }
+ SDL_SetSurfaceAlphaMod (surface, 255);
+ return SDL_SetSurfaceBlendMode (surface, SDL_BLENDMODE_NONE);
+}
+
+int
+TFB_GetColorKey (SDL_Surface *surface, Uint32 *key)
+{
+ if (!surface || !key)
+ {
+ return -1;
+ }
+ return SDL_GetColorKey (surface, key);
+}
+
+int
+TFB_SetColorKey (SDL_Surface *surface, Uint32 key, int rleaccel)
+{
+ if (!surface)
+ {
+ return -1;
+ }
+ SDL_SetSurfaceRLE (surface, rleaccel);
+ return SDL_SetColorKey (surface, SDL_TRUE, key);
+}
+
+int
+TFB_DisableColorKey (SDL_Surface *surface)
+{
+ if (!surface)
+ {
+ return -1;
+ }
+ return SDL_SetColorKey (surface, SDL_FALSE, 0);
+}
+
+int
+TFB_SetColors (SDL_Surface *surface, SDL_Color *colors, int firstcolor, int ncolors)
+{
+ if (!surface || !colors || !surface->format || !surface->format->palette)
+ {
+ return 0;
+ }
+ if (SDL_SetPaletteColors (surface->format->palette, colors, firstcolor, ncolors) == 0)
+ {
+ // SDL2's success code is opposite from SDL1's SDL_SetColors
+ return 1;
+ }
+ return 0;
+}
+
+int
+TFB_SupportsHardwareScaling (void)
+{
+ return 1;
+}
+#endif
diff --git a/src/libs/graphics/sdl/sdl2_pure.c b/src/libs/graphics/sdl/sdl2_pure.c
new file mode 100644
index 0000000..c6503db
--- /dev/null
+++ b/src/libs/graphics/sdl/sdl2_pure.c
@@ -0,0 +1,465 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "pure.h"
+#include "libs/graphics/bbox.h"
+#include "libs/log.h"
+#include "scalers.h"
+#include "uqmversion.h"
+
+#if SDL_MAJOR_VERSION > 1
+
+typedef struct tfb_sdl2_screeninfo_s {
+ SDL_Surface *scaled;
+ SDL_Texture *texture;
+ BOOLEAN dirty, active;
+ SDL_Rect updated;
+} TFB_SDL2_SCREENINFO;
+
+static TFB_SDL2_SCREENINFO SDL2_Screens[TFB_GFX_NUMSCREENS];
+
+static SDL_Window *window = NULL;
+static SDL_Renderer *renderer = NULL;
+static const char *rendererBackend = NULL;
+
+static int ScreenFilterMode;
+
+static TFB_ScaleFunc scaler = NULL;
+
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+#define A_MASK 0xff000000
+#define B_MASK 0x00ff0000
+#define G_MASK 0x0000ff00
+#define R_MASK 0x000000ff
+#else
+#define A_MASK 0x000000ff
+#define B_MASK 0x0000ff00
+#define G_MASK 0x00ff0000
+#define R_MASK 0xff000000
+#endif
+
+static void TFB_SDL2_Preprocess (int force_full_redraw, int transition_amount, int fade_amount);
+static void TFB_SDL2_Postprocess (void);
+static void TFB_SDL2_UploadTransitionScreen (void);
+static void TFB_SDL2_Scaled_ScreenLayer (SCREEN screen, Uint8 a, SDL_Rect *rect);
+static void TFB_SDL2_Unscaled_ScreenLayer (SCREEN screen, Uint8 a, SDL_Rect *rect);
+static void TFB_SDL2_ColorLayer (Uint8 r, Uint8 g, Uint8 b, Uint8 a, SDL_Rect *rect);
+
+static TFB_GRAPHICS_BACKEND sdl2_scaled_backend = {
+ TFB_SDL2_Preprocess,
+ TFB_SDL2_Postprocess,
+ TFB_SDL2_UploadTransitionScreen,
+ TFB_SDL2_Scaled_ScreenLayer,
+ TFB_SDL2_ColorLayer };
+
+static TFB_GRAPHICS_BACKEND sdl2_unscaled_backend = {
+ TFB_SDL2_Preprocess,
+ TFB_SDL2_Postprocess,
+ TFB_SDL2_UploadTransitionScreen,
+ TFB_SDL2_Unscaled_ScreenLayer,
+ TFB_SDL2_ColorLayer };
+
+static SDL_Surface *
+Create_Screen (int w, int h)
+{
+ SDL_Surface *newsurf = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h,
+ 32, R_MASK, G_MASK, B_MASK, 0);
+ if (newsurf == 0)
+ {
+ log_add (log_Error, "Couldn't create screen buffers: %s",
+ SDL_GetError());
+ }
+ return newsurf;
+}
+
+static int
+ReInit_Screen (SDL_Surface **screen, int w, int h)
+{
+ if (*screen)
+ SDL_FreeSurface (*screen);
+ *screen = Create_Screen (w, h);
+
+ return *screen == 0 ? -1 : 0;
+}
+
+static int
+FindBestRenderDriver (void)
+{
+ int i, n;
+ if (!rendererBackend) {
+ /* If the user has no preference, just let SDL2 choose */
+ return -1;
+ }
+ n = SDL_GetNumRenderDrivers ();
+ log_add (log_Info, "Searching for render driver \"%s\".", rendererBackend);
+
+ for (i = 0; i < n; i++) {
+ SDL_RendererInfo info;
+ if (SDL_GetRenderDriverInfo (i, &info) < 0) {
+ continue;
+ }
+ if (!strcmp(info.name, rendererBackend)) {
+ return i;
+ }
+ log_add (log_Info, "Skipping render driver \"%s\"", info.name);
+ }
+ /* We did not find any accelerated drivers that weren't D3D9.
+ * Return -1 to ask SDL2 to do its best. */
+ log_add (log_Info, "Render driver \"%s\" not available, using system default", rendererBackend);
+ return -1;
+}
+
+int
+TFB_Pure_ConfigureVideo (int driver, int flags, int width, int height, int togglefullscreen)
+{
+ int i;
+ GraphicsDriver = driver;
+ (void) togglefullscreen;
+ if (window == NULL)
+ {
+ SDL_RendererInfo info;
+ char caption[200];
+
+ sprintf (caption, "The Ur-Quan Masters v%d.%d.%d%s",
+ UQM_MAJOR_VERSION, UQM_MINOR_VERSION,
+ UQM_PATCH_VERSION, UQM_EXTRA_VERSION);
+ window = SDL_CreateWindow (caption,
+ SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
+ width, height, 0);
+ if (flags & TFB_GFXFLAGS_FULLSCREEN)
+ {
+ /* If we create the window fullscreen, it will have
+ * no icon if and when it becomes windowed. */
+ SDL_SetWindowFullscreen (window, SDL_WINDOW_FULLSCREEN_DESKTOP);
+ }
+ if (!window)
+ {
+ return -1;
+ }
+ renderer = SDL_CreateRenderer (window, FindBestRenderDriver (), 0);
+ if (!renderer)
+ {
+ return -1;
+ }
+ if (SDL_GetRendererInfo (renderer, &info) == 0)
+ {
+ log_add (log_Info, "SDL2 renderer '%s' selected.\n", info.name);
+ }
+ else
+ {
+ log_add (log_Info, "SDL2 renderer had no name.");
+ }
+ SDL_RenderSetLogicalSize (renderer, ScreenWidth, ScreenHeight);
+ for (i = 0; i < TFB_GFX_NUMSCREENS; i++)
+ {
+ SDL2_Screens[i].scaled = NULL;
+ SDL2_Screens[i].texture = NULL;
+ SDL2_Screens[i].dirty = TRUE;
+ SDL2_Screens[i].active = TRUE;
+ if (0 != ReInit_Screen (&SDL_Screens[i], ScreenWidth, ScreenHeight))
+ {
+ return -1;
+ }
+ }
+ SDL2_Screens[1].active = FALSE;
+ SDL_Screen = SDL_Screens[0];
+ TransitionScreen = SDL_Screens[2];
+ format_conv_surf = SDL_CreateRGBSurface(SDL_SWSURFACE, 0, 0,
+ 32, R_MASK, G_MASK, B_MASK, A_MASK);
+ if (!format_conv_surf)
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ if (flags & TFB_GFXFLAGS_FULLSCREEN)
+ {
+ SDL_SetWindowFullscreen (window, SDL_WINDOW_FULLSCREEN_DESKTOP);
+ }
+ else
+ {
+ SDL_SetWindowFullscreen (window, 0);
+ SDL_SetWindowSize (window, width, height);
+ }
+ }
+
+ if (GfxFlags & TFB_GFXFLAGS_SCALE_ANY)
+ {
+ /* Linear scaling */
+ SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1");
+ }
+ else
+ {
+ /* Nearest-neighbor scaling */
+ SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0");
+ }
+
+ if (GfxFlags & TFB_GFXFLAGS_SCALE_SOFT_ONLY)
+ {
+ for (i = 0; i < TFB_GFX_NUMSCREENS; i++)
+ {
+ if (!SDL2_Screens[i].active)
+ {
+ continue;
+ }
+ if (0 != ReInit_Screen(&SDL2_Screens[i].scaled,
+ ScreenWidth * 2, ScreenHeight * 2))
+ {
+ return -1;
+ }
+ if (SDL2_Screens[i].texture)
+ {
+ SDL_DestroyTexture (SDL2_Screens[i].texture);
+ SDL2_Screens[i].texture = NULL;
+ }
+ SDL2_Screens[i].texture = SDL_CreateTexture (renderer, SDL_PIXELFORMAT_RGBX8888, SDL_TEXTUREACCESS_STREAMING, ScreenWidth * 2, ScreenHeight * 2);
+ SDL_LockSurface (SDL2_Screens[i].scaled);
+ SDL_UpdateTexture (SDL2_Screens[i].texture, NULL, SDL2_Screens[i].scaled->pixels, SDL2_Screens[i].scaled->pitch);
+ SDL_UnlockSurface (SDL2_Screens[i].scaled);
+ }
+ scaler = Scale_PrepPlatform (flags, SDL2_Screens[0].scaled->format);
+ graphics_backend = &sdl2_scaled_backend;
+ }
+ else
+ {
+ for (i = 0; i < TFB_GFX_NUMSCREENS; i++)
+ {
+ if (SDL2_Screens[i].scaled)
+ {
+ SDL_FreeSurface (SDL2_Screens[i].scaled);
+ SDL2_Screens[i].scaled = NULL;
+ }
+ if (SDL2_Screens[i].texture)
+ {
+ SDL_DestroyTexture (SDL2_Screens[i].texture);
+ SDL2_Screens[i].texture = NULL;
+ }
+ SDL2_Screens[i].texture = SDL_CreateTexture (renderer, SDL_PIXELFORMAT_RGBX8888, SDL_TEXTUREACCESS_STREAMING, ScreenWidth, ScreenHeight);
+ SDL_LockSurface (SDL_Screens[i]);
+ SDL_UpdateTexture (SDL2_Screens[i].texture, NULL, SDL_Screens[i]->pixels, SDL_Screens[i]->pitch);
+ SDL_UnlockSurface (SDL_Screens[i]);
+ }
+ scaler = NULL;
+ graphics_backend = &sdl2_unscaled_backend;
+ }
+
+ /* We succeeded, so alter the screen size to our new sizes */
+ ScreenWidthActual = width;
+ ScreenHeightActual = height;
+
+ return 0;
+}
+
+int
+TFB_Pure_InitGraphics (int driver, int flags, const char *renderer, int width, int height)
+{
+ log_add (log_Info, "Initializing SDL.");
+ log_add (log_Info, "SDL initialized.");
+ log_add (log_Info, "Initializing Screen.");
+
+ ScreenWidth = 320;
+ ScreenHeight = 240;
+ rendererBackend = renderer;
+
+ if (TFB_Pure_ConfigureVideo (driver, flags, width, height, 0))
+ {
+ log_add (log_Fatal, "Could not initialize video: %s",
+ SDL_GetError ());
+ exit (EXIT_FAILURE);
+ }
+
+ /* Initialize scalers (let them precompute whatever) */
+ Scale_Init ();
+
+ return 0;
+}
+
+void
+TFB_Pure_UninitGraphics (void)
+{
+ if (renderer) {
+ SDL_DestroyRenderer (renderer);
+ }
+ if (window) {
+ SDL_DestroyWindow (window);
+ }
+}
+
+static void
+TFB_SDL2_UploadTransitionScreen (void)
+{
+ SDL2_Screens[TFB_SCREEN_TRANSITION].updated.x = 0;
+ SDL2_Screens[TFB_SCREEN_TRANSITION].updated.y = 0;
+ SDL2_Screens[TFB_SCREEN_TRANSITION].updated.w = ScreenWidth;
+ SDL2_Screens[TFB_SCREEN_TRANSITION].updated.h = ScreenHeight;
+ SDL2_Screens[TFB_SCREEN_TRANSITION].dirty = TRUE;
+}
+
+static void
+TFB_SDL2_UpdateTexture (SDL_Texture *dest, SDL_Surface *src, SDL_Rect *rect)
+{
+ char *srcBytes;
+ SDL_LockSurface (src);
+ srcBytes = src->pixels;
+ if (rect)
+ {
+ /* SDL2 screen surfaces are always 32bpp */
+ srcBytes += (src->pitch * rect->y) + (rect->x * 4);
+ }
+ /* 2020-08-02: At time of writing, the documentation for
+ * SDL_UpdateTexture states this: "If the texture is intended to be
+ * updated often, it is preferred to create the texture as streaming
+ * and use [SDL_LockTexture and SDL_UnlockTexture]." Unfortunately,
+ * SDL_LockTexture will corrupt driver-space memory in the 32-bit
+ * Direct3D 9 driver on Intel Integrated graphics chips, resulting
+ * in an immediate crash with no detectable errors from the API up
+ * to that point.
+ *
+ * We also cannot simply forbid the Direct3D driver outright, because
+ * pre-Windows 10 machines appear to fail to initialize D3D11 even
+ * while claiming to support it.
+ *
+ * These bugs may be fixed in the future, but in the meantime we
+ * rely on this allegedly slower but definitely more reliable
+ * function. */
+ SDL_UpdateTexture (dest, rect, srcBytes, src->pitch);
+ SDL_UnlockSurface (src);
+}
+
+static void
+TFB_SDL2_ScanLines (void)
+{
+ int y;
+ SDL_SetRenderDrawColor (renderer, 0, 0, 0, 64);
+ SDL_SetRenderDrawBlendMode (renderer, SDL_BLENDMODE_BLEND);
+ SDL_RenderSetLogicalSize (renderer, ScreenWidth * 2, ScreenHeight * 2);
+ for (y = 0; y < ScreenHeight * 2; y += 2)
+ {
+ SDL_RenderDrawLine (renderer, 0, y, ScreenWidth * 2 - 1, y);
+ }
+ SDL_RenderSetLogicalSize (renderer, ScreenWidth, ScreenHeight);
+}
+
+static void
+TFB_SDL2_Preprocess (int force_full_redraw, int transition_amount, int fade_amount)
+{
+ (void) transition_amount;
+ (void) fade_amount;
+
+ if (force_full_redraw == TFB_REDRAW_YES)
+ {
+ SDL2_Screens[TFB_SCREEN_MAIN].updated.x = 0;
+ SDL2_Screens[TFB_SCREEN_MAIN].updated.y = 0;
+ SDL2_Screens[TFB_SCREEN_MAIN].updated.w = ScreenWidth;
+ SDL2_Screens[TFB_SCREEN_MAIN].updated.h = ScreenHeight;
+ SDL2_Screens[TFB_SCREEN_MAIN].dirty = TRUE;
+ }
+ else if (TFB_BBox.valid)
+ {
+ SDL2_Screens[TFB_SCREEN_MAIN].updated.x = TFB_BBox.region.corner.x;
+ SDL2_Screens[TFB_SCREEN_MAIN].updated.y = TFB_BBox.region.corner.y;
+ SDL2_Screens[TFB_SCREEN_MAIN].updated.w = TFB_BBox.region.extent.width;
+ SDL2_Screens[TFB_SCREEN_MAIN].updated.h = TFB_BBox.region.extent.height;
+ SDL2_Screens[TFB_SCREEN_MAIN].dirty = TRUE;
+ }
+
+ SDL_SetRenderDrawBlendMode (renderer, SDL_BLENDMODE_NONE);
+ SDL_SetRenderDrawColor (renderer, 0, 0, 0, 255);
+ SDL_RenderClear (renderer);
+}
+
+static void
+TFB_SDL2_Unscaled_ScreenLayer (SCREEN screen, Uint8 a, SDL_Rect *rect)
+{
+ SDL_Texture *texture = SDL2_Screens[screen].texture;
+ if (SDL2_Screens[screen].dirty)
+ {
+ TFB_SDL2_UpdateTexture (texture, SDL_Screens[screen], &SDL2_Screens[screen].updated);
+ }
+ if (a == 255)
+ {
+ SDL_SetTextureBlendMode (texture, SDL_BLENDMODE_NONE);
+ }
+ else
+ {
+ SDL_SetTextureBlendMode (texture, SDL_BLENDMODE_BLEND);
+ SDL_SetTextureAlphaMod (texture, a);
+ }
+ SDL_RenderCopy (renderer, texture, rect, rect);
+}
+
+static void
+TFB_SDL2_Scaled_ScreenLayer (SCREEN screen, Uint8 a, SDL_Rect *rect)
+{
+ SDL_Texture *texture = SDL2_Screens[screen].texture;
+ SDL_Rect srcRect, *pSrcRect = NULL;
+ if (SDL2_Screens[screen].dirty)
+ {
+ SDL_Surface *src = SDL2_Screens[screen].scaled;
+ SDL_Rect scaled_update = SDL2_Screens[screen].updated;
+ scaler (SDL_Screens[screen], src, &SDL2_Screens[screen].updated);
+ scaled_update.x *= 2;
+ scaled_update.y *= 2;
+ scaled_update.w *= 2;
+ scaled_update.h *= 2;
+ TFB_SDL2_UpdateTexture (texture, src, &scaled_update);
+ }
+ if (a == 255)
+ {
+ SDL_SetTextureBlendMode (texture, SDL_BLENDMODE_NONE);
+ }
+ else
+ {
+ SDL_SetTextureBlendMode (texture, SDL_BLENDMODE_BLEND);
+ SDL_SetTextureAlphaMod (texture, a);
+ }
+ /* The texture has twice the resolution when scaled, but the
+ * screen's logical resolution has not changed, so the clip
+ * rectangle does not need to be scaled. The *source* clip
+ * rect, however, must be scaled to match. */
+ if (rect)
+ {
+ srcRect = *rect;
+ srcRect.x *= 2;
+ srcRect.y *= 2;
+ srcRect.w *= 2;
+ srcRect.h *= 2;
+ pSrcRect = &srcRect;
+ }
+ SDL_RenderCopy (renderer, texture, pSrcRect, rect);
+}
+
+static void
+TFB_SDL2_ColorLayer (Uint8 r, Uint8 g, Uint8 b, Uint8 a, SDL_Rect *rect)
+{
+ SDL_SetRenderDrawBlendMode (renderer, a == 255 ? SDL_BLENDMODE_NONE
+ : SDL_BLENDMODE_BLEND);
+ SDL_SetRenderDrawColor (renderer, r, g, b, a);
+ SDL_RenderFillRect (renderer, rect);
+}
+
+static void
+TFB_SDL2_Postprocess (void)
+{
+ if (GfxFlags & TFB_GFXFLAGS_SCANLINES)
+ TFB_SDL2_ScanLines ();
+
+ SDL_RenderPresent (renderer);
+}
+
+#endif
diff --git a/src/libs/graphics/sdl/sdl_common.c b/src/libs/graphics/sdl/sdl_common.c
new file mode 100644
index 0000000..b699bf8
--- /dev/null
+++ b/src/libs/graphics/sdl/sdl_common.c
@@ -0,0 +1,308 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "sdl_common.h"
+#include "opengl.h"
+#include "pure.h"
+#include "primitives.h"
+#include "options.h"
+#include "uqmversion.h"
+#include "libs/graphics/drawcmd.h"
+#include "libs/graphics/dcqueue.h"
+#include "libs/graphics/cmap.h"
+#include "libs/input/sdl/input.h"
+ // for ProcessInputEvent()
+#include "libs/graphics/bbox.h"
+#include "port.h"
+#include "libs/uio.h"
+#include "libs/log.h"
+#include "libs/memlib.h"
+#include "libs/vidlib.h"
+
+SDL_Surface *SDL_Screen;
+SDL_Surface *TransitionScreen;
+
+SDL_Surface *SDL_Screens[TFB_GFX_NUMSCREENS];
+
+SDL_Surface *format_conv_surf = NULL;
+
+static volatile BOOLEAN abortFlag = FALSE;
+
+int GfxFlags = 0;
+
+TFB_GRAPHICS_BACKEND *graphics_backend = NULL;
+
+volatile int QuitPosted = 0;
+volatile int GameActive = 1; // Track the SDL_ACTIVEEVENT state SDL_APPACTIVE
+
+int
+TFB_InitGraphics (int driver, int flags, const char *renderer, int width, int height)
+{
+ int result, i;
+ char caption[200];
+
+ /* Null out screen pointers the first time */
+ for (i = 0; i < TFB_GFX_NUMSCREENS; i++)
+ {
+ SDL_Screens[i] = NULL;
+ }
+
+ GfxFlags = flags;
+
+ if (driver == TFB_GFXDRIVER_SDL_OPENGL)
+ {
+#ifdef HAVE_OPENGL
+ result = TFB_GL_InitGraphics (driver, flags, width, height);
+#else
+ driver = TFB_GFXDRIVER_SDL_PURE;
+ log_add (log_Warning, "OpenGL support not compiled in,"
+ " so using pure SDL driver");
+ result = TFB_Pure_InitGraphics (driver, flags, renderer, width, height);
+#endif
+ }
+ else
+ {
+ result = TFB_Pure_InitGraphics (driver, flags, renderer, width, height);
+ }
+
+#if SDL_MAJOR_VERSION == 1
+ /* Other versions do this when setting up the window */
+ sprintf (caption, "The Ur-Quan Masters v%d.%d.%d%s",
+ UQM_MAJOR_VERSION, UQM_MINOR_VERSION,
+ UQM_PATCH_VERSION, UQM_EXTRA_VERSION);
+ SDL_WM_SetCaption (caption, NULL);
+#endif
+
+ if (flags & TFB_GFXFLAGS_FULLSCREEN)
+ SDL_ShowCursor (SDL_DISABLE);
+
+ Init_DrawCommandQueue ();
+
+ TFB_DrawCanvas_Initialize ();
+
+ return 0;
+}
+
+void
+TFB_UninitGraphics (void)
+{
+ int i;
+
+ Uninit_DrawCommandQueue ();
+
+ for (i = 0; i < TFB_GFX_NUMSCREENS; i++)
+ UnInit_Screen (&SDL_Screens[i]);
+
+ TFB_Pure_UninitGraphics ();
+#ifdef HAVE_OPENGL
+ TFB_GL_UninitGraphics ();
+#endif
+
+ UnInit_Screen (&format_conv_surf);
+}
+
+void
+TFB_ProcessEvents ()
+{
+ SDL_Event Event;
+
+ while (SDL_PollEvent (&Event) > 0)
+ {
+ /* Run through the InputEvent filter. */
+ ProcessInputEvent (&Event);
+ /* Handle graphics and exposure events. */
+ switch (Event.type) {
+#if 0 /* Currently disabled in mainline */
+ case SDL_ACTIVEEVENT: /* Lose/gain visibility or focus */
+ /* Up to three different state changes can occur in one event. */
+ /* Here, disregard least significant change (mouse focus). */
+ // This controls the automatic sleep/pause when minimized.
+ // On small displays (e.g. mobile devices), APPINPUTFOCUS would
+ // be an appropriate substitution for APPACTIVE:
+ // if (Event.active.state & SDL_APPINPUTFOCUS)
+ if (Event.active.state & SDL_APPACTIVE)
+ GameActive = Event.active.gain;
+ break;
+ case SDL_VIDEORESIZE: /* User resized video mode */
+ // TODO
+ break;
+#endif
+ case SDL_QUIT:
+ QuitPosted = 1;
+ break;
+#if SDL_MAJOR_VERSION == 1
+ case SDL_VIDEOEXPOSE: /* Screen needs to be redrawn */
+ TFB_SwapBuffers (TFB_REDRAW_EXPOSE);
+ break;
+#else
+ case SDL_WINDOWEVENT:
+ if (Event.window.event == SDL_WINDOWEVENT_EXPOSED)
+ {
+ /* Screen needs to be redrawn */
+ TFB_SwapBuffers (TFB_REDRAW_EXPOSE);
+ }
+ break;
+#endif
+ default:
+ break;
+ }
+ }
+}
+
+static BOOLEAN system_box_active = 0;
+static SDL_Rect system_box;
+
+void
+SetSystemRect (const RECT *r)
+{
+ system_box_active = TRUE;
+ system_box.x = r->corner.x;
+ system_box.y = r->corner.y;
+ system_box.w = r->extent.width;
+ system_box.h = r->extent.height;
+}
+
+void
+ClearSystemRect (void)
+{
+ system_box_active = FALSE;
+}
+
+void
+TFB_SwapBuffers (int force_full_redraw)
+{
+ static int last_fade_amount = 255, last_transition_amount = 255;
+ static int fade_amount = 255, transition_amount = 255;
+
+ fade_amount = GetFadeAmount ();
+ transition_amount = TransitionAmount;
+
+ if (force_full_redraw == TFB_REDRAW_NO && !TFB_BBox.valid &&
+ fade_amount == 255 && transition_amount == 255 &&
+ last_fade_amount == 255 && last_transition_amount == 255)
+ return;
+
+ if (force_full_redraw == TFB_REDRAW_NO &&
+ (fade_amount != 255 || transition_amount != 255 ||
+ last_fade_amount != 255 || last_transition_amount != 255))
+ force_full_redraw = TFB_REDRAW_FADING;
+
+ last_fade_amount = fade_amount;
+ last_transition_amount = transition_amount;
+
+ graphics_backend->preprocess (force_full_redraw, transition_amount,
+ fade_amount);
+ graphics_backend->screen (TFB_SCREEN_MAIN, 255, NULL);
+
+ if (transition_amount != 255)
+ {
+ SDL_Rect r;
+ r.x = TransitionClipRect.corner.x;
+ r.y = TransitionClipRect.corner.y;
+ r.w = TransitionClipRect.extent.width;
+ r.h = TransitionClipRect.extent.height;
+ graphics_backend->screen (TFB_SCREEN_TRANSITION,
+ 255 - transition_amount, &r);
+ }
+
+ if (fade_amount != 255)
+ {
+ if (fade_amount < 255)
+ {
+ graphics_backend->color (0, 0, 0, 255 - fade_amount, NULL);
+ }
+ else
+ {
+ graphics_backend->color (255, 255, 255,
+ fade_amount - 255, NULL);
+ }
+ }
+
+ if (system_box_active)
+ {
+ graphics_backend->screen (TFB_SCREEN_MAIN, 255, &system_box);
+ }
+
+ graphics_backend->postprocess ();
+}
+
+/* Probably ought to clean this away at some point. */
+SDL_Surface *
+TFB_DisplayFormatAlpha (SDL_Surface *surface)
+{
+ SDL_Surface* newsurf;
+ SDL_PixelFormat* dstfmt;
+ const SDL_PixelFormat* srcfmt = surface->format;
+
+ // figure out what format to use (alpha/no alpha)
+ if (surface->format->Amask)
+ dstfmt = format_conv_surf->format;
+ else
+ dstfmt = SDL_Screen->format;
+
+ if (srcfmt->BytesPerPixel == dstfmt->BytesPerPixel &&
+ srcfmt->Rmask == dstfmt->Rmask &&
+ srcfmt->Gmask == dstfmt->Gmask &&
+ srcfmt->Bmask == dstfmt->Bmask &&
+ srcfmt->Amask == dstfmt->Amask)
+ return surface; // no conversion needed
+
+ newsurf = SDL_ConvertSurface (surface, dstfmt, surface->flags);
+ // Colorkeys and surface-level alphas cannot work at the same time,
+ // so we need to disable one of them
+ if (TFB_HasColorKey (surface) && newsurf &&
+ TFB_HasColorKey (newsurf) &&
+ TFB_HasSurfaceAlphaMod (newsurf))
+ {
+ TFB_DisableSurfaceAlphaMod (newsurf);
+ }
+
+ return newsurf;
+}
+
+// This function should only be called from the graphics thread,
+// like from a TFB_DrawCommand_Callback command.
+TFB_Canvas
+TFB_GetScreenCanvas (SCREEN screen)
+{
+ return SDL_Screens[screen];
+}
+
+void
+TFB_UploadTransitionScreen (void)
+{
+ graphics_backend->uploadTransitionScreen ();
+}
+
+int
+TFB_HasColorKey (SDL_Surface *surface)
+{
+ Uint32 key;
+ return TFB_GetColorKey (surface, &key) == 0;
+}
+
+void
+UnInit_Screen (SDL_Surface **screen)
+{
+ if (*screen == NULL) {
+ return;
+ }
+
+ SDL_FreeSurface (*screen);
+ *screen = NULL;
+}
diff --git a/src/libs/graphics/sdl/sdl_common.h b/src/libs/graphics/sdl/sdl_common.h
new file mode 100644
index 0000000..76d1cb6
--- /dev/null
+++ b/src/libs/graphics/sdl/sdl_common.h
@@ -0,0 +1,63 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef SDL_COMMON_H
+#define SDL_COMMON_H
+
+#include "port.h"
+#include SDL_INCLUDE(SDL.h)
+
+#include "../gfxintrn.h"
+#include "libs/graphics/tfb_draw.h"
+#include "libs/graphics/gfx_common.h"
+
+// The Graphics Backend vtable
+typedef struct _tfb_graphics_backend {
+ void (*preprocess) (int force_redraw, int transition_amount, int fade_amount);
+ void (*postprocess) (void);
+ void (*uploadTransitionScreen) (void);
+ void (*screen) (SCREEN screen, Uint8 alpha, SDL_Rect *rect);
+ void (*color) (Uint8 r, Uint8 g, Uint8 b, Uint8 a, SDL_Rect *rect);
+} TFB_GRAPHICS_BACKEND;
+
+extern TFB_GRAPHICS_BACKEND *graphics_backend;
+
+extern SDL_Surface *SDL_Screen;
+extern SDL_Surface *TransitionScreen;
+
+extern SDL_Surface *SDL_Screens[TFB_GFX_NUMSCREENS];
+
+extern SDL_Surface *format_conv_surf;
+
+SDL_Surface* TFB_DisplayFormatAlpha (SDL_Surface *surface);
+int TFB_HasSurfaceAlphaMod (SDL_Surface *surface);
+int TFB_GetSurfaceAlphaMod (SDL_Surface *surface, Uint8 *alpha);
+int TFB_SetSurfaceAlphaMod (SDL_Surface *surface, Uint8 alpha);
+int TFB_DisableSurfaceAlphaMod (SDL_Surface *surface);
+int TFB_HasColorKey (SDL_Surface *surface);
+int TFB_GetColorKey (SDL_Surface *surface, Uint32 *key);
+int TFB_SetColorKey (SDL_Surface *surface, Uint32 key, int rleaccel);
+int TFB_DisableColorKey (SDL_Surface *surface);
+int TFB_SetColors (SDL_Surface *surface, SDL_Color *colors, int firstcolor, int ncolors);
+
+#if SDL_MAJOR_VERSION == 1
+int SDL1_ReInit_Screen (SDL_Surface **screen, SDL_Surface *templat, int w, int h);
+#endif
+void UnInit_Screen (SDL_Surface **screen);
+
+#endif
diff --git a/src/libs/graphics/sdl/sdluio.c b/src/libs/graphics/sdl/sdluio.c
new file mode 100644
index 0000000..5e4554d
--- /dev/null
+++ b/src/libs/graphics/sdl/sdluio.c
@@ -0,0 +1,153 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "sdluio.h"
+
+#include "port.h"
+#include "libs/uio.h"
+#include SDL_INCLUDE(SDL.h)
+#include SDL_INCLUDE(SDL_error.h)
+#include SDL_INCLUDE(SDL_rwops.h)
+#include "libs/memlib.h"
+#include "png2sdl.h"
+#include <errno.h>
+#include <string.h>
+
+
+static SDL_RWops *sdluio_makeRWops (uio_Stream *stream);
+
+#if 0
+// For use for initialisation, using structure assignment.
+static SDL_RWops sdluio_templateRWops =
+{
+ .seek = sdluio_seek,
+ .read = sdluio_read,
+ .write = sdluio_write,
+ .close = sdluio_close,
+};
+#endif
+
+SDL_Surface *
+sdluio_loadImage (uio_DirHandle *dir, const char *fileName) {
+ uio_Stream *stream;
+ SDL_RWops *rwops;
+ SDL_Surface *result = NULL;
+
+ stream = uio_fopen (dir, fileName, "rb");
+ if (stream == NULL)
+ {
+ SDL_SetError ("Couldn't open '%s': %s", fileName,
+ strerror(errno));
+ return NULL;
+ }
+ rwops = sdluio_makeRWops (stream);
+ if (rwops) {
+ result = TFB_png_to_sdl (rwops);
+ SDL_RWclose (rwops);
+ }
+ return result;
+}
+
+#if SDL_MAJOR_VERSION == 1
+int
+sdluio_seek (SDL_RWops *context, int offset, int whence)
+#else
+Sint64
+sdluio_seek (SDL_RWops *context, Sint64 offset, int whence)
+#endif
+{
+ if (uio_fseek ((uio_Stream *) context->hidden.unknown.data1, offset,
+ whence) == -1)
+ {
+ SDL_SetError ("Error seeking in uio_Stream: %s",
+ strerror(errno));
+ return -1;
+ }
+ return uio_ftell ((uio_Stream *) context->hidden.unknown.data1);
+}
+
+#if SDL_MAJOR_VERSION == 1
+int
+sdluio_read (SDL_RWops *context, void *ptr, int size, int maxnum)
+#else
+size_t
+sdluio_read (SDL_RWops *context, void *ptr, size_t size, size_t maxnum)
+#endif
+{
+ size_t numRead;
+
+ numRead = uio_fread (ptr, (size_t) size, (size_t) maxnum,
+ (uio_Stream *) context->hidden.unknown.data1);
+ if (numRead == 0 && uio_ferror ((uio_Stream *)
+ context->hidden.unknown.data1))
+ {
+ SDL_SetError ("Error reading from uio_Stream: %s",
+ strerror(errno));
+ return 0;
+ }
+ return (int) numRead;
+}
+
+#if SDL_MAJOR_VERSION == 1
+int
+sdluio_write (SDL_RWops *context, const void *ptr, int size, int num)
+#else
+size_t
+sdluio_write (SDL_RWops *context, const void *ptr, size_t size, size_t num)
+#endif
+{
+ size_t numWritten;
+
+ numWritten = uio_fwrite (ptr, (size_t) size, (size_t) num,
+ (uio_Stream *) context->hidden.unknown.data1);
+ if (numWritten == 0 && uio_ferror ((uio_Stream *)
+ context->hidden.unknown.data1))
+ {
+ SDL_SetError ("Error writing to uio_Stream: %s",
+ strerror(errno));
+ return 0;
+ }
+ return (size_t) numWritten;
+}
+
+int
+sdluio_close (SDL_RWops *context) {
+ int result;
+
+ result = uio_fclose ((uio_Stream *) context->hidden.unknown.data1);
+ HFree (context);
+ return result;
+}
+
+static SDL_RWops *
+sdluio_makeRWops (uio_Stream *stream) {
+ SDL_RWops *result;
+
+ result = HMalloc (sizeof (SDL_RWops));
+#if 0
+ *(struct SDL_RWops *) result = sdluio_templateRWops;
+ // structure assignment
+#endif
+ result->seek = sdluio_seek;
+ result->read = sdluio_read;
+ result->write = sdluio_write;
+ result->close = sdluio_close;
+ result->hidden.unknown.data1 = stream;
+ return result;
+}
+
+
+
diff --git a/src/libs/graphics/sdl/sdluio.h b/src/libs/graphics/sdl/sdluio.h
new file mode 100644
index 0000000..0f8c701
--- /dev/null
+++ b/src/libs/graphics/sdl/sdluio.h
@@ -0,0 +1,39 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_GRAPHICS_SDL_SDLUIO_H_
+#define LIBS_GRAPHICS_SDL_SDLUIO_H_
+
+#include "port.h"
+#include "libs/uio.h"
+#include SDL_INCLUDE(SDL.h)
+#include SDL_INCLUDE(SDL_rwops.h)
+
+SDL_Surface *sdluio_loadImage (uio_DirHandle *dir, const char *fileName);
+#if SDL_MAJOR_VERSION == 1
+int sdluio_seek (SDL_RWops *context, int offset, int whence);
+int sdluio_read (SDL_RWops *context, void *ptr, int size, int maxnum);
+int sdluio_write (SDL_RWops *context, const void *ptr, int size, int num);
+#else
+Sint64 sdlui_seek (SDL_RWops *context, Sint64 offset, int whence);
+size_t sdlui_read (SDL_RWops *context, void *ptr, size_t size, size_t maxnum);
+size_t sdlui_write (SDL_RWops *contex, const void *ptr, size_t size, size_t num);
+#endif
+int sdluio_close (SDL_RWops *context);
+
+
+#endif /* LIBS_GRAPHICS_SDL_SDLUIO_H_ */
+
diff --git a/src/libs/graphics/sdl/triscan2x.c b/src/libs/graphics/sdl/triscan2x.c
new file mode 100644
index 0000000..830dc7c
--- /dev/null
+++ b/src/libs/graphics/sdl/triscan2x.c
@@ -0,0 +1,155 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+// Core algorithm of the Triscan screen scaler (based on Scale2x)
+// (for scale2x please see http://scale2x.sf.net)
+// Template
+// When this file is built standalone is produces a plain C version
+// Also #included by 2xscalers_mmx.c for an MMX version
+
+#include "libs/graphics/sdl/sdl_common.h"
+#include "types.h"
+#include "scalers.h"
+#include "scaleint.h"
+#include "2xscalers.h"
+
+
+// Triscan scaling to 2x
+// derivative of scale2x -- scale2x.sf.net
+// The name expands to either
+// Scale_TriScanFilter (for plain C) or
+// Scale_MMX_TriScanFilter (for MMX)
+// [others when platforms are added]
+void
+SCALE_(TriScanFilter) (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r)
+{
+ int x, y;
+ const int w = src->w, h = src->h;
+ int xend, yend;
+ int dsrc, ddst;
+ SDL_Rect *region = r;
+ SDL_Rect limits;
+ SDL_PixelFormat *fmt = dst->format;
+ const int sp = src->pitch, dp = dst->pitch;
+ const int bpp = fmt->BytesPerPixel;
+ const int slen = sp / bpp, dlen = dp / bpp;
+ // for clarity purposes, the 'pixels' array here is transposed
+ Uint32 pixels[3][3];
+ Uint32 *src_p = (Uint32 *)src->pixels;
+ Uint32 *dst_p = (Uint32 *)dst->pixels;
+
+ int prevline, nextline;
+
+ // these macros are for clarity; they make the current pixel (0,0)
+ // and allow to access pixels in all directions
+ #define PIX(x, y) (pixels[1 + (x)][1 + (y)])
+
+ #define TRISCAN_YUV_MED 100
+ // medium tolerance pixel comparison
+ #define TRISCAN_CMPYUV(p1, p2) \
+ (PIX p1 == PIX p2 || SCALE_CMPYUV (PIX p1, PIX p2, TRISCAN_YUV_MED))
+
+
+ SCALE_(PlatInit) ();
+
+ // expand updated region if necessary
+ // pixels neighbooring the updated region may
+ // change as a result of updates
+ limits.x = 0;
+ limits.y = 0;
+ limits.w = src->w;
+ limits.h = src->h;
+ Scale_ExpandRect (region, 1, &limits);
+
+ xend = region->x + region->w;
+ yend = region->y + region->h;
+ dsrc = slen - region->w;
+ ddst = (dlen - region->w) * 2;
+
+ // move ptrs to the first updated pixel
+ src_p += slen * region->y + region->x;
+ dst_p += (dlen * region->y + region->x) * 2;
+
+ for (y = region->y; y < yend; ++y, dst_p += ddst, src_p += dsrc)
+ {
+ if (y > 0)
+ prevline = -slen;
+ else
+ prevline = 0;
+
+ if (y < h - 1)
+ nextline = slen;
+ else
+ nextline = 0;
+
+ // prime the (tiny) sliding-window pixel arrays
+ PIX( 1, 0) = src_p[0];
+
+ if (region->x > 0)
+ PIX( 0, 0) = src_p[-1];
+ else
+ PIX( 0, 0) = PIX( 1, 0);
+
+ for (x = region->x; x < xend; ++x, ++src_p, dst_p += 2)
+ {
+ // slide the window
+ PIX(-1, 0) = PIX( 0, 0);
+
+ PIX( 0, -1) = src_p[prevline];
+ PIX( 0, 0) = PIX( 1, 0);
+ PIX( 0, 1) = src_p[nextline];
+
+ if (x < w - 1)
+ PIX( 1, 0) = src_p[1];
+ else
+ PIX( 1, 0) = PIX( 0, 0);
+
+ if (!TRISCAN_CMPYUV (( 0, -1), ( 0, 1)) &&
+ !TRISCAN_CMPYUV ((-1, 0), ( 1, 0)))
+ {
+ if (TRISCAN_CMPYUV ((-1, 0), ( 0, -1)))
+ dst_p[0] = Scale_Blend_11 (PIX(-1, 0), PIX(0, -1));
+ else
+ dst_p[0] = PIX(0, 0);
+
+ if (TRISCAN_CMPYUV (( 1, 0), ( 0, -1)))
+ dst_p[1] = Scale_Blend_11 (PIX(1, 0), PIX(0, -1));
+ else
+ dst_p[1] = PIX(0, 0);
+
+ if (TRISCAN_CMPYUV ((-1, 0), ( 0, 1)))
+ dst_p[dlen] = Scale_Blend_11 (PIX(-1, 0), PIX(0, 1));
+ else
+ dst_p[dlen] = PIX(0, 0);
+
+ if (TRISCAN_CMPYUV (( 1, 0), ( 0, 1)))
+ dst_p[dlen+1] = Scale_Blend_11 (PIX(1, 0), PIX(0, 1));
+ else
+ dst_p[dlen+1] = PIX(0, 0);
+ }
+ else
+ {
+ dst_p[0] = PIX(0, 0);
+ dst_p[1] = PIX(0, 0);
+ dst_p[dlen] = PIX(0, 0);
+ dst_p[dlen+1] = PIX(0, 0);
+ }
+ }
+ }
+
+ SCALE_(PlatDone) ();
+}
+
diff --git a/src/libs/graphics/tfb_draw.c b/src/libs/graphics/tfb_draw.c
new file mode 100644
index 0000000..1ac3c34
--- /dev/null
+++ b/src/libs/graphics/tfb_draw.c
@@ -0,0 +1,493 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "gfx_common.h"
+#include "tfb_draw.h"
+#include "drawcmd.h"
+#include "libs/log.h"
+#include "libs/memlib.h"
+
+
+static const HOT_SPOT NullHs = {0, 0};
+
+void
+TFB_DrawScreen_Line (int x1, int y1, int x2, int y2, Color color,
+ DrawMode mode, SCREEN dest)
+{
+ TFB_DrawCommand DC;
+
+ DC.Type = TFB_DRAWCOMMANDTYPE_LINE;
+ DC.data.line.x1 = x1;
+ DC.data.line.y1 = y1;
+ DC.data.line.x2 = x2;
+ DC.data.line.y2 = y2;
+ DC.data.line.color = color;
+ DC.data.line.drawMode = mode;
+ DC.data.line.destBuffer = dest;
+
+ TFB_EnqueueDrawCommand (&DC);
+}
+
+void
+TFB_DrawScreen_Rect (RECT *rect, Color color, DrawMode mode, SCREEN dest)
+{
+ RECT locRect;
+ TFB_DrawCommand DC;
+
+ if (!rect)
+ {
+ locRect.corner.x = locRect.corner.y = 0;
+ locRect.extent.width = ScreenWidth;
+ locRect.extent.height = ScreenHeight;
+ rect = &locRect;
+ }
+
+ DC.Type = TFB_DRAWCOMMANDTYPE_RECTANGLE;
+ DC.data.rect.rect = *rect;
+ DC.data.rect.color = color;
+ DC.data.rect.drawMode = mode;
+ DC.data.rect.destBuffer = dest;
+
+ TFB_EnqueueDrawCommand (&DC);
+}
+
+void
+TFB_DrawScreen_Image (TFB_Image *img, int x, int y, int scale,
+ int scaleMode, TFB_ColorMap *cmap, DrawMode mode, SCREEN dest)
+{
+ TFB_DrawCommand DC;
+
+ DC.Type = TFB_DRAWCOMMANDTYPE_IMAGE;
+ DC.data.image.image = img;
+ DC.data.image.colormap = cmap;
+ DC.data.image.x = x;
+ DC.data.image.y = y;
+ DC.data.image.scale = (scale == GSCALE_IDENTITY) ? 0 : scale;
+ DC.data.image.scaleMode = scaleMode;
+ DC.data.image.drawMode = mode;
+ DC.data.image.destBuffer = dest;
+
+ TFB_EnqueueDrawCommand (&DC);
+}
+
+void
+TFB_DrawScreen_FilledImage (TFB_Image *img, int x, int y, int scale,
+ int scaleMode, Color color, DrawMode mode, SCREEN dest)
+{
+ TFB_DrawCommand DC;
+
+ DC.Type = TFB_DRAWCOMMANDTYPE_FILLEDIMAGE;
+ DC.data.filledimage.image = img;
+ DC.data.filledimage.x = x;
+ DC.data.filledimage.y = y;
+ DC.data.filledimage.scale = (scale == GSCALE_IDENTITY) ? 0 : scale;
+ DC.data.filledimage.scaleMode = scaleMode;
+ DC.data.filledimage.color = color;
+ DC.data.filledimage.drawMode = mode;
+ DC.data.filledimage.destBuffer = dest;
+
+ TFB_EnqueueDrawCommand (&DC);
+}
+
+void
+TFB_DrawScreen_FontChar (TFB_Char *fontChar, TFB_Image *backing,
+ int x, int y, DrawMode mode, SCREEN dest)
+{
+ TFB_DrawCommand DC;
+
+ DC.Type = TFB_DRAWCOMMANDTYPE_FONTCHAR;
+ DC.data.fontchar.fontchar = fontChar;
+ DC.data.fontchar.backing = backing;
+ DC.data.fontchar.x = x;
+ DC.data.fontchar.y = y;
+ DC.data.fontchar.drawMode = mode;
+ DC.data.fontchar.destBuffer = dest;
+
+ TFB_EnqueueDrawCommand (&DC);
+}
+
+void
+TFB_DrawScreen_CopyToImage (TFB_Image *img, const RECT *r, SCREEN src)
+{
+ TFB_DrawCommand DC;
+
+ DC.Type = TFB_DRAWCOMMANDTYPE_COPYTOIMAGE;
+ DC.data.copytoimage.rect = *r;
+ DC.data.copytoimage.image = img;
+ DC.data.copytoimage.srcBuffer = src;
+
+ TFB_EnqueueDrawCommand (&DC);
+}
+
+void
+TFB_DrawScreen_Copy (const RECT *r, SCREEN src, SCREEN dest)
+{
+ RECT locRect;
+ TFB_DrawCommand DC;
+
+ if (!r)
+ {
+ locRect.corner.x = locRect.corner.y = 0;
+ locRect.extent.width = ScreenWidth;
+ locRect.extent.height = ScreenHeight;
+ r = &locRect;
+ }
+
+ DC.Type = TFB_DRAWCOMMANDTYPE_COPY;
+ DC.data.copy.rect = *r;
+ DC.data.copy.srcBuffer = src;
+ DC.data.copy.destBuffer = dest;
+
+ TFB_EnqueueDrawCommand (&DC);
+}
+
+void
+TFB_DrawScreen_SetMipmap (TFB_Image *img, TFB_Image *mmimg, int hotx, int hoty)
+{
+ TFB_DrawCommand DC;
+
+ DC.Type = TFB_DRAWCOMMANDTYPE_SETMIPMAP;
+ DC.data.setmipmap.image = img;
+ DC.data.setmipmap.mipmap = mmimg;
+ DC.data.setmipmap.hotx = hotx;
+ DC.data.setmipmap.hoty = hoty;
+
+ TFB_EnqueueDrawCommand (&DC);
+}
+
+void
+TFB_DrawScreen_DeleteImage (TFB_Image *img)
+{
+ if (img)
+ {
+ TFB_DrawCommand DC;
+
+ DC.Type = TFB_DRAWCOMMANDTYPE_DELETEIMAGE;
+ DC.data.deleteimage.image = img;
+
+ TFB_EnqueueDrawCommand (&DC);
+ }
+}
+
+void
+TFB_DrawScreen_DeleteData (void *data)
+ // data must be a result of HXalloc() call
+{
+ if (data)
+ {
+ TFB_DrawCommand DC;
+
+ DC.Type = TFB_DRAWCOMMANDTYPE_DELETEDATA;
+ DC.data.deletedata.data = data;
+
+ TFB_EnqueueDrawCommand (&DC);
+ }
+}
+
+void
+TFB_DrawScreen_WaitForSignal (void)
+{
+ TFB_DrawCommand DrawCommand;
+ Semaphore s;
+ s = GetMyThreadLocal ()->flushSem;
+ DrawCommand.Type = TFB_DRAWCOMMANDTYPE_SENDSIGNAL;
+ DrawCommand.data.sendsignal.sem = s;
+ Lock_DCQ (1);
+ TFB_BatchReset ();
+ TFB_EnqueueDrawCommand (&DrawCommand);
+ Unlock_DCQ();
+ SetSemaphore (s);
+}
+
+void
+TFB_DrawScreen_ReinitVideo (int driver, int flags, int width, int height)
+{
+ TFB_DrawCommand DrawCommand;
+ DrawCommand.Type = TFB_DRAWCOMMANDTYPE_REINITVIDEO;
+ DrawCommand.data.reinitvideo.driver = driver;
+ DrawCommand.data.reinitvideo.flags = flags;
+ DrawCommand.data.reinitvideo.width = width;
+ DrawCommand.data.reinitvideo.height = height;
+ TFB_EnqueueDrawCommand (&DrawCommand);
+}
+
+void
+TFB_DrawScreen_Callback (void (*callback) (void *arg), void *arg)
+{
+ TFB_DrawCommand DrawCommand;
+ DrawCommand.Type = TFB_DRAWCOMMANDTYPE_CALLBACK;
+ DrawCommand.data.callback.callback = callback;
+ DrawCommand.data.callback.arg = arg;
+ TFB_EnqueueDrawCommand(&DrawCommand);
+}
+
+void
+TFB_DrawImage_Line (int x1, int y1, int x2, int y2, Color color,
+ DrawMode mode, TFB_Image *target)
+{
+ LockMutex (target->mutex);
+ TFB_DrawCanvas_Line (x1, y1, x2, y2, color, mode, target->NormalImg);
+ target->dirty = TRUE;
+ UnlockMutex (target->mutex);
+}
+
+void
+TFB_DrawImage_Rect (RECT *rect, Color color, DrawMode mode, TFB_Image *target)
+{
+ LockMutex (target->mutex);
+ TFB_DrawCanvas_Rect (rect, color, mode, target->NormalImg);
+ target->dirty = TRUE;
+ UnlockMutex (target->mutex);
+}
+
+void
+TFB_DrawImage_Image (TFB_Image *img, int x, int y, int scale,
+ int scaleMode, TFB_ColorMap *cmap, DrawMode mode, TFB_Image *target)
+{
+ LockMutex (target->mutex);
+ TFB_DrawCanvas_Image (img, x, y, scale, scaleMode, cmap,
+ mode, target->NormalImg);
+ target->dirty = TRUE;
+ UnlockMutex (target->mutex);
+}
+
+void
+TFB_DrawImage_FilledImage (TFB_Image *img, int x, int y, int scale,
+ int scaleMode, Color color, DrawMode mode, TFB_Image *target)
+{
+ LockMutex (target->mutex);
+ TFB_DrawCanvas_FilledImage (img, x, y, scale, scaleMode, color,
+ mode, target->NormalImg);
+ target->dirty = TRUE;
+ UnlockMutex (target->mutex);
+}
+
+void
+TFB_DrawImage_FontChar (TFB_Char *fontChar, TFB_Image *backing,
+ int x, int y, DrawMode mode, TFB_Image *target)
+{
+ LockMutex (target->mutex);
+ TFB_DrawCanvas_FontChar (fontChar, backing, x, y, mode, target->NormalImg);
+ target->dirty = TRUE;
+ UnlockMutex (target->mutex);
+}
+
+
+TFB_Image *
+TFB_DrawImage_New (TFB_Canvas canvas)
+{
+ TFB_Image *img = HMalloc (sizeof (TFB_Image));
+ img->mutex = CreateMutex ("image lock", SYNC_CLASS_VIDEO);
+ img->ScaledImg = NULL;
+ img->MipmapImg = NULL;
+ img->FilledImg = NULL;
+ img->colormap_index = -1;
+ img->colormap_version = 0;
+ img->NormalHs = NullHs;
+ img->MipmapHs = NullHs;
+ img->last_scale_hs = NullHs;
+ img->last_scale_type = -1;
+ img->last_scale = 0;
+ img->dirty = FALSE;
+ TFB_DrawCanvas_GetExtent (canvas, &img->extent);
+
+ if (TFB_DrawCanvas_IsPaletted (canvas))
+ {
+ img->NormalImg = canvas;
+ }
+ else
+ {
+ img->NormalImg = TFB_DrawCanvas_ToScreenFormat (canvas);
+ }
+
+ return img;
+}
+
+TFB_Image*
+TFB_DrawImage_CreateForScreen (int w, int h, BOOLEAN withalpha)
+{
+ TFB_Image* img = HMalloc (sizeof (TFB_Image));
+ img->mutex = CreateMutex ("image lock", SYNC_CLASS_VIDEO);
+ img->ScaledImg = NULL;
+ img->MipmapImg = NULL;
+ img->FilledImg = NULL;
+ img->colormap_index = -1;
+ img->colormap_version = 0;
+ img->NormalHs = NullHs;
+ img->MipmapHs = NullHs;
+ img->last_scale_hs = NullHs;
+ img->last_scale_type = -1;
+ img->last_scale = 0;
+ img->extent.width = w;
+ img->extent.height = h;
+
+ img->NormalImg = TFB_DrawCanvas_New_ForScreen (w, h, withalpha);
+
+ return img;
+}
+
+TFB_Image *
+TFB_DrawImage_New_Rotated (TFB_Image *img, int angle)
+{
+ TFB_Canvas dst;
+ EXTENT size;
+ TFB_Image* newimg;
+
+ /* sanity check */
+ if (!img->NormalImg)
+ {
+ log_add (log_Warning, "TFB_DrawImage_New_Rotated: "
+ "source canvas is NULL! Failing.");
+ return NULL;
+ }
+
+ TFB_DrawCanvas_GetRotatedExtent (img->NormalImg, angle, &size);
+ dst = TFB_DrawCanvas_New_RotationTarget (img->NormalImg, angle);
+ if (!dst)
+ {
+ log_add (log_Warning, "TFB_DrawImage_New_Rotated: "
+ "rotation target canvas not created! Failing.");
+ return NULL;
+ }
+ TFB_DrawCanvas_Rotate (img->NormalImg, dst, angle, size);
+
+ newimg = TFB_DrawImage_New (dst);
+ return newimg;
+}
+
+void
+TFB_DrawImage_SetMipmap (TFB_Image *img, TFB_Image *mmimg, int hotx, int hoty)
+{
+ bool imgpal;
+ bool mmpal;
+
+ if (!img || !mmimg)
+ return;
+
+ LockMutex (img->mutex);
+ LockMutex (mmimg->mutex);
+
+ // Either both images must be using the same colormap, or mipmap image
+ // must not be paletted. This restriction is due to the current
+ // implementation of fill-stamp, which replaces the palette with
+ // fill color.
+ imgpal = TFB_DrawCanvas_IsPaletted (img->NormalImg);
+ mmpal = TFB_DrawCanvas_IsPaletted (mmimg->NormalImg);
+ if (!mmpal || (mmpal && imgpal &&
+ img->colormap_index == mmimg->colormap_index))
+ {
+ img->MipmapImg = mmimg->NormalImg;
+ img->MipmapHs.x = hotx;
+ img->MipmapHs.y = hoty;
+ }
+ else
+ {
+ img->MipmapImg = NULL;
+ }
+
+ UnlockMutex (mmimg->mutex);
+ UnlockMutex (img->mutex);
+}
+
+void
+TFB_DrawImage_Delete (TFB_Image *image)
+{
+ if (image == 0)
+ {
+ log_add (log_Warning, "INTERNAL ERROR: Tried to delete a null image!");
+ /* Should we die here? */
+ return;
+ }
+ LockMutex (image->mutex);
+
+ TFB_DrawCanvas_Delete (image->NormalImg);
+
+ if (image->ScaledImg)
+ {
+ TFB_DrawCanvas_Delete (image->ScaledImg);
+ image->ScaledImg = 0;
+ }
+
+ if (image->FilledImg)
+ {
+ TFB_DrawCanvas_Delete (image->FilledImg);
+ image->FilledImg = 0;
+ }
+
+ UnlockMutex (image->mutex);
+ DestroyMutex (image->mutex);
+
+ HFree (image);
+}
+
+void
+TFB_DrawImage_FixScaling (TFB_Image *image, int target, int type)
+{
+ if (image->dirty || !image->ScaledImg ||
+ target != image->last_scale ||
+ type != image->last_scale_type)
+ {
+ image->dirty = FALSE;
+ image->ScaledImg = TFB_DrawCanvas_New_ScaleTarget (image->NormalImg,
+ image->ScaledImg, type, image->last_scale_type);
+
+ if (type == TFB_SCALE_NEAREST)
+ TFB_DrawCanvas_Rescale_Nearest (image->NormalImg,
+ image->ScaledImg, target, &image->NormalHs,
+ &image->extent, &image->last_scale_hs);
+ else if (type == TFB_SCALE_BILINEAR)
+ TFB_DrawCanvas_Rescale_Bilinear (image->NormalImg,
+ image->ScaledImg, target, &image->NormalHs,
+ &image->extent, &image->last_scale_hs);
+ else
+ TFB_DrawCanvas_Rescale_Trilinear (image->NormalImg,
+ image->MipmapImg, image->ScaledImg, target,
+ &image->NormalHs, &image->MipmapHs,
+ &image->extent, &image->last_scale_hs);
+
+ image->last_scale_type = type;
+ image->last_scale = target;
+ }
+}
+
+BOOLEAN
+TFB_DrawImage_Intersect (TFB_Image *img1, POINT img1org,
+ TFB_Image *img2, POINT img2org, const RECT *interRect)
+{
+ BOOLEAN ret;
+
+ LockMutex (img1->mutex);
+ LockMutex (img2->mutex);
+ ret = TFB_DrawCanvas_Intersect (img1->NormalImg, img1org,
+ img2->NormalImg, img2org, interRect);
+ UnlockMutex (img2->mutex);
+ UnlockMutex (img1->mutex);
+
+ return ret;
+}
+
+void
+TFB_DrawImage_CopyRect (TFB_Image *source, const RECT *srcRect,
+ TFB_Image *target, POINT dstPt)
+{
+ LockMutex (source->mutex);
+ LockMutex (target->mutex);
+ TFB_DrawCanvas_CopyRect (source->NormalImg, srcRect,
+ target->NormalImg, dstPt);
+ target->dirty = TRUE;
+ UnlockMutex (target->mutex);
+ UnlockMutex (source->mutex);
+}
diff --git a/src/libs/graphics/tfb_draw.h b/src/libs/graphics/tfb_draw.h
new file mode 100644
index 0000000..11de5b0
--- /dev/null
+++ b/src/libs/graphics/tfb_draw.h
@@ -0,0 +1,199 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef TFB_DRAW_H
+#define TFB_DRAW_H
+
+#include "libs/threadlib.h"
+
+
+typedef void *TFB_Canvas;
+
+typedef enum {
+ TFB_SCREEN_MAIN,
+ TFB_SCREEN_EXTRA,
+ TFB_SCREEN_TRANSITION,
+
+ TFB_GFX_NUMSCREENS
+} SCREEN;
+
+#include "libs/graphics/gfx_common.h"
+#include "libs/graphics/cmap.h"
+
+typedef struct tfb_image
+{
+ TFB_Canvas NormalImg;
+ TFB_Canvas ScaledImg;
+ TFB_Canvas MipmapImg;
+ TFB_Canvas FilledImg;
+ int colormap_index;
+ int colormap_version;
+ HOT_SPOT NormalHs;
+ HOT_SPOT MipmapHs;
+ HOT_SPOT last_scale_hs;
+ int last_scale;
+ int last_scale_type;
+ Color last_fill;
+ EXTENT extent;
+ Mutex mutex;
+ BOOLEAN dirty;
+} TFB_Image;
+
+typedef struct tfb_char
+{
+ EXTENT extent;
+ EXTENT disp;
+ // Display extent
+ HOT_SPOT HotSpot;
+ BYTE* data;
+ DWORD pitch;
+ // Pitch is for storing all chars of a page
+ // in one rectangular pixel matrix
+} TFB_Char;
+
+// we do not support paletted format for now
+typedef struct tfb_pixelformat
+{
+ int BitsPerPixel;
+ int BytesPerPixel;
+ DWORD Rmask, Gmask, Bmask, Amask;
+ DWORD Rshift, Gshift, Bshift, Ashift;
+ DWORD Rloss, Gloss, Bloss, Aloss;
+} TFB_PixelFormat;
+
+// Drawing commands
+
+void TFB_DrawScreen_Line (int x1, int y1, int x2, int y2, Color color,
+ DrawMode, SCREEN dest);
+void TFB_DrawScreen_Rect (RECT *rect, Color, DrawMode, SCREEN dest);
+void TFB_DrawScreen_Image (TFB_Image *img, int x, int y, int scale,
+ int scaleMode, TFB_ColorMap *, DrawMode, SCREEN dest);
+void TFB_DrawScreen_Copy (const RECT *r, SCREEN src, SCREEN dest);
+void TFB_DrawScreen_FilledImage (TFB_Image *img, int x, int y, int scale,
+ int scaleMode, Color, DrawMode, SCREEN dest);
+void TFB_DrawScreen_FontChar (TFB_Char *, TFB_Image *backing, int x, int y,
+ DrawMode, SCREEN dest);
+
+void TFB_DrawScreen_CopyToImage (TFB_Image *img, const RECT *r, SCREEN src);
+void TFB_DrawScreen_SetMipmap (TFB_Image *img, TFB_Image *mmimg, int hotx,
+ int hoty);
+void TFB_DrawScreen_DeleteImage (TFB_Image *img);
+void TFB_DrawScreen_DeleteData (void *);
+void TFB_DrawScreen_WaitForSignal (void);
+void TFB_DrawScreen_ReinitVideo (int driver, int flags, int width, int height);
+void TFB_DrawScreen_Callback (void (*callback) (void *arg), void *arg);
+
+TFB_Image *TFB_DrawImage_New (TFB_Canvas canvas);
+TFB_Image *TFB_DrawImage_CreateForScreen (int w, int h, BOOLEAN withalpha);
+TFB_Image *TFB_DrawImage_New_Rotated (TFB_Image *img, int angle);
+void TFB_DrawImage_SetMipmap (TFB_Image *img, TFB_Image *mmimg, int hotx,
+ int hoty);
+void TFB_DrawImage_Delete (TFB_Image *image);
+void TFB_DrawImage_FixScaling (TFB_Image *image, int target, int type);
+BOOLEAN TFB_DrawImage_Intersect (TFB_Image *img1, POINT img1org,
+ TFB_Image *img2, POINT img2org, const RECT *interRect);
+void TFB_DrawImage_CopyRect (TFB_Image *source, const RECT *srcRect,
+ TFB_Image *target, POINT dstPt);
+
+void TFB_DrawImage_Line (int x1, int y1, int x2, int y2, Color color,
+ DrawMode, TFB_Image *target);
+void TFB_DrawImage_Rect (RECT *rect, Color, DrawMode, TFB_Image *target);
+void TFB_DrawImage_Image (TFB_Image *img, int x, int y, int scale,
+ int scaleMode, TFB_ColorMap *, DrawMode, TFB_Image *target);
+void TFB_DrawImage_FilledImage (TFB_Image *img, int x, int y, int scale,
+ int scaleMode, Color, DrawMode, TFB_Image *target);
+void TFB_DrawImage_FontChar (TFB_Char *, TFB_Image *backing, int x, int y,
+ DrawMode, TFB_Image *target);
+
+TFB_Canvas TFB_DrawCanvas_LoadFromFile (void *dir, const char *fileName);
+TFB_Canvas TFB_DrawCanvas_New_TrueColor (int w, int h, BOOLEAN hasalpha);
+TFB_Canvas TFB_DrawCanvas_New_ForScreen (int w, int h, BOOLEAN withalpha);
+TFB_Canvas TFB_DrawCanvas_New_Paletted (int w, int h, Color palette[256],
+ int transparent_index);
+TFB_Canvas TFB_DrawCanvas_New_ScaleTarget (TFB_Canvas canvas,
+ TFB_Canvas oldcanvas, int type, int last_type);
+TFB_Canvas TFB_DrawCanvas_New_RotationTarget (TFB_Canvas src, int angle);
+TFB_Canvas TFB_DrawCanvas_ToScreenFormat (TFB_Canvas canvas);
+BOOLEAN TFB_DrawCanvas_IsPaletted (TFB_Canvas canvas);
+void TFB_DrawCanvas_Rescale_Nearest (TFB_Canvas src, TFB_Canvas dst,
+ int scale, HOT_SPOT* src_hs, EXTENT* size, HOT_SPOT* dst_hs);
+void TFB_DrawCanvas_Rescale_Bilinear (TFB_Canvas src, TFB_Canvas dst,
+ int scale, HOT_SPOT* src_hs, EXTENT* size, HOT_SPOT* dst_hs);
+void TFB_DrawCanvas_Rescale_Trilinear (TFB_Canvas src, TFB_Canvas mipmap,
+ TFB_Canvas dst, int scale, HOT_SPOT* src_hs, HOT_SPOT* mm_hs,
+ EXTENT* size, HOT_SPOT* dst_hs);
+void TFB_DrawCanvas_GetScaledExtent (TFB_Canvas src_canvas, HOT_SPOT* src_hs,
+ TFB_Canvas src_mipmap, HOT_SPOT* mm_hs,
+ int scale, int type, EXTENT *size, HOT_SPOT *hs);
+void TFB_DrawCanvas_Rotate (TFB_Canvas src, TFB_Canvas dst, int angle,
+ EXTENT size);
+void TFB_DrawCanvas_GetRotatedExtent (TFB_Canvas src, int angle, EXTENT *size);
+void TFB_DrawCanvas_GetExtent (TFB_Canvas canvas, EXTENT *size);
+void TFB_DrawCanvas_SetClipRect (TFB_Canvas canvas, const RECT *clipRect);
+
+void TFB_DrawCanvas_Delete (TFB_Canvas canvas);
+
+void TFB_DrawCanvas_Line (int x1, int y1, int x2, int y2, Color color,
+ DrawMode, TFB_Canvas target);
+void TFB_DrawCanvas_Rect (RECT *rect, Color, DrawMode, TFB_Canvas target);
+void TFB_DrawCanvas_Image (TFB_Image *img, int x, int y, int scale,
+ int scaleMode, TFB_ColorMap *, DrawMode, TFB_Canvas target);
+void TFB_DrawCanvas_FilledImage (TFB_Image *img, int x, int y, int scale,
+ int scaleMode, Color, DrawMode, TFB_Canvas target);
+void TFB_DrawCanvas_FontChar (TFB_Char *, TFB_Image *backing, int x, int y,
+ DrawMode, TFB_Canvas target);
+void TFB_DrawCanvas_CopyRect (TFB_Canvas source, const RECT *srcRect,
+ TFB_Canvas target, POINT dstPt);
+
+BOOLEAN TFB_DrawCanvas_GetFontCharData (TFB_Canvas canvas, BYTE *outData,
+ unsigned dataPitch);
+Color *TFB_DrawCanvas_ExtractPalette (TFB_Canvas canvas);
+void TFB_DrawCanvas_SetPalette (TFB_Canvas target, Color palette[256]);
+int TFB_DrawCanvas_GetTransparentIndex (TFB_Canvas canvas);
+void TFB_DrawCanvas_SetTransparentIndex (TFB_Canvas canvas, int i,
+ BOOLEAN rleaccel);
+BOOLEAN TFB_DrawCanvas_GetTransparentColor (TFB_Canvas canvas,
+ Color *color);
+void TFB_DrawCanvas_SetTransparentColor (TFB_Canvas canvas,
+ Color color, BOOLEAN rleaccel);
+void TFB_DrawCanvas_CopyTransparencyInfo (TFB_Canvas src, TFB_Canvas dst);
+void TFB_DrawCanvas_Initialize (void);
+void TFB_DrawCanvas_Lock (TFB_Canvas canvas);
+void TFB_DrawCanvas_Unlock (TFB_Canvas canvas);
+void TFB_DrawCanvas_GetScreenFormat (TFB_PixelFormat *fmt);
+int TFB_DrawCanvas_GetStride (TFB_Canvas canvas);
+void *TFB_DrawCanvas_GetLine (TFB_Canvas canvas, int line);
+Color TFB_DrawCanvas_GetPixel (TFB_Canvas canvas, int x, int y);
+BOOLEAN TFB_DrawCanvas_Intersect (TFB_Canvas canvas1, POINT c1org,
+ TFB_Canvas canvas2, POINT c2org, const RECT *interRect);
+
+BOOLEAN TFB_DrawCanvas_GetPixelColors (TFB_Canvas, Color *pixels,
+ int width, int height);
+BOOLEAN TFB_DrawCanvas_SetPixelColors (TFB_Canvas, const Color *pixels,
+ int width, int height);
+BOOLEAN TFB_DrawCanvas_GetPixelIndexes (TFB_Canvas, BYTE *data,
+ int width, int height);
+BOOLEAN TFB_DrawCanvas_SetPixelIndexes (TFB_Canvas, const BYTE *data,
+ int width, int height);
+
+const char *TFB_DrawCanvas_GetError (void);
+
+TFB_Canvas TFB_GetScreenCanvas (SCREEN screen);
+
+#endif
+
diff --git a/src/libs/graphics/tfb_prim.c b/src/libs/graphics/tfb_prim.c
new file mode 100644
index 0000000..f8a2df4
--- /dev/null
+++ b/src/libs/graphics/tfb_prim.c
@@ -0,0 +1,237 @@
+// Copyright Michael Martin, 2003
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* The original Primitive routines do various elaborate checks to
+ * ensure we're within bounds for the clipping. Since clipping is
+ * handled by the underlying TFB_Canvas implementation, we need not
+ * worry about this. */
+
+#include "gfxintrn.h"
+#include "gfx_common.h"
+#include "tfb_draw.h"
+#include "tfb_prim.h"
+#include "cmap.h"
+#include "libs/log.h"
+
+void
+TFB_Prim_Point (POINT *p, Color color, DrawMode mode, POINT ctxOrigin)
+{
+ RECT r;
+
+ // The caller must scale the origin!
+ r.corner.x = p->x + ctxOrigin.x;
+ r.corner.y = p->y + ctxOrigin.y;
+ r.extent.width = r.extent.height = 1;
+
+ if (_CurFramePtr->Type == SCREEN_DRAWABLE)
+ TFB_DrawScreen_Rect (&r, color, mode, TFB_SCREEN_MAIN);
+ else
+ TFB_DrawImage_Rect (&r, color, mode, _CurFramePtr->image);
+}
+
+void
+TFB_Prim_Rect (RECT *r, Color color, DrawMode mode, POINT ctxOrigin)
+{
+ RECT arm;
+ int gscale;
+
+ // XXX: Rect prim scaling is currently unused
+ // We scale the rect size just to be consistent with stamp prim,
+ // which does same. The caller must scale the origin!
+ gscale = GetGraphicScale ();
+ arm = *r;
+ arm.extent.width = r->extent.width;
+ arm.extent.height = 1;
+ TFB_Prim_FillRect (&arm, color, mode, ctxOrigin);
+ arm.extent.height = r->extent.height;
+ arm.extent.width = 1;
+ TFB_Prim_FillRect (&arm, color, mode, ctxOrigin);
+ // rounding error correction here
+ arm.corner.x += ((r->extent.width * gscale + (GSCALE_IDENTITY >> 1))
+ / GSCALE_IDENTITY) - 1;
+ TFB_Prim_FillRect (&arm, color, mode, ctxOrigin);
+ arm.corner.x = r->corner.x;
+ arm.corner.y += ((r->extent.height * gscale + (GSCALE_IDENTITY >> 1))
+ / GSCALE_IDENTITY) - 1;
+ arm.extent.width = r->extent.width;
+ arm.extent.height = 1;
+ TFB_Prim_FillRect (&arm, color, mode, ctxOrigin);
+}
+
+void
+TFB_Prim_FillRect (RECT *r, Color color, DrawMode mode, POINT ctxOrigin)
+{
+ RECT rect;
+ int gscale;
+
+ rect.corner.x = r->corner.x + ctxOrigin.x;
+ rect.corner.y = r->corner.y + ctxOrigin.y;
+ rect.extent.width = r->extent.width;
+ rect.extent.height = r->extent.height;
+
+ // XXX: Rect prim scaling is currently unused
+ // We scale the rect size just to be consistent with stamp prim,
+ // which does same. The caller must scale the origin!
+ gscale = GetGraphicScale ();
+ if (gscale != GSCALE_IDENTITY)
+ { // rounding error correction here
+ rect.extent.width = (rect.extent.width * gscale
+ + (GSCALE_IDENTITY >> 1)) / GSCALE_IDENTITY;
+ rect.extent.height = (rect.extent.height * gscale
+ + (GSCALE_IDENTITY >> 1)) / GSCALE_IDENTITY;
+ }
+
+ if (_CurFramePtr->Type == SCREEN_DRAWABLE)
+ TFB_DrawScreen_Rect (&rect, color, mode, TFB_SCREEN_MAIN);
+ else
+ TFB_DrawImage_Rect (&rect, color, mode, _CurFramePtr->image);
+}
+
+void
+TFB_Prim_Line (LINE *line, Color color, DrawMode mode, POINT ctxOrigin)
+{
+ int x1, y1, x2, y2;
+
+ // The caller must scale the origins!
+ x1=line->first.x + ctxOrigin.x;
+ y1=line->first.y + ctxOrigin.y;
+ x2=line->second.x + ctxOrigin.x;
+ y2=line->second.y + ctxOrigin.y;
+
+ if (_CurFramePtr->Type == SCREEN_DRAWABLE)
+ TFB_DrawScreen_Line (x1, y1, x2, y2, color, mode, TFB_SCREEN_MAIN);
+ else
+ TFB_DrawImage_Line (x1, y1, x2, y2, color, mode, _CurFramePtr->image);
+}
+
+void
+TFB_Prim_Stamp (STAMP *stmp, DrawMode mode, POINT ctxOrigin)
+{
+ int x, y;
+ FRAME SrcFramePtr;
+ TFB_Image *img;
+ TFB_ColorMap *cmap = NULL;
+
+ SrcFramePtr = stmp->frame;
+ if (!SrcFramePtr)
+ {
+ log_add (log_Warning, "TFB_Prim_Stamp: Tried to draw a NULL frame"
+ " (Stamp address = %p)", (void *) stmp);
+ return;
+ }
+ img = SrcFramePtr->image;
+
+ if (!img)
+ {
+ log_add (log_Warning, "Non-existent image to TFB_Prim_Stamp()");
+ return;
+ }
+
+ LockMutex (img->mutex);
+
+ img->NormalHs = SrcFramePtr->HotSpot;
+ // We scale the image size here, but the caller must scale the origin!
+ x = stmp->origin.x + ctxOrigin.x;
+ y = stmp->origin.y + ctxOrigin.y;
+
+ if (TFB_DrawCanvas_IsPaletted(img->NormalImg) && img->colormap_index != -1)
+ {
+ // returned cmap is addrefed, must release later
+ cmap = TFB_GetColorMap (img->colormap_index);
+ }
+
+ UnlockMutex (img->mutex);
+
+ if (_CurFramePtr->Type == SCREEN_DRAWABLE)
+ {
+ TFB_DrawScreen_Image (img, x, y, GetGraphicScale (),
+ GetGraphicScaleMode (), cmap, mode, TFB_SCREEN_MAIN);
+ }
+ else
+ {
+ TFB_DrawImage_Image (img, x, y, GetGraphicScale (),
+ GetGraphicScaleMode (), cmap, mode, _CurFramePtr->image);
+ }
+}
+
+void
+TFB_Prim_StampFill (STAMP *stmp, Color color, DrawMode mode, POINT ctxOrigin)
+{
+ int x, y;
+ FRAME SrcFramePtr;
+ TFB_Image *img;
+
+ SrcFramePtr = stmp->frame;
+ if (!SrcFramePtr)
+ {
+ log_add (log_Warning, "TFB_Prim_StampFill: Tried to draw a NULL frame"
+ " (Stamp address = %p)", (void *) stmp);
+ return;
+ }
+ img = SrcFramePtr->image;
+
+ if (!img)
+ {
+ log_add (log_Warning, "Non-existent image to TFB_Prim_StampFill()");
+ return;
+ }
+
+ LockMutex (img->mutex);
+
+ img->NormalHs = SrcFramePtr->HotSpot;
+ // We scale the image size here, but the caller must scale the origin!
+ x = stmp->origin.x + ctxOrigin.x;
+ y = stmp->origin.y + ctxOrigin.y;
+
+ UnlockMutex (img->mutex);
+
+ if (_CurFramePtr->Type == SCREEN_DRAWABLE)
+ {
+ TFB_DrawScreen_FilledImage (img, x, y, GetGraphicScale (),
+ GetGraphicScaleMode (), color, mode, TFB_SCREEN_MAIN);
+ }
+ else
+ {
+ TFB_DrawImage_FilledImage (img, x, y, GetGraphicScale (),
+ GetGraphicScaleMode (), color, mode, _CurFramePtr->image);
+ }
+}
+
+void
+TFB_Prim_FontChar (POINT charOrigin, TFB_Char *fontChar, TFB_Image *backing,
+ DrawMode mode, POINT ctxOrigin)
+{
+ int x, y;
+
+ // Text prim does not scale
+ x = charOrigin.x + ctxOrigin.x;
+ y = charOrigin.y + ctxOrigin.y;
+
+ if (_CurFramePtr->Type == SCREEN_DRAWABLE)
+ {
+ TFB_DrawScreen_FontChar (fontChar, backing, x, y, mode,
+ TFB_SCREEN_MAIN);
+ }
+ else
+ {
+ TFB_DrawImage_FontChar (fontChar, backing, x, y, mode,
+ _CurFramePtr->image);
+ }
+}
+
+// Text rendering is in font.c, under the name _text_blt
diff --git a/src/libs/graphics/tfb_prim.h b/src/libs/graphics/tfb_prim.h
new file mode 100644
index 0000000..6fcd546
--- /dev/null
+++ b/src/libs/graphics/tfb_prim.h
@@ -0,0 +1,30 @@
+// Copyright Michael Martin, 2003
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "libs/gfxlib.h"
+#include "tfb_draw.h"
+
+
+void TFB_Prim_Line (LINE *, Color, DrawMode, POINT ctxOrigin);
+void TFB_Prim_Point (POINT *, Color, DrawMode, POINT ctxOrigin);
+void TFB_Prim_Rect (RECT *, Color, DrawMode, POINT ctxOrigin);
+void TFB_Prim_FillRect (RECT *, Color, DrawMode, POINT ctxOrigin);
+void TFB_Prim_Stamp (STAMP *, DrawMode, POINT ctxOrigin);
+void TFB_Prim_StampFill (STAMP *, Color, DrawMode, POINT ctxOrigin);
+void TFB_Prim_FontChar (POINT charOrigin, TFB_Char *fontChar,
+ TFB_Image *backing, DrawMode, POINT ctxOrigin);
diff --git a/src/libs/graphics/widgets.c b/src/libs/graphics/widgets.c
new file mode 100644
index 0000000..0c444bc
--- /dev/null
+++ b/src/libs/graphics/widgets.c
@@ -0,0 +1,941 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "gfx_common.h"
+#include "widgets.h"
+#include "libs/strlib.h"
+
+WIDGET *widget_focus = NULL;
+
+/* Some basic color defines */
+#define WIDGET_ACTIVE_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x00), 0x0E)
+#define WIDGET_INACTIVE_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x18, 0x18, 0x1F), 0x00)
+#define WIDGET_INACTIVE_SELECTED_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x1F), 0x0F)
+#define WIDGET_CURSOR_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x00), 0x00)
+#define WIDGET_DIALOG_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x14, 0x14, 0x14), 0x07)
+#define WIDGET_DIALOG_TEXT_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x00), 0x00)
+
+static Color win_bg_clr =
+ BUILD_COLOR (MAKE_RGB15_INIT (0x18, 0x18, 0x1F), 0x00);
+static Color win_medium_clr =
+ BUILD_COLOR (MAKE_RGB15_INIT (0x10, 0x10, 0x18), 0x00);
+static Color win_dark_clr =
+ BUILD_COLOR (MAKE_RGB15_INIT (0x08, 0x08, 0x10), 0x00);
+
+static FONT cur_font;
+
+void
+DrawShadowedBox (RECT *r, Color bg, Color dark, Color medium)
+{
+ RECT t;
+ Color oldcolor;
+
+ BatchGraphics ();
+
+ t.corner.x = r->corner.x - 2;
+ t.corner.y = r->corner.y - 2;
+ t.extent.width = r->extent.width + 4;
+ t.extent.height = r->extent.height + 4;
+ oldcolor = SetContextForeGroundColor (dark);
+ DrawFilledRectangle (&t);
+
+ t.corner.x += 2;
+ t.corner.y += 2;
+ t.extent.width -= 2;
+ t.extent.height -= 2;
+ SetContextForeGroundColor (medium);
+ DrawFilledRectangle (&t);
+
+ t.corner.x -= 1;
+ t.corner.y += r->extent.height + 1;
+ t.extent.height = 1;
+ DrawFilledRectangle (&t);
+
+ t.corner.x += r->extent.width + 2;
+ t.corner.y -= r->extent.height + 2;
+ t.extent.width = 1;
+ DrawFilledRectangle (&t);
+
+ SetContextForeGroundColor (bg);
+ DrawFilledRectangle (r);
+
+ SetContextForeGroundColor (oldcolor);
+ UnbatchGraphics ();
+}
+
+// windowRect, if not NULL, will be filled with the dimensions of the
+// window drawn.
+void
+DrawLabelAsWindow (WIDGET_LABEL *label, RECT *windowRect)
+{
+ Color oldfg = SetContextForeGroundColor (WIDGET_DIALOG_TEXT_COLOR);
+ FONT oldfont = 0;
+ FRAME oldFontEffect = SetContextFontEffect (NULL);
+ RECT r;
+ TEXT t;
+ int i, win_w, win_h;
+
+ if (cur_font)
+ oldfont = SetContextFont (cur_font);
+
+ /* Compute the dimensions of the label */
+ win_h = label->height ((WIDGET *)label) + 16;
+ win_w = 0;
+ for (i = 0; i < label->line_count; i++)
+ {
+ int len = utf8StringCount (label->lines[i]);
+ if (len > win_w)
+ {
+ win_w = len;
+ }
+ }
+ win_w = (win_w * 6) + 16;
+
+ BatchGraphics ();
+ r.corner.x = (ScreenWidth - win_w) >> 1;
+ r.corner.y = (ScreenHeight - win_h) >> 1;
+ r.extent.width = win_w;
+ r.extent.height = win_h;
+ DrawShadowedBox (&r, win_bg_clr, win_dark_clr, win_medium_clr);
+
+ t.baseline.x = r.corner.x + (r.extent.width >> 1);
+ t.baseline.y = r.corner.y + 16;
+ for (i = 0; i < label->line_count; i++)
+ {
+ t.pStr = label->lines[i];
+ t.align = ALIGN_CENTER;
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += 8;
+ }
+
+ UnbatchGraphics ();
+
+ SetContextFontEffect (oldFontEffect);
+ if (oldfont)
+ SetContextFont (oldfont);
+ SetContextForeGroundColor (oldfg);
+
+ if (windowRect != NULL) {
+ // Add the outer border added by DrawShadowedBox.
+ // XXX: It may be nicer to add a border size parameter to
+ // DrawShadowedBox, instead of assuming 2 here.
+ windowRect->corner.x = r.corner.x - 2;
+ windowRect->corner.y = r.corner.y - 2;
+ windowRect->extent.width = r.extent.width + 4;
+ windowRect->extent.height = r.extent.height + 4;
+ }
+}
+
+void
+Widget_SetWindowColors (Color bg, Color dark, Color medium)
+{
+ win_bg_clr = bg;
+ win_dark_clr = dark;
+ win_medium_clr = medium;
+}
+
+FONT
+Widget_SetFont (FONT newFont)
+{
+ FONT oldFont = cur_font;
+ cur_font = newFont;
+ return oldFont;
+}
+
+static void
+Widget_DrawToolTips (int numlines, const char **tips)
+{
+ RECT r;
+ FONT oldfont = 0;
+ FRAME oldFontEffect = SetContextFontEffect (NULL);
+ Color oldtext = SetContextForeGroundColor (WIDGET_INACTIVE_SELECTED_COLOR);
+ TEXT t;
+ int i;
+
+ if (cur_font)
+ oldfont = SetContextFont (cur_font);
+
+ r.corner.x = 2;
+ r.corner.y = 2;
+ r.extent.width = ScreenWidth - 4;
+ r.extent.height = ScreenHeight - 4;
+
+ t.align = ALIGN_CENTER;
+ t.CharCount = ~0;
+ t.baseline.x = r.corner.x + (r.extent.width >> 1);
+ t.baseline.y = r.corner.y + (r.extent.height - 8 - 8 * numlines);
+
+ for (i = 0; i < numlines; i++)
+ {
+ t.pStr = tips[i];
+ font_DrawText(&t);
+ t.baseline.y += 8;
+ }
+
+ SetContextFontEffect (oldFontEffect);
+ if (oldfont)
+ SetContextFont (oldfont);
+ SetContextForeGroundColor (oldtext);
+}
+
+void
+Widget_DrawMenuScreen (WIDGET *_self, int x, int y)
+{
+ RECT r;
+ Color title, oldtext;
+ Color inactive, default_color, selected;
+ FONT oldfont = 0;
+ FRAME oldFontEffect = SetContextFontEffect (NULL);
+ TEXT t;
+ int widget_index, height, widget_y;
+
+ WIDGET_MENU_SCREEN *self = (WIDGET_MENU_SCREEN *)_self;
+
+ if (cur_font)
+ oldfont = SetContextFont (cur_font);
+
+ r.corner.x = 2;
+ r.corner.y = 2;
+ r.extent.width = ScreenWidth - 4;
+ r.extent.height = ScreenHeight - 4;
+
+ title = WIDGET_INACTIVE_SELECTED_COLOR;
+ selected = WIDGET_ACTIVE_COLOR;
+ inactive = WIDGET_INACTIVE_COLOR;
+ default_color = title;
+
+ DrawStamp (&self->bgStamp);
+
+ oldtext = SetContextForeGroundColor (title);
+ t.baseline.x = r.corner.x + (r.extent.width >> 1);
+ t.baseline.y = r.corner.y + 8;
+ t.pStr = self->title;
+ t.align = ALIGN_CENTER;
+ t.CharCount = ~0;
+ font_DrawText (&t);
+ t.baseline.y += 8;
+ t.pStr = self->subtitle;
+ font_DrawText (&t);
+
+ height = 0;
+ for (widget_index = 0; widget_index < self->num_children; widget_index++)
+ {
+ WIDGET *child = self->child[widget_index];
+ height += (*child->height)(child);
+ height += 8; /* spacing */
+ }
+
+ height -= 8;
+
+ widget_y = (ScreenHeight - height) >> 1;
+ for (widget_index = 0; widget_index < self->num_children; widget_index++)
+ {
+ WIDGET *c = self->child[widget_index];
+ (*c->draw)(c, 0, widget_y);
+ widget_y += (*c->height)(c) + 8;
+ }
+
+ SetContextFontEffect (oldFontEffect);
+ if (oldfont)
+ SetContextFont (oldfont);
+ SetContextForeGroundColor (oldtext);
+
+ (void) x;
+ (void) y;
+}
+
+void
+Widget_DrawChoice (WIDGET *_self, int x, int y)
+{
+ WIDGET_CHOICE *self = (WIDGET_CHOICE *)_self;
+ Color oldtext;
+ Color inactive, default_color, selected;
+ FONT oldfont = 0;
+ FRAME oldFontEffect = SetContextFontEffect (NULL);
+ TEXT t;
+ int i, home_x, home_y;
+
+ if (cur_font)
+ oldfont = SetContextFont (cur_font);
+
+ default_color = WIDGET_INACTIVE_SELECTED_COLOR;
+ selected = WIDGET_ACTIVE_COLOR;
+ inactive = WIDGET_INACTIVE_COLOR;
+
+ t.baseline.x = x;
+ t.baseline.y = y;
+ t.align = ALIGN_LEFT;
+ t.CharCount = ~0;
+ t.pStr = self->category;
+ if (widget_focus == _self)
+ {
+ oldtext = SetContextForeGroundColor (selected);
+ }
+ else
+ {
+ oldtext = SetContextForeGroundColor (default_color);
+ }
+ font_DrawText (&t);
+
+ home_x = t.baseline.x + 3 * (ScreenWidth / ((self->maxcolumns + 1) * 2));
+ home_y = t.baseline.y;
+ t.align = ALIGN_CENTER;
+ for (i = 0; i < self->numopts; i++)
+ {
+ t.baseline.x = home_x + ((i % 3) *
+ (ScreenWidth / (self->maxcolumns + 1)));
+ t.baseline.y = home_y + (8 * (i / 3));
+ t.pStr = self->options[i].optname;
+ if ((widget_focus == _self) &&
+ (self->highlighted == i))
+ {
+ SetContextForeGroundColor (selected);
+ Widget_DrawToolTips (3, self->options[i].tooltip);
+ }
+ else if (i == self->selected)
+ {
+ SetContextForeGroundColor (default_color);
+ }
+ else
+ {
+ SetContextForeGroundColor (inactive);
+ }
+ font_DrawText (&t);
+ }
+ SetContextFontEffect (oldFontEffect);
+ if (oldfont)
+ SetContextFont (oldfont);
+ SetContextForeGroundColor (oldtext);
+}
+
+void
+Widget_DrawButton (WIDGET *_self, int x, int y)
+{
+ WIDGET_BUTTON *self = (WIDGET_BUTTON *)_self;
+ Color oldtext;
+ Color inactive, selected;
+ FONT oldfont = 0;
+ FRAME oldFontEffect = SetContextFontEffect (NULL);
+ TEXT t;
+
+ if (cur_font)
+ oldfont = SetContextFont (cur_font);
+
+ selected = WIDGET_ACTIVE_COLOR;
+ inactive = WIDGET_INACTIVE_COLOR;
+
+ t.baseline.x = 160;
+ t.baseline.y = y;
+ t.align = ALIGN_CENTER;
+ t.CharCount = ~0;
+ t.pStr = self->name;
+ if (widget_focus == _self)
+ {
+ Widget_DrawToolTips (3, self->tooltip);
+ oldtext = SetContextForeGroundColor (selected);
+ }
+ else
+ {
+ oldtext = SetContextForeGroundColor (inactive);
+ }
+ font_DrawText (&t);
+ SetContextFontEffect (oldFontEffect);
+ if (oldfont)
+ SetContextFont (oldfont);
+ SetContextForeGroundColor (oldtext);
+ (void) x;
+}
+
+void
+Widget_DrawLabel (WIDGET *_self, int x, int y)
+{
+ WIDGET_LABEL *self = (WIDGET_LABEL *)_self;
+ Color oldtext = SetContextForeGroundColor (WIDGET_INACTIVE_SELECTED_COLOR);
+ FONT oldfont = 0;
+ FRAME oldFontEffect = SetContextFontEffect (NULL);
+ TEXT t;
+ int i;
+
+ if (cur_font)
+ oldfont = SetContextFont (cur_font);
+
+ t.baseline.x = 160;
+ t.baseline.y = y;
+ t.align = ALIGN_CENTER;
+ t.CharCount = ~0;
+
+ for (i = 0; i < self->line_count; i++)
+ {
+ t.pStr = self->lines[i];
+ font_DrawText (&t);
+ t.baseline.y += 8;
+ }
+ SetContextFontEffect (oldFontEffect);
+ if (oldfont)
+ SetContextFont (oldfont);
+ SetContextForeGroundColor (oldtext);
+ (void) x;
+}
+
+void
+Widget_DrawSlider(WIDGET *_self, int x, int y)
+{
+ WIDGET_SLIDER *self = (WIDGET_SLIDER *)_self;
+ Color oldtext;
+ Color inactive, default_color, selected;
+ FONT oldfont = 0;
+ FRAME oldFontEffect = SetContextFontEffect (NULL);
+ TEXT t;
+ RECT r;
+ int tick = (ScreenWidth - x) / 8;
+
+ if (cur_font)
+ oldfont = SetContextFont (cur_font);
+
+ default_color = WIDGET_INACTIVE_SELECTED_COLOR;
+ selected = WIDGET_ACTIVE_COLOR;
+ inactive = WIDGET_INACTIVE_COLOR;
+
+ t.baseline.x = x;
+ t.baseline.y = y;
+ t.align = ALIGN_LEFT;
+ t.CharCount = ~0;
+ t.pStr = self->category;
+ if (widget_focus == _self)
+ {
+ Widget_DrawToolTips (3, self->tooltip);
+ oldtext = SetContextForeGroundColor (selected);
+ }
+ else
+ {
+ oldtext = SetContextForeGroundColor (default_color);
+ }
+ font_DrawText (&t);
+
+ r.corner.x = t.baseline.x + 3 * tick;
+ r.corner.y = t.baseline.y - 4;
+ r.extent.height = 2;
+ r.extent.width = 3 * tick;
+ DrawFilledRectangle (&r);
+
+ r.extent.width = 3;
+ r.extent.height = 8;
+ r.corner.y = t.baseline.y - 7;
+ r.corner.x = t.baseline.x + 3 * tick + (3 * tick * (self->value - self->min) /
+ (self->max - self->min)) - 1;
+ DrawFilledRectangle (&r);
+
+ (*self->draw_value)(self, t.baseline.x + 7 * tick, t.baseline.y);
+
+ SetContextFontEffect (oldFontEffect);
+ if (oldfont)
+ SetContextFont (oldfont);
+ SetContextForeGroundColor (oldtext);
+}
+
+void
+Widget_Slider_DrawValue (WIDGET_SLIDER *self, int x, int y)
+{
+ TEXT t;
+ char buffer[16];
+
+ sprintf (buffer, "%d", self->value);
+
+ t.baseline.x = x;
+ t.baseline.y = y;
+ t.align = ALIGN_CENTER;
+ t.CharCount = ~0;
+ t.pStr = buffer;
+
+ font_DrawText (&t);
+}
+
+void
+Widget_DrawTextEntry (WIDGET *_self, int x, int y)
+{
+ WIDGET_TEXTENTRY *self = (WIDGET_TEXTENTRY *)_self;
+ Color oldtext;
+ Color inactive, default_color, selected;
+ FONT oldfont = 0;
+ FRAME oldFontEffect = SetContextFontEffect (NULL);
+ TEXT t;
+
+ if (cur_font)
+ oldfont = SetContextFont (cur_font);
+
+ default_color = WIDGET_INACTIVE_SELECTED_COLOR;
+ selected = WIDGET_ACTIVE_COLOR;
+ inactive = WIDGET_INACTIVE_COLOR;
+
+ BatchGraphics ();
+
+ t.baseline.x = x;
+ t.baseline.y = y;
+ t.align = ALIGN_LEFT;
+ t.CharCount = ~0;
+ t.pStr = self->category;
+ if (widget_focus == _self)
+ {
+ oldtext = SetContextForeGroundColor (selected);
+ }
+ else
+ {
+ oldtext = SetContextForeGroundColor (default_color);
+ }
+ font_DrawText (&t);
+
+ /* Force string termination */
+ self->value[WIDGET_TEXTENTRY_WIDTH-1] = 0;
+
+ t.baseline.y = y;
+ t.CharCount = utf8StringCount (self->value);
+ t.pStr = self->value;
+
+ if (!(self->state & WTE_EDITING))
+ { // normal or selected state
+ t.baseline.x = 160;
+ t.align = ALIGN_CENTER;
+
+ if (widget_focus == _self)
+ {
+ oldtext = SetContextForeGroundColor (selected);
+ }
+ else
+ {
+ oldtext = SetContextForeGroundColor (inactive);
+ }
+ font_DrawText (&t);
+ }
+ else
+ { // editing state
+ COUNT i;
+ RECT text_r;
+ BYTE char_deltas[WIDGET_TEXTENTRY_WIDTH];
+ BYTE *pchar_deltas;
+ RECT r;
+ SIZE leading;
+
+ t.baseline.x = 90;
+ t.align = ALIGN_LEFT;
+
+ // calc background box dimensions
+ // XXX: this may need some tuning, especially if a
+ // different font is used. The font 'leading' values
+ // are not what they should be.
+#define BOX_VERT_OFFSET 2
+ GetContextFontLeading (&leading);
+ r.corner.x = t.baseline.x - 1;
+ r.corner.y = t.baseline.y - leading + BOX_VERT_OFFSET;
+ r.extent.width = ScreenWidth - r.corner.x - 10;
+ r.extent.height = leading + 2;
+
+ TextRect (&t, &text_r, char_deltas);
+#if 0
+ // XXX: this should potentially be used in ChangeCallback
+ if ((text_r.extent.width + 2) >= r.extent.width)
+ { // the text does not fit the input box size and so
+ // will not fit when displayed later
+ UnbatchGraphics ();
+ // disallow the change
+ return (FALSE);
+ }
+#endif
+
+ oldtext = SetContextForeGroundColor (selected);
+ DrawFilledRectangle (&r);
+
+ // calculate the cursor position and draw it
+ pchar_deltas = char_deltas;
+ for (i = self->cursor_pos; i > 0; --i)
+ r.corner.x += (SIZE)*pchar_deltas++;
+ if (self->cursor_pos < t.CharCount) /* cursor mid-line */
+ --r.corner.x;
+ if (self->state & WTE_BLOCKCUR)
+ { // Use block cursor for keyboardless systems
+ if (self->cursor_pos == t.CharCount)
+ { // cursor at end-line -- use insertion point
+ r.extent.width = 1;
+ }
+ else if (self->cursor_pos + 1 == t.CharCount)
+ { // extra pixel for last char margin
+ r.extent.width = (SIZE)*pchar_deltas + 2;
+ }
+ else
+ { // normal mid-line char
+ r.extent.width = (SIZE)*pchar_deltas + 1;
+ }
+ }
+ else
+ { // Insertion point cursor
+ r.extent.width = 1;
+ }
+ // position cursor within input field rect
+ ++r.corner.x;
+ ++r.corner.y;
+ r.extent.height -= 2;
+ SetContextForeGroundColor (WIDGET_CURSOR_COLOR);
+ DrawFilledRectangle (&r);
+
+ SetContextForeGroundColor (inactive);
+ font_DrawText (&t);
+ }
+
+ UnbatchGraphics ();
+ SetContextFontEffect (oldFontEffect);
+ if (oldfont)
+ SetContextFont (oldfont);
+ SetContextForeGroundColor (oldtext);
+}
+
+void
+Widget_DrawControlEntry (WIDGET *_self, int x, int y)
+{
+ WIDGET_CONTROLENTRY *self = (WIDGET_CONTROLENTRY *)_self;
+ Color oldtext;
+ Color inactive, default_color, selected;
+ FONT oldfont = 0;
+ FRAME oldFontEffect = SetContextFontEffect (NULL);
+ TEXT t;
+ int i, home_x, home_y;
+
+ if (cur_font)
+ oldfont = SetContextFont (cur_font);
+
+ default_color = WIDGET_INACTIVE_SELECTED_COLOR;
+ selected = WIDGET_ACTIVE_COLOR;
+ inactive = WIDGET_INACTIVE_COLOR;
+
+ t.baseline.x = x;
+ t.baseline.y = y;
+ t.align = ALIGN_LEFT;
+ t.CharCount = ~0;
+ t.pStr = self->category;
+ if (widget_focus == _self)
+ {
+ oldtext = SetContextForeGroundColor (selected);
+ }
+ else
+ {
+ oldtext = SetContextForeGroundColor (default_color);
+ }
+ font_DrawText (&t);
+
+ // 3 * ScreenWidth / ((self->maxcolumns + 1) * 2)) as per CHOICE, but only two options.
+ home_x = t.baseline.x + (ScreenWidth / 2);
+ home_y = t.baseline.y;
+ t.align = ALIGN_CENTER;
+ for (i = 0; i < 2; i++)
+ {
+ t.baseline.x = home_x + ((i % 3) * (ScreenWidth / 3)); // self->maxcolumns + 1 as per CHOICE.
+ t.baseline.y = home_y + (8 * (i / 3));
+ t.pStr = self->controlname[i];
+ if (!t.pStr[0])
+ {
+ t.pStr = "---";
+ }
+ if ((widget_focus == _self) &&
+ (self->highlighted == i))
+ {
+ SetContextForeGroundColor (selected);
+ }
+ else
+ {
+ SetContextForeGroundColor (default_color);
+ }
+ font_DrawText (&t);
+ }
+ SetContextFontEffect (oldFontEffect);
+ if (oldfont)
+ SetContextFont (oldfont);
+ SetContextForeGroundColor (oldtext);
+}
+
+int
+Widget_HeightChoice (WIDGET *_self)
+{
+ return ((((WIDGET_CHOICE *)_self)->numopts + 2) / 3) * 8;
+}
+
+int
+Widget_HeightFullScreen (WIDGET *_self)
+{
+ (void)_self;
+ return ScreenHeight;
+}
+
+int
+Widget_HeightOneLine (WIDGET *_self)
+{
+ (void)_self;
+ return 8;
+}
+
+int
+Widget_HeightLabel (WIDGET *_self)
+{
+ WIDGET_LABEL *self = (WIDGET_LABEL *)_self;
+ return self->line_count * 8;
+}
+
+int
+Widget_WidthFullScreen (WIDGET *_self)
+{
+ (void)_self;
+ return ScreenWidth;
+}
+
+int
+Widget_ReceiveFocusSimple (WIDGET *_self, int event)
+{
+ widget_focus = _self;
+ (void)event;
+ return TRUE;
+}
+
+int
+Widget_ReceiveFocusChoice (WIDGET *_self, int event)
+{
+ WIDGET_CHOICE *self = (WIDGET_CHOICE *)_self;
+ widget_focus = _self;
+ self->highlighted = self->selected;
+ (void)event;
+ return TRUE;
+}
+
+int
+Widget_ReceiveFocusControlEntry (WIDGET *_self, int event)
+{
+ WIDGET_CONTROLENTRY *self = (WIDGET_CONTROLENTRY *)_self;
+ int oldval = 0;
+ if (widget_focus->tag == WIDGET_TYPE_CONTROLENTRY)
+ {
+ oldval = ((WIDGET_CONTROLENTRY *)widget_focus)->highlighted;
+ }
+ widget_focus = _self;
+ self->highlighted = oldval;
+ (void)event;
+ return TRUE;
+}
+
+int
+Widget_ReceiveFocusMenuScreen (WIDGET *_self, int event)
+{
+ WIDGET_MENU_SCREEN *self = (WIDGET_MENU_SCREEN *)_self;
+ int x, last_x, dx;
+ for (x = 0; x < self->num_children; x++)
+ {
+ self->child[x]->parent = _self;
+ }
+ if (event == WIDGET_EVENT_UP)
+ {
+ x = self->num_children - 1;
+ dx = -1;
+ last_x = -1;
+ }
+ else if (event == WIDGET_EVENT_DOWN)
+ {
+ x = 0;
+ dx = 1;
+ last_x = self->num_children;
+ }
+ else
+ {
+ /* Leave highlighted value the same */
+ WIDGET *child = self->child[self->highlighted];
+ child->receiveFocus (child, event);
+ return TRUE;
+ }
+ for ( ; x != last_x; x += dx)
+ {
+ WIDGET *child = self->child[x];
+ if ((*child->receiveFocus)(child, event))
+ {
+ self->highlighted = x;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+int
+Widget_ReceiveFocusRefuseFocus (WIDGET *self, int event)
+{
+ (void)self;
+ (void)event;
+ return FALSE;
+}
+
+int
+Widget_HandleEventIgnoreAll (WIDGET *self, int event)
+{
+ (void)event;
+ (void)self;
+ return FALSE;
+}
+
+int
+Widget_HandleEventChoice (WIDGET *_self, int event)
+{
+ WIDGET_CHOICE *self = (WIDGET_CHOICE *)_self;
+ switch (event)
+ {
+ case WIDGET_EVENT_LEFT:
+ self->highlighted -= 1;
+ if (self->highlighted < 0)
+ self->highlighted = self->numopts - 1;
+ return TRUE;
+ case WIDGET_EVENT_RIGHT:
+ self->highlighted += 1;
+ if (self->highlighted >= self->numopts)
+ self->highlighted = 0;
+ return TRUE;
+ case WIDGET_EVENT_SELECT:
+ {
+ int oldval = self->selected;
+ self->selected = self->highlighted;
+ if (self->onChange)
+ {
+ (*(self->onChange))(self, oldval);
+ }
+ return TRUE;
+ }
+ default:
+ return FALSE;
+ }
+}
+
+int
+Widget_HandleEventSlider (WIDGET *_self, int event)
+{
+ WIDGET_SLIDER *self = (WIDGET_SLIDER *)_self;
+ switch (event)
+ {
+ case WIDGET_EVENT_LEFT:
+ self->value -= self->step;
+ if (self->value < self->min)
+ self->value = self->min;
+ return TRUE;
+ case WIDGET_EVENT_RIGHT:
+ self->value += self->step;
+ if (self->value > self->max)
+ self->value = self->max;
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+int
+Widget_HandleEventMenuScreen (WIDGET *_self, int event)
+{
+ WIDGET_MENU_SCREEN *self = (WIDGET_MENU_SCREEN *)_self;
+ int x, last_x, dx;
+ switch (event)
+ {
+ case WIDGET_EVENT_UP:
+ dx = -1;
+ break;
+ case WIDGET_EVENT_DOWN:
+ dx = 1;
+ break;
+ case WIDGET_EVENT_CANCEL:
+ /* On cancel, shift focus to last element and send a SELECT. */
+ self->highlighted = self->num_children - 1;
+ widget_focus = self->child[self->highlighted];
+ return (widget_focus->handleEvent)(widget_focus, WIDGET_EVENT_SELECT);
+ default:
+ return FALSE;
+ }
+ last_x = self->highlighted;
+ x = self->highlighted + dx;
+ while (x != last_x)
+ {
+ WIDGET *child;
+ if (x == -1)
+ x = self->num_children - 1;
+ if (x == self->num_children)
+ x = 0;
+ child = self->child[x];
+ if ((*child->receiveFocus)(child, event))
+ {
+ self->highlighted = x;
+ return TRUE;
+ }
+ x += dx;
+ }
+ return FALSE;
+}
+
+int
+Widget_HandleEventTextEntry (WIDGET *_self, int event)
+{
+ WIDGET_TEXTENTRY *self = (WIDGET_TEXTENTRY *)_self;
+ if (event == WIDGET_EVENT_SELECT) {
+ if (!self->handleEventSelect)
+ return FALSE;
+ return (*self->handleEventSelect)(self);
+ }
+ return FALSE;
+}
+
+int
+Widget_HandleEventControlEntry (WIDGET *_self, int event)
+{
+ WIDGET_CONTROLENTRY *self = (WIDGET_CONTROLENTRY *)_self;
+ if (event == WIDGET_EVENT_SELECT)
+ {
+ if (self->onChange)
+ {
+ (self->onChange)(self);
+ return TRUE;
+ }
+ }
+ if (event == WIDGET_EVENT_DELETE)
+ {
+ if (self->onDelete)
+ {
+ (self->onDelete)(self);
+ return TRUE;
+ }
+ }
+ if ((event == WIDGET_EVENT_RIGHT) ||
+ (event == WIDGET_EVENT_LEFT))
+ {
+ self->highlighted = 1-self->highlighted;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+int
+Widget_Event (int event)
+{
+ WIDGET *widget = widget_focus;
+ while (widget != NULL)
+ {
+ if ((*widget->handleEvent)(widget, event))
+ return TRUE;
+ widget = widget->parent;
+ }
+ return FALSE;
+}
diff --git a/src/libs/graphics/widgets.h b/src/libs/graphics/widgets.h
new file mode 100644
index 0000000..4548e35
--- /dev/null
+++ b/src/libs/graphics/widgets.h
@@ -0,0 +1,222 @@
+// Copyright Michael Martin, 2004.
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_GRAPHICS_WIDGETS_H_
+#define LIBS_GRAPHICS_WIDGETS_H_
+
+#include "libs/gfxlib.h"
+
+enum {
+ WIDGET_EVENT_UP,
+ WIDGET_EVENT_DOWN,
+ WIDGET_EVENT_LEFT,
+ WIDGET_EVENT_RIGHT,
+ WIDGET_EVENT_SELECT,
+ WIDGET_EVENT_CANCEL,
+ WIDGET_EVENT_DELETE,
+ NUM_WIDGET_EVENTS
+};
+
+typedef enum {
+ WIDGET_TYPE_MENU_SCREEN,
+ WIDGET_TYPE_CHOICE,
+ WIDGET_TYPE_BUTTON,
+ WIDGET_TYPE_LABEL,
+ WIDGET_TYPE_SLIDER,
+ WIDGET_TYPE_TEXTENTRY,
+ WIDGET_TYPE_CONTROLENTRY,
+ NUM_WIDGET_TYPES
+} WIDGET_TYPE;
+
+#define WIDGET_TEXTENTRY_WIDTH 50
+#define WIDGET_CONTROLENTRY_WIDTH 16
+
+typedef struct _widget {
+ WIDGET_TYPE tag;
+ struct _widget *parent;
+ int (*handleEvent)(struct _widget *self, int event);
+ int (*receiveFocus)(struct _widget *self, int event);
+ void (*draw)(struct _widget *self, int x, int y);
+ int (*height)(struct _widget *self);
+ int (*width)(struct _widget *self);
+} WIDGET;
+
+typedef struct _widget_menu_screen {
+ WIDGET_TYPE tag;
+ struct _widget *parent;
+ int (*handleEvent)(struct _widget *self, int event);
+ int (*receiveFocus)(struct _widget *self, int event);
+ void (*draw)(struct _widget *self, int x, int y);
+ int (*height)(struct _widget *self);
+ int (*width)(struct _widget *self);
+ const char *title;
+ const char *subtitle;
+ STAMP bgStamp;
+ int num_children;
+ struct _widget **child;
+ int highlighted;
+} WIDGET_MENU_SCREEN;
+
+typedef struct {
+ const char *optname;
+ const char *tooltip[3];
+} CHOICE_OPTION;
+
+typedef struct _widget_choice {
+ WIDGET_TYPE tag;
+ struct _widget *parent;
+ int (*handleEvent)(struct _widget *self, int event);
+ int (*receiveFocus)(struct _widget *self, int event);
+ void (*draw)(struct _widget *self, int x, int y);
+ int (*height)(struct _widget *self);
+ int (*width)(struct _widget *self);
+ const char *category;
+ int numopts;
+ int maxcolumns;
+ CHOICE_OPTION *options;
+ int selected, highlighted;
+ void (*onChange)(struct _widget_choice *self, int oldval);
+} WIDGET_CHOICE;
+
+typedef struct _widget_button {
+ WIDGET_TYPE tag;
+ struct _widget *parent;
+ int (*handleEvent)(struct _widget *self, int event);
+ int (*receiveFocus)(struct _widget *self, int event);
+ void (*draw)(struct _widget *self, int x, int y);
+ int (*height)(struct _widget *self);
+ int (*width)(struct _widget *self);
+ const char *name;
+ const char *tooltip[3];
+} WIDGET_BUTTON;
+
+typedef struct _widget_label {
+ WIDGET_TYPE tag;
+ struct _widget *parent;
+ int (*handleEvent)(struct _widget *self, int event);
+ int (*receiveFocus)(struct _widget *self, int event);
+ void (*draw)(struct _widget *self, int x, int y);
+ int (*height)(struct _widget *self);
+ int (*width)(struct _widget *self);
+ int line_count;
+ const char **lines;
+} WIDGET_LABEL;
+
+typedef struct _widget_slider {
+ WIDGET_TYPE tag;
+ struct _widget *parent;
+ int (*handleEvent)(struct _widget *self, int event);
+ int (*receiveFocus)(struct _widget *self, int event);
+ void (*draw)(struct _widget *self, int x, int y);
+ int (*height)(struct _widget *self);
+ int (*width)(struct _widget *self);
+ void (*draw_value)(struct _widget_slider *self, int x, int y);
+ int min, max, step;
+ int value;
+ const char *category;
+ const char *tooltip[3];
+} WIDGET_SLIDER;
+
+typedef enum {
+ WTE_NORMAL = 0,
+ WTE_EDITING,
+ WTE_BLOCKCUR,
+
+} WIDGET_TEXTENTRY_STATE;
+
+typedef struct _widget_textentry {
+ WIDGET_TYPE tag;
+ struct _widget *parent;
+ int (*handleEvent)(struct _widget *self, int event);
+ int (*receiveFocus)(struct _widget *self, int event);
+ void (*draw)(struct _widget *self, int x, int y);
+ int (*height)(struct _widget *self);
+ int (*width)(struct _widget *self);
+ int (*handleEventSelect)(struct _widget_textentry *self);
+ // handleEventSelect is an overridable callback event
+ // called by the default handleEvent implementation
+ // can be NULL, in which case SELECT is ignored
+ void (*onChange)(struct _widget_textentry *self);
+ const char *category;
+ char value[WIDGET_TEXTENTRY_WIDTH];
+ int maxlen;
+ WIDGET_TEXTENTRY_STATE state;
+ int cursor_pos;
+} WIDGET_TEXTENTRY;
+
+typedef struct _widget_controlentry {
+ WIDGET_TYPE tag;
+ struct _widget *parent;
+ int (*handleEvent)(struct _widget *self, int event);
+ int (*receiveFocus)(struct _widget *self, int event);
+ void (*draw)(struct _widget *self, int x, int y);
+ int (*height)(struct _widget *self);
+ int (*width)(struct _widget *self);
+ void (*onChange)(struct _widget_controlentry *self);
+ void (*onDelete)(struct _widget_controlentry *self);
+ const char *category;
+ int controlindex;
+ int highlighted;
+ char controlname[2][WIDGET_CONTROLENTRY_WIDTH];
+} WIDGET_CONTROLENTRY;
+
+void DrawShadowedBox (RECT *r, Color bg, Color dark, Color medium);
+void DrawLabelAsWindow (WIDGET_LABEL *label, RECT *windowRect);
+void Widget_SetWindowColors (Color bg, Color dark, Color medium);
+FONT Widget_SetFont (FONT newFont);
+
+
+int Widget_Event (int event);
+
+/* Methods for filling in widgets with */
+
+int Widget_ReceiveFocusMenuScreen (WIDGET *_self, int event);
+int Widget_ReceiveFocusChoice (WIDGET *_self, int event);
+int Widget_ReceiveFocusSimple (WIDGET *_self, int event);
+int Widget_ReceiveFocusSlider (WIDGET *_self, int event);
+int Widget_ReceiveFocusControlEntry (WIDGET *_self, int event);
+int Widget_ReceiveFocusRefuseFocus (WIDGET *_self, int event);
+
+int Widget_HandleEventMenuScreen (WIDGET *_self, int event);
+int Widget_HandleEventChoice (WIDGET *_self, int event);
+int Widget_HandleEventSlider (WIDGET *_self, int event);
+int Widget_HandleEventTextEntry (WIDGET *_self, int event);
+int Widget_HandleEventControlEntry (WIDGET *_self, int event);
+int Widget_HandleEventIgnoreAll (WIDGET *_self, int event);
+
+int Widget_HeightChoice (WIDGET *_self);
+int Widget_HeightFullScreen (WIDGET *_self);
+int Widget_HeightOneLine (WIDGET *_self);
+int Widget_HeightLabel (WIDGET *_self);
+
+int Widget_WidthFullScreen (WIDGET *_self);
+
+void Widget_DrawMenuScreen (WIDGET *_self, int x, int y);
+void Widget_DrawChoice (WIDGET *_self, int x, int y);
+void Widget_DrawButton (WIDGET *_self, int x, int y);
+void Widget_DrawLabel (WIDGET *_self, int x, int y);
+void Widget_DrawSlider (WIDGET *_self, int x, int y);
+void Widget_DrawTextEntry (WIDGET *_self, int x, int y);
+void Widget_DrawControlEntry (WIDGET *_self, int x, int y);
+
+void Widget_Slider_DrawValue (WIDGET_SLIDER *self, int x, int y);
+
+/* Other implementations will need these values */
+extern WIDGET *widget_focus;
+
+#endif /* LIBS_GRAPHICS_WIDGETS_H_ */