diff options
Diffstat (limited to 'src/libs/graphics/sdl')
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) (); +} + |