summaryrefslogtreecommitdiff
path: root/src/libs/graphics/sdl
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/graphics/sdl')
-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
36 files changed, 13226 insertions, 0 deletions
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) ();
+}
+