diff options
Diffstat (limited to 'src/libs/graphics')
69 files changed, 21234 insertions, 0 deletions
diff --git a/src/libs/graphics/Makeinfo b/src/libs/graphics/Makeinfo new file mode 100644 index 0000000..1a9f7ee --- /dev/null +++ b/src/libs/graphics/Makeinfo @@ -0,0 +1,12 @@ +if [ "$uqm_GFXMODULE" = "sdl" ]; then + uqm_SUBDIRS="sdl" +fi + +uqm_CFILES="boxint.c clipline.c cmap.c context.c drawable.c filegfx.c + bbox.c dcqueue.c gfxload.c + font.c frame.c gfx_common.c intersec.c loaddisp.c + pixmap.c resgfx.c tfb_draw.c tfb_prim.c widgets.c" + +uqm_HFILES="bbox.h cmap.h context.h dcqueue.h drawable.h drawcmd.h font.h + gfx_common.h gfxintrn.h prim.h tfb_draw.h tfb_prim.h widgets.h" + diff --git a/src/libs/graphics/bbox.c b/src/libs/graphics/bbox.c new file mode 100644 index 0000000..ce57d32 --- /dev/null +++ b/src/libs/graphics/bbox.c @@ -0,0 +1,133 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "port.h" +#include "libs/graphics/bbox.h" + +TFB_BoundingBox TFB_BBox; +int maxWidth; +int maxHeight; + +void +TFB_BBox_Init (int width, int height) +{ + maxWidth = width; + maxHeight = height; + TFB_BBox.clip.extent.width = width; + TFB_BBox.clip.extent.height = height; +} + +void +TFB_BBox_Reset (void) +{ + TFB_BBox.valid = 0; +} + +void +TFB_BBox_SetClipRect (const RECT *r) +{ + if (!r) + { /* No clipping -- full rect */ + TFB_BBox.clip.corner.x = 0; + TFB_BBox.clip.corner.y = 0; + TFB_BBox.clip.extent.width = maxWidth; + TFB_BBox.clip.extent.height = maxHeight; + return; + } + + TFB_BBox.clip = *r; + + /* Make sure the cliprect is sane */ + if (TFB_BBox.clip.corner.x < 0) + TFB_BBox.clip.corner.x = 0; + + if (TFB_BBox.clip.corner.y < 0) + TFB_BBox.clip.corner.y = 0; + + if (TFB_BBox.clip.corner.x + TFB_BBox.clip.extent.width > maxWidth) + TFB_BBox.clip.extent.width = maxWidth - TFB_BBox.clip.corner.x; + + if (TFB_BBox.clip.corner.y + TFB_BBox.clip.extent.height > maxHeight) + TFB_BBox.clip.extent.height = maxHeight - TFB_BBox.clip.corner.y; +} + +void +TFB_BBox_RegisterPoint (int x, int y) +{ + int x1 = TFB_BBox.clip.corner.x; + int y1 = TFB_BBox.clip.corner.y; + int x2 = TFB_BBox.clip.corner.x + TFB_BBox.clip.extent.width - 1; + int y2 = TFB_BBox.clip.corner.y + TFB_BBox.clip.extent.height - 1; + + /* Constrain coordinates */ + if (x < x1) x = x1; + if (x >= x2) x = x2; + if (y < y1) y = y1; + if (y >= y2) y = y2; + + /* Is this the first point? If so, set a pixel-region and return. */ + if (!TFB_BBox.valid) + { + TFB_BBox.valid = 1; + TFB_BBox.region.corner.x = x; + TFB_BBox.region.corner.y = y; + TFB_BBox.region.extent.width = 1; + TFB_BBox.region.extent.height = 1; + return; + } + + /* Otherwise expand the rectangle if necessary. */ + x1 = TFB_BBox.region.corner.x; + y1 = TFB_BBox.region.corner.y; + x2 = TFB_BBox.region.corner.x + TFB_BBox.region.extent.width - 1; + y2 = TFB_BBox.region.corner.y + TFB_BBox.region.extent.height - 1; + + if (x < x1) { + TFB_BBox.region.corner.x = x; + TFB_BBox.region.extent.width += x1 - x; + } + if (y < y1) { + TFB_BBox.region.corner.y = y; + TFB_BBox.region.extent.height += y1 - y; + } + if (x > x2) { + TFB_BBox.region.extent.width += x - x2; + } + if (y > y2) { + TFB_BBox.region.extent.height += y - y2; + } +} + +void +TFB_BBox_RegisterRect (const RECT *r) +{ + /* RECT will still register as a corner point of the cliprect even + * if it does not intersect with the cliprect at all. This is not + * a problem, as more is not less. */ + TFB_BBox_RegisterPoint (r->corner.x, r->corner.y); + TFB_BBox_RegisterPoint (r->corner.x + r->extent.width - 1, + r->corner.y + r->extent.height - 1); +} + +void +TFB_BBox_RegisterCanvas (TFB_Canvas c, int x, int y) +{ + RECT r; + r.corner.x = x; + r.corner.y = y; + TFB_DrawCanvas_GetExtent (c, &r.extent); + TFB_BBox_RegisterRect (&r); +} diff --git a/src/libs/graphics/bbox.h b/src/libs/graphics/bbox.h new file mode 100644 index 0000000..33d3000 --- /dev/null +++ b/src/libs/graphics/bbox.h @@ -0,0 +1,46 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef BBOX_H_INCL__ +#define BBOX_H_INCL__ + +#include "libs/gfxlib.h" +#include "libs/graphics/tfb_draw.h" + +/* Bounding Box operations. These operations are NOT synchronized. + * However, they should only be accessed by TFB_FlushGraphics and + * TFB_SwapBuffers, or the routines that they exclusively call -- all + * of which are only callable by the thread that is permitted to touch + * the screen. No explicit locks should therefore be required. */ + +typedef struct { + int valid; // If zero, the next point registered becomes the region + RECT region; // The actual modified rectangle + RECT clip; // Points outside of this rectangle are pushed to + // the closest border point +} TFB_BoundingBox; + +extern TFB_BoundingBox TFB_BBox; + +void TFB_BBox_RegisterPoint (int x, int y); +void TFB_BBox_RegisterRect (const RECT *r); +void TFB_BBox_RegisterCanvas (TFB_Canvas c, int x, int y); + +void TFB_BBox_Init (int width, int height); +void TFB_BBox_Reset (void); +void TFB_BBox_SetClipRect (const RECT *r); + +#endif /* BBOX_H_INCL__ */ diff --git a/src/libs/graphics/boxint.c b/src/libs/graphics/boxint.c new file mode 100644 index 0000000..0500311 --- /dev/null +++ b/src/libs/graphics/boxint.c @@ -0,0 +1,183 @@ +//Copyright Paul Reiche, Fred Ford. 1992-2002 + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "gfxintrn.h" + +#undef MIN +#define MIN(a, b) (((a) <= (b)) ? (a) : (b)) +#undef MAX +#define MAX(a, b) (((a) >= (b)) ? (a) : (b)) + +INTERSECT_CODE +BoxIntersect (RECT *pr1, RECT *pr2, RECT *pinter) +{ + INTERSECT_CODE intersect_code; + COORD x1; + SIZE w1, w2, delta; + + intersect_code = INTERSECT_NOCLIP; + + x1 = pr1->corner.x - pr2->corner.x; + + w1 = pr1->extent.width; + w2 = pr2->extent.width; + if ((delta = w2 - x1) <= w1) + { + if (delta != w1) + { + w1 = delta; + intersect_code &= ~INTERSECT_NOCLIP; + } + intersect_code |= INTERSECT_RIGHT; + } + if (x1 <= 0) + { + if (x1 < 0) + { + w1 += x1; + x1 = 0; + intersect_code &= ~INTERSECT_NOCLIP; + } + intersect_code |= INTERSECT_LEFT; + } + + if (w1 > 0) + { +#define h2 w2 + COORD y1; + SIZE h1; + + y1 = pr1->corner.y - pr2->corner.y; + + h1 = pr1->extent.height; + h2 = pr2->extent.height; + if ((delta = h2 - y1) <= h1) + { + if (delta != h1) + { + h1 = delta; + intersect_code &= ~INTERSECT_NOCLIP; + } + intersect_code |= INTERSECT_BOTTOM; + } + if (y1 <= 0) + { + if (y1 < 0) + { + h1 += y1; + y1 = 0; + intersect_code &= ~INTERSECT_NOCLIP; + } + intersect_code |= INTERSECT_TOP; + } + + if (h1 > 0) + { + pinter->corner.x = x1 + pr2->corner.x; + pinter->corner.y = y1 + pr2->corner.y; + pinter->extent.width = w1; + pinter->extent.height = h1; + + return (intersect_code); + } +#undef h2 + } + + return ((INTERSECT_CODE)0); +} + +void +BoxUnion (RECT *pr1, RECT *pr2, RECT *punion) +{ +#if NEVER // Part of lower FIXME. + COORD x2, y2, w2, h2; +#endif // NEVER + + // Union is A AND B, put together, correct? Returns a bigger box that + // encompasses the two. + punion->corner.x = MIN(pr1->corner.x, pr2->corner.x); + punion->corner.y = MIN(pr1->corner.y, pr2->corner.y); + + punion->extent.width = MAX(pr1->corner.x + pr1->extent.width, + pr2->corner.x + pr2->extent.width) - punion->corner.x; + punion->extent.height = MAX(pr1->corner.y + pr1->extent.height, + pr2->corner.y + pr2->extent.height) - punion->corner.y; + + +#if NEVER // FIXME - I think this is broken, but keeping it around for reference + // FIXME - just in case. + +#if 1 /* alter based on 0 widths */ + + x2 = + (pr1->corner.x < pr2->corner.x)? pr1->corner.x : pr2->corner.x; + + y2 = + (pr1->corner.y < pr2->corner.y)? pr1->corner.y : pr2->corner.y; + + w2 = ( + ((pr1->corner.x + pr1->extent.width) > (pr2->corner.x + pr2->extent.width))? + (pr1->corner.x + pr1->extent.width) : (pr2->corner.x + pr2->extent.width) + ) - punion->corner.x; + + h2 = ( + ((pr1->corner.y + pr1->extent.height) > (pr2->corner.y + pr2->extent.height))? + (pr1->corner.y + pr1->extent.height) : (pr2->corner.y + pr2->extent.height) + ) - punion->corner.y; +#else + SIZE delta; + COORD x1, y1, w1, h1; + + x1 = pr1->corner.x; + w1 = pr1->extent.width; + x2 = pr2->corner.x; + w2 = pr2->extent.width; + if ((delta = x1 - x2) >= 0) + w1 += delta; + else + { + w2 -= delta; + x2 += delta; + } + + y1 = pr1->corner.y; + h1 = pr1->extent.height; + y2 = pr2->corner.y; + h2 = pr2->extent.height; + if ((delta = y1 - y2) >= 0) + h1 += delta; + else + { + h2 -= delta; + y2 += delta; + } + + if ((delta = w1 - w2) > 0) + w2 += delta; + if ((delta = h1 - h2) > 0) + h2 += delta; +#endif + + punion->corner.x = x2; + punion->corner.y = y2; + punion->extent.width = w2; + punion->extent.height = h2; + +#endif // NEVER +} + diff --git a/src/libs/graphics/clipline.c b/src/libs/graphics/clipline.c new file mode 100644 index 0000000..ab2d7dd --- /dev/null +++ b/src/libs/graphics/clipline.c @@ -0,0 +1,241 @@ +//Copyright Paul Reiche, Fred Ford. 1992-2002 + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "gfxintrn.h" + +INTERSECT_CODE +_clip_line (const RECT *pClipRect, BRESENHAM_LINE *pLine) +{ + COORD p; + COORD x0, y0, xmin, ymin, xmax, ymax; + SIZE abs_delta_x, abs_delta_y; + INTERSECT_CODE intersect_code; + + xmin = pClipRect->corner.x; + ymin = pClipRect->corner.y; + xmax = pClipRect->corner.x + pClipRect->extent.width - 1; + ymax = pClipRect->corner.y + pClipRect->extent.height - 1; + if (pLine->first.x <= pLine->second.x) + pLine->end_points_exchanged = FALSE; + else + { + p = pLine->first.x; + pLine->first.x = pLine->second.x; + pLine->second.x = p; + + p = pLine->first.y; + pLine->first.y = pLine->second.y; + pLine->second.y = p; + + pLine->end_points_exchanged = TRUE; + } + + if (pLine->first.x > xmax || pLine->second.x < xmin || + (pLine->first.y > ymax && pLine->second.y > ymax) || + (pLine->first.y < ymin && pLine->second.y < ymin)) + return ((INTERSECT_CODE)0); + + intersect_code = INTERSECT_NOCLIP; + x0 = y0 = 0; + abs_delta_x = (pLine->second.x - pLine->first.x) << 1; + abs_delta_y = (pLine->second.y - pLine->first.y) << 1; + pLine->abs_delta_x = abs_delta_x; + pLine->abs_delta_y = abs_delta_y; + if (abs_delta_y == 0) + { + if (pLine->first.x < xmin) + { + pLine->first.x = xmin; + intersect_code |= INTERSECT_LEFT; + } + if (pLine->second.x > xmax) + { + pLine->second.x = xmax; + intersect_code |= INTERSECT_RIGHT; + } + } + else if (abs_delta_x == 0) + { + if (abs_delta_y < 0) + { + p = pLine->first.y; + pLine->first.y = pLine->second.y; + pLine->second.y = p; + + pLine->abs_delta_y = + abs_delta_y = -abs_delta_y; + } + + if (pLine->first.y < ymin) + { + pLine->first.y = ymin; + intersect_code |= INTERSECT_TOP; + } + if (pLine->second.y > ymax) + { + pLine->second.y = ymax; + intersect_code |= INTERSECT_BOTTOM; + } + } + else + { + COORD x1, y1; + + p = pLine->first.x; + x1 = pLine->second.x - p; + xmin = xmin - p; + xmax = xmax - p; + + p = pLine->first.y; + if (abs_delta_y > 0) + { + y1 = pLine->second.y - p; + ymin = ymin - p; + ymax = ymax - p; + } + else + { + y1 = p - pLine->second.y; + ymin = p - ymin; + ymax = p - ymax; + + p = ymin; + ymin = ymax; + ymax = p; + abs_delta_y = -abs_delta_y; + } + + if (abs_delta_x > abs_delta_y) + { + SIZE half_dx; + + half_dx = abs_delta_x >> 1; + if (x0 < xmin) + { + if ((y0 = (COORD)(((long)abs_delta_y * + (x0 = xmin) + half_dx) / abs_delta_x)) > ymax) + return ((INTERSECT_CODE)0); + intersect_code |= INTERSECT_LEFT; + } + if (x1 > xmax) + { + if ((y1 = (COORD)(((long)abs_delta_y * + (x1 = xmax) + half_dx) / abs_delta_x)) < ymin) + return ((INTERSECT_CODE)0); + intersect_code |= INTERSECT_RIGHT; + } + if (y0 < ymin) + { + if ((x0 = (COORD)(((long)abs_delta_x * + (y0 = ymin) - half_dx + (abs_delta_y - 1)) / + abs_delta_y)) > xmax) + return ((INTERSECT_CODE)0); + intersect_code |= INTERSECT_TOP; + intersect_code &= ~INTERSECT_LEFT; + } + if (y1 > ymax) + { + if ((x1 = (COORD)(((long)abs_delta_x * + ((y1 = ymax) + 1) - half_dx + (abs_delta_y - 1)) / + abs_delta_y) - 1) < xmin) + return ((INTERSECT_CODE)0); + intersect_code |= INTERSECT_BOTTOM; + intersect_code &= ~INTERSECT_RIGHT; + } + } + else + { + SIZE half_dy; + + half_dy = abs_delta_y >> 1; + if (y0 < ymin) + { + if ((x0 = (COORD)(((long)abs_delta_x * + (y0 = ymin) + half_dy) / abs_delta_y)) > xmax) + return ((INTERSECT_CODE)0); + intersect_code |= INTERSECT_TOP; + } + if (y1 > ymax) + { + if ((x1 = (COORD)(((long)abs_delta_x * + (y1 = ymax) + half_dy) / abs_delta_y)) < xmin) + return ((INTERSECT_CODE)0); + intersect_code |= INTERSECT_BOTTOM; + } + if (x0 < xmin) + { + if ((y0 = (COORD)(((long)abs_delta_y * + (x0 = xmin) - half_dy + (abs_delta_x - 1)) / + abs_delta_x)) > ymax) + return ((INTERSECT_CODE)0); + intersect_code |= INTERSECT_LEFT; + intersect_code &= ~INTERSECT_TOP; + } + if (x1 > xmax) + { + if ((y1 = (COORD)(((long)abs_delta_y * + ((x1 = xmax) + 1) - half_dy + (abs_delta_x - 1)) / + abs_delta_x) - 1) < ymin) + return ((INTERSECT_CODE)0); + intersect_code |= INTERSECT_RIGHT; + intersect_code &= ~INTERSECT_BOTTOM; + } + } + + pLine->second.x = pLine->first.x + x1; + pLine->first.x += x0; + if (pLine->abs_delta_y > 0) + { + pLine->second.y = pLine->first.y + y1; + pLine->first.y += y0; + } + else + { + INTERSECT_CODE y_code; + + pLine->second.y = pLine->first.y - y1; + pLine->first.y -= y0; + + y_code = (INTERSECT_CODE)(intersect_code + & (INTERSECT_TOP | INTERSECT_BOTTOM)); + if (y_code && y_code != (INTERSECT_TOP | INTERSECT_BOTTOM)) + intersect_code ^= (INTERSECT_TOP | INTERSECT_BOTTOM); + } + } + + if (!(intersect_code & INTERSECT_ALL_SIDES)) + { + if (abs_delta_x > abs_delta_y) + pLine->error_term = -(SIZE)(abs_delta_x >> 1); + else + pLine->error_term = -(SIZE)(abs_delta_y >> 1); + } + else + { + intersect_code &= ~INTERSECT_NOCLIP; + if (abs_delta_x > abs_delta_y) + pLine->error_term = (SIZE)((x0 * (long)abs_delta_y) - + (y0 * (long)abs_delta_x)) - (abs_delta_x >> 1); + else + pLine->error_term = (SIZE)((y0 * (long)abs_delta_x) - + (x0 * (long)abs_delta_y)) - (abs_delta_y >> 1); + } + + return (pLine->intersect_code = intersect_code); +} + diff --git a/src/libs/graphics/cmap.c b/src/libs/graphics/cmap.c new file mode 100644 index 0000000..53cd13f --- /dev/null +++ b/src/libs/graphics/cmap.c @@ -0,0 +1,663 @@ +//Copyright Paul Reiche, Fred Ford. 1992-2002 + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "libs/graphics/cmap.h" +#include "libs/threadlib.h" +#include "libs/timelib.h" +#include "libs/inplib.h" +#include "libs/strlib.h" + // for GetStringAddress() +#include "libs/log.h" +#include <string.h> +#include <stdlib.h> + + +typedef struct xform_control +{ + int CMapIndex; // -1 means unused + COLORMAPPTR CMapPtr; + SIZE Ticks; + DWORD StartTime; + DWORD EndTime; + Color OldCMap[NUMBER_OF_PLUTVALS]; +} XFORM_CONTROL; + +#define MAX_XFORMS 16 +static struct +{ + XFORM_CONTROL TaskControl[MAX_XFORMS]; + volatile int Highest; + // 'pending' is Highest >= 0 + Mutex Lock; +} XFormControl; + +static int fadeAmount = FADE_NORMAL_INTENSITY; +static int fadeDelta; +static TimeCount fadeStartTime; +static sint32 fadeInterval; +static Mutex fadeLock; + +#define SPARE_COLORMAPS 20 + +// Colormaps are rapidly replaced in some parts of the game, so +// it pays to have some spares on hand +static TFB_ColorMap *poolhead; +static int poolcount; + +static TFB_ColorMap * colormaps[MAX_COLORMAPS]; +static int mapcount; +static Mutex maplock; + + +static void release_colormap (TFB_ColorMap *map); +static void delete_colormap (TFB_ColorMap *map); + + +void +InitColorMaps (void) +{ + int i; + + // init colormaps + maplock = CreateMutex ("Colormaps Lock", SYNC_CLASS_TOPLEVEL | SYNC_CLASS_VIDEO); + + // init xform control + XFormControl.Highest = -1; + XFormControl.Lock = CreateMutex ("Transform Lock", SYNC_CLASS_TOPLEVEL | SYNC_CLASS_VIDEO); + for (i = 0; i < MAX_XFORMS; ++i) + XFormControl.TaskControl[i].CMapIndex = -1; + + fadeLock = CreateMutex ("Fade Lock", SYNC_CLASS_TOPLEVEL | SYNC_CLASS_VIDEO); +} + +void +UninitColorMaps (void) +{ + int i; + TFB_ColorMap *next; + + for (i = 0; i < MAX_COLORMAPS; ++i) + { + TFB_ColorMap *map = colormaps[i]; + if (!map) + continue; + release_colormap (map); + colormaps[i] = 0; + } + + // free spares + for ( ; poolhead; poolhead = next, --poolcount) + { + next = poolhead->next; + delete_colormap (poolhead); + } + + DestroyMutex (fadeLock); + // uninit xform control + DestroyMutex (XFormControl.Lock); + + // uninit colormaps + DestroyMutex (maplock); +} + +static inline TFB_ColorMap * +alloc_colormap (void) + // returns an addrefed object +{ + TFB_ColorMap *map; + + if (poolhead) + { // have some spares + map = poolhead; + poolhead = map->next; + --poolcount; + } + else + { // no spares, need a new one + map = HMalloc (sizeof (*map)); + map->palette = AllocNativePalette (); + if (!map->palette) + { + HFree (map); + return NULL; + } + } + map->next = NULL; + map->index = -1; + map->refcount = 1; + map->version = 0; + + return map; +} + +static TFB_ColorMap * +clone_colormap (TFB_ColorMap *from, int index) + // returns an addrefed object +{ + TFB_ColorMap *map; + + map = alloc_colormap (); + if (!map) + { + log_add (log_Warning, "FATAL: clone_colormap(): " + "could not allocate a map"); + exit (EXIT_FAILURE); + } + else + { // fresh new map + map->index = index; + if (from) + map->version = from->version; + } + map->version++; + + return map; +} + +static void +delete_colormap (TFB_ColorMap *map) +{ + FreeNativePalette (map->palette); + HFree (map); +} + +static inline void +free_colormap (TFB_ColorMap *map) +{ + if (!map) + { + log_add (log_Warning, "free_colormap(): tried to free a NULL map"); + return; + } + + if (poolcount < SPARE_COLORMAPS) + { // return to the spare pool + map->next = poolhead; + poolhead = map; + ++poolcount; + } + else + { // don't need any more spares + delete_colormap (map); + } +} + +static inline TFB_ColorMap * +get_colormap (int index) +{ + TFB_ColorMap *map; + + map = colormaps[index]; + if (!map) + { + log_add (log_Fatal, "BUG: get_colormap(): map not present"); + exit (EXIT_FAILURE); + } + + map->refcount++; + return map; +} + +static void +release_colormap (TFB_ColorMap *map) +{ + if (!map) + return; + + if (map->refcount <= 0) + { + log_add (log_Warning, "BUG: release_colormap(): refcount not >0"); + return; + } + + map->refcount--; + if (map->refcount == 0) + free_colormap (map); +} + +void +TFB_ReturnColorMap (TFB_ColorMap *map) +{ + LockMutex (maplock); + release_colormap (map); + UnlockMutex (maplock); +} + +TFB_ColorMap * +TFB_GetColorMap (int index) +{ + TFB_ColorMap *map; + + LockMutex (maplock); + map = get_colormap (index); + UnlockMutex (maplock); + + return map; +} + +void +GetColorMapColors (Color *colors, TFB_ColorMap *map) +{ + int i; + + if (!map) + return; + + for (i = 0; i < NUMBER_OF_PLUTVALS; ++i) + colors[i] = GetNativePaletteColor (map->palette, i); +} + +BOOLEAN +SetColorMap (COLORMAPPTR map) +{ + int start, end; + int total_size; + UBYTE *colors = (UBYTE*)map; + TFB_ColorMap **mpp; + + if (!map) + return TRUE; + + start = *colors++; + end = *colors++; + if (start > end) + { + log_add (log_Warning, "ERROR: SetColorMap(): " + "starting map (%d) not less or eq ending (%d)", + start, end); + return FALSE; + } + if (start >= MAX_COLORMAPS) + { + log_add (log_Warning, "ERROR: SetColorMap(): " + "starting map (%d) beyond range (0-%d)", + start, (int)MAX_COLORMAPS - 1); + return FALSE; + } + if (end >= MAX_COLORMAPS) + { + log_add (log_Warning, "SetColorMap(): " + "ending map (%d) beyond range (0-%d)\n", + end, (int)MAX_COLORMAPS - 1); + end = MAX_COLORMAPS - 1; + } + + total_size = end + 1; + + LockMutex (maplock); + + if (total_size > mapcount) + mapcount = total_size; + + // parse the supplied PLUTs into our colormaps + for (mpp = colormaps + start; start <= end; ++start, ++mpp) + { + int i; + TFB_ColorMap *newmap; + TFB_ColorMap *oldmap; + + oldmap = *mpp; + newmap = clone_colormap (oldmap, start); + + for (i = 0; i < NUMBER_OF_PLUTVALS; ++i, colors += PLUTVAL_BYTE_SIZE) + { + Color color; + + color.a = 0xff; + color.r = colors[PLUTVAL_RED]; + color.g = colors[PLUTVAL_GREEN]; + color.b = colors[PLUTVAL_BLUE]; + SetNativePaletteColor (newmap->palette, i, color); + } + + *mpp = newmap; + release_colormap (oldmap); + } + + UnlockMutex (maplock); + + return TRUE; +} + +/* Fade Transforms */ + +int +GetFadeAmount (void) +{ + int newAmount; + + LockMutex (fadeLock); + + if (fadeInterval) + { // have a pending fade + TimeCount Now = GetTimeCounter (); + sint32 elapsed; + + elapsed = Now - fadeStartTime; + if (elapsed > fadeInterval) + elapsed = fadeInterval; + + newAmount = fadeAmount + (long)fadeDelta * elapsed / fadeInterval; + + if (elapsed >= fadeInterval) + { // fade is over + fadeAmount = newAmount; + fadeInterval = 0; + } + } + else + { // no fade pending, return the current + newAmount = fadeAmount; + } + + UnlockMutex (fadeLock); + + return newAmount; +} + +static void +finishPendingFade (void) +{ + if (fadeInterval) + { // end the fade immediately + fadeAmount += fadeDelta; + fadeInterval = 0; + } +} + +static void +FlushFadeXForms (void) +{ + LockMutex (fadeLock); + finishPendingFade (); + UnlockMutex (fadeLock); +} + +DWORD +FadeScreen (ScreenFadeType fadeType, SIZE TimeInterval) +{ + TimeCount TimeOut; + int FadeEnd; + + switch (fadeType) + { + case FadeAllToBlack: + case FadeSomeToBlack: + FadeEnd = FADE_NO_INTENSITY; + break; + case FadeAllToColor: + case FadeSomeToColor: + FadeEnd = FADE_NORMAL_INTENSITY; + break; + case FadeAllToWhite: + case FadeSomeToWhite: + FadeEnd = FADE_FULL_INTENSITY; + break; + default: + return (GetTimeCounter ()); + } + + // Don't make users wait for fades + if (QuitPosted) + TimeInterval = 0; + + LockMutex (fadeLock); + + finishPendingFade (); + + if (TimeInterval <= 0) + { // end the fade immediately + fadeAmount = FadeEnd; + // cancel any pending fades + fadeInterval = 0; + TimeOut = GetTimeCounter (); + } + else + { + fadeInterval = TimeInterval; + fadeDelta = FadeEnd - fadeAmount; + fadeStartTime = GetTimeCounter (); + TimeOut = fadeStartTime + TimeInterval + 1; + } + + UnlockMutex (fadeLock); + + return TimeOut; +} + +/* Colormap Transforms */ + +static void +finish_colormap_xform (int which) +{ + SetColorMap (XFormControl.TaskControl[which].CMapPtr); + XFormControl.TaskControl[which].CMapIndex = -1; + // check Highest ptr + if (which == XFormControl.Highest) + { + do + --which; + while (which >= 0 && XFormControl.TaskControl[which].CMapIndex == -1); + + XFormControl.Highest = which; + } +} + +static inline BYTE +blendChan (BYTE c1, BYTE c2, int weight, int scale) +{ + return c1 + ((int)c2 - c1) * weight / scale; +} + +/* This gives the XFormColorMap task a timeslice to do its thing + * Only one thread should ever be allowed to be calling this at any time + */ +BOOLEAN +XFormColorMap_step (void) +{ + BOOLEAN Changed = FALSE; + int x; + DWORD Now = GetTimeCounter (); + + LockMutex (XFormControl.Lock); + + for (x = 0; x <= XFormControl.Highest; ++x) + { + XFORM_CONTROL *control = &XFormControl.TaskControl[x]; + int index = control->CMapIndex; + int TicksLeft = control->EndTime - Now; + TFB_ColorMap *curmap; + + if (index < 0) + continue; // unused slot + + LockMutex (maplock); + + curmap = colormaps[index]; + if (!curmap) + { + UnlockMutex (maplock); + log_add (log_Error, "BUG: XFormColorMap_step(): no current map"); + finish_colormap_xform (x); + continue; + } + + if (TicksLeft > 0) + { +#define XFORM_SCALE 0x10000 + TFB_ColorMap *newmap = NULL; + UBYTE *newClr; + Color *oldClr; + int frac; + int i; + + newmap = clone_colormap (curmap, index); + + oldClr = control->OldCMap; + newClr = (UBYTE*)control->CMapPtr + 2; + + frac = (int)(control->Ticks - TicksLeft) * XFORM_SCALE + / control->Ticks; + + for (i = 0; i < NUMBER_OF_PLUTVALS; ++i, ++oldClr, + newClr += PLUTVAL_BYTE_SIZE) + { + Color color; + + color.a = 0xff; + color.r = blendChan (oldClr->r, newClr[PLUTVAL_RED], + frac, XFORM_SCALE); + color.g = blendChan (oldClr->g, newClr[PLUTVAL_GREEN], + frac, XFORM_SCALE); + color.b = blendChan (oldClr->b, newClr[PLUTVAL_BLUE], + frac, XFORM_SCALE); + SetNativePaletteColor (newmap->palette, i, color); + } + + colormaps[index] = newmap; + release_colormap (curmap); + } + + UnlockMutex (maplock); + + if (TicksLeft <= 0) + { // asked for immediate xform or already done + finish_colormap_xform (x); + } + + Changed = TRUE; + } + + UnlockMutex (XFormControl.Lock); + + return Changed; +} + +static void +FlushPLUTXForms (void) +{ + int i; + + LockMutex (XFormControl.Lock); + + for (i = 0; i <= XFormControl.Highest; ++i) + { + if (XFormControl.TaskControl[i].CMapIndex >= 0) + finish_colormap_xform (i); + } + XFormControl.Highest = -1; // all gone + + UnlockMutex (XFormControl.Lock); +} + +static DWORD +XFormPLUT (COLORMAPPTR ColorMapPtr, SIZE TimeInterval) +{ + TFB_ColorMap *map; + XFORM_CONTROL *control; + int index; + int x; + int first_avail = -1; + DWORD EndTime; + DWORD Now; + + Now = GetTimeCounter (); + index = *(UBYTE*)ColorMapPtr; + + LockMutex (XFormControl.Lock); + // Find an available slot, or reuse if required + for (x = 0; x <= XFormControl.Highest + && index != XFormControl.TaskControl[x].CMapIndex; + ++x) + { + if (first_avail == -1 && XFormControl.TaskControl[x].CMapIndex == -1) + first_avail = x; + } + + if (index == XFormControl.TaskControl[x].CMapIndex) + { // already xforming this colormap -- cancel and reuse slot + finish_colormap_xform (x); + } + else if (first_avail >= 0) + { // picked up a slot along the way + x = first_avail; + } + else if (x >= MAX_XFORMS) + { // flush some xforms if the queue is full + log_add (log_Debug, "WARNING: XFormPLUT(): no slots available"); + x = XFormControl.Highest; + finish_colormap_xform (x); + } + // take next unused one + control = &XFormControl.TaskControl[x]; + if (x > XFormControl.Highest) + XFormControl.Highest = x; + + // make a copy of the current map + LockMutex (maplock); + map = colormaps[index]; + if (!map) + { + UnlockMutex (maplock); + UnlockMutex (XFormControl.Lock); + log_add (log_Warning, "BUG: XFormPLUT(): no current map"); + return (0); + } + GetColorMapColors (control->OldCMap, map); + UnlockMutex (maplock); + + control->CMapIndex = index; + control->CMapPtr = ColorMapPtr; + control->Ticks = TimeInterval; + if (control->Ticks < 0) + control->Ticks = 0; /* prevent negative fade */ + control->StartTime = Now; + control->EndTime = EndTime = Now + control->Ticks; + + UnlockMutex (XFormControl.Lock); + + return (EndTime); +} + +DWORD +XFormColorMap (COLORMAPPTR ColorMapPtr, SIZE TimeInterval) +{ + if (!ColorMapPtr) + return (0); + + // Don't make users wait for transforms + if (QuitPosted) + TimeInterval = 0; + + return XFormPLUT (ColorMapPtr, TimeInterval); +} + +void +FlushColorXForms (void) +{ + FlushFadeXForms (); + FlushPLUTXForms (); +} + +// The type conversions are implicit and will generate errors +// or warnings if types change imcompatibly +COLORMAPPTR +GetColorMapAddress (COLORMAP colormap) +{ + return GetStringAddress (colormap); +} diff --git a/src/libs/graphics/cmap.h b/src/libs/graphics/cmap.h new file mode 100644 index 0000000..f27f789 --- /dev/null +++ b/src/libs/graphics/cmap.h @@ -0,0 +1,77 @@ +//Copyright Paul Reiche, Fred Ford. 1992-2002 + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef CMAP_H +#define CMAP_H + +#include "libs/gfxlib.h" + +#define MAX_COLORMAPS 250 + +// These are pertinent to colortable file format +// We load colormaps as binary and parse them when needed +#define PLUTVAL_BYTE_SIZE 3 +// Channel order in colormap tables +#define PLUTVAL_RED 0 +#define PLUTVAL_GREEN 1 +#define PLUTVAL_BLUE 2 + +#define NUMBER_OF_PLUTVALS 256 +// Size of the colormap in a colortable file +#define PLUT_BYTE_SIZE (PLUTVAL_BYTE_SIZE * NUMBER_OF_PLUTVALS) + +#define FADE_NO_INTENSITY 0 +#define FADE_NORMAL_INTENSITY 255 +#define FADE_FULL_INTENSITY 510 + +typedef struct NativePalette NativePalette; + +typedef struct tfb_colormap +{ + int index; + // Colormap index as the game sees it + int version; + // Version goes up every time the colormap changes. This may + // be due to SetColorMap() or at every transformation step + // of XFormColorMap(). Paletted TFB_Images track the last + // colormap version they were drawn with for optimization. + int refcount; + struct tfb_colormap *next; + // for spares linking + NativePalette *palette; +} TFB_ColorMap; + +extern int GetFadeAmount (void); + +extern void InitColorMaps (void); +extern void UninitColorMaps (void); + +extern void GetColorMapColors (Color *colors, TFB_ColorMap *); + +extern TFB_ColorMap * TFB_GetColorMap (int index); +extern void TFB_ReturnColorMap (TFB_ColorMap *map); + +extern BOOLEAN XFormColorMap_step (void); + +// Native +NativePalette* AllocNativePalette (void); +void FreeNativePalette (NativePalette *); +void SetNativePaletteColor (NativePalette *, int index, Color); +Color GetNativePaletteColor (NativePalette *, int index); + +#endif /* CMAP_H */ diff --git a/src/libs/graphics/context.c b/src/libs/graphics/context.c new file mode 100644 index 0000000..d609ded --- /dev/null +++ b/src/libs/graphics/context.c @@ -0,0 +1,404 @@ +//Copyright Paul Reiche, Fred Ford. 1992-2002 + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "gfxintrn.h" + +GRAPHICS_STATUS _GraphicsStatusFlags; +CONTEXT _pCurContext; + +#ifdef DEBUG +// We keep track of all contexts +CONTEXT firstContext; + // The first one in the list. +CONTEXT *contextEnd = &firstContext; + // Where to put the next context. +#endif + +PRIMITIVE _locPrim; + +FONT _CurFontPtr; + +#define DEFAULT_FORE_COLOR BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x1F), 0x0F) +#define DEFAULT_BACK_COLOR BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x00), 0x00) + +#define DEFAULT_DRAW_MODE MAKE_DRAW_MODE (DRAW_DEFAULT, 255) + +CONTEXT +SetContext (CONTEXT Context) +{ + CONTEXT LastContext; + + LastContext = _pCurContext; + if (Context != LastContext) + { + if (LastContext) + { + UnsetContextFlags ( + MAKE_WORD (0, GRAPHICS_ACTIVE | DRAWABLE_ACTIVE)); + SetContextFlags ( + MAKE_WORD (0, _GraphicsStatusFlags + & (GRAPHICS_ACTIVE | DRAWABLE_ACTIVE))); + + DeactivateContext (); + } + + _pCurContext = Context; + if (_pCurContext) + { + ActivateContext (); + + _GraphicsStatusFlags &= ~(GRAPHICS_ACTIVE | DRAWABLE_ACTIVE); + _GraphicsStatusFlags |= HIBYTE (_get_context_flags ()); + + SetPrimColor (&_locPrim, _get_context_fg_color ()); + + _CurFramePtr = _get_context_fg_frame (); + _CurFontPtr = _get_context_font (); + } + } + + return (LastContext); +} + +#ifdef DEBUG +CONTEXT +CreateContextAux (const char *name) +#else /* if !defined(DEBUG) */ +CONTEXT +CreateContextAux (void) +#endif /* !defined(DEBUG) */ +{ + CONTEXT NewContext; + + NewContext = AllocContext (); + if (NewContext) + { + /* initialize context */ +#ifdef DEBUG + NewContext->name = name; + NewContext->next = NULL; + *contextEnd = NewContext; + contextEnd = &NewContext->next; +#endif /* DEBUG */ + + NewContext->Mode = DEFAULT_DRAW_MODE; + NewContext->ForeGroundColor = DEFAULT_FORE_COLOR; + NewContext->BackGroundColor = DEFAULT_BACK_COLOR; + } + + return NewContext; +} + +#ifdef DEBUG +// Loop through the list of context to the pointer which points to the +// specified context. This is either 'firstContext' or the address of +// the 'next' field of some other context. +static CONTEXT * +FindContextPtr (CONTEXT context) { + CONTEXT *ptr; + + for (ptr = &firstContext; *ptr != NULL; ptr = &(*ptr)->next) { + if (*ptr == context) + break; + } + return ptr; +} +#endif /* DEBUG */ + +BOOLEAN +DestroyContext (CONTEXT ContextRef) +{ + TFB_Image *img; + + if (ContextRef == 0) + return (FALSE); + + if (_pCurContext && _pCurContext == ContextRef) + SetContext ((CONTEXT)0); + +#ifdef DEBUG + // Unlink the context. + { + CONTEXT *contextPtr = FindContextPtr (ContextRef); + if (contextEnd == &ContextRef->next) + contextEnd = contextPtr; + *contextPtr = ContextRef->next; + } +#endif /* DEBUG */ + + img = ContextRef->FontBacking; + if (img) + TFB_DrawImage_Delete (img); + + FreeContext (ContextRef); + return TRUE; +} + +Color +SetContextForeGroundColor (Color color) +{ + Color oldColor; + + if (!ContextActive ()) + return DEFAULT_FORE_COLOR; + + oldColor = _get_context_fg_color (); + if (!sameColor(oldColor, color)) + { + SwitchContextForeGroundColor (color); + + if (!(_get_context_fbk_flags () & FBK_IMAGE)) + { + SetContextFBkFlags (FBK_DIRTY); + } + } + SetPrimColor (&_locPrim, color); + + return (oldColor); +} + +Color +GetContextForeGroundColor (void) +{ + if (!ContextActive ()) + return DEFAULT_FORE_COLOR; + + return _get_context_fg_color (); +} + +Color +SetContextBackGroundColor (Color color) +{ + Color oldColor; + + if (!ContextActive ()) + return DEFAULT_BACK_COLOR; + + oldColor = _get_context_bg_color (); + if (!sameColor(oldColor, color)) + SwitchContextBackGroundColor (color); + + return oldColor; +} + +Color +GetContextBackGroundColor (void) +{ + if (!ContextActive ()) + return DEFAULT_BACK_COLOR; + + return _get_context_bg_color (); +} + +DrawMode +SetContextDrawMode (DrawMode mode) +{ + DrawMode oldMode; + + if (!ContextActive ()) + return DEFAULT_DRAW_MODE; + + oldMode = _get_context_draw_mode (); + SwitchContextDrawMode (mode); + + return oldMode; +} + +DrawMode +GetContextDrawMode (void) +{ + if (!ContextActive ()) + return DEFAULT_DRAW_MODE; + + return _get_context_draw_mode (); +} + +// Returns a rect based at 0,0 and the size of context foreground frame +static inline RECT +_get_context_fg_rect (void) +{ + RECT r = { {0, 0}, {0, 0} }; + if (_CurFramePtr) + r.extent = GetFrameBounds (_CurFramePtr); + return r; +} + +BOOLEAN +SetContextClipRect (RECT *lpRect) +{ + if (!ContextActive ()) + return (FALSE); + + if (lpRect) + { + if (rectsEqual (*lpRect, _get_context_fg_rect ())) + { // Cliprect is undefined to mirror GetContextClipRect() + _pCurContext->ClipRect.extent.width = 0; + } + else + { // We have a cliprect + _pCurContext->ClipRect = *lpRect; + } + } + else + { // Set cliprect as undefined + _pCurContext->ClipRect.extent.width = 0; + } + + return TRUE; +} + +BOOLEAN +GetContextClipRect (RECT *lpRect) +{ + if (!ContextActive ()) + return (FALSE); + + *lpRect = _pCurContext->ClipRect; + if (!_pCurContext->ClipRect.extent.width) + { // Though the cliprect is undefined, drawing will be clipped + // to the extent of the foreground frame + *lpRect = _get_context_fg_rect (); + } + + return (_pCurContext->ClipRect.extent.width != 0); +} + +POINT +SetContextOrigin (POINT orgOffset) +{ + // XXX: This is a hack, kind of. But that's what the original did. + return SetFrameHot (_CurFramePtr, orgOffset); +} + +FRAME +SetContextFontEffect (FRAME EffectFrame) +{ + FRAME LastEffect; + + if (!ContextActive ()) + return (NULL); + + LastEffect = _get_context_fonteff (); + if (EffectFrame != LastEffect) + { + SwitchContextFontEffect (EffectFrame); + + if (EffectFrame != 0) + { + SetContextFBkFlags (FBK_IMAGE); + } + else + { + UnsetContextFBkFlags (FBK_IMAGE); + } + } + + return LastEffect; +} + +void +FixContextFontEffect (void) +{ + SIZE w, h; + TFB_Image* img; + + if (!ContextActive () || (_get_context_font_backing () != 0 + && !(_get_context_fbk_flags () & FBK_DIRTY))) + return; + + if (!GetContextFontLeading (&h) || !GetContextFontLeadingWidth (&w)) + return; + + img = _pCurContext->FontBacking; + if (img) + TFB_DrawScreen_DeleteImage (img); + + img = TFB_DrawImage_CreateForScreen (w, h, TRUE); + if (_get_context_fbk_flags () & FBK_IMAGE) + { // image pattern backing + FRAME EffectFrame = _get_context_fonteff (); + + TFB_DrawImage_Image (EffectFrame->image, + -EffectFrame->HotSpot.x, -EffectFrame->HotSpot.y, + 0, 0, NULL, DRAW_REPLACE_MODE, img); + } + else + { // solid color backing + RECT r = { {0, 0}, {w, h} }; + Color color = _get_context_fg_color (); + + TFB_DrawImage_Rect (&r, color, DRAW_REPLACE_MODE, img); + } + + _pCurContext->FontBacking = img; + UnsetContextFBkFlags (FBK_DIRTY); +} + +// 'area' may be NULL to copy the entire CONTEXT cliprect +// 'area' is relative to the CONTEXT cliprect +DRAWABLE +CopyContextRect (const RECT* area) +{ + RECT clipRect; + RECT fgRect; + RECT r; + + if (!ContextActive () || !_CurFramePtr) + return NULL; + + fgRect = _get_context_fg_rect (); + GetContextClipRect (&clipRect); + r = clipRect; + if (area) + { // a portion of the context + r.corner.x += area->corner.x; + r.corner.y += area->corner.y; + r.extent = area->extent; + } + // TODO: Should this take CONTEXT origin into account too? + // validate the rect + if (!BoxIntersect (&r, &fgRect, &r)) + return NULL; + + if (_CurFramePtr->Type == SCREEN_DRAWABLE) + return LoadDisplayPixmap (&r, NULL); + else + return CopyFrameRect (_CurFramePtr, &r); +} + +#ifdef DEBUG +const char * +GetContextName (CONTEXT context) +{ + return context->name; +} + +CONTEXT +GetFirstContext (void) +{ + return firstContext; +} + +CONTEXT +GetNextContext (CONTEXT context) +{ + return context->next; +} +#endif /* DEBUG */ + diff --git a/src/libs/graphics/context.h b/src/libs/graphics/context.h new file mode 100644 index 0000000..09b50cf --- /dev/null +++ b/src/libs/graphics/context.h @@ -0,0 +1,147 @@ +//Copyright Paul Reiche, Fred Ford. 1992-2002 + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef LIBS_GRAPHICS_CONTEXT_H_ +#define LIBS_GRAPHICS_CONTEXT_H_ + +#include "tfb_draw.h" +#include "libs/memlib.h" + +typedef UWORD FBK_FLAGS; +#define FBK_DIRTY (1 << 0) +#define FBK_IMAGE (1 << 1) + +struct context_desc +{ + UWORD Flags; + // Low nibble currently unused + // High nibble contains GRAPHICS_STATUS + + Color ForeGroundColor, BackGroundColor; + DrawMode Mode; + FRAME ForeGroundFrame; + FONT Font; + + RECT ClipRect; + + FRAME FontEffect; + TFB_Image *FontBacking; + FBK_FLAGS BackingFlags; + +#ifdef DEBUG + const char *name; + CONTEXT next; +#endif +}; + +#define AllocContext() HCalloc (sizeof (CONTEXT_DESC)) +#define FreeContext HFree + +extern CONTEXT _pCurContext; +extern PRIMITIVE _locPrim; + +#define _get_context_fg_color() (_pCurContext->ForeGroundColor) +#define _get_context_bg_color() (_pCurContext->BackGroundColor) +#define _get_context_flags() (_pCurContext->Flags) +#define _get_context_fg_frame() (_pCurContext->ForeGroundFrame) +#define _get_context_font() (_pCurContext->Font) +#define _get_context_fbk_flags() (_pCurContext->BackingFlags) +#define _get_context_fonteff() (_pCurContext->FontEffect) +#define _get_context_font_backing() (_pCurContext->FontBacking) +#define _get_context_draw_mode() (_pCurContext->Mode) + +#define SwitchContextDrawMode(m) \ +{ \ + _pCurContext->Mode = (m); \ +} +#define SwitchContextForeGroundColor(c) \ +{ \ + _pCurContext->ForeGroundColor = (c); \ +} +#define SwitchContextBackGroundColor(c) \ +{ \ + _pCurContext->BackGroundColor = (c); \ +} +#define SetContextFlags(f) \ +{ \ + _pCurContext->Flags |= (f); \ +} +#define UnsetContextFlags(f) \ +{ \ + _pCurContext->Flags &= ~(f); \ +} +#define SwitchContextFGFrame(f) \ +{ \ + _pCurContext->ForeGroundFrame = (f); \ +} +#define SwitchContextFont(f) \ +{ \ + _pCurContext->Font = (f); \ + SetContextFBkFlags (FBK_DIRTY); \ +} +#define SwitchContextBGFunc(f) \ +{ \ + _pCurContext->BackGroundFunc = (f); \ +} +#define SetContextFBkFlags(f) \ +{ \ + _pCurContext->BackingFlags |= (f); \ +} +#define UnsetContextFBkFlags(f) \ +{ \ + _pCurContext->BackingFlags &= ~(f); \ +} +#define SwitchContextFontEffect(f) \ +{ \ + _pCurContext->FontEffect = (f); \ + SetContextFBkFlags (FBK_DIRTY); \ +} + +typedef BYTE GRAPHICS_STATUS; + +extern GRAPHICS_STATUS _GraphicsStatusFlags; +#define GRAPHICS_ACTIVE (GRAPHICS_STATUS)(1 << 0) +#define GRAPHICS_VISIBLE (GRAPHICS_STATUS)(1 << 1) +#define CONTEXT_ACTIVE (GRAPHICS_STATUS)(1 << 2) +#define DRAWABLE_ACTIVE (GRAPHICS_STATUS)(1 << 3) +#define DeactivateGraphics() (_GraphicsStatusFlags &= ~GRAPHICS_ACTIVE) +#define ActivateGraphics() (_GraphicsStatusFlags |= GRAPHICS_ACTIVE) +#define GraphicsActive() (_GraphicsStatusFlags & GRAPHICS_ACTIVE) +#define DeactivateVisible() (_GraphicsStatusFlags &= ~GRAPHICS_VISIBLE) +#define ActivateVisible() (_GraphicsStatusFlags |= GRAPHICS_VISIBLE) +#define DeactivateContext() (_GraphicsStatusFlags &= ~CONTEXT_ACTIVE) +#define ActivateContext() (_GraphicsStatusFlags |= CONTEXT_ACTIVE) +#define ContextActive() (_GraphicsStatusFlags & CONTEXT_ACTIVE) +#define DeactivateDrawable() (_GraphicsStatusFlags &= ~DRAWABLE_ACTIVE) +#define ActivateDrawable() (_GraphicsStatusFlags |= DRAWABLE_ACTIVE) +#define DrawableActive() (_GraphicsStatusFlags & DRAWABLE_ACTIVE) + +#define SYSTEM_ACTIVE (GRAPHICS_STATUS)(CONTEXT_ACTIVE | DRAWABLE_ACTIVE) + +#define GraphicsSystemActive() \ + ((_GraphicsStatusFlags & SYSTEM_ACTIVE) == SYSTEM_ACTIVE) +#define GraphicsStatus() \ + (_GraphicsStatusFlags & (GRAPHICS_STATUS)(GRAPHICS_ACTIVE \ + | GRAPHICS_VISIBLE)) + +// pValidRect or origin may be NULL +BOOLEAN GetContextValidRect (RECT *pValidRect, POINT *origin); +extern void FixContextFontEffect (void); + +#endif /* LIBS_GRAPHICS_CONTEXT_H_ */ + diff --git a/src/libs/graphics/dcqueue.c b/src/libs/graphics/dcqueue.c new file mode 100644 index 0000000..70c0662 --- /dev/null +++ b/src/libs/graphics/dcqueue.c @@ -0,0 +1,670 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "port.h" +#include "libs/threadlib.h" +#include "libs/graphics/drawcmd.h" +#include "libs/graphics/drawable.h" +#include "libs/graphics/context.h" +#include "libs/graphics/dcqueue.h" +#include "libs/graphics/gfx_common.h" +#include "libs/graphics/bbox.h" +#include "libs/timelib.h" +#include "libs/log.h" +#include "libs/misc.h" + // for TFB_DEBUG_HALT + + +static RecursiveMutex DCQ_Mutex; + +CondVar RenderingCond; + +TFB_DrawCommand DCQ[DCQ_MAX]; + +TFB_DrawCommandQueue DrawCommandQueue; + +#define FPS_PERIOD (ONE_SECOND / 100) +int RenderedFrames = 0; + + +// Wait for the queue to be emptied. +static void +TFB_WaitForSpace (int requested_slots) +{ + int old_depth, i; + log_add (log_Debug, "DCQ overload (Size = %d, FullSize = %d, " + "Requested = %d). Sleeping until renderer is done.", + DrawCommandQueue.Size, DrawCommandQueue.FullSize, + requested_slots); + // Restore the DCQ locking level. I *think* this is + // always 1, but... + TFB_BatchReset (); + old_depth = GetRecursiveMutexDepth (DCQ_Mutex); + for (i = 0; i < old_depth; i++) + UnlockRecursiveMutex (DCQ_Mutex); + WaitCondVar (RenderingCond); + for (i = 0; i < old_depth; i++) + LockRecursiveMutex (DCQ_Mutex); + log_add (log_Debug, "DCQ clear (Size = %d, FullSize = %d). Continuing.", + DrawCommandQueue.Size, DrawCommandQueue.FullSize); +} + +void +Lock_DCQ (int slots) +{ + LockRecursiveMutex (DCQ_Mutex); + while (DrawCommandQueue.FullSize >= DCQ_MAX - slots) + { + TFB_WaitForSpace (slots); + } +} + +void +Unlock_DCQ (void) +{ + UnlockRecursiveMutex (DCQ_Mutex); +} + +// Always have the DCQ locked when calling this. +static void +Synchronize_DCQ (void) +{ + if (!DrawCommandQueue.Batching) + { + int front = DrawCommandQueue.Front; + int back = DrawCommandQueue.InsertionPoint; + DrawCommandQueue.Back = DrawCommandQueue.InsertionPoint; + if (front <= back) + { + DrawCommandQueue.Size = (back - front); + } + else + { + DrawCommandQueue.Size = (back + DCQ_MAX - front); + } + DrawCommandQueue.FullSize = DrawCommandQueue.Size; + } +} + +void +TFB_BatchGraphics (void) +{ + LockRecursiveMutex (DCQ_Mutex); + DrawCommandQueue.Batching++; + UnlockRecursiveMutex (DCQ_Mutex); +} + +void +TFB_UnbatchGraphics (void) +{ + LockRecursiveMutex (DCQ_Mutex); + if (DrawCommandQueue.Batching) + { + DrawCommandQueue.Batching--; + } + Synchronize_DCQ (); + UnlockRecursiveMutex (DCQ_Mutex); +} + +// Cancel all pending batch operations, making them unbatched. This will +// cause a small amount of flicker when invoked, but prevents +// batching problems from freezing the game. +void +TFB_BatchReset (void) +{ + LockRecursiveMutex (DCQ_Mutex); + DrawCommandQueue.Batching = 0; + Synchronize_DCQ (); + UnlockRecursiveMutex (DCQ_Mutex); +} + + +// Draw Command Queue Stuff + +void +Init_DrawCommandQueue (void) +{ + DrawCommandQueue.Back = 0; + DrawCommandQueue.Front = 0; + DrawCommandQueue.InsertionPoint = 0; + DrawCommandQueue.Batching = 0; + DrawCommandQueue.FullSize = 0; + DrawCommandQueue.Size = 0; + + TFB_BBox_Init (ScreenWidth, ScreenHeight); + + DCQ_Mutex = CreateRecursiveMutex ("DCQ", + SYNC_CLASS_TOPLEVEL | SYNC_CLASS_VIDEO); + + RenderingCond = CreateCondVar ("DCQ empty", + SYNC_CLASS_TOPLEVEL | SYNC_CLASS_VIDEO); +} + +void +Uninit_DrawCommandQueue (void) +{ + if (RenderingCond) + { + DestroyCondVar (RenderingCond); + RenderingCond = 0; + } + + if (DCQ_Mutex) + { + DestroyRecursiveMutex (DCQ_Mutex); + DCQ_Mutex = 0; + } +} + +void +TFB_DrawCommandQueue_Push (TFB_DrawCommand* Command) +{ + Lock_DCQ (1); + DCQ[DrawCommandQueue.InsertionPoint] = *Command; + DrawCommandQueue.InsertionPoint = (DrawCommandQueue.InsertionPoint + 1) + % DCQ_MAX; + DrawCommandQueue.FullSize++; + Synchronize_DCQ (); + Unlock_DCQ (); +} + +int +TFB_DrawCommandQueue_Pop (TFB_DrawCommand *target) +{ + LockRecursiveMutex (DCQ_Mutex); + + if (DrawCommandQueue.Size == 0) + { + Unlock_DCQ (); + return (0); + } + + if (DrawCommandQueue.Front == DrawCommandQueue.Back && + DrawCommandQueue.Size != DCQ_MAX) + { + log_add (log_Debug, "Augh! Assertion failure in DCQ! " + "Front == Back, Size != DCQ_MAX"); + DrawCommandQueue.Size = 0; + Unlock_DCQ (); + return (0); + } + + *target = DCQ[DrawCommandQueue.Front]; + DrawCommandQueue.Front = (DrawCommandQueue.Front + 1) % DCQ_MAX; + + DrawCommandQueue.Size--; + DrawCommandQueue.FullSize--; + UnlockRecursiveMutex (DCQ_Mutex); + + return 1; +} + +void +TFB_DrawCommandQueue_Clear () +{ + LockRecursiveMutex (DCQ_Mutex); + DrawCommandQueue.Size = 0; + DrawCommandQueue.Front = 0; + DrawCommandQueue.Back = 0; + DrawCommandQueue.Batching = 0; + DrawCommandQueue.FullSize = 0; + DrawCommandQueue.InsertionPoint = 0; + UnlockRecursiveMutex (DCQ_Mutex); +} + +static void +checkExclusiveThread (TFB_DrawCommand* DrawCommand) +{ +#ifdef DEBUG_DCQ_THREADS + static uint32 exclusiveThreadId; + extern uint32 SDL_ThreadID(void); + + // Only one thread is currently allowed to enqueue commands + // This is not a technical limitation but rather a semantical one atm. + if (DrawCommand->Type == TFB_DRAWCOMMANDTYPE_REINITVIDEO) + { // TFB_DRAWCOMMANDTYPE_REINITVIDEO is an exception + // It is queued from the main() thread, which is safe to do + return; + } + + if (!exclusiveThreadId) + exclusiveThreadId = SDL_ThreadID(); + else + assert (SDL_ThreadID() == exclusiveThreadId); +#else + (void) DrawCommand; // suppress unused warning +#endif +} + +void +TFB_EnqueueDrawCommand (TFB_DrawCommand* DrawCommand) +{ + if (TFB_DEBUG_HALT) + { + return; + } + + checkExclusiveThread (DrawCommand); + + if (DrawCommand->Type <= TFB_DRAWCOMMANDTYPE_COPYTOIMAGE + && _CurFramePtr->Type == SCREEN_DRAWABLE) + { + static RECT scissor_rect; + + // Set the clipping region. + // We allow drawing with no current context set, so the whole screen + if ((_pCurContext && !rectsEqual (scissor_rect, _pCurContext->ClipRect)) + || (!_pCurContext && scissor_rect.extent.width != 0)) + { + // Enqueue command to set the glScissor spec + TFB_DrawCommand DC; + + if (_pCurContext) + scissor_rect = _pCurContext->ClipRect; + else + scissor_rect.extent.width = 0; + + if (scissor_rect.extent.width) + { + DC.Type = TFB_DRAWCOMMANDTYPE_SCISSORENABLE; + DC.data.scissor.rect = scissor_rect; + } + else + { + DC.Type = TFB_DRAWCOMMANDTYPE_SCISSORDISABLE; + } + + TFB_EnqueueDrawCommand(&DC); + } + } + + TFB_DrawCommandQueue_Push (DrawCommand); +} + +static void +computeFPS (void) +{ + static TimeCount last_time; + static TimePeriod fps_counter; + TimeCount current_time; + TimePeriod delta_time; + + current_time = GetTimeCounter (); + delta_time = current_time - last_time; + last_time = current_time; + + fps_counter += delta_time; + if (fps_counter > FPS_PERIOD) + { + log_add (log_User, "fps %.2f, effective %.2f", + (float)ONE_SECOND / delta_time, + (float)ONE_SECOND * RenderedFrames / fps_counter); + + fps_counter = 0; + RenderedFrames = 0; + } +} + +// Only call from main() thread!! +void +TFB_FlushGraphics (void) +{ + int commands_handled; + BOOLEAN livelock_deterrence; + + // This is technically a locking violation on DrawCommandQueue.Size, + // but it is likely to not be very destructive. + if (DrawCommandQueue.Size == 0) + { + static int last_fade = 255; + static int last_transition = 255; + int current_fade = GetFadeAmount (); + int current_transition = TransitionAmount; + + if ((current_fade != 255 && current_fade != last_fade) || + (current_transition != 255 && + current_transition != last_transition) || + (current_fade == 255 && last_fade != 255) || + (current_transition == 255 && last_transition != 255)) + { + TFB_SwapBuffers (TFB_REDRAW_FADING); + // if fading, redraw every frame + } + else + { + TaskSwitch (); + } + + last_fade = current_fade; + last_transition = current_transition; + BroadcastCondVar (RenderingCond); + return; + } + + if (GfxFlags & TFB_GFXFLAGS_SHOWFPS) + computeFPS (); + + commands_handled = 0; + livelock_deterrence = FALSE; + + if (DrawCommandQueue.FullSize > DCQ_FORCE_BREAK_SIZE) + { + TFB_BatchReset (); + } + + if (DrawCommandQueue.Size > DCQ_FORCE_SLOWDOWN_SIZE) + { + Lock_DCQ (-1); + livelock_deterrence = TRUE; + } + + TFB_BBox_Reset (); + + for (;;) + { + TFB_DrawCommand DC; + + if (!TFB_DrawCommandQueue_Pop (&DC)) + { + // the Queue is now empty. + break; + } + + ++commands_handled; + if (!livelock_deterrence && commands_handled + DrawCommandQueue.Size + > DCQ_LIVELOCK_MAX) + { + // log_add (log_Debug, "Initiating livelock deterrence!"); + livelock_deterrence = TRUE; + + Lock_DCQ (-1); + } + + switch (DC.Type) + { + case TFB_DRAWCOMMANDTYPE_SETMIPMAP: + { + TFB_DrawCommand_SetMipmap *cmd = &DC.data.setmipmap; + TFB_DrawImage_SetMipmap (cmd->image, cmd->mipmap, + cmd->hotx, cmd->hoty); + break; + } + + case TFB_DRAWCOMMANDTYPE_IMAGE: + { + TFB_DrawCommand_Image *cmd = &DC.data.image; + TFB_Image *DC_image = cmd->image; + const int x = cmd->x; + const int y = cmd->y; + + TFB_DrawCanvas_Image (DC_image, x, y, + cmd->scale, cmd->scaleMode, cmd->colormap, + cmd->drawMode, + TFB_GetScreenCanvas (cmd->destBuffer)); + + if (cmd->destBuffer == TFB_SCREEN_MAIN) + { + LockMutex (DC_image->mutex); + if (cmd->scale) + TFB_BBox_RegisterCanvas (DC_image->ScaledImg, + x - DC_image->last_scale_hs.x, + y - DC_image->last_scale_hs.y); + else + TFB_BBox_RegisterCanvas (DC_image->NormalImg, + x - DC_image->NormalHs.x, + y - DC_image->NormalHs.y); + UnlockMutex (DC_image->mutex); + } + + break; + } + + case TFB_DRAWCOMMANDTYPE_FILLEDIMAGE: + { + TFB_DrawCommand_FilledImage *cmd = &DC.data.filledimage; + TFB_Image *DC_image = cmd->image; + const int x = cmd->x; + const int y = cmd->y; + + TFB_DrawCanvas_FilledImage (DC_image, x, y, + cmd->scale, cmd->scaleMode, cmd->color, + cmd->drawMode, + TFB_GetScreenCanvas (cmd->destBuffer)); + + if (cmd->destBuffer == TFB_SCREEN_MAIN) + { + LockMutex (DC_image->mutex); + if (cmd->scale) + TFB_BBox_RegisterCanvas (DC_image->ScaledImg, + x - DC_image->last_scale_hs.x, + y - DC_image->last_scale_hs.y); + else + TFB_BBox_RegisterCanvas (DC_image->NormalImg, + x - DC_image->NormalHs.x, + y - DC_image->NormalHs.y); + UnlockMutex (DC_image->mutex); + } + + break; + } + + case TFB_DRAWCOMMANDTYPE_FONTCHAR: + { + TFB_DrawCommand_FontChar *cmd = &DC.data.fontchar; + TFB_Char *DC_char = cmd->fontchar; + const int x = cmd->x; + const int y = cmd->y; + + TFB_DrawCanvas_FontChar (DC_char, cmd->backing, x, y, + cmd->drawMode, TFB_GetScreenCanvas (cmd->destBuffer)); + + if (cmd->destBuffer == TFB_SCREEN_MAIN) + { + RECT r; + + r.corner.x = x - DC_char->HotSpot.x; + r.corner.y = y - DC_char->HotSpot.y; + r.extent.width = DC_char->extent.width; + r.extent.height = DC_char->extent.height; + + TFB_BBox_RegisterRect (&r); + } + + break; + } + + case TFB_DRAWCOMMANDTYPE_LINE: + { + TFB_DrawCommand_Line *cmd = &DC.data.line; + + if (cmd->destBuffer == TFB_SCREEN_MAIN) + { + TFB_BBox_RegisterPoint (cmd->x1, cmd->y1); + TFB_BBox_RegisterPoint (cmd->x2, cmd->y2); + } + TFB_DrawCanvas_Line (cmd->x1, cmd->y1, cmd->x2, cmd->y2, + cmd->color, cmd->drawMode, + TFB_GetScreenCanvas (cmd->destBuffer)); + break; + } + + case TFB_DRAWCOMMANDTYPE_RECTANGLE: + { + TFB_DrawCommand_Rect *cmd = &DC.data.rect; + + if (cmd->destBuffer == TFB_SCREEN_MAIN) + TFB_BBox_RegisterRect (&cmd->rect); + TFB_DrawCanvas_Rect (&cmd->rect, cmd->color, cmd->drawMode, + TFB_GetScreenCanvas (cmd->destBuffer)); + + break; + } + + case TFB_DRAWCOMMANDTYPE_SCISSORENABLE: + { + TFB_DrawCommand_Scissor *cmd = &DC.data.scissor; + + TFB_DrawCanvas_SetClipRect ( + TFB_GetScreenCanvas (TFB_SCREEN_MAIN), &cmd->rect); + TFB_BBox_SetClipRect (&DC.data.scissor.rect); + break; + } + + case TFB_DRAWCOMMANDTYPE_SCISSORDISABLE: + TFB_DrawCanvas_SetClipRect ( + TFB_GetScreenCanvas (TFB_SCREEN_MAIN), NULL); + TFB_BBox_SetClipRect (NULL); + break; + + case TFB_DRAWCOMMANDTYPE_COPYTOIMAGE: + { + TFB_DrawCommand_CopyToImage *cmd = &DC.data.copytoimage; + TFB_Image *DC_image = cmd->image; + const POINT dstPt = {0, 0}; + + if (DC_image == 0) + { + log_add (log_Debug, "DCQ ERROR: COPYTOIMAGE passed null " + "image ptr"); + break; + } + LockMutex (DC_image->mutex); + TFB_DrawCanvas_CopyRect ( + TFB_GetScreenCanvas (cmd->srcBuffer), &cmd->rect, + DC_image->NormalImg, dstPt); + UnlockMutex (DC_image->mutex); + break; + } + + case TFB_DRAWCOMMANDTYPE_COPY: + { + TFB_DrawCommand_Copy *cmd = &DC.data.copy; + const RECT r = cmd->rect; + + if (cmd->destBuffer == TFB_SCREEN_MAIN) + TFB_BBox_RegisterRect (&cmd->rect); + + TFB_DrawCanvas_CopyRect ( + TFB_GetScreenCanvas (cmd->srcBuffer), &r, + TFB_GetScreenCanvas (cmd->destBuffer), r.corner); + break; + } + + case TFB_DRAWCOMMANDTYPE_DELETEIMAGE: + { + TFB_Image *DC_image = DC.data.deleteimage.image; + TFB_DrawImage_Delete (DC_image); + break; + } + + case TFB_DRAWCOMMANDTYPE_DELETEDATA: + { + void *data = DC.data.deletedata.data; + HFree (data); + break; + } + + case TFB_DRAWCOMMANDTYPE_SENDSIGNAL: + ClearSemaphore (DC.data.sendsignal.sem); + break; + + case TFB_DRAWCOMMANDTYPE_REINITVIDEO: + { + TFB_DrawCommand_ReinitVideo *cmd = &DC.data.reinitvideo; + int oldDriver = GraphicsDriver; + int oldFlags = GfxFlags; + int oldWidth = ScreenWidthActual; + int oldHeight = ScreenHeightActual; + if (TFB_ReInitGraphics (cmd->driver, cmd->flags, + cmd->width, cmd->height)) + { + log_add (log_Error, "Could not provide requested mode: " + "reverting to last known driver."); + // We don't know what exactly failed, so roll it all back + if (TFB_ReInitGraphics (oldDriver, oldFlags, + oldWidth, oldHeight)) + { + log_add (log_Fatal, + "Couldn't reinit at that point either. " + "Your video has been somehow tied in knots."); + exit (EXIT_FAILURE); + } + } + TFB_SwapBuffers (TFB_REDRAW_YES); + break; + } + + case TFB_DRAWCOMMANDTYPE_CALLBACK: + { + DC.data.callback.callback (DC.data.callback.arg); + break; + } + } + } + + if (livelock_deterrence) + Unlock_DCQ (); + + TFB_SwapBuffers (TFB_REDRAW_NO); + RenderedFrames++; + BroadcastCondVar (RenderingCond); +} + +void +TFB_PurgeDanglingGraphics (void) +{ + Lock_DCQ (-1); + + for (;;) + { + TFB_DrawCommand DC; + + if (!TFB_DrawCommandQueue_Pop (&DC)) + { + // the Queue is now empty. + break; + } + + switch (DC.Type) + { + case TFB_DRAWCOMMANDTYPE_DELETEIMAGE: + { + TFB_Image *DC_image = DC.data.deleteimage.image; + TFB_DrawImage_Delete (DC_image); + break; + } + case TFB_DRAWCOMMANDTYPE_DELETEDATA: + { + void *data = DC.data.deletedata.data; + HFree (data); + break; + } + case TFB_DRAWCOMMANDTYPE_IMAGE: + { + TFB_ColorMap *cmap = DC.data.image.colormap; + if (cmap) + TFB_ReturnColorMap (cmap); + break; + } + case TFB_DRAWCOMMANDTYPE_SENDSIGNAL: + { + ClearSemaphore (DC.data.sendsignal.sem); + break; + } + } + } + Unlock_DCQ (); +} diff --git a/src/libs/graphics/dcqueue.h b/src/libs/graphics/dcqueue.h new file mode 100644 index 0000000..deed685 --- /dev/null +++ b/src/libs/graphics/dcqueue.h @@ -0,0 +1,55 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef DCQUEUE_H +#define DCQUEUE_H + +// Maximum size of the DCQ. The larger the DCQ, the larger frameskips +// become tolerable before initiating livelock deterrence and game +// slowdown. Other constants for controlling the frameskip/slowdown +// balance may be found in sdl_common.c near TFB_FlushGraphics. + +// Livelock deterrance constants. Because the entire screen is rarely +// refreshed, we may not drop draw commands on the floor with abandon. +// Furthermore, if the main program is queuing commands at a speed +// comparable to our processing of the commands, we never finish and +// the game freezes. Thus, if the queue starts out larger than +// DCQ_FORCE_SLOWDOWN_SIZE, or DCQ_LIVELOCK_MAX commands find +// themselves being processed in one go, livelock deterrence is +// enabled, and TFB_FlushGraphics locks the DCQ until it has processed +// all entries. If batched but pending commands exceed DCQ_FORCE_BREAK_SIZE, +// a continuity break is performed. This will effectively slow down the +// game logic, a fate we seek to avoid - however, it seems to be unavoidable +// on slower machines. Even there, it's seems nonexistent outside of +// communications screens. --Michael + +#ifdef DCQ_OF_DOOM +#define DCQ_MAX 512 +#define DCQ_FORCE_SLOWDOWN_SIZE 128 +#define DCQ_FORCE_BREAK_SIZE 512 +#define DCQ_LIVELOCK_MAX 256 +#else +#define DCQ_MAX 16384 +#define DCQ_FORCE_SLOWDOWN_SIZE 4096 +#define DCQ_FORCE_BREAK_SIZE 16384 +#define DCQ_LIVELOCK_MAX 4096 +#endif + +extern CondVar RenderingCond; + +#endif + + diff --git a/src/libs/graphics/drawable.c b/src/libs/graphics/drawable.c new file mode 100644 index 0000000..9766bc7 --- /dev/null +++ b/src/libs/graphics/drawable.c @@ -0,0 +1,501 @@ +//Copyright Paul Reiche, Fred Ford. 1992-2002 + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "libs/gfxlib.h" +#include "libs/graphics/context.h" +#include "libs/graphics/drawable.h" +#include "libs/graphics/tfb_draw.h" +#include "libs/memlib.h" +#include "tfb_draw.h" +#include <math.h> + +#ifndef M_PI +# define M_PI 3.14159265358979323846 +#endif + +FRAME _CurFramePtr; + +FRAME +SetContextFGFrame (FRAME Frame) +{ + FRAME LastFrame; + + if (Frame != (LastFrame = (FRAME)_CurFramePtr)) + { + if (LastFrame) + DeactivateDrawable (); + + _CurFramePtr = Frame; + if (_CurFramePtr) + ActivateDrawable (); + + if (ContextActive ()) + { + SwitchContextFGFrame (Frame); + } + } + + return (LastFrame); +} + +FRAME +GetContextFGFrame (void) +{ + return _CurFramePtr; +} + +static DRAWABLE +request_drawable (COUNT NumFrames, DRAWABLE_TYPE DrawableType, + CREATE_FLAGS flags, SIZE width, SIZE height) +{ + DRAWABLE Drawable; + COUNT i; + + Drawable = AllocDrawable (NumFrames); + if (!Drawable) + return NULL; + + Drawable->Flags = flags; + Drawable->MaxIndex = NumFrames - 1; + + for (i = 0; i < NumFrames; ++i) + { + FRAME FramePtr = &Drawable->Frame[i]; + + if (DrawableType == RAM_DRAWABLE && width > 0 && height > 0) + { + FramePtr->image = TFB_DrawImage_New (TFB_DrawCanvas_New_TrueColor ( + width, height, (flags & WANT_ALPHA) ? TRUE : FALSE)); + } + + FramePtr->Type = DrawableType; + FramePtr->Index = i; + SetFrameBounds (FramePtr, width, height); + } + + return Drawable; +} + +DRAWABLE +CreateDisplay (CREATE_FLAGS CreateFlags, SIZE *pwidth, SIZE *pheight) +{ + DRAWABLE Drawable; + + // TODO: ScreenWidth and ScreenHeight should be passed in + // instead of returned. + Drawable = request_drawable (1, SCREEN_DRAWABLE, + (CreateFlags & (WANT_PIXMAP | WANT_MASK)), + ScreenWidth, ScreenHeight); + if (Drawable) + { + FRAME F; + + F = CaptureDrawable (Drawable); + if (F == 0) + DestroyDrawable (Drawable); + else + { + *pwidth = GetFrameWidth (F); + *pheight = GetFrameHeight (F); + + ReleaseDrawable (F); + return (Drawable); + } + } + + *pwidth = *pheight = 0; + return (0); +} + +DRAWABLE +AllocDrawable (COUNT n) +{ + DRAWABLE Drawable; + Drawable = (DRAWABLE) HCalloc(sizeof (DRAWABLE_DESC)); + if (Drawable) + { + int i; + Drawable->Frame = (FRAME)HMalloc (sizeof (FRAME_DESC) * n); + if (Drawable->Frame == NULL) + { + HFree (Drawable); + return NULL; + } + + /* Zero out the newly allocated frames, since HMalloc doesn't have + * MEM_ZEROINIT. */ + for (i = 0; i < n; i++) { + FRAME F; + F = &Drawable->Frame[i]; + F->parent = Drawable; + F->Type = 0; + F->Index = 0; + F->image = 0; + F->Bounds.width = 0; + F->Bounds.height = 0; + F->HotSpot.x = 0; + F->HotSpot.y = 0; + } + } + return Drawable; +} + +DRAWABLE +CreateDrawable (CREATE_FLAGS CreateFlags, SIZE width, SIZE height, COUNT + num_frames) +{ + DRAWABLE Drawable; + + Drawable = request_drawable (num_frames, RAM_DRAWABLE, + (CreateFlags & (WANT_MASK | WANT_PIXMAP + | WANT_ALPHA | MAPPED_TO_DISPLAY)), + width, height); + if (Drawable) + { + FRAME F; + + F = CaptureDrawable (Drawable); + if (F) + { + ReleaseDrawable (F); + + return (Drawable); + } + } + + return (0); +} + +BOOLEAN +DestroyDrawable (DRAWABLE Drawable) +{ + if (_CurFramePtr && (Drawable == _CurFramePtr->parent)) + SetContextFGFrame ((FRAME)NULL); + + if (Drawable) + { + FreeDrawable (Drawable); + + return (TRUE); + } + + return (FALSE); +} + +BOOLEAN +GetFrameRect (FRAME FramePtr, RECT *pRect) +{ + if (FramePtr) + { + pRect->corner.x = -FramePtr->HotSpot.x; + pRect->corner.y = -FramePtr->HotSpot.y; + pRect->extent = GetFrameBounds (FramePtr); + + return (TRUE); + } + + return (FALSE); +} + +HOT_SPOT +SetFrameHot (FRAME FramePtr, HOT_SPOT HotSpot) +{ + if (FramePtr) + { + HOT_SPOT OldHot; + + OldHot = FramePtr->HotSpot; + FramePtr->HotSpot = HotSpot; + + return (OldHot); + } + + return (MAKE_HOT_SPOT (0, 0)); +} + +HOT_SPOT +GetFrameHot (FRAME FramePtr) +{ + if (FramePtr) + { + return FramePtr->HotSpot; + } + + return (MAKE_HOT_SPOT (0, 0)); +} + +DRAWABLE +RotateFrame (FRAME Frame, int angle_deg) +{ + DRAWABLE Drawable; + FRAME RotFramePtr; + double dx, dy; + double d; + double angle = angle_deg * M_PI / 180; + + if (!Frame) + return NULL; + + assert (Frame->Type != SCREEN_DRAWABLE); + + Drawable = request_drawable (1, RAM_DRAWABLE, WANT_PIXMAP, 0, 0); + if (!Drawable) + return 0; + RotFramePtr = CaptureDrawable (Drawable); + if (!RotFramePtr) + { + FreeDrawable (Drawable); + return 0; + } + + RotFramePtr->image = TFB_DrawImage_New_Rotated ( + Frame->image, angle_deg); + SetFrameBounds (RotFramePtr, RotFramePtr->image->extent.width, + RotFramePtr->image->extent.height); + + /* now we need to rotate the hot-spot, eww */ + dx = Frame->HotSpot.x - (GetFrameWidth (Frame) / 2); + dy = Frame->HotSpot.y - (GetFrameHeight (Frame) / 2); + d = sqrt ((double)dx*dx + (double)dy*dy); + if ((int)d != 0) + { + double organg = atan2 (-dy, dx); + dx = cos (organg + angle) * d; + dy = -sin (organg + angle) * d; + } + RotFramePtr->HotSpot.x = (GetFrameWidth (RotFramePtr) / 2) + (int)dx; + RotFramePtr->HotSpot.y = (GetFrameHeight (RotFramePtr) / 2) + (int)dy; + + ReleaseDrawable (RotFramePtr); + + return Drawable; +} + +// color.a is ignored +void +SetFrameTransparentColor (FRAME frame, Color color) +{ + TFB_Image *img; + + if (!frame) + return; + + assert (frame->Type != SCREEN_DRAWABLE); + + img = frame->image; + LockMutex (img->mutex); + + // TODO: This should defer to TFB_DrawImage instead + TFB_DrawCanvas_SetTransparentColor (img->NormalImg, color, FALSE); + + UnlockMutex (img->mutex); +} + +Color +GetFramePixel (FRAME frame, POINT pixelPt) +{ + TFB_Image *img; + Color ret; + + if (!frame) + return BUILD_COLOR_RGBA (0, 0, 0, 0); + + assert (frame->Type != SCREEN_DRAWABLE); + + img = frame->image; + LockMutex (img->mutex); + + // TODO: This should defer to TFB_DrawImage instead + ret = TFB_DrawCanvas_GetPixel (img->NormalImg, pixelPt.x, pixelPt.y); + + UnlockMutex (img->mutex); + + return ret; +} + +static FRAME +makeMatchingFrame (FRAME frame, int width, int height) +{ + DRAWABLE drawable; + FRAME newFrame; + CREATE_FLAGS flags; + + flags = GetFrameParentDrawable (frame)->Flags; + drawable = CreateDrawable (flags, width, height, 1); + if (!drawable) + return NULL; + newFrame = CaptureDrawable (drawable); + if (!newFrame) + { + FreeDrawable (drawable); + return NULL; + } + + return newFrame; +} + +// Creates an new DRAWABLE containing a copy of specified FRAME's rect +// Source FRAME must not be a SCREEN_DRAWABLE +DRAWABLE +CopyFrameRect (FRAME frame, const RECT *area) +{ + FRAME newFrame; + POINT nullPt = MAKE_POINT (0, 0); + + if (!frame) + return NULL; + + assert (frame->Type != SCREEN_DRAWABLE); + + newFrame = makeMatchingFrame (frame, area->extent.width, + area->extent.height); + if (!newFrame) + return NULL; + + TFB_DrawImage_CopyRect (frame->image, area, newFrame->image, nullPt); + + return ReleaseDrawable (newFrame); +} + +// Creates an new DRAWABLE mostly identical to specified FRAME +// Source FRAME must not be a SCREEN_DRAWABLE +DRAWABLE +CloneFrame (FRAME frame) +{ + FRAME newFrame; + RECT r; + + if (!frame) + return NULL; + + assert (frame->Type != SCREEN_DRAWABLE); + + GetFrameRect (frame, &r); + r.corner.x = 0; + r.corner.y = 0; + + newFrame = CaptureDrawable (CopyFrameRect (frame, &r)); + if (!newFrame) + return NULL; + + // copy the hot-spot + newFrame->HotSpot = frame->HotSpot; + + return ReleaseDrawable (newFrame); +} + +// Creates a new DRAWABLE of specified size and scales the passed +// frame onto it. The aspect ratio is not preserved. +DRAWABLE +RescaleFrame (FRAME frame, int width, int height) +{ + FRAME newFrame; + TFB_Image *img; + TFB_Canvas src, dst; + + if (!frame) + return NULL; + + assert (frame->Type != SCREEN_DRAWABLE); + + newFrame = makeMatchingFrame (frame, width, height); + if (!newFrame) + return NULL; + + // scale the hot-spot + newFrame->HotSpot.x = frame->HotSpot.x * width / frame->Bounds.width; + newFrame->HotSpot.y = frame->HotSpot.y * height / frame->Bounds.height; + + img = frame->image; + LockMutex (img->mutex); + // NOTE: We do not lock the target image because nothing has a + // reference to it yet! + src = img->NormalImg; + dst = newFrame->image->NormalImg; + TFB_DrawCanvas_Rescale_Nearest (src, dst, -1, NULL, NULL, NULL); + + UnlockMutex (img->mutex); + + return ReleaseDrawable (newFrame); +} + +BOOLEAN +ReadFramePixelColors (FRAME frame, Color *pixels, int width, int height) +{ + TFB_Image *img; + + if (!frame) + return FALSE; + + assert (frame->Type != SCREEN_DRAWABLE); + + // TODO: Do we need to lock the img->mutex here? + img = frame->image; + return TFB_DrawCanvas_GetPixelColors (img->NormalImg, pixels, + width, height); +} + +// Warning: this functions bypasses DCQ, which is why it is not a DrawXXX +BOOLEAN +WriteFramePixelColors (FRAME frame, const Color *pixels, int width, int height) +{ + TFB_Image *img; + + if (!frame) + return FALSE; + + assert (frame->Type != SCREEN_DRAWABLE); + + // TODO: Do we need to lock the img->mutex here? + img = frame->image; + return TFB_DrawCanvas_SetPixelColors (img->NormalImg, pixels, + width, height); +} + +BOOLEAN +ReadFramePixelIndexes (FRAME frame, BYTE *pixels, int width, int height) +{ + TFB_Image *img; + + if (!frame) + return FALSE; + + assert (frame->Type != SCREEN_DRAWABLE); + + // TODO: Do we need to lock the img->mutex here? + img = frame->image; + return TFB_DrawCanvas_GetPixelIndexes (img->NormalImg, pixels, + width, height); +} + +// Warning: this functions bypasses DCQ, which is why it is not a DrawXXX +BOOLEAN +WriteFramePixelIndexes (FRAME frame, const BYTE *pixels, int width, int height) +{ + TFB_Image *img; + + if (!frame) + return FALSE; + + assert (frame->Type != SCREEN_DRAWABLE); + + // TODO: Do we need to lock the img->mutex here? + img = frame->image; + return TFB_DrawCanvas_SetPixelIndexes (img->NormalImg, pixels, + width, height); +} diff --git a/src/libs/graphics/drawable.h b/src/libs/graphics/drawable.h new file mode 100644 index 0000000..98f5ada --- /dev/null +++ b/src/libs/graphics/drawable.h @@ -0,0 +1,88 @@ +//Copyright Paul Reiche, Fred Ford. 1992-2002 + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef LIBS_GRAPHICS_DRAWABLE_H_ +#define LIBS_GRAPHICS_DRAWABLE_H_ + +#include <stdio.h> +#include "tfb_draw.h" + +#define ValidPrimType(pt) ((pt)<NUM_PRIMS) + +typedef struct bresenham_line +{ + POINT first, second; + SIZE abs_delta_x, abs_delta_y; + SIZE error_term; + BOOLEAN end_points_exchanged; + INTERSECT_CODE intersect_code; +} BRESENHAM_LINE; + +typedef UWORD DRAWABLE_TYPE; +#define ROM_DRAWABLE 0 +#define RAM_DRAWABLE 1 +#define SCREEN_DRAWABLE 2 + +struct frame_desc +{ + DRAWABLE_TYPE Type; + UWORD Index; + HOT_SPOT HotSpot; + EXTENT Bounds; + TFB_Image *image; + struct drawable_desc *parent; +}; + +struct drawable_desc +{ + CREATE_FLAGS Flags; + UWORD MaxIndex; + FRAME_DESC *Frame; +}; + +#define GetFrameWidth(f) ((f)->Bounds.width) +#define GetFrameHeight(f) ((f)->Bounds.height) +#define GetFrameBounds(f) ((f)->Bounds) +#define SetFrameBounds(f,w,h) \ + ((f)->Bounds.width=(w), \ + ((f))->Bounds.height=(h)) + +#define DRAWABLE_PRIORITY DEFAULT_MEM_PRIORITY + +extern DRAWABLE AllocDrawable (COUNT num_frames); +#define FreeDrawable(D) _ReleaseCelData (D) + +typedef struct +{ + RECT Box; + FRAME FramePtr; +} IMAGE_BOX; + +extern INTERSECT_CODE _clip_line (const RECT *pClipRect, + BRESENHAM_LINE *pLine); + +extern void *_GetCelData (uio_Stream *fp, DWORD length); +extern BOOLEAN _ReleaseCelData (void *handle); + +extern FRAME _CurFramePtr; + +// ClipRect is relative to ctxOrigin +extern void _text_blt (RECT *pClipRect, TEXT *TextPtr, POINT ctxOrigin); + +#endif /* LIBS_GRAPHICS_DRAWABLE_H_ */ + diff --git a/src/libs/graphics/drawcmd.h b/src/libs/graphics/drawcmd.h new file mode 100644 index 0000000..6d44153 --- /dev/null +++ b/src/libs/graphics/drawcmd.h @@ -0,0 +1,202 @@ +//Copyright Paul Reiche, Fred Ford. 1992-2002 + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef DRAWCMD_H +#define DRAWCMD_H + +#include "libs/graphics/tfb_draw.h" + +enum +{ + TFB_DRAWCOMMANDTYPE_LINE, + TFB_DRAWCOMMANDTYPE_RECTANGLE, + TFB_DRAWCOMMANDTYPE_IMAGE, + TFB_DRAWCOMMANDTYPE_FILLEDIMAGE, + TFB_DRAWCOMMANDTYPE_FONTCHAR, + + TFB_DRAWCOMMANDTYPE_COPY, + TFB_DRAWCOMMANDTYPE_COPYTOIMAGE, + + TFB_DRAWCOMMANDTYPE_SCISSORENABLE, + TFB_DRAWCOMMANDTYPE_SCISSORDISABLE, + + TFB_DRAWCOMMANDTYPE_SETMIPMAP, + TFB_DRAWCOMMANDTYPE_DELETEIMAGE, + TFB_DRAWCOMMANDTYPE_DELETEDATA, + TFB_DRAWCOMMANDTYPE_SENDSIGNAL, + TFB_DRAWCOMMANDTYPE_REINITVIDEO, + TFB_DRAWCOMMANDTYPE_CALLBACK, +}; + +typedef struct tfb_dc_line +{ + int x1, y1, x2, y2; + Color color; + DrawMode drawMode; + SCREEN destBuffer; +} TFB_DrawCommand_Line; + +typedef struct tfb_dc_rect +{ + RECT rect; + Color color; + DrawMode drawMode; + SCREEN destBuffer; +} TFB_DrawCommand_Rect; + +typedef struct tfb_dc_img +{ + TFB_Image *image; + int x, y; + SCREEN destBuffer; + TFB_ColorMap *colormap; + DrawMode drawMode; + int scale; + int scaleMode; +} TFB_DrawCommand_Image; + +typedef struct tfb_dc_filledimg +{ + TFB_Image *image; + int x, y; + Color color; + SCREEN destBuffer; + DrawMode drawMode; + int scale; + int scaleMode; +} TFB_DrawCommand_FilledImage; + +typedef struct tfb_dc_fontchar +{ + TFB_Char *fontchar; + TFB_Image *backing; + int x, y; + DrawMode drawMode; + SCREEN destBuffer; +} TFB_DrawCommand_FontChar; + +typedef struct tfb_dc_copy +{ + RECT rect; + SCREEN srcBuffer, destBuffer; +} TFB_DrawCommand_Copy; + +typedef struct tfb_dc_copyimg +{ + TFB_Image *image; + RECT rect; + SCREEN srcBuffer; +} TFB_DrawCommand_CopyToImage; + +typedef struct tfb_dc_scissor +{ + RECT rect; +} TFB_DrawCommand_Scissor; + +typedef struct tfb_dc_setmip +{ + TFB_Image *image; + TFB_Image *mipmap; + int hotx, hoty; +} TFB_DrawCommand_SetMipmap; + +typedef struct tfb_dc_delimg +{ + TFB_Image *image; +} TFB_DrawCommand_DeleteImage; + +typedef struct tfb_dc_deldata +{ + void *data; + // data must be a result of HXalloc() call +} TFB_DrawCommand_DeleteData; + +typedef struct tfb_dc_signal +{ + Semaphore sem; +} TFB_DrawCommand_SendSignal; + +typedef struct tfb_dc_reinit_video +{ + int driver, flags, width, height; +} TFB_DrawCommand_ReinitVideo; + +typedef struct tfb_dc_callback +{ + void (*callback)(void *arg); + void *arg; +} TFB_DrawCommand_Callback; + +typedef struct tfb_drawcommand +{ + int Type; + union { + TFB_DrawCommand_Line line; + TFB_DrawCommand_Rect rect; + TFB_DrawCommand_Image image; + TFB_DrawCommand_FilledImage filledimage; + TFB_DrawCommand_FontChar fontchar; + TFB_DrawCommand_Copy copy; + TFB_DrawCommand_CopyToImage copytoimage; + TFB_DrawCommand_Scissor scissor; + TFB_DrawCommand_SetMipmap setmipmap; + TFB_DrawCommand_DeleteImage deleteimage; + TFB_DrawCommand_DeleteData deletedata; + TFB_DrawCommand_SendSignal sendsignal; + TFB_DrawCommand_ReinitVideo reinitvideo; + TFB_DrawCommand_Callback callback; + } data; +} TFB_DrawCommand; + +// Queue Stuff + +typedef struct tfb_drawcommandqueue +{ + int Front; + int Back; + int InsertionPoint; + int Batching; + volatile int FullSize; + volatile int Size; +} TFB_DrawCommandQueue; + +void Init_DrawCommandQueue (void); + +void Uninit_DrawCommandQueue (void); + +void TFB_BatchGraphics (void); + +void TFB_UnbatchGraphics (void); + +void TFB_BatchReset (void); + +void TFB_DrawCommandQueue_Push (TFB_DrawCommand* Command); + +int TFB_DrawCommandQueue_Pop (TFB_DrawCommand* Command); + +void TFB_DrawCommandQueue_Clear (void); + +extern TFB_DrawCommandQueue DrawCommandQueue; + +void TFB_EnqueueDrawCommand (TFB_DrawCommand* DrawCommand); + +void Lock_DCQ (int slots); + +void Unlock_DCQ (void); + +#endif diff --git a/src/libs/graphics/filegfx.c b/src/libs/graphics/filegfx.c new file mode 100644 index 0000000..c6af7cd --- /dev/null +++ b/src/libs/graphics/filegfx.c @@ -0,0 +1,72 @@ +//Copyright Paul Reiche, Fred Ford. 1992-2002 + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "gfxintrn.h" +#include "options.h" +#include "libs/reslib.h" + + +DRAWABLE +LoadGraphicFile (const char *pStr) +{ + uio_Stream *fp; + + // FIXME: this theoretically needs a mechanism to prevent races + if (_cur_resfile_name) + // something else is loading resources atm + return 0; + + fp = res_OpenResFile (contentDir, pStr, "rb"); + if (fp != NULL) + { + DRAWABLE hData; + + _cur_resfile_name = pStr; + hData = (DRAWABLE)_GetCelData (fp, LengthResFile (fp)); + _cur_resfile_name = 0; + res_CloseResFile (fp); + return hData; + } + + return (NULL); +} + +FONT +LoadFontFile (const char *pStr) +{ + uio_Stream *fp; + + // FIXME: this theoretically needs a mechanism to prevent races + if (_cur_resfile_name) + // something else is loading resources atm + return 0; + + fp = res_OpenResFile (contentDir, pStr, "rb"); + if (fp != NULL) + { + FONT hData; + + _cur_resfile_name = pStr; + hData = (FONT)_GetFontData (fp, LengthResFile (fp)); + _cur_resfile_name = 0; + res_CloseResFile (fp); + return hData; + } + + return (0); +} diff --git a/src/libs/graphics/font.c b/src/libs/graphics/font.c new file mode 100644 index 0000000..638c814 --- /dev/null +++ b/src/libs/graphics/font.c @@ -0,0 +1,334 @@ +//Copyright Paul Reiche, Fred Ford. 1992-2002 + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "gfxintrn.h" +#include "tfb_prim.h" +#include "libs/log.h" + +static inline TFB_Char *getCharFrame (FONT_DESC *fontPtr, UniChar ch); + + +FONT +SetContextFont (FONT Font) +{ + FONT LastFont; + + LastFont = _CurFontPtr; + _CurFontPtr = Font; + if (ContextActive ()) + SwitchContextFont (Font); + + return (LastFont); +} + +BOOLEAN +DestroyFont (FONT FontRef) +{ + if (FontRef == NULL) + return (FALSE); + + if (_CurFontPtr && _CurFontPtr == FontRef) + SetContextFont ((FONT)NULL); + + return (FreeFont (FontRef)); +} + +// XXX: Should be in frame.c (renamed to something decent?) +void +font_DrawText (TEXT *lpText) +{ + RECT ClipRect; + POINT origin; + TEXT text; + + FixContextFontEffect (); + if (!GraphicsSystemActive () || !GetContextValidRect (NULL, &origin)) + return; + + // TextRect() clobbers TEXT.CharCount so we have to make a copy + text = *lpText; + if (!TextRect (&text, &ClipRect, NULL)) + return; + // ClipRect is relative to origin + _text_blt (&ClipRect, &text, origin); +} + +/* Draw the stroke by drawing the same text in the + * background color one pixel shifted to all 4 directions. + */ +void +font_DrawTracedText (TEXT *pText, Color text, Color trace) +{ + // Preserve current foreground color for full correctness + Color oldfg = SetContextForeGroundColor (trace); + pText->baseline.x--; + font_DrawText (pText); + pText->baseline.x += 2; + font_DrawText (pText); + pText->baseline.x--; + pText->baseline.y--; + font_DrawText (pText); + pText->baseline.y += 2; + font_DrawText (pText); + pText->baseline.y--; + SetContextForeGroundColor (text); + font_DrawText (pText); + SetContextForeGroundColor (oldfg); +} + +BOOLEAN +GetContextFontLeading (SIZE *pheight) +{ + if (_CurFontPtr != 0) + { + *pheight = (SIZE)_CurFontPtr->Leading; + return (TRUE); + } + + *pheight = 0; + return (FALSE); +} + +BOOLEAN +GetContextFontLeadingWidth (SIZE *pwidth) +{ + if (_CurFontPtr != 0) + { + *pwidth = (SIZE)_CurFontPtr->LeadingWidth; + return (TRUE); + } + + *pwidth = 0; + return (FALSE); +} + +BOOLEAN +TextRect (TEXT *lpText, RECT *pRect, BYTE *pdelta) +{ + BYTE char_delta_array[MAX_DELTAS]; + FONT FontPtr; + + FontPtr = _CurFontPtr; + if (FontPtr != 0 && lpText->CharCount != 0) + { + COORD top_y, bot_y; + SIZE width; + UniChar next_ch = 0; + const char *pStr; + COUNT num_chars; + + num_chars = lpText->CharCount; + /* At this point lpText->CharCount contains the *maximum* number of + * characters that lpText->pStr may contain. + * After the while loop below, it will contain the actual number. + */ + if (pdelta == 0) + { + pdelta = char_delta_array; + if (num_chars > MAX_DELTAS) + { + num_chars = MAX_DELTAS; + lpText->CharCount = MAX_DELTAS; + } + } + + top_y = 0; + bot_y = 0; + width = 0; + pStr = lpText->pStr; + if (num_chars > 0) + { + next_ch = getCharFromString (&pStr); + if (next_ch == '\0') + num_chars = 0; + } + while (num_chars--) + { + UniChar ch; + SIZE last_width; + TFB_Char *charFrame; + + last_width = width; + + ch = next_ch; + if (num_chars > 0) + { + next_ch = getCharFromString (&pStr); + if (next_ch == '\0') + { + lpText->CharCount -= num_chars; + num_chars = 0; + } + } + + charFrame = getCharFrame (FontPtr, ch); + if (charFrame != NULL && charFrame->disp.width) + { + COORD y; + + y = -charFrame->HotSpot.y; + if (y < top_y) + top_y = y; + y += charFrame->disp.height; + if (y > bot_y) + bot_y = y; + + width += charFrame->disp.width; +#if 0 + if (num_chars && next_ch < (UNICODE) MAX_CHARS + && !(FontPtr->KernTab[ch] + & (FontPtr->KernTab[next_ch] >> 2))) + width -= FontPtr->KernAmount; +#endif + } + + *pdelta++ = (BYTE)(width - last_width); + } + + if (width > 0 && (bot_y -= top_y) > 0) + { + /* subtract off default character spacing */ + if (pdelta[-1] > 0) + { + --pdelta[-1]; + --width; + } + + if (lpText->align == ALIGN_LEFT) + pRect->corner.x = 0; + else if (lpText->align == ALIGN_CENTER) + pRect->corner.x = -(width >> 1); + else + pRect->corner.x = -width; + pRect->corner.y = top_y; + pRect->extent.width = width; + pRect->extent.height = bot_y; + + pRect->corner.x += lpText->baseline.x; + pRect->corner.y += lpText->baseline.y; + + return (TRUE); + } + } + + pRect->corner = lpText->baseline; + pRect->extent.width = 0; + pRect->extent.height = 0; + + return (FALSE); +} + +void +_text_blt (RECT *pClipRect, TEXT *TextPtr, POINT ctxOrigin) +{ + FONT FontPtr; + COUNT num_chars; + UniChar next_ch; + const char *pStr; + POINT origin; + TFB_Image *backing; + DrawMode mode = _get_context_draw_mode (); + + FontPtr = _CurFontPtr; + if (FontPtr == NULL) + return; + backing = _get_context_font_backing (); + if (!backing) + return; + + origin.x = pClipRect->corner.x; + origin.y = TextPtr->baseline.y; + num_chars = TextPtr->CharCount; + if (num_chars == 0) + return; + + pStr = TextPtr->pStr; + + next_ch = getCharFromString (&pStr); + if (next_ch == '\0') + num_chars = 0; + while (num_chars--) + { + UniChar ch; + TFB_Char* fontChar; + + ch = next_ch; + if (num_chars > 0) + { + next_ch = getCharFromString (&pStr); + if (next_ch == '\0') + num_chars = 0; + } + + fontChar = getCharFrame (FontPtr, ch); + if (fontChar != NULL && fontChar->disp.width) + { + RECT r; + + r.corner.x = origin.x - fontChar->HotSpot.x; + r.corner.y = origin.y - fontChar->HotSpot.y; + r.extent.width = fontChar->disp.width; + r.extent.height = fontChar->disp.height; + if (BoxIntersect (&r, pClipRect, &r)) + { + TFB_Prim_FontChar (origin, fontChar, backing, mode, + ctxOrigin); + } + + origin.x += fontChar->disp.width; +#if 0 + if (num_chars && next_ch < (UNICODE) MAX_CHARS + && !(FontPtr->KernTab[ch] + & (FontPtr->KernTab[next_ch] >> 2))) + origin.x -= FontPtr->KernAmount; +#endif + } + } +} + +static inline TFB_Char * +getCharFrame (FONT_DESC *fontPtr, UniChar ch) +{ + UniChar pageStart = ch & CHARACTER_PAGE_MASK; + size_t charIndex; + + FONT_PAGE *page = fontPtr->fontPages; + for (;;) + { + if (page == NULL) + return NULL; + + if (page->pageStart == pageStart) + break; + + page = page->next; + } + + charIndex = ch - page->firstChar; + if (ch >= page->firstChar && charIndex < page->numChars + && page->charDesc[charIndex].data) + { + return &page->charDesc[charIndex]; + } + else + { + //log_add (log_Debug, "Character %u not present", (unsigned int) ch); + return NULL; + } +} + diff --git a/src/libs/graphics/font.h b/src/libs/graphics/font.h new file mode 100644 index 0000000..b6bd13b --- /dev/null +++ b/src/libs/graphics/font.h @@ -0,0 +1,71 @@ +//Copyright Paul Reiche, Fred Ford. 1992-2002 + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef LIBS_GRAPHICS_FONT_H_ +#define LIBS_GRAPHICS_FONT_H_ + +#include "libs/memlib.h" + +#define MAX_DELTAS 100 + +typedef struct FontPage +{ + struct FontPage *next; + UniChar pageStart; +#define CHARACTER_PAGE_MASK 0xfffff800 + UniChar firstChar; + size_t numChars; + TFB_Char *charDesc; +} FONT_PAGE; + +static inline FONT_PAGE * +AllocFontPage (int numChars) +{ + FONT_PAGE *result = HMalloc (sizeof (FONT_PAGE)); + result->charDesc = HCalloc (numChars * sizeof *result->charDesc); + return result; +} + +static inline void +FreeFontPage (FONT_PAGE *page) +{ + HFree (page->charDesc); + HFree (page); +} + +struct font_desc +{ + UWORD Leading; + UWORD LeadingWidth; + FONT_PAGE *fontPages; +}; + +#define CHAR_DESCPTR PCHAR_DESC + +#define FONT_PRIORITY DEFAULT_MEM_PRIORITY + +#define AllocFont(size) (FONT)HCalloc (sizeof (FONT_DESC) + (size)) +#define FreeFont _ReleaseFontData + +extern FONT _CurFontPtr; + +extern void *_GetFontData (uio_Stream *fp, DWORD length); +extern BOOLEAN _ReleaseFontData (void *handle); + +#endif /* LIBS_GRAPHICS_FONT_H_ */ + diff --git a/src/libs/graphics/frame.c b/src/libs/graphics/frame.c new file mode 100644 index 0000000..0539746 --- /dev/null +++ b/src/libs/graphics/frame.c @@ -0,0 +1,266 @@ +//Copyright Paul Reiche, Fred Ford. 1992-2002 + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "gfxintrn.h" +#include "gfx_common.h" +#include "tfb_draw.h" +#include "tfb_prim.h" + +HOT_SPOT +MAKE_HOT_SPOT (COORD x, COORD y) +{ + HOT_SPOT hs; + hs.x = x; + hs.y = y; + return hs; +} + +// XXX: INTERNAL_PRIMITIVE and INTERNAL_PRIM_DESC are not used +typedef union +{ + POINT Point; + STAMP Stamp; + BRESENHAM_LINE Line; + TEXT Text; + RECT Rect; +} INTERNAL_PRIM_DESC; + +typedef struct +{ + PRIM_LINKS Links; + GRAPHICS_PRIM Type; + Color Color; + INTERNAL_PRIM_DESC Object; +} INTERNAL_PRIMITIVE; + + +// pValidRect or origin may be NULL +BOOLEAN +GetContextValidRect (RECT *pValidRect, POINT *origin) +{ + RECT tempRect; + POINT tempPt; + + if (!pValidRect) + pValidRect = &tempRect; + if (!origin) + origin = &tempPt; + + // Start with a rect the size of foreground frame + pValidRect->corner.x = 0; + pValidRect->corner.y = 0; + pValidRect->extent = GetFrameBounds (_CurFramePtr); + *origin = _CurFramePtr->HotSpot; + + if (_pCurContext->ClipRect.extent.width) + { + // If the cliprect is completely outside of the valid frame + // bounds we have nothing to draw + if (!BoxIntersect (&_pCurContext->ClipRect, + pValidRect, pValidRect)) + return (FALSE); + + // Foreground frame hotspot defines a drawing position offset + // WRT the context cliprect + origin->x += _pCurContext->ClipRect.corner.x; + origin->y += _pCurContext->ClipRect.corner.y; + } + + return (TRUE); +} + +static void +ClearBackGround (RECT *pClipRect) +{ + RECT clearRect; + Color color = _get_context_bg_color (); + clearRect.corner.x = 0; + clearRect.corner.y = 0; + clearRect.extent = pClipRect->extent; + TFB_Prim_FillRect (&clearRect, color, DRAW_REPLACE_MODE, + pClipRect->corner); +} + +void +DrawBatch (PRIMITIVE *lpBasePrim, PRIM_LINKS PrimLinks, + BATCH_FLAGS BatchFlags) +{ + RECT ValidRect; + POINT origin; + + if (GraphicsSystemActive () && GetContextValidRect (&ValidRect, &origin)) + { + COUNT CurIndex; + PRIMITIVE *lpPrim; + DrawMode mode = _get_context_draw_mode (); + + BatchGraphics (); + + if (BatchFlags & BATCH_BUILD_PAGE) + { + ClearBackGround (&ValidRect); + } + + CurIndex = GetPredLink (PrimLinks); + + for (; CurIndex != END_OF_LIST; + CurIndex = GetSuccLink (GetPrimLinks (lpPrim))) + { + GRAPHICS_PRIM PrimType; + PRIMITIVE *lpWorkPrim; + RECT ClipRect; + Color color; + + lpPrim = &lpBasePrim[CurIndex]; + PrimType = GetPrimType (lpPrim); + if (!ValidPrimType (PrimType)) + continue; + + lpWorkPrim = lpPrim; + + switch (PrimType) + { + case POINT_PRIM: + color = GetPrimColor (lpWorkPrim); + TFB_Prim_Point (&lpWorkPrim->Object.Point, color, + mode, origin); + break; + case STAMP_PRIM: + TFB_Prim_Stamp (&lpWorkPrim->Object.Stamp, mode, origin); + break; + case STAMPFILL_PRIM: + color = GetPrimColor (lpWorkPrim); + TFB_Prim_StampFill (&lpWorkPrim->Object.Stamp, color, + mode, origin); + break; + case LINE_PRIM: + color = GetPrimColor (lpWorkPrim); + TFB_Prim_Line (&lpWorkPrim->Object.Line, color, + mode, origin); + break; + case TEXT_PRIM: + if (!TextRect (&lpWorkPrim->Object.Text, &ClipRect, NULL)) + continue; + // ClipRect is relative to origin + _text_blt (&ClipRect, &lpWorkPrim->Object.Text, origin); + break; + case RECT_PRIM: + color = GetPrimColor (lpWorkPrim); + TFB_Prim_Rect (&lpWorkPrim->Object.Rect, color, + mode, origin); + break; + case RECTFILL_PRIM: + color = GetPrimColor (lpWorkPrim); + TFB_Prim_FillRect (&lpWorkPrim->Object.Rect, color, + mode, origin); + break; + } + } + + UnbatchGraphics (); + } +} + +void +ClearDrawable (void) +{ + RECT ValidRect; + + if (GraphicsSystemActive () && GetContextValidRect (&ValidRect, NULL)) + { + ClearBackGround (&ValidRect); + } +} + +void +DrawPoint (POINT *lpPoint) +{ + POINT origin; + + if (GraphicsSystemActive () && GetContextValidRect (NULL, &origin)) + { + Color color = GetPrimColor (&_locPrim); + DrawMode mode = _get_context_draw_mode (); + TFB_Prim_Point (lpPoint, color, mode, origin); + } +} + +void +DrawRectangle (RECT *lpRect) +{ + POINT origin; + + if (GraphicsSystemActive () && GetContextValidRect (NULL, &origin)) + { + Color color = GetPrimColor (&_locPrim); + DrawMode mode = _get_context_draw_mode (); + TFB_Prim_Rect (lpRect, color, mode, origin); + } +} + +void +DrawFilledRectangle (RECT *lpRect) +{ + POINT origin; + + if (GraphicsSystemActive () && GetContextValidRect (NULL, &origin)) + { + Color color = GetPrimColor (&_locPrim); + DrawMode mode = _get_context_draw_mode (); + TFB_Prim_FillRect (lpRect, color, mode, origin); + } +} + +void +DrawLine (LINE *lpLine) +{ + POINT origin; + + if (GraphicsSystemActive () && GetContextValidRect (NULL, &origin)) + { + Color color = GetPrimColor (&_locPrim); + DrawMode mode = _get_context_draw_mode (); + TFB_Prim_Line (lpLine, color, mode, origin); + } +} + +void +DrawStamp (STAMP *stmp) +{ + POINT origin; + + if (GraphicsSystemActive () && GetContextValidRect (NULL, &origin)) + { + DrawMode mode = _get_context_draw_mode (); + TFB_Prim_Stamp (stmp, mode, origin); + } +} + +void +DrawFilledStamp (STAMP *stmp) +{ + POINT origin; + + if (GraphicsSystemActive () && GetContextValidRect (NULL, &origin)) + { + Color color = GetPrimColor (&_locPrim); + DrawMode mode = _get_context_draw_mode (); + TFB_Prim_StampFill (stmp, color, mode, origin); + } +} + diff --git a/src/libs/graphics/gfx_common.c b/src/libs/graphics/gfx_common.c new file mode 100644 index 0000000..405f614 --- /dev/null +++ b/src/libs/graphics/gfx_common.c @@ -0,0 +1,196 @@ +//Copyright Paul Reiche, Fred Ford. 1992-2002 + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "gfxintrn.h" +#include "libs/graphics/gfx_common.h" +#include "libs/graphics/drawcmd.h" +#include "libs/timelib.h" +#include "libs/misc.h" + // for TFB_DEBUG_HALT + + +int ScreenWidth; +int ScreenHeight; +int ScreenWidthActual; +int ScreenHeightActual; +int ScreenColorDepth; +int GraphicsDriver; +int TFB_DEBUG_HALT = 0; + +volatile int TransitionAmount = 255; +RECT TransitionClipRect; + +static int gscale = GSCALE_IDENTITY; +static int gscale_mode = TFB_SCALE_NEAREST; + +void +DrawFromExtraScreen (RECT *r) +{ + TFB_DrawScreen_Copy(r, TFB_SCREEN_EXTRA, TFB_SCREEN_MAIN); +} + +void +LoadIntoExtraScreen (RECT *r) +{ + TFB_DrawScreen_Copy(r, TFB_SCREEN_MAIN, TFB_SCREEN_EXTRA); +} + +int +SetGraphicScale (int scale) +{ + int old_scale = gscale; + gscale = (scale ? scale : GSCALE_IDENTITY); + return old_scale; +} + +int +GetGraphicScale (void) +{ + return gscale; +} + +int +SetGraphicScaleMode (int mode) +{ + int old_mode = gscale_mode; + assert (mode >= TFB_SCALE_NEAREST && mode <= TFB_SCALE_TRILINEAR); + gscale_mode = mode; + return old_mode; +} + +int +GetGraphicScaleMode (void) +{ + return gscale_mode; +} + +/* Batching and Unbatching functions. A "Batch" is a collection of + DrawCommands that will never be flipped to the screen half-rendered. + BatchGraphics and UnbatchGraphics function vaguely like a non-blocking + recursive lock to do this respect. */ +void +BatchGraphics (void) +{ + TFB_BatchGraphics (); +} + +void +UnbatchGraphics (void) +{ + TFB_UnbatchGraphics (); +} + +/* Sleeps this thread until all Draw Commands queued by that thread have + been processed. */ + +void +FlushGraphics (void) +{ + TFB_DrawScreen_WaitForSignal (); +} + +static void +ExpandRect (RECT *rect, int expansion) +{ + if (rect->corner.x - expansion >= 0) + { + rect->extent.width += expansion; + rect->corner.x -= expansion; + } + else + { + rect->extent.width += rect->corner.x; + rect->corner.x = 0; + } + + if (rect->corner.y - expansion >= 0) + { + rect->extent.height += expansion; + rect->corner.y -= expansion; + } + else + { + rect->extent.height += rect->corner.y; + rect->corner.y = 0; + } + + if (rect->corner.x + rect->extent.width + expansion <= ScreenWidth) + rect->extent.width += expansion; + else + rect->extent.width = ScreenWidth - rect->corner.x; + + if (rect->corner.y + rect->extent.height + expansion <= ScreenHeight) + rect->extent.height += expansion; + else + rect->extent.height = ScreenHeight - rect->corner.y; +} + +void +SetTransitionSource (const RECT *pRect) +{ + RECT ActualRect; + + if (pRect) + { /* expand the rect to accomodate scalers in OpenGL mode */ + ActualRect = *pRect; + pRect = &ActualRect; + ExpandRect (&ActualRect, 2); + } + TFB_DrawScreen_Copy (pRect, TFB_SCREEN_MAIN, TFB_SCREEN_TRANSITION); +} + +// ScreenTransition() is synchronous (does not return until transition done) +void +ScreenTransition (int TransType, const RECT *pRect) +{ + const TimePeriod DURATION = ONE_SECOND * 31 / 60; + TimeCount startTime; + (void) TransType; /* dodge compiler warning */ + + if (pRect) + { + TransitionClipRect = *pRect; + } + else + { + TransitionClipRect.corner.x = 0; + TransitionClipRect.corner.y = 0; + TransitionClipRect.extent.width = ScreenWidth; + TransitionClipRect.extent.height = ScreenHeight; + } + + TFB_UploadTransitionScreen (); + + TransitionAmount = 0; + FlushGraphics (); + startTime = GetTimeCounter (); + while (TransitionAmount < 255) + { + TimePeriod deltaT; + int newAmount; + + SleepThread (ONE_SECOND / 100); + + deltaT = GetTimeCounter () - startTime; + newAmount = deltaT * 255 / DURATION; + if (newAmount > 255) + newAmount = 255; + + TransitionAmount = newAmount; + } +} diff --git a/src/libs/graphics/gfx_common.h b/src/libs/graphics/gfx_common.h new file mode 100644 index 0000000..e9e421a --- /dev/null +++ b/src/libs/graphics/gfx_common.h @@ -0,0 +1,112 @@ +//Copyright Paul Reiche, Fred Ford. 1992-2002 + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef GFX_COMMON_H +#define GFX_COMMON_H + +#include <stdio.h> +#include <stdlib.h> + +#include "libs/gfxlib.h" + +// driver for TFB_InitGraphics +enum +{ + TFB_GFXDRIVER_SDL_OPENGL, + TFB_GFXDRIVER_SDL_PURE, +}; + +// forced redraw +enum +{ + TFB_REDRAW_NO = 0, + TFB_REDRAW_FADING, + TFB_REDRAW_EXPOSE, + TFB_REDRAW_YES +}; + +// flags for TFB_InitGraphics +#define TFB_GFXFLAGS_FULLSCREEN (1<<0) +#define TFB_GFXFLAGS_SHOWFPS (1<<1) +#define TFB_GFXFLAGS_SCANLINES (1<<2) +#define TFB_GFXFLAGS_SCALE_BILINEAR (1<<3) +#define TFB_GFXFLAGS_SCALE_BIADAPT (1<<4) +#define TFB_GFXFLAGS_SCALE_BIADAPTADV (1<<5) +#define TFB_GFXFLAGS_SCALE_TRISCAN (1<<6) +#define TFB_GFXFLAGS_SCALE_HQXX (1<<7) +#define TFB_GFXFLAGS_SCALE_ANY \ + ( TFB_GFXFLAGS_SCALE_BILINEAR | \ + TFB_GFXFLAGS_SCALE_BIADAPT | \ + TFB_GFXFLAGS_SCALE_BIADAPTADV | \ + TFB_GFXFLAGS_SCALE_TRISCAN | \ + TFB_GFXFLAGS_SCALE_HQXX ) +#define TFB_GFXFLAGS_SCALE_SOFT_ONLY \ + ( TFB_GFXFLAGS_SCALE_ANY & ~TFB_GFXFLAGS_SCALE_BILINEAR ) + +// The flag variable itself +extern int GfxFlags; + +// The following functions are driver-defined +void TFB_PreInit (void); +int TFB_InitGraphics (int driver, int flags, const char *renderer, + int width, int height); +int TFB_ReInitGraphics (int driver, int flags, int width, int height); +void TFB_UninitGraphics (void); +void TFB_ProcessEvents (void); +bool TFB_SetGamma (float gamma); +void TFB_UploadTransitionScreen (void); +int TFB_SupportsHardwareScaling (void); +// This function should not be called directly +void TFB_SwapBuffers (int force_full_redraw); + +#define GSCALE_IDENTITY 256 + +typedef enum { + TFB_SCALE_STEP, /* not really a scaler */ + TFB_SCALE_NEAREST, + TFB_SCALE_BILINEAR, + TFB_SCALE_TRILINEAR +} SCALE; + +void LoadIntoExtraScreen (RECT *r); +void DrawFromExtraScreen (RECT *r); +int SetGraphicScale (int scale); +int GetGraphicScale (void); +int SetGraphicScaleMode (int mode /* enum SCALE */); +int GetGraphicScaleMode (void); +void SetTransitionSource (const RECT *pRect); +void ScreenTransition (int transition, const RECT *pRect); + +// TODO: there should be accessor functions for these +extern volatile int TransitionAmount; +extern RECT TransitionClipRect; + +extern float FrameRate; +extern int FrameRateTickBase; + +void TFB_FlushGraphics (void); // Only call from main thread!! +void TFB_PurgeDanglingGraphics (void); // Only call from main thread as part of shutdown. + +extern int ScreenWidth; +extern int ScreenHeight; +extern int ScreenWidthActual; +extern int ScreenHeightActual; +extern int ScreenColorDepth; +extern int GraphicsDriver; + +#endif diff --git a/src/libs/graphics/gfxintrn.h b/src/libs/graphics/gfxintrn.h new file mode 100644 index 0000000..72281e6 --- /dev/null +++ b/src/libs/graphics/gfxintrn.h @@ -0,0 +1,32 @@ +//Copyright Paul Reiche, Fred Ford. 1992-2002 + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef LIBS_GRAPHICS_GFXINTRN_H_ +#define LIBS_GRAPHICS_GFXINTRN_H_ + +#include <stdio.h> +#include <string.h> + +#include "libs/gfxlib.h" +#include "libs/reslib.h" +#include "context.h" +#include "drawable.h" +#include "font.h" + +#endif /* LIBS_GRAPHICS_GFXINTRN_H_ */ + diff --git a/src/libs/graphics/gfxload.c b/src/libs/graphics/gfxload.c new file mode 100644 index 0000000..969a910 --- /dev/null +++ b/src/libs/graphics/gfxload.c @@ -0,0 +1,597 @@ +//Copyright Paul Reiche, Fred Ford. 1992-2002 + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <string.h> +#include <stdio.h> + +#include "options.h" +#include "port.h" +#include "libs/uio.h" +#include "libs/reslib.h" + // for _cur_resfile_name +#include "libs/log.h" +#include "libs/memlib.h" +#include "libs/graphics/tfb_draw.h" +#include "libs/graphics/drawable.h" +#include "libs/graphics/font.h" + + +typedef struct anidata +{ + int transparent_color; + int colormap_index; + int hotspot_x; + int hotspot_y; +} AniData; + +extern uio_Repository *repository; +static uio_AutoMount *autoMount[] = { NULL }; + +static void +process_image (FRAME FramePtr, TFB_Canvas img[], AniData *ani, int cel_ct) +{ + TFB_Image *tfbimg; + int hx, hy; + + FramePtr->Type = ROM_DRAWABLE; + FramePtr->Index = cel_ct; + + // handle transparency cases + if (TFB_DrawCanvas_IsPaletted (img[cel_ct])) + { // indexed color image + if (ani[cel_ct].transparent_color >= 0) + { + TFB_DrawCanvas_SetTransparentIndex (img[cel_ct], + ani[cel_ct].transparent_color, FALSE); + } + } + else + { // special transparency cases for truecolor images + if (ani[cel_ct].transparent_color == 0) + { // make RGB=0,0,0 transparent + Color color = {0, 0, 0, 0}; + TFB_DrawCanvas_SetTransparentColor (img[cel_ct], color, FALSE); + } + } + if (ani[cel_ct].transparent_color == -1) + { // enforce -1 to mean 'no transparency' + TFB_DrawCanvas_SetTransparentIndex (img[cel_ct], -1, FALSE); + // set transparent_color == -2 to use PNG tRNS transparency + } + + hx = ani[cel_ct].hotspot_x; + hy = ani[cel_ct].hotspot_y; + + FramePtr->image = TFB_DrawImage_New (img[cel_ct]); + + tfbimg = FramePtr->image; + tfbimg->colormap_index = ani[cel_ct].colormap_index; + img[cel_ct] = tfbimg->NormalImg; + + FramePtr->HotSpot = MAKE_HOT_SPOT (hx, hy); + SetFrameBounds (FramePtr, tfbimg->extent.width, tfbimg->extent.height); + +#ifdef CLIPDEBUG + { + /* for debugging clipping: + draws white (or most matching color from palette) pixels to + every corner of the image + */ + Color color = {0xff, 0xff, 0xff, 0xff}; + RECT r = {{0, 0}, {1, 1}}; + if (tfbimg->extent.width > 2 && tfbimg->extent.height > 2) + { + TFB_DrawImage_Rect (&r, color, tfbimg); + r.corner.x = tfbimg->extent.width - 1; + TFB_DrawImage_Rect (&r, color, tfbimg); + r.corner.y = tfbimg->extent.height - 1; + TFB_DrawImage_Rect (&r, color, tfbimg); + r.corner.x = 0; + TFB_DrawImage_Rect (&r, color, tfbimg); + } + } +#endif +} + +static void +processFontChar (TFB_Char* CharPtr, TFB_Canvas canvas) +{ + BYTE* newdata; + size_t dpitch; + + TFB_DrawCanvas_GetExtent (canvas, &CharPtr->extent); + + // Currently, each font char has its own separate data + // but that can change to common mem area + dpitch = CharPtr->extent.width; + newdata = HMalloc (dpitch * CharPtr->extent.height * sizeof (BYTE)); + TFB_DrawCanvas_GetFontCharData (canvas, newdata, dpitch); + + CharPtr->data = newdata; + CharPtr->pitch = dpitch; + CharPtr->disp.width = CharPtr->extent.width + 1; + CharPtr->disp.height = CharPtr->extent.height + 1; + // XXX: why the +1? + // I brought it into this function from the only calling + // function, but I don't know why it was there in the first + // place. + // XXX: the +1 appears to be for character and line spacing + // text_blt just adds the frame width to move to the next char + + { + // This tunes the font positioning to be about what it should + // TODO: prolly needs a little tweaking still + + int tune_amount = 0; + + if (CharPtr->extent.height == 8) + tune_amount = -1; + else if (CharPtr->extent.height == 9) + tune_amount = -2; + else if (CharPtr->extent.height > 9) + tune_amount = -3; + + CharPtr->HotSpot = MAKE_HOT_SPOT (0, + CharPtr->extent.height + tune_amount); + } +} + +void * +_GetCelData (uio_Stream *fp, DWORD length) +{ + int cel_total, cel_index, n; + DWORD opos; + char CurrentLine[1024], filename[PATH_MAX]; + TFB_Canvas *img; + AniData *ani; + DRAWABLE Drawable; + uio_MountHandle *aniMount = 0; + uio_DirHandle *aniDir = 0; + uio_Stream *aniFile = 0; + + opos = uio_ftell (fp); + + { + char *s1, *s2; + char aniDirName[PATH_MAX]; + const char *aniFileName; + uint8 buf[4] = { 0, 0, 0, 0 }; + uint32 header; + + if (_cur_resfile_name == 0 + || (((s2 = 0), (s1 = strrchr (_cur_resfile_name, '/')) == 0) + && (s2 = strrchr (_cur_resfile_name, '\\')) == 0)) + { + n = 0; + } + else + { + if (s2 > s1) + s1 = s2; + n = s1 - _cur_resfile_name + 1; + } + + uio_fread(buf, 4, 1, fp); + header = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); + if (_cur_resfile_name && header == 0x04034b50) + { + // zipped ani file + if (n) + { + strncpy (aniDirName, _cur_resfile_name, n - 1); + aniDirName[n - 1] = 0; + aniFileName = _cur_resfile_name + n; + } + else + { + strcpy(aniDirName, "."); + aniFileName = _cur_resfile_name; + } + aniDir = uio_openDir (repository, aniDirName, 0); + aniMount = uio_mountDir (repository, aniDirName, uio_FSTYPE_ZIP, + aniDir, aniFileName, "/", autoMount, + uio_MOUNT_RDONLY | uio_MOUNT_TOP, + NULL); + aniFile = uio_fopen (aniDir, aniFileName, "r"); + opos = 0; + n = 0; + } + else + { + // unpacked ani file + strncpy (filename, _cur_resfile_name, n); + aniFile = fp; + aniDir = contentDir; + } + } + + cel_total = 0; + uio_fseek (aniFile, opos, SEEK_SET); + while (uio_fgets (CurrentLine, sizeof (CurrentLine), aniFile)) + { + ++cel_total; + } + + img = HMalloc (sizeof (TFB_Canvas) * cel_total); + ani = HMalloc (sizeof (AniData) * cel_total); + if (!img || !ani) + { + log_add (log_Warning, "Couldn't allocate space for '%s'", _cur_resfile_name); + if (aniMount) + { + uio_fclose(aniFile); + uio_closeDir(aniDir); + uio_unmountDir(aniMount); + } + HFree (img); + HFree (ani); + return NULL; + } + + cel_index = 0; + uio_fseek (aniFile, opos, SEEK_SET); + while (uio_fgets (CurrentLine, sizeof (CurrentLine), aniFile) && cel_index < cel_total) + { + sscanf (CurrentLine, "%s %d %d %d %d", &filename[n], + &ani[cel_index].transparent_color, &ani[cel_index].colormap_index, + &ani[cel_index].hotspot_x, &ani[cel_index].hotspot_y); + + img[cel_index] = TFB_DrawCanvas_LoadFromFile (aniDir, filename); + if (img[cel_index] == NULL) + { + const char *err; + + err = TFB_DrawCanvas_GetError (); + log_add (log_Warning, "_GetCelData: Unable to load image!"); + if (err != NULL) + log_add (log_Warning, "Gfx Driver reports: %s", err); + } + else + { + ++cel_index; + } + + if ((int)uio_ftell (aniFile) - (int)opos >= (int)length) + break; + } + + Drawable = NULL; + if (cel_index && (Drawable = AllocDrawable (cel_index))) + { + if (!Drawable) + { + while (cel_index--) + TFB_DrawCanvas_Delete (img[cel_index]); + + HFree (Drawable); + Drawable = NULL; + } + else + { + FRAME FramePtr; + + Drawable->Flags = WANT_PIXMAP; + Drawable->MaxIndex = cel_index - 1; + + FramePtr = &Drawable->Frame[cel_index]; + while (--FramePtr, cel_index--) + process_image (FramePtr, img, ani, cel_index); + } + } + + if (Drawable == NULL) + log_add (log_Warning, "Couldn't get cel data for '%s'", + _cur_resfile_name); + + if (aniMount) + { + uio_fclose(aniFile); + uio_closeDir(aniDir); + uio_unmountDir(aniMount); + } + + HFree (img); + HFree (ani); + return Drawable; +} + +BOOLEAN +_ReleaseCelData (void *handle) +{ + DRAWABLE DrawablePtr; + int cel_ct; + FRAME FramePtr = NULL; + + if ((DrawablePtr = handle) == 0) + return (FALSE); + + cel_ct = DrawablePtr->MaxIndex + 1; + FramePtr = DrawablePtr->Frame; + + HFree (handle); + if (FramePtr) + { + int i; + for (i = 0; i < cel_ct; i++) + { + TFB_Image *img = FramePtr[i].image; + if (img) + { + FramePtr[i].image = NULL; + TFB_DrawScreen_DeleteImage (img); + } + } + HFree (FramePtr); + } + + return (TRUE); +} + +typedef struct BuildCharDesc +{ + TFB_Canvas canvas; + UniChar index; +} BuildCharDesc; + +static int +compareBCDIndex (const void *arg1, const void *arg2) +{ + const BuildCharDesc *bcd1 = (const BuildCharDesc *) arg1; + const BuildCharDesc *bcd2 = (const BuildCharDesc *) arg2; + + return (int) bcd1->index - (int) bcd2->index; +} + +void * +_GetFontData (uio_Stream *fp, DWORD length) +{ + COUNT numDirEntries; + DIRENTRY fontDir = NULL; + BuildCharDesc *bcds = NULL; + size_t numBCDs = 0; + int dirEntryI; + uio_DirHandle *fontDirHandle = NULL; + uio_MountHandle *fontMount = NULL; + FONT fontPtr = NULL; + + if (_cur_resfile_name == 0) + goto err; + + if (fp != (uio_Stream*)~0) + { + // font is zipped instead of being in a directory + + char *s1, *s2; + int n; + const char *fontZipName; + char fontDirName[PATH_MAX]; + + if ((((s2 = 0), (s1 = strrchr (_cur_resfile_name, '/')) == 0) + && (s2 = strrchr (_cur_resfile_name, '\\')) == 0)) + { + strcpy(fontDirName, "."); + fontZipName = _cur_resfile_name; + } + else + { + if (s2 > s1) + s1 = s2; + n = s1 - _cur_resfile_name + 1; + strncpy (fontDirName, _cur_resfile_name, n - 1); + fontDirName[n - 1] = 0; + fontZipName = _cur_resfile_name + n; + } + + fontDirHandle = uio_openDir (repository, fontDirName, 0); + fontMount = uio_mountDir (repository, _cur_resfile_name, uio_FSTYPE_ZIP, + fontDirHandle, fontZipName, "/", autoMount, + uio_MOUNT_RDONLY | uio_MOUNT_TOP, + NULL); + uio_closeDir (fontDirHandle); + } + + fontDir = CaptureDirEntryTable (LoadDirEntryTable (contentDir, + _cur_resfile_name, ".", match_MATCH_SUBSTRING)); + if (fontDir == 0) + goto err; + numDirEntries = GetDirEntryTableCount (fontDir); + + fontDirHandle = uio_openDirRelative (contentDir, _cur_resfile_name, 0); + if (fontDirHandle == NULL) + goto err; + + bcds = HMalloc (numDirEntries * sizeof (BuildCharDesc)); + if (bcds == NULL) + goto err; + + // Load the surfaces for all dir Entries + for (dirEntryI = 0; dirEntryI < numDirEntries; dirEntryI++) + { + char *char_name; + unsigned int charIndex; + TFB_Canvas canvas; + EXTENT size; + + char_name = GetDirEntryAddress (SetAbsDirEntryTableIndex ( + fontDir, dirEntryI)); + if (sscanf (char_name, "%x.", &charIndex) != 1) + continue; + + if (charIndex > 0xffff) + continue; + + canvas = TFB_DrawCanvas_LoadFromFile (fontDirHandle, char_name); + if (canvas == NULL) + continue; + + TFB_DrawCanvas_GetExtent (canvas, &size); + if (size.width == 0 || size.height == 0) + { + TFB_DrawCanvas_Delete (canvas); + continue; + } + + bcds[numBCDs].canvas = canvas; + bcds[numBCDs].index = charIndex; + numBCDs++; + } + uio_closeDir (fontDirHandle); + DestroyDirEntryTable (ReleaseDirEntryTable (fontDir)); + if (fontMount != 0) + uio_unmountDir(fontMount); + +#if 0 + if (numBCDs == 0) + goto err; +#endif + + // sort on the character index + qsort (bcds, numBCDs, sizeof (BuildCharDesc), compareBCDIndex); + + fontPtr = AllocFont (0); + if (fontPtr == NULL) + goto err; + + fontPtr->Leading = 0; + fontPtr->LeadingWidth = 0; + + { + size_t startBCD = 0; + UniChar pageStart; + FONT_PAGE **pageEndPtr = &fontPtr->fontPages; + while (startBCD < numBCDs) + { + // Process one character page. + size_t endBCD; + pageStart = bcds[startBCD].index & CHARACTER_PAGE_MASK; + + endBCD = startBCD; + while (endBCD < numBCDs && + (bcds[endBCD].index & CHARACTER_PAGE_MASK) == pageStart) + endBCD++; + + { + size_t bcdI; + int numChars = bcds[endBCD - 1].index + 1 + - bcds[startBCD].index; + FONT_PAGE *page = AllocFontPage (numChars); + page->pageStart = pageStart; + page->firstChar = bcds[startBCD].index; + page->numChars = numChars; + *pageEndPtr = page; + pageEndPtr = &page->next; + + for (bcdI = startBCD; bcdI < endBCD; bcdI++) + { + // Process one character. + BuildCharDesc *bcd = &bcds[bcdI]; + TFB_Char *destChar = + &page->charDesc[bcd->index - page->firstChar]; + + if (destChar->data != NULL) + { + // There's already an image for this character. + log_add (log_Debug, "Duplicate image for character %d " + "for font %s.", (int) bcd->index, + _cur_resfile_name); + TFB_DrawCanvas_Delete (bcd->canvas); + continue; + } + + processFontChar (destChar, bcd->canvas); + TFB_DrawCanvas_Delete (bcd->canvas); + + if (destChar->disp.height > fontPtr->Leading) + fontPtr->Leading = destChar->disp.height; + if (destChar->disp.width > fontPtr->LeadingWidth) + fontPtr->LeadingWidth = destChar->disp.width; + } + } + + startBCD = endBCD; + } + *pageEndPtr = NULL; + } + + fontPtr->Leading++; + + HFree (bcds); + + (void) fp; /* Satisfying compiler (unused parameter) */ + (void) length; /* Satisfying compiler (unused parameter) */ + return fontPtr; + +err: + if (fontPtr != 0) + HFree (fontPtr); + + if (bcds != NULL) + { + size_t bcdI; + for (bcdI = 0; bcdI < numBCDs; bcdI++) + TFB_DrawCanvas_Delete (bcds[bcdI].canvas); + HFree (bcds); + } + + if (fontDirHandle != NULL) + uio_closeDir (fontDirHandle); + + if (fontDir != 0) + DestroyDirEntryTable (ReleaseDirEntryTable (fontDir)); + + if (fontMount != 0) + uio_unmountDir(fontMount); + + return 0; +} + +BOOLEAN +_ReleaseFontData (void *handle) +{ + FONT font = (FONT) handle; + if (font == NULL) + return FALSE; + + { + FONT_PAGE *page; + FONT_PAGE *nextPage; + + for (page = font->fontPages; page != NULL; page = nextPage) + { + size_t charI; + for (charI = 0; charI < page->numChars; charI++) + { + TFB_Char *c = &page->charDesc[charI]; + + if (c->data == NULL) + continue; + + // XXX: fix this if fonts get per-page data + // rather than per-char + TFB_DrawScreen_DeleteData (c->data); + } + + nextPage = page->next; + FreeFontPage (page); + } + } + + HFree (font); + + return TRUE; +} diff --git a/src/libs/graphics/intersec.c b/src/libs/graphics/intersec.c new file mode 100644 index 0000000..02a5226 --- /dev/null +++ b/src/libs/graphics/intersec.c @@ -0,0 +1,415 @@ +//Copyright Paul Reiche, Fred Ford. 1992-2002 + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "libs/graphics/context.h" +#include "libs/graphics/drawable.h" +#include "libs/graphics/tfb_draw.h" +#include "libs/log.h" + +//#define DEBUG_INTERSEC + +static inline BOOLEAN +images_intersect (IMAGE_BOX *box1, IMAGE_BOX *box2, const RECT *rect) +{ + return TFB_DrawImage_Intersect (box1->FramePtr->image, box1->Box.corner, + box2->FramePtr->image, box2->Box.corner, rect); +} + +static TIME_VALUE +frame_intersect (INTERSECT_CONTROL *pControl0, RECT *pr0, + INTERSECT_CONTROL *pControl1, RECT *pr1, TIME_VALUE t0, + TIME_VALUE t1) +{ + SIZE time_error0, time_error1; + SIZE cycle0, cycle1; + SIZE dx_0, dy_0, dx_1, dy_1; + SIZE xincr0, yincr0, xincr1, yincr1; + SIZE xerror0, xerror1, yerror0, yerror1; + RECT r_intersect; + IMAGE_BOX IB0, IB1; + BOOLEAN check0, check1; + + IB0.FramePtr = pControl0->IntersectStamp.frame; + IB0.Box.corner = pr0->corner; + IB0.Box.extent.width = GetFrameWidth (IB0.FramePtr); + IB0.Box.extent.height = GetFrameHeight (IB0.FramePtr); + IB1.FramePtr = pControl1->IntersectStamp.frame; + IB1.Box.corner = pr1->corner; + IB1.Box.extent.width = GetFrameWidth (IB1.FramePtr); + IB1.Box.extent.height = GetFrameHeight (IB1.FramePtr); + + dx_0 = pr0->extent.width; + dy_0 = pr0->extent.height; + if (dx_0 >= 0) + xincr0 = 1; + else + { + xincr0 = -1; + dx_0 = -dx_0; + } + if (dy_0 >= 0) + yincr0 = 1; + else + { + yincr0 = -1; + dy_0 = -dy_0; + } + if (dx_0 >= dy_0) + cycle0 = dx_0; + else + cycle0 = dy_0; + xerror0 = yerror0 = cycle0; + + dx_1 = pr1->extent.width; + dy_1 = pr1->extent.height; + if (dx_1 >= 0) + xincr1 = 1; + else + { + xincr1 = -1; + dx_1 = -dx_1; + } + if (dy_1 >= 0) + yincr1 = 1; + else + { + yincr1 = -1; + dy_1 = -dy_1; + } + if (dx_1 >= dy_1) + cycle1 = dx_1; + else + cycle1 = dy_1; + xerror1 = yerror1 = cycle1; + + check0 = check1 = FALSE; + if (t0 <= 1) + { + time_error0 = time_error1 = 0; + if (t0 == 0) + { + ++t0; + goto CheckFirstIntersection; + } + } + else + { + SIZE delta; + COUNT start; + long error; + + start = (COUNT)cycle0 * (COUNT)(t0 - 1); + time_error0 = start & ((1 << TIME_SHIFT) - 1); + if ((start >>= (COUNT)TIME_SHIFT) > 0) + { + if ((error = (long)xerror0 + - (long)dx_0 * (long)start) > 0) + xerror0 = (SIZE)error; + else + { + delta = -(SIZE)(error / (long)cycle0) + 1; + IB0.Box.corner.x += xincr0 * delta; + xerror0 = (SIZE)(error + (long)cycle0 * (long)delta); + } + if ((error = (long)yerror0 + - (long)dy_0 * (long)start) > 0) + yerror0 = (SIZE)error; + else + { + delta = -(SIZE)(error / (long)cycle0) + 1; + IB0.Box.corner.y += yincr0 * delta; + yerror0 = (SIZE)(error + (long)cycle0 * (long)delta); + } + pr0->corner = IB0.Box.corner; + } + + start = (COUNT)cycle1 * (COUNT)(t0 - 1); + time_error1 = start & ((1 << TIME_SHIFT) - 1); + if ((start >>= (COUNT)TIME_SHIFT) > 0) + { + if ((error = (long)xerror1 + - (long)dx_1 * (long)start) > 0) + xerror1 = (SIZE)error; + else + { + delta = -(SIZE)(error / (long)cycle1) + 1; + IB1.Box.corner.x += xincr1 * delta; + xerror1 = (SIZE)(error + (long)cycle1 * (long)delta); + } + if ((error = (long)yerror1 + - (long)dy_1 * (long)start) > 0) + yerror1 = (SIZE)error; + else + { + delta = -(SIZE)(error / (long)cycle1) + 1; + IB1.Box.corner.y += yincr1 * delta; + yerror1 = (SIZE)(error + (long)cycle1 * (long)delta); + } + pr1->corner = IB1.Box.corner; + } + } + + pControl0->last_time_val = pControl1->last_time_val = t0; + do + { + ++t0; + if ((time_error0 += cycle0) >= (1 << TIME_SHIFT)) + { + if ((xerror0 -= dx_0) <= 0) + { + IB0.Box.corner.x += xincr0; + xerror0 += cycle0; + } + if ((yerror0 -= dy_0) <= 0) + { + IB0.Box.corner.y += yincr0; + yerror0 += cycle0; + } + + check0 = TRUE; + time_error0 -= (1 << TIME_SHIFT); + } + + if ((time_error1 += cycle1) >= (1 << TIME_SHIFT)) + { + if ((xerror1 -= dx_1) <= 0) + { + IB1.Box.corner.x += xincr1; + xerror1 += cycle1; + } + if ((yerror1 -= dy_1) <= 0) + { + IB1.Box.corner.y += yincr1; + yerror1 += cycle1; + } + + check1 = TRUE; + time_error1 -= (1 << TIME_SHIFT); + } + + if (check0 || check1) + { /* if check0 && check1, this may not be quite right -- + * if shapes had a pixel's separation to begin with + * and both moved toward each other, you would actually + * get a pixel overlap but since the last positions were + * separated by a pixel, the shapes wouldn't be touching + * each other. + */ +CheckFirstIntersection: + if (BoxIntersect (&IB0.Box, &IB1.Box, &r_intersect) + && images_intersect (&IB0, &IB1, &r_intersect)) + return (t0); + + if (check0) + { + pr0->corner = IB0.Box.corner; + pControl0->last_time_val = t0; + check0 = FALSE; + } + if (check1) + { + pr1->corner = IB1.Box.corner; + pControl1->last_time_val = t0; + check1 = FALSE; + } + } + } while (t0 <= t1); + + return ((TIME_VALUE)0); +} + +TIME_VALUE +DrawablesIntersect (INTERSECT_CONTROL *pControl0, + INTERSECT_CONTROL *pControl1, TIME_VALUE max_time_val) +{ + SIZE dy; + SIZE time_y_0, time_y_1; + RECT r0, r1; + FRAME FramePtr0, FramePtr1; + + if (!ContextActive () || max_time_val == 0) + return ((TIME_VALUE)0); + else if (max_time_val > MAX_TIME_VALUE) + max_time_val = MAX_TIME_VALUE; + + pControl0->last_time_val = pControl1->last_time_val = 0; + + r0.corner = pControl0->IntersectStamp.origin; + r1.corner = pControl1->IntersectStamp.origin; + + r0.extent.width = pControl0->EndPoint.x - r0.corner.x; + r0.extent.height = pControl0->EndPoint.y - r0.corner.y; + r1.extent.width = pControl1->EndPoint.x - r1.corner.x; + r1.extent.height = pControl1->EndPoint.y - r1.corner.y; + + FramePtr0 = pControl0->IntersectStamp.frame; + if (FramePtr0 == 0) + return(0); + r0.corner.x -= FramePtr0->HotSpot.x; + r0.corner.y -= FramePtr0->HotSpot.y; + + FramePtr1 = pControl1->IntersectStamp.frame; + if (FramePtr1 == 0) + return(0); + r1.corner.x -= FramePtr1->HotSpot.x; + r1.corner.y -= FramePtr1->HotSpot.y; + + dy = r1.corner.y - r0.corner.y; + time_y_0 = dy - GetFrameHeight (FramePtr0) + 1; + time_y_1 = dy + GetFrameHeight (FramePtr1) - 1; + dy = r0.extent.height - r1.extent.height; + + if ((time_y_0 <= 0 && time_y_1 >= 0) + || (time_y_0 > 0 && dy >= time_y_0) + || (time_y_1 < 0 && dy <= time_y_1)) + { + SIZE dx; + SIZE time_x_0, time_x_1; + + dx = r1.corner.x - r0.corner.x; + time_x_0 = dx - GetFrameWidth (FramePtr0) + 1; + time_x_1 = dx + GetFrameWidth (FramePtr1) - 1; + dx = r0.extent.width - r1.extent.width; + + if ((time_x_0 <= 0 && time_x_1 >= 0) + || (time_x_0 > 0 && dx >= time_x_0) + || (time_x_1 < 0 && dx <= time_x_1)) + { + TIME_VALUE intersect_time; + + if (dx == 0 && dy == 0) + time_y_0 = time_y_1 = 0; + else + { + SIZE t; + long time_beg, time_end, fract; + + if (time_y_1 < 0) + { + t = time_y_0; + time_y_0 = -time_y_1; + time_y_1 = -t; + } + else if (time_y_0 <= 0) + { + if (dy < 0) + time_y_1 = -time_y_0; + time_y_0 = 0; + } + if (dy < 0) + dy = -dy; + if (dy < time_y_1) + time_y_1 = dy; + /* just to be safe, widen search area */ + --time_y_0; + ++time_y_1; + + if (time_x_1 < 0) + { + t = time_x_0; + time_x_0 = -time_x_1; + time_x_1 = -t; + } + else if (time_x_0 <= 0) + { + if (dx < 0) + time_x_1 = -time_x_0; + time_x_0 = 0; + } + if (dx < 0) + dx = -dx; + if (dx < time_x_1) + time_x_1 = dx; + /* just to be safe, widen search area */ + --time_x_0; + ++time_x_1; + +#ifdef DEBUG_INTERSEC + log_add (log_Debug, "FramePtr0<%d, %d> --> <%d, %d>", + GetFrameWidth (FramePtr0), GetFrameHeight (FramePtr0), + r0.corner.x, r0.corner.y); + log_add (log_Debug, "FramePtr1<%d, %d> --> <%d, %d>", + GetFrameWidth (FramePtr1), GetFrameHeight (FramePtr1), + r1.corner.x, r1.corner.y); + log_add (log_Debug, "time_x(%d, %d)-%d, time_y(%d, %d)-%d", + time_x_0, time_x_1, dx, time_y_0, time_y_1, dy); +#endif /* DEBUG_INTERSEC */ + if (dx == 0) + { + time_beg = time_y_0; + time_end = time_y_1; + fract = dy; + } + else if (dy == 0) + { + time_beg = time_x_0; + time_end = time_x_1; + fract = dx; + } + else + { + long time_x, time_y; + + time_x = (long)time_x_0 * (long)dy; + time_y = (long)time_y_0 * (long)dx; + time_beg = time_x < time_y ? time_y : time_x; + + time_x = (long)time_x_1 * (long)dy; + time_y = (long)time_y_1 * (long)dx; + time_end = time_x > time_y ? time_y : time_x; + + fract = (long)dx * (long)dy; + } + + if ((time_beg <<= TIME_SHIFT) < fract) + time_y_0 = 0; + else + time_y_0 = (SIZE)(time_beg / fract); + + if (time_end >= fract /* just in case of overflow */ + || (time_end <<= TIME_SHIFT) >= + fract * (long)max_time_val) + time_y_1 = max_time_val - 1; + else + time_y_1 = (SIZE)((time_end + fract - 1) / fract) - 1; + } + +#ifdef DEBUG_INTERSEC + log_add (log_Debug, "start_time = %d, end_time = %d", + time_y_0, time_y_1); +#endif /* DEBUG_INTERSEC */ + if (time_y_0 <= time_y_1 + && (intersect_time = frame_intersect ( + pControl0, &r0, pControl1, &r1, + (TIME_VALUE)time_y_0, (TIME_VALUE)time_y_1))) + { + FramePtr0 = pControl0->IntersectStamp.frame; + pControl0->EndPoint.x = r0.corner.x + FramePtr0->HotSpot.x; + pControl0->EndPoint.y = r0.corner.y + FramePtr0->HotSpot.y; + FramePtr1 = pControl1->IntersectStamp.frame; + pControl1->EndPoint.x = r1.corner.x + FramePtr1->HotSpot.x; + pControl1->EndPoint.y = r1.corner.y + FramePtr1->HotSpot.y; + + return (intersect_time); + } + } + } + + return ((TIME_VALUE)0); +} + diff --git a/src/libs/graphics/loaddisp.c b/src/libs/graphics/loaddisp.c new file mode 100644 index 0000000..ccc7919 --- /dev/null +++ b/src/libs/graphics/loaddisp.c @@ -0,0 +1,65 @@ +//Copyright Paul Reiche, Fred Ford. 1992-2002 + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "libs/gfxlib.h" +#include "libs/graphics/drawable.h" +#include "libs/log.h" + + +// Reads a piece of screen into a passed FRAME or a newly created one +DRAWABLE +LoadDisplayPixmap (const RECT *area, FRAME frame) +{ + // TODO: This should just return a FRAME instead of DRAWABLE + DRAWABLE buffer = GetFrameParentDrawable (frame); + COUNT index; + + if (!buffer) + { // asked to create a new DRAWABLE instead + buffer = CreateDrawable (WANT_PIXMAP | MAPPED_TO_DISPLAY, + area->extent.width, area->extent.height, 1); + if (!buffer) + return NULL; + + index = 0; + } + else + { + index = GetFrameIndex (frame); + } + + frame = SetAbsFrameIndex (CaptureDrawable (buffer), index); + + if (_CurFramePtr->Type != SCREEN_DRAWABLE + || frame->Type == SCREEN_DRAWABLE + || !(GetFrameParentDrawable (frame)->Flags & MAPPED_TO_DISPLAY)) + { + log_add (log_Warning, "Unimplemented function activated: " + "LoadDisplayPixmap()"); + } + else + { + TFB_Image *img = frame->image; + TFB_DrawScreen_CopyToImage (img, area, TFB_SCREEN_MAIN); + } + + ReleaseDrawable (frame); + + return buffer; +} + diff --git a/src/libs/graphics/pixmap.c b/src/libs/graphics/pixmap.c new file mode 100644 index 0000000..6e68244 --- /dev/null +++ b/src/libs/graphics/pixmap.c @@ -0,0 +1,170 @@ +//Copyright Paul Reiche, Fred Ford. 1992-2002 + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "gfxintrn.h" +#include "libs/log.h" + +DRAWABLE +GetFrameParentDrawable (FRAME f) +{ + if (f != NULL) + { + return f->parent; + } + return NULL; +} + +FRAME +CaptureDrawable (DRAWABLE DrawablePtr) +{ + if (DrawablePtr) + { + return &DrawablePtr->Frame[0]; + } + + return NULL; +} + +DRAWABLE +ReleaseDrawable (FRAME FramePtr) +{ + if (FramePtr != 0) + { + DRAWABLE Drawable; + + Drawable = GetFrameParentDrawable (FramePtr); + + return (Drawable); + } + + return NULL; +} + +COUNT +GetFrameCount (FRAME FramePtr) +{ + DRAWABLE_DESC *DrawablePtr; + + if (FramePtr == 0) + return (0); + + DrawablePtr = GetFrameParentDrawable (FramePtr); + return DrawablePtr->MaxIndex + 1; +} + +COUNT +GetFrameIndex (FRAME FramePtr) +{ + if (FramePtr == 0) + return (0); + + return FramePtr->Index; +} + +FRAME +SetAbsFrameIndex (FRAME FramePtr, COUNT FrameIndex) +{ + if (FramePtr != 0) + { + DRAWABLE_DESC *DrawablePtr; + + DrawablePtr = GetFrameParentDrawable (FramePtr); + + FrameIndex = FrameIndex % (DrawablePtr->MaxIndex + 1); + FramePtr = &DrawablePtr->Frame[FrameIndex]; + } + + return FramePtr; +} + +FRAME +SetRelFrameIndex (FRAME FramePtr, SIZE FrameOffs) +{ + if (FramePtr != 0) + { + COUNT num_frames; + DRAWABLE_DESC *DrawablePtr; + + DrawablePtr = GetFrameParentDrawable (FramePtr); + num_frames = DrawablePtr->MaxIndex + 1; + if (FrameOffs < 0) + { + while ((FrameOffs += num_frames) < 0) + ; + } + + FrameOffs = ((SWORD)FramePtr->Index + FrameOffs) % num_frames; + FramePtr = &DrawablePtr->Frame[FrameOffs]; + } + + return FramePtr; +} + +FRAME +SetEquFrameIndex (FRAME DstFramePtr, FRAME SrcFramePtr) +{ + COUNT Index; + + if (!DstFramePtr || !SrcFramePtr) + return 0; + + Index = GetFrameIndex (SrcFramePtr); +#ifdef DEBUG + { + DRAWABLE_DESC *DrawablePtr = GetFrameParentDrawable (DstFramePtr); + if (Index > DrawablePtr->MaxIndex) + log_add (log_Debug, "SetEquFrameIndex: source index (%d) beyond " + "destination range (%d)", (int)Index, + (int)DrawablePtr->MaxIndex); + } +#endif + + return SetAbsFrameIndex (DstFramePtr, Index); +} + +FRAME +IncFrameIndex (FRAME FramePtr) +{ + DRAWABLE_DESC *DrawablePtr; + + if (FramePtr == 0) + return (0); + + DrawablePtr = GetFrameParentDrawable (FramePtr); + if (FramePtr->Index < DrawablePtr->MaxIndex) + return ++FramePtr; + else + return DrawablePtr->Frame; +} + +FRAME +DecFrameIndex (FRAME FramePtr) +{ + if (FramePtr == 0) + return (0); + + if (FramePtr->Index > 0) + return --FramePtr; + else + { + DRAWABLE_DESC *DrawablePtr; + + DrawablePtr = GetFrameParentDrawable (FramePtr); + return &DrawablePtr->Frame[DrawablePtr->MaxIndex]; + } +} diff --git a/src/libs/graphics/prim.h b/src/libs/graphics/prim.h new file mode 100644 index 0000000..bce3c2a --- /dev/null +++ b/src/libs/graphics/prim.h @@ -0,0 +1,80 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef LIBS_GRAPHICS_PRIM_H_ +#define LIBS_GRAPHICS_PRIM_H_ + +enum gfx_object +{ + POINT_PRIM = 0, + STAMP_PRIM, + STAMPFILL_PRIM, + LINE_PRIM, + TEXT_PRIM, + RECT_PRIM, + RECTFILL_PRIM, + + NUM_PRIMS +}; +typedef BYTE GRAPHICS_PRIM; + +typedef union +{ + POINT Point; + STAMP Stamp; + LINE Line; + TEXT Text; + RECT Rect; +} PRIM_DESC; + +typedef DWORD PRIM_LINKS; + +typedef struct +{ + PRIM_LINKS Links; + GRAPHICS_PRIM Type; + Color color; + PRIM_DESC Object; +} PRIMITIVE; + +#define END_OF_LIST ((COUNT)0xFFFF) + +#define GetPredLink(l) LOWORD(l) +#define GetSuccLink(l) HIWORD(l) +#define MakeLinks MAKE_DWORD +#define SetPrimLinks(pPrim,p,s) ((pPrim)->Links = MakeLinks (p, s)) +#define GetPrimLinks(pPrim) ((pPrim)->Links) +#define SetPrimType(pPrim,t) ((pPrim)->Type = t) +#define GetPrimType(pPrim) ((pPrim)->Type) +#define SetPrimColor(pPrim,c) ((pPrim)->color = c) +#define GetPrimColor(pPrim) ((pPrim)->color) + +static inline void +SetPrimNextLink (PRIMITIVE *pPrim, COUNT Link) +{ + SetPrimLinks (pPrim, END_OF_LIST, Link); +} + + +static inline COUNT +GetPrimNextLink (PRIMITIVE *pPrim) +{ + return GetSuccLink (GetPrimLinks (pPrim)); +} + + +#endif /* PRIM_H */ + + diff --git a/src/libs/graphics/resgfx.c b/src/libs/graphics/resgfx.c new file mode 100644 index 0000000..c0760dc --- /dev/null +++ b/src/libs/graphics/resgfx.c @@ -0,0 +1,54 @@ +//Copyright Paul Reiche, Fred Ford. 1992-2002 + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "gfxintrn.h" + +static void +GetCelFileData (const char *pathname, RESOURCE_DATA *resdata) +{ + resdata->ptr = LoadResourceFromPath (pathname, _GetCelData); +} + +static void +GetFontFileData (const char *pathname, RESOURCE_DATA *resdata) +{ + resdata->ptr = LoadResourceFromPath (pathname, _GetFontData); +} + + +BOOLEAN +InstallGraphicResTypes (void) +{ + InstallResTypeVectors ("GFXRES", GetCelFileData, _ReleaseCelData, NULL); + InstallResTypeVectors ("FONTRES", GetFontFileData, _ReleaseFontData, NULL); + return (TRUE); +} + +/* Needs to be void * because it could be either a DRAWABLE or a FONT. */ +void * +LoadGraphicInstance (RESOURCE res) +{ + void *hData; + + hData = res_GetResource (res); + if (hData) + res_DetachResource (res); + + return (hData); +} + diff --git a/src/libs/graphics/sdl/2xscalers.c b/src/libs/graphics/sdl/2xscalers.c new file mode 100644 index 0000000..a612d2c --- /dev/null +++ b/src/libs/graphics/sdl/2xscalers.c @@ -0,0 +1,260 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "libs/graphics/sdl/sdl_common.h" +#include "types.h" +#include "scalers.h" +#include "scaleint.h" +#include "2xscalers.h" + + +// Scaler function lookup table +// +const Scale_FuncDef_t +Scale_C_Functions[] = +{ + {TFB_GFXFLAGS_SCALE_BILINEAR, Scale_BilinearFilter}, + {TFB_GFXFLAGS_SCALE_BIADAPT, Scale_BiAdaptFilter}, + {TFB_GFXFLAGS_SCALE_BIADAPTADV, Scale_BiAdaptAdvFilter}, + {TFB_GFXFLAGS_SCALE_TRISCAN, Scale_TriScanFilter}, + {TFB_GFXFLAGS_SCALE_HQXX, Scale_HqFilter}, + // Default + {0, Scale_Nearest} +}; + +// See +// nearest2x.c -- Nearest Neighboor scaling +// bilinear2x.c -- Bilinear scaling +// biadv2x.c -- Advanced Biadapt scaling +// triscan2x.c -- Triscan scaling + +// Biadapt scaling to 2x +void +SCALE_(BiAdaptFilter) (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r) +{ + int x, y; + const int w = src->w, h = src->h; + int xend, yend; + int dsrc, ddst; + SDL_Rect *region = r; + SDL_Rect limits; + SDL_PixelFormat *fmt = dst->format; + const int sp = src->pitch, dp = dst->pitch; + const int bpp = fmt->BytesPerPixel; + const int slen = sp / bpp, dlen = dp / bpp; + Uint32 *src_p = (Uint32 *)src->pixels; + Uint32 *dst_p = (Uint32 *)dst->pixels; + Uint32 pixval_tl, pixval_tr, pixval_bl, pixval_br; + + // these macros are for clarity; they make the current pixel (0,0) + // and allow to access pixels in all directions + #define SRC(x, y) (src_p + (x) + ((y) * slen)) + + SCALE_(PlatInit) (); + + // expand updated region if necessary + // pixels neighbooring the updated region may + // change as a result of updates + limits.x = 0; + limits.y = 0; + limits.w = src->w; + limits.h = src->h; + Scale_ExpandRect (region, 2, &limits); + + xend = region->x + region->w; + yend = region->y + region->h; + dsrc = slen - region->w; + ddst = (dlen - region->w) * 2; + + // move ptrs to the first updated pixel + src_p += slen * region->y + region->x; + dst_p += (dlen * region->y + region->x) * 2; + + for (y = region->y; y < yend; ++y, dst_p += ddst, src_p += dsrc) + { + for (x = region->x; x < xend; ++x, ++src_p, ++dst_p) + { + pixval_tl = SCALE_GETPIX (SRC (0, 0)); + + SCALE_SETPIX (dst_p, pixval_tl); + + if (y + 1 < h) + { + // check pixel below the current one + pixval_bl = SCALE_GETPIX (SRC (0, 1)); + + if (pixval_tl == pixval_bl) + SCALE_SETPIX (dst_p + dlen, pixval_tl); + else + SCALE_SETPIX (dst_p + dlen, Scale_Blend_11 ( + pixval_tl, pixval_bl) + ); + } + else + { + // last pixel in column - propagate + SCALE_SETPIX (dst_p + dlen, pixval_tl); + pixval_bl = pixval_tl; + } + ++dst_p; + + if (x + 1 >= w) + { + // last pixel in row - propagate + SCALE_SETPIX (dst_p, pixval_tl); + + if (pixval_tl == pixval_bl) + SCALE_SETPIX (dst_p + dlen, pixval_tl); + else + SCALE_SETPIX (dst_p + dlen, Scale_Blend_11 ( + pixval_tl, pixval_bl) + ); + continue; + } + + // check pixel to the right from the current one + pixval_tr = SCALE_GETPIX (SRC (1, 0)); + + if (pixval_tl == pixval_tr) + SCALE_SETPIX (dst_p, pixval_tr); + else + SCALE_SETPIX (dst_p, Scale_Blend_11 ( + pixval_tl, pixval_tr) + ); + + if (y + 1 >= h) + { + // last pixel in column - propagate + SCALE_SETPIX (dst_p + dlen, pixval_tl); + continue; + } + + // check pixel to the bottom-right + pixval_br = SCALE_GETPIX (SRC (1, 1)); + + if (pixval_tl == pixval_br && pixval_tr == pixval_bl) + { + int cl, cr; + Uint32 clr; + + if (pixval_tl == pixval_tr) + { + // all 4 are equal - propagate + SCALE_SETPIX (dst_p + dlen, pixval_tl); + continue; + } + + // both pairs are equal, have to resolve the pixel + // race; we try detecting which color is + // the background by looking for a line or an edge + // examine 8 pixels surrounding the current quad + + cl = cr = 1; + + if (x > 0) + { + clr = SCALE_GETPIX (SRC (-1, 0)); + if (clr == pixval_tl) + cl++; + else if (clr == pixval_tr) + cr++; + + clr = SCALE_GETPIX (SRC (-1, 1)); + if (clr == pixval_tl) + cl++; + else if (clr == pixval_tr) + cr++; + } + + if (y > 0) + { + clr = SCALE_GETPIX (SRC (0, -1)); + if (clr == pixval_tl) + cl++; + else if (clr == pixval_tr) + cr++; + + clr = SCALE_GETPIX (SRC (1, -1)); + if (clr == pixval_tl) + cl++; + else if (clr == pixval_tr) + cr++; + } + + if (x + 2 < w) + { + clr = SCALE_GETPIX (SRC (2, 0)); + if (clr == pixval_tl) + cl++; + else if (clr == pixval_tr) + cr++; + + clr = SCALE_GETPIX (SRC (2, 1)); + if (clr == pixval_tl) + cl++; + else if (clr == pixval_tr) + cr++; + } + + if (y + 2 < h) + { + clr = SCALE_GETPIX (SRC (0, 2)); + if (clr == pixval_tl) + cl++; + else if (clr == pixval_tr) + cr++; + + clr = SCALE_GETPIX (SRC (1, 2)); + if (clr == pixval_tl) + cl++; + else if (clr == pixval_tr) + cr++; + } + + // least count wins + if (cl > cr) + SCALE_SETPIX (dst_p + dlen, pixval_tr); + else if (cr > cl) + SCALE_SETPIX (dst_p + dlen, pixval_tl); + else + SCALE_SETPIX (dst_p + dlen, + Scale_Blend_11 (pixval_tl, pixval_tr)); + } + else if (pixval_tl == pixval_br) + { + // main diagonal is same color + // use its value + SCALE_SETPIX (dst_p + dlen, pixval_tl); + } + else if (pixval_tr == pixval_bl) + { + // 2nd diagonal is same color + // use its value + SCALE_SETPIX (dst_p + dlen, pixval_tr); + } + else + { + // blend all 4 + SCALE_SETPIX (dst_p + dlen, Scale_Blend_1111 ( + pixval_tl, pixval_bl, pixval_tr, pixval_br + )); + } + } + } + + SCALE_(PlatDone) (); +} + diff --git a/src/libs/graphics/sdl/2xscalers.h b/src/libs/graphics/sdl/2xscalers.h new file mode 100644 index 0000000..f004fcd --- /dev/null +++ b/src/libs/graphics/sdl/2xscalers.h @@ -0,0 +1,30 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef LIBS_GRAPHICS_SDL_2XSCALERS_H_ +#define LIBS_GRAPHICS_SDL_2XSCALERS_H_ + +void Scale_Nearest (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r); +void Scale_BilinearFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r); +void Scale_BiAdaptFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r); +void Scale_BiAdaptAdvFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r); +void Scale_TriScanFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r); +void Scale_HqFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r); + +extern const Scale_FuncDef_t Scale_C_Functions[]; + + +#endif /* LIBS_GRAPHICS_SDL_2XSCALERS_H_ */ diff --git a/src/libs/graphics/sdl/2xscalers_3dnow.c b/src/libs/graphics/sdl/2xscalers_3dnow.c new file mode 100644 index 0000000..da34e82 --- /dev/null +++ b/src/libs/graphics/sdl/2xscalers_3dnow.c @@ -0,0 +1,102 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "port.h" +#include "libs/platform.h" + +#if defined(MMX_ASM) + +#include "libs/graphics/sdl/sdl_common.h" +#include "types.h" +#include "scalers.h" +#include "scaleint.h" +#include "2xscalers.h" +#include "2xscalers_mmx.h" + +// 3DNow! name for all functions +#undef SCALE_ +#define SCALE_(name) Scale ## _3DNow_ ## name + +// Tell them which opcodes we want to support +#undef USE_MOVNTQ +#define USE_PREFETCH AMD_PREFETCH +#undef USE_PSADBW +// Bring in inline asm functions +#include "scalemmx.h" + + +// Scaler function lookup table +// +const Scale_FuncDef_t +Scale_3DNow_Functions[] = +{ + {TFB_GFXFLAGS_SCALE_BILINEAR, Scale_3DNow_BilinearFilter}, + {TFB_GFXFLAGS_SCALE_BIADAPT, Scale_BiAdaptFilter}, + {TFB_GFXFLAGS_SCALE_BIADAPTADV, Scale_MMX_BiAdaptAdvFilter}, + {TFB_GFXFLAGS_SCALE_TRISCAN, Scale_MMX_TriScanFilter}, + {TFB_GFXFLAGS_SCALE_HQXX, Scale_MMX_HqFilter}, + // Default + {0, Scale_3DNow_Nearest} +}; + + +void +Scale_3DNow_PrepPlatform (const SDL_PixelFormat* fmt) +{ + Scale_MMX_PrepPlatform (fmt); +} + +// Nearest Neighbor scaling to 2x +// void Scale_3DNow_Nearest (SDL_Surface *src, +// SDL_Surface *dst, SDL_Rect *r) + +#include "nearest2x.c" + + +// Bilinear scaling to 2x +// void Scale_3DNow_BilinearFilter (SDL_Surface *src, +// SDL_Surface *dst, SDL_Rect *r) + +#include "bilinear2x.c" + + +#if 0 && NO_IMPROVEMENT + +// Advanced Biadapt scaling to 2x +// void Scale_3DNow_BiAdaptAdvFilter (SDL_Surface *src, +// SDL_Surface *dst, SDL_Rect *r) + +#include "biadv2x.c" + + +// Triscan scaling to 2x +// derivative of scale2x -- scale2x.sf.net +// void Scale_3DNow_TriScanFilter (SDL_Surface *src, +// SDL_Surface *dst, SDL_Rect *r) + +#include "triscan2x.c" + +// Hq2x scaling +// (adapted from 'hq2x' by Maxim Stepin -- www.hiend3d.com/hq2x.html) +// void Scale_3DNow_HqFilter (SDL_Surface *src, +// SDL_Surface *dst, SDL_Rect *r) + +#include "hq2x.c" + +#endif /* NO_IMPROVEMENT */ + +#endif /* MMX_ASM */ + diff --git a/src/libs/graphics/sdl/2xscalers_mmx.c b/src/libs/graphics/sdl/2xscalers_mmx.c new file mode 100644 index 0000000..c321b16 --- /dev/null +++ b/src/libs/graphics/sdl/2xscalers_mmx.c @@ -0,0 +1,136 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "port.h" +#include "libs/platform.h" + +#if defined(MMX_ASM) + +#include "libs/graphics/sdl/sdl_common.h" +#include "types.h" +#include "scalers.h" +#include "scaleint.h" +#include "2xscalers.h" +#include "2xscalers_mmx.h" + +// MMX name for all functions +#undef SCALE_ +#define SCALE_(name) Scale ## _MMX_ ## name + +// Tell them which opcodes we want to support +#undef USE_MOVNTQ +#undef USE_PREFETCH +#undef USE_PSADBW +// And Bring in inline asm functions +#include "scalemmx.h" + + +// Scaler function lookup table +// +const Scale_FuncDef_t +Scale_MMX_Functions[] = +{ + {TFB_GFXFLAGS_SCALE_BILINEAR, Scale_MMX_BilinearFilter}, + {TFB_GFXFLAGS_SCALE_BIADAPT, Scale_BiAdaptFilter}, + {TFB_GFXFLAGS_SCALE_BIADAPTADV, Scale_MMX_BiAdaptAdvFilter}, + {TFB_GFXFLAGS_SCALE_TRISCAN, Scale_MMX_TriScanFilter}, + {TFB_GFXFLAGS_SCALE_HQXX, Scale_MMX_HqFilter}, + // Default + {0, Scale_MMX_Nearest} +}; + +// MMX transformation multipliers +Uint64 mmx_888to555_mult; +Uint64 mmx_Y_mult; +Uint64 mmx_U_mult; +Uint64 mmx_V_mult; +// Uint64 mmx_YUV_threshold = 0x00300706; original hq2x threshold +//Uint64 mmx_YUV_threshold = 0x0030100e; +Uint64 mmx_YUV_threshold = 0x0040120c; + +void +Scale_MMX_PrepPlatform (const SDL_PixelFormat* fmt) +{ + // prepare the channel-shuffle multiplier + mmx_888to555_mult = ((Uint64)0x0400) << (fmt->Rshift * 2) + | ((Uint64)0x0020) << (fmt->Gshift * 2) + | ((Uint64)0x0001) << (fmt->Bshift * 2); + + // prepare the RGB->YUV multipliers + mmx_Y_mult = ((Uint64)(uint16)YUV_matrix[YUV_XFORM_R][YUV_XFORM_Y]) + << (fmt->Rshift * 2) + | ((Uint64)(uint16)YUV_matrix[YUV_XFORM_G][YUV_XFORM_Y]) + << (fmt->Gshift * 2) + | ((Uint64)(uint16)YUV_matrix[YUV_XFORM_B][YUV_XFORM_Y]) + << (fmt->Bshift * 2); + + mmx_U_mult = ((Uint64)(uint16)YUV_matrix[YUV_XFORM_R][YUV_XFORM_U]) + << (fmt->Rshift * 2) + | ((Uint64)(uint16)YUV_matrix[YUV_XFORM_G][YUV_XFORM_U]) + << (fmt->Gshift * 2) + | ((Uint64)(uint16)YUV_matrix[YUV_XFORM_B][YUV_XFORM_U]) + << (fmt->Bshift * 2); + + mmx_V_mult = ((Uint64)(uint16)YUV_matrix[YUV_XFORM_R][YUV_XFORM_V]) + << (fmt->Rshift * 2) + | ((Uint64)(uint16)YUV_matrix[YUV_XFORM_G][YUV_XFORM_V]) + << (fmt->Gshift * 2) + | ((Uint64)(uint16)YUV_matrix[YUV_XFORM_B][YUV_XFORM_V]) + << (fmt->Bshift * 2); + + mmx_YUV_threshold = (SCALE_DIFFYUV_TY << 16) | (SCALE_DIFFYUV_TU << 8) + | SCALE_DIFFYUV_TV; +} + + +// Nearest Neighbor scaling to 2x +// void Scale_MMX_Nearest (SDL_Surface *src, +// SDL_Surface *dst, SDL_Rect *r) + +#include "nearest2x.c" + + +// Bilinear scaling to 2x +// void Scale_MMX_BilinearFilter (SDL_Surface *src, +// SDL_Surface *dst, SDL_Rect *r) + +#include "bilinear2x.c" + + +// Advanced Biadapt scaling to 2x +// void Scale_MMX_BiAdaptAdvFilter (SDL_Surface *src, +// SDL_Surface *dst, SDL_Rect *r) + +#include "biadv2x.c" + + +// Triscan scaling to 2x +// derivative of 'scale2x' -- scale2x.sf.net +// void Scale_MMX_TriScanFilter (SDL_Surface *src, +// SDL_Surface *dst, SDL_Rect *r) + +#include "triscan2x.c" + +// Hq2x scaling +// (adapted from 'hq2x' by Maxim Stepin -- www.hiend3d.com/hq2x.html) +// void Scale_MMX_HqFilter (SDL_Surface *src, +// SDL_Surface *dst, SDL_Rect *r) + +#include "hq2x.c" + + +#endif /* MMX_ASM */ + diff --git a/src/libs/graphics/sdl/2xscalers_mmx.h b/src/libs/graphics/sdl/2xscalers_mmx.h new file mode 100644 index 0000000..c8dba32 --- /dev/null +++ b/src/libs/graphics/sdl/2xscalers_mmx.h @@ -0,0 +1,56 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef LIBS_GRAPHICS_SDL_2XSCALERS_MMX_H_ +#define LIBS_GRAPHICS_SDL_2XSCALERS_MMX_H_ + +// MMX versions +void Scale_MMX_PrepPlatform (const SDL_PixelFormat* fmt); + +void Scale_MMX_Nearest (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r); +void Scale_MMX_BilinearFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r); +void Scale_MMX_BiAdaptAdvFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r); +void Scale_MMX_TriScanFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r); +void Scale_MMX_HqFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r); + +extern const Scale_FuncDef_t Scale_MMX_Functions[]; + + +// SSE (Intel)/MMX Ext (Athlon) versions +void Scale_SSE_PrepPlatform (const SDL_PixelFormat* fmt); + +void Scale_SSE_Nearest (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r); +void Scale_SSE_BilinearFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r); +void Scale_SSE_BiAdaptAdvFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r); +void Scale_SSE_TriScanFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r); +void Scale_SSE_HqFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r); + +extern const Scale_FuncDef_t Scale_SSE_Functions[]; + + +// 3DNow (AMD K6/Athlon) versions +void Scale_3DNow_PrepPlatform (const SDL_PixelFormat* fmt); + +void Scale_3DNow_Nearest (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r); +void Scale_3DNow_BilinearFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r); +void Scale_3DNow_BiAdaptAdvFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r); +void Scale_3DNow_TriScanFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r); +void Scale_3DNow_HqFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r); + +extern const Scale_FuncDef_t Scale_3DNow_Functions[]; + + +#endif /* LIBS_GRAPHICS_SDL_2XSCALERS_MMX_H_ */ diff --git a/src/libs/graphics/sdl/2xscalers_sse.c b/src/libs/graphics/sdl/2xscalers_sse.c new file mode 100644 index 0000000..a089744 --- /dev/null +++ b/src/libs/graphics/sdl/2xscalers_sse.c @@ -0,0 +1,100 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "port.h" +#include "libs/platform.h" + +#if defined(MMX_ASM) + +#include "libs/graphics/sdl/sdl_common.h" +#include "types.h" +#include "scalers.h" +#include "scaleint.h" +#include "2xscalers.h" +#include "2xscalers_mmx.h" + +// SSE name for all functions +#undef SCALE_ +#define SCALE_(name) Scale ## _SSE_ ## name + +// Tell them which opcodes we want to support +#define USE_MOVNTQ +#define USE_PREFETCH INTEL_PREFETCH +#define USE_PSADBW +// Bring in inline asm functions +#include "scalemmx.h" + + +// Scaler function lookup table +// +const Scale_FuncDef_t +Scale_SSE_Functions[] = +{ + {TFB_GFXFLAGS_SCALE_BILINEAR, Scale_SSE_BilinearFilter}, + {TFB_GFXFLAGS_SCALE_BIADAPT, Scale_BiAdaptFilter}, + {TFB_GFXFLAGS_SCALE_BIADAPTADV, Scale_SSE_BiAdaptAdvFilter}, + {TFB_GFXFLAGS_SCALE_TRISCAN, Scale_SSE_TriScanFilter}, + {TFB_GFXFLAGS_SCALE_HQXX, Scale_MMX_HqFilter}, + // Default + {0, Scale_SSE_Nearest} +}; + + +void +Scale_SSE_PrepPlatform (const SDL_PixelFormat* fmt) +{ + Scale_MMX_PrepPlatform (fmt); +} + +// Nearest Neighbor scaling to 2x +// void Scale_SSE_Nearest (SDL_Surface *src, +// SDL_Surface *dst, SDL_Rect *r) + +#include "nearest2x.c" + + +// Bilinear scaling to 2x +// void Scale_SSE_BilinearFilter (SDL_Surface *src, +// SDL_Surface *dst, SDL_Rect *r) + +#include "bilinear2x.c" + + +// Advanced Biadapt scaling to 2x +// void Scale_SSE_BiAdaptAdvFilter (SDL_Surface *src, +// SDL_Surface *dst, SDL_Rect *r) + +#include "biadv2x.c" + + +// Triscan scaling to 2x +// derivative of scale2x -- scale2x.sf.net +// void Scale_SSE_TriScanFilter (SDL_Surface *src, +// SDL_Surface *dst, SDL_Rect *r) + +#include "triscan2x.c" + +#if 0 && NO_IMPROVEMENT +// Hq2x scaling +// (adapted from 'hq2x' by Maxim Stepin -- www.hiend3d.com/hq2x.html) +// void Scale_SSE_HqFilter (SDL_Surface *src, +// SDL_Surface *dst, SDL_Rect *r) + +#include "hq2x.c" +#endif + +#endif /* MMX_ASM */ + diff --git a/src/libs/graphics/sdl/Makeinfo b/src/libs/graphics/sdl/Makeinfo new file mode 100644 index 0000000..f940b76 --- /dev/null +++ b/src/libs/graphics/sdl/Makeinfo @@ -0,0 +1,9 @@ +uqm_CFILES="opengl.c palette.c primitives.c pure.c sdl2_pure.c + sdl_common.c sdl1_common.c sdl2_common.c + scalers.c 2xscalers.c + 2xscalers_mmx.c 2xscalers_sse.c 2xscalers_3dnow.c + nearest2x.c bilinear2x.c biadv2x.c triscan2x.c hq2x.c + canvas.c png2sdl.c sdluio.c rotozoom.c" +uqm_HFILES="2xscalers.h 2xscalers_mmx.h opengl.h palette.h png2sdl.h + primitives.h pure.h rotozoom.h scaleint.h scalemmx.h + scalers.h sdl_common.h sdluio.h" diff --git a/src/libs/graphics/sdl/biadv2x.c b/src/libs/graphics/sdl/biadv2x.c new file mode 100644 index 0000000..b38cdf7 --- /dev/null +++ b/src/libs/graphics/sdl/biadv2x.c @@ -0,0 +1,532 @@ +/* + * Portions Copyright (C) 2003-2005 Alex Volkov (codepro@usa.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +// Core algorithm of the Advanced BiAdaptive screen scaler +// Template +// When this file is built standalone is produces a plain C version +// Also #included by 2xscalers_mmx.c for an MMX version + +#include "libs/graphics/sdl/sdl_common.h" +#include "types.h" +#include "scalers.h" +#include "scaleint.h" +#include "2xscalers.h" + + +// Advanced biadapt scaling to 2x +// The name expands to either +// Scale_BiAdaptAdvFilter (for plain C) or +// Scale_MMX_BiAdaptAdvFilter (for MMX) +// [others when platforms are added] +void +SCALE_(BiAdaptAdvFilter) (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r) +{ + int x, y; + const int w = src->w, h = src->h; + int xend, yend; + int dsrc, ddst; + SDL_Rect *region = r; + SDL_Rect limits; + SDL_PixelFormat *fmt = dst->format; + const int sp = src->pitch, dp = dst->pitch; + const int bpp = fmt->BytesPerPixel; + const int slen = sp / bpp, dlen = dp / bpp; + // for clarity purposes, the 'pixels' array here is transposed + Uint32 pixels[4][4]; + static int resolve_coord[][2] = + { + {0, -1}, {1, -1}, { 2, 0}, { 2, 1}, + {1, 2}, {0, 2}, {-1, 1}, {-1, 0}, + {100, 100} // term + }; + Uint32 *src_p = (Uint32 *)src->pixels; + Uint32 *dst_p = (Uint32 *)dst->pixels; + + // these macros are for clarity; they make the current pixel (0,0) + // and allow to access pixels in all directions + #define PIX(x, y) (pixels[1 + (x)][1 + (y)]) + #define SRC(x, y) (src_p + (x) + ((y) * slen)) + // commonly used operations, for clarity also + // others are defined at their respective bpp levels + #define BIADAPT_RGBHIGH 8000 + #define BIADAPT_YUVLOW 30 + #define BIADAPT_YUVMED 70 + #define BIADAPT_YUVHIGH 130 + + // high tolerance pixel comparison + #define BIADAPT_CMPRGB_HIGH(p1, p2) \ + (p1 == p2 || SCALE_CMPRGB (p1, p2) <= BIADAPT_RGBHIGH) + + // low tolerance pixel comparison + #define BIADAPT_CMPYUV_LOW(p1, p2) \ + (p1 == p2 || SCALE_CMPYUV (p1, p2, BIADAPT_YUVLOW)) + // medium tolerance pixel comparison + #define BIADAPT_CMPYUV_MED(p1, p2) \ + (p1 == p2 || SCALE_CMPYUV (p1, p2, BIADAPT_YUVMED)) + // high tolerance pixel comparison + #define BIADAPT_CMPYUV_HIGH(p1, p2) \ + (p1 == p2 || SCALE_CMPYUV (p1, p2, BIADAPT_YUVHIGH)) + + SCALE_(PlatInit) (); + + // expand updated region if necessary + // pixels neighbooring the updated region may + // change as a result of updates + limits.x = 0; + limits.y = 0; + limits.w = src->w; + limits.h = src->h; + Scale_ExpandRect (region, 2, &limits); + + xend = region->x + region->w; + yend = region->y + region->h; + dsrc = slen - region->w; + ddst = (dlen - region->w) * 2; + + #define SCALE_GETPIX(p) ( *(Uint32 *)(p) ) + #define SCALE_SETPIX(p, c) ( *(Uint32 *)(p) = (c) ) + + // move ptrs to the first updated pixel + src_p += slen * region->y + region->x; + dst_p += (dlen * region->y + region->x) * 2; + + for (y = region->y; y < yend; ++y, dst_p += ddst, src_p += dsrc) + { + for (x = region->x; x < xend; ++x, ++src_p, ++dst_p) + { + // pixel equality counter + int cmatch; + + // most pixels will fall into 'all 4 equal' + // pattern, so we check it first + cmatch = 0; + + PIX (0, 0) = SCALE_GETPIX (SRC (0, 0)); + + SCALE_SETPIX (dst_p, PIX (0, 0)); + + if (y + 1 < h) + { + // check pixel below the current one + PIX (0, 1) = SCALE_GETPIX (SRC (0, 1)); + + if (PIX (0, 0) == PIX (0, 1)) + { + SCALE_SETPIX (dst_p + dlen, PIX (0, 0)); + cmatch |= 1; + } + } + else + { + // last pixel in column - propagate + PIX (0, 1) = PIX (0, 0); + SCALE_SETPIX (dst_p + dlen, PIX (0, 0)); + cmatch |= 1; + + } + + if (x + 1 < w) + { + // check pixel to the right from the current one + PIX (1, 0) = SCALE_GETPIX (SRC (1, 0)); + + if (PIX (0, 0) == PIX (1, 0)) + { + SCALE_SETPIX (dst_p + 1, PIX (0, 0)); + cmatch |= 2; + } + } + else + { + // last pixel in row - propagate + PIX (1, 0) = PIX (0, 0); + SCALE_SETPIX (dst_p + 1, PIX (0, 0)); + cmatch |= 2; + } + + if (cmatch == 3) + { + if (y + 1 >= h || x + 1 >= w) + { + // last pixel in row/column and nearest + // neighboor is identical + dst_p++; + SCALE_SETPIX (dst_p + dlen, PIX (0, 0)); + continue; + } + + // check pixel to the bottom-right + PIX (1, 1) = SCALE_GETPIX (SRC (1, 1)); + + if (PIX (0, 0) == PIX (1, 1)) + { + // all 4 are equal - propagate + dst_p++; + SCALE_SETPIX (dst_p + dlen, PIX (0, 0)); + continue; + } + } + + // some neighboors are different, lets check them + + if (x > 0) + PIX (-1, 0) = SCALE_GETPIX (SRC (-1, 0)); + else + PIX (-1, 0) = PIX (0, 0); + + if (x + 2 < w) + PIX (2, 0) = SCALE_GETPIX (SRC (2, 0)); + else + PIX (2, 0) = PIX (1, 0); + + if (y + 1 < h) + { + if (x > 0) + PIX (-1, 1) = SCALE_GETPIX (SRC (-1, 1)); + else + PIX (-1, 1) = PIX (0, 1); + + if (x + 2 < w) + { + PIX (1, 1) = SCALE_GETPIX (SRC (1, 1)); + PIX (2, 1) = SCALE_GETPIX (SRC (2, 1)); + } + else if (x + 1 < w) + { + PIX (1, 1) = SCALE_GETPIX (SRC (1, 1)); + PIX (2, 1) = PIX (1, 1); + } + else + { + PIX (1, 1) = PIX (0, 1); + PIX (2, 1) = PIX (0, 1); + } + } + else + { + // last pixel in column + PIX (-1, 1) = PIX (-1, 0); + PIX (1, 1) = PIX (1, 0); + PIX (2, 1) = PIX (2, 0); + } + + if (y + 2 < h) + { + PIX (0, 2) = SCALE_GETPIX (SRC (0, 2)); + + if (x > 0) + PIX (-1, 2) = SCALE_GETPIX (SRC (-1, 2)); + else + PIX (-1, 2) = PIX (0, 2); + + if (x + 2 < w) + { + PIX (1, 2) = SCALE_GETPIX (SRC (1, 2)); + PIX (2, 2) = SCALE_GETPIX (SRC (2, 2)); + } + else if (x + 1 < w) + { + PIX (1, 2) = SCALE_GETPIX (SRC (1, 2)); + PIX (2, 2) = PIX (1, 2); + } + else + { + PIX (1, 2) = PIX (0, 2); + PIX (2, 2) = PIX (0, 2); + } + } + else + { + // last pixel in column + PIX (-1, 2) = PIX (-1, 1); + PIX (0, 2) = PIX (0, 1); + PIX (1, 2) = PIX (1, 1); + PIX (2, 2) = PIX (2, 1); + } + + if (y > 0) + { + PIX (0, -1) = SCALE_GETPIX (SRC (0, -1)); + + if (x > 0) + PIX (-1, -1) = SCALE_GETPIX (SRC (-1, -1)); + else + PIX (-1, -1) = PIX (0, -1); + + if (x + 2 < w) + { + PIX (1, -1) = SCALE_GETPIX (SRC (1, -1)); + PIX (2, -1) = SCALE_GETPIX (SRC (2, -1)); + } + else if (x + 1 < w) + { + PIX (1, -1) = SCALE_GETPIX (SRC (1, -1)); + PIX (2, -1) = PIX (1, -1); + } + else + { + PIX (1, -1) = PIX (0, -1); + PIX (2, -1) = PIX (0, -1); + } + } + else + { + PIX (-1, -1) = PIX (-1, 0); + PIX (0, -1) = PIX (0, 0); + PIX (1, -1) = PIX (1, 0); + PIX (2, -1) = PIX (2, 0); + } + + // check pixel below the current one + if (!(cmatch & 1)) + { + if (SCALE_CMPYUV (PIX (0, 0), PIX (0, 1), BIADAPT_YUVLOW)) + { + SCALE_SETPIX (dst_p + dlen, Scale_Blend_11 ( + PIX (0, 0), PIX (0, 1)) + ); + cmatch |= 1; + } + // detect a 2:1 line going across the current pixel + else if ( (PIX (0, 0) == PIX (-1, 0) + && PIX (0, 0) == PIX (1, 1) + && PIX (0, 0) == PIX (2, 1) && + + ((!BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (-1, -1)) + && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (0, -1)) + && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (1, 0)) + && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (2, 0))) || + (!BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (-1, 1)) + && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (1, 2)) + && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (2, 2))))) || + + (PIX (0, 0) == PIX (1, 0) + && PIX (0, 0) == PIX (-1, 1) + && PIX (0, 0) == PIX (2, -1) && + + ((!BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (-1, 0)) + && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (0, -1)) + && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (1, -1))) || + (!BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (-1, 2)) + && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (1, 1)) + && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (2, 0))))) ) + { + SCALE_SETPIX (dst_p + dlen, PIX (0, 0)); + } + // detect a 2:1 line going across the pixel below current + else if ( (PIX (0, 1) == PIX (-1, 0) + && PIX (0, 1) == PIX (1, 1) + && PIX (0, 1) == PIX (2, 2) && + + ((!BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (-1, -1)) + && !BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (1, 0)) + && !BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (2, 1))) || + (!BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (-1, 1)) + && !BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (0, 2)) + && !BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (1, 2))))) || + + (PIX (0, 1) == PIX (1, 0) + && PIX (0, 1) == PIX (-1, 1) + && PIX (0, 1) == PIX (2, 0) && + + ((!BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (-1, 0)) + && !BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (1, -1)) + && !BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (2, -1))) || + (!BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (-1, 2)) + && !BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (0, 2)) + && !BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (1, 1)) + && !BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (2, 1))))) ) + { + SCALE_SETPIX (dst_p + dlen, PIX (0, 1)); + } + else + SCALE_SETPIX (dst_p + dlen, Scale_Blend_11 ( + PIX (0, 0), PIX (0, 1)) + ); + } + + dst_p++; + + // check pixel to the right from the current one + if (!(cmatch & 2)) + { + if (SCALE_CMPYUV (PIX (0, 0), PIX (1, 0), BIADAPT_YUVLOW)) + { + SCALE_SETPIX (dst_p, Scale_Blend_11 ( + PIX (0, 0), PIX (1, 0)) + ); + cmatch |= 2; + } + // detect a 1:2 line going across the current pixel + else if ( (PIX (0, 0) == PIX (1, -1) + && PIX (0, 0) == PIX (0, 1) + && PIX (0, 0) == PIX (-1, 2) && + + ((!BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (0, -1)) + && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (-1, 0)) + && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (-1, 1))) || + (!BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (2, -1)) + && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (1, 1)) + && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (0, 2))))) || + + (PIX (0, 0) == PIX (0, -1) + && PIX (0, 0) == PIX (1, 1) + && PIX (0, 0) == PIX (1, 2) && + + ((!BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (-1, -1)) + && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (-1, 0)) + && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (0, 1)) + && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (0, 2))) || + (!BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (1, -1)) + && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (2, 1)) + && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (2, 2))))) ) + + { + SCALE_SETPIX (dst_p, PIX (0, 0)); + } + // detect a 1:2 line going across the pixel to the right + else if ( (PIX (1, 0) == PIX (1, -1) + && PIX (1, 0) == PIX (0, 1) + && PIX (1, 0) == PIX (0, 2) && + + ((!BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (0, -1)) + && !BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (-1, 1)) + && !BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (-1, 2))) || + (!BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (2, -1)) + && !BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (2, 0)) + && !BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (1, 1)) + && !BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (1, 2))))) || + + (PIX (1, 0) == PIX (0, -1) + && PIX (1, 0) == PIX (1, 1) + && PIX (1, 0) == PIX (2, 2) && + + ((!BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (-1, -1)) + && !BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (0, 1)) + && !BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (1, 2))) || + (!BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (1, -1)) + && !BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (2, 0)) + && !BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (2, 1))))) ) + { + SCALE_SETPIX (dst_p, PIX (1, 0)); + } + else + SCALE_SETPIX (dst_p, Scale_Blend_11 ( + PIX (0, 0), PIX (1, 0)) + ); + } + + if (PIX (0, 0) == PIX (1, 1) && PIX (1, 0) == PIX (0, 1)) + { + // diagonals are equal + int *coord; + int cl, cr; + Uint32 clr; + + // both pairs are equal, have to resolve the pixel + // race; we try detecting which color is + // the background by looking for a line or an edge + // examine 8 pixels surrounding the current quad + + cl = cr = 2; + for (coord = resolve_coord[0]; *coord < 100; coord += 2) + { + clr = PIX (coord[0], coord[1]); + + if (BIADAPT_CMPYUV_MED (clr, PIX (0, 0))) + cl++; + else if (BIADAPT_CMPYUV_MED (clr, PIX (1, 0))) + cr++; + } + + // least count wins + if (cl > cr) + clr = PIX (1, 0); + else if (cr > cl) + clr = PIX (0, 0); + else + clr = Scale_Blend_11 (PIX (0, 0), PIX (1, 0)); + + SCALE_SETPIX (dst_p + dlen, clr); + continue; + } + + if (cmatch == 3 + || (BIADAPT_CMPYUV_LOW (PIX (1, 0), PIX (0, 1)) + && BIADAPT_CMPYUV_LOW (PIX (1, 0), PIX (1, 1)))) + { + SCALE_SETPIX (dst_p + dlen, Scale_Blend_11 ( + PIX (0, 1), PIX (1, 0)) + ); + continue; + } + else if (cmatch && BIADAPT_CMPYUV_LOW (PIX (0, 0), PIX (1, 1))) + { + SCALE_SETPIX (dst_p + dlen, Scale_Blend_11 ( + PIX (0, 0), PIX (1, 1)) + ); + continue; + } + + // check pixel to the bottom-right + if (BIADAPT_CMPYUV_HIGH (PIX (0, 0), PIX (1, 1)) + && BIADAPT_CMPYUV_HIGH (PIX (1, 0), PIX (0, 1))) + { + if (SCALE_GETY (PIX (0, 0)) > SCALE_GETY (PIX (1, 0))) + { + SCALE_SETPIX (dst_p + dlen, Scale_Blend_11 ( + PIX (0, 0), PIX (1, 1)) + ); + } + else + { + SCALE_SETPIX (dst_p + dlen, Scale_Blend_11 ( + PIX (1, 0), PIX (0, 1)) + ); + } + } + else if (BIADAPT_CMPYUV_HIGH (PIX (0, 0), PIX (1, 1))) + { + // main diagonal is same color + // use its value + SCALE_SETPIX (dst_p + dlen, Scale_Blend_11 ( + PIX (0, 0), PIX (1, 1)) + ); + } + else if (BIADAPT_CMPYUV_HIGH (PIX (1, 0), PIX (0, 1))) + { + // 2nd diagonal is same color + // use its value + SCALE_SETPIX (dst_p + dlen, Scale_Blend_11 ( + PIX (1, 0), PIX (0, 1)) + ); + } + else + { + // blend all 4 + SCALE_SETPIX (dst_p + dlen, Scale_Blend_1111 ( + PIX (0, 0), PIX (0, 1), + PIX (1, 0), PIX (1, 1) + )); + } + } + } + + SCALE_(PlatDone) (); +} + diff --git a/src/libs/graphics/sdl/bilinear2x.c b/src/libs/graphics/sdl/bilinear2x.c new file mode 100644 index 0000000..a12eb93 --- /dev/null +++ b/src/libs/graphics/sdl/bilinear2x.c @@ -0,0 +1,112 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +// Core algorithm of the BiLinear screen scaler +// Template +// When this file is built standalone is produces a plain C version +// Also #included by 2xscalers_mmx.c for an MMX version + +#include "libs/graphics/sdl/sdl_common.h" +#include "types.h" +#include "scalers.h" +#include "scaleint.h" +#include "2xscalers.h" + + +// Bilinear scaling to 2x +// The name expands to either +// Scale_BilinearFilter (for plain C) or +// Scale_MMX_BilinearFilter (for MMX) +// Scale_SSE_BilinearFilter (for SSE) +// [others when platforms are added] +void +SCALE_(BilinearFilter) (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r) +{ + int x, y; + const int w = src->w, h = src->h; + int xend, yend; + int dsrc, ddst; + SDL_Rect *region = r; + SDL_Rect limits; + SDL_PixelFormat *fmt = dst->format; + const int pitch = src->pitch, dp = dst->pitch; + const int bpp = fmt->BytesPerPixel; + const int len = pitch / bpp, dlen = dp / bpp; + Uint32 p[4]; // influential pixels array + Uint32 *srow0 = (Uint32 *) src->pixels; + Uint32 *dst_p = (Uint32 *) dst->pixels; + + SCALE_(PlatInit) (); + + // expand updated region if necessary + // pixels neighbooring the updated region may + // change as a result of updates + limits.x = 0; + limits.y = 0; + limits.w = w; + limits.h = h; + Scale_ExpandRect (region, 1, &limits); + + xend = region->x + region->w; + yend = region->y + region->h; + dsrc = len - region->w; + ddst = (dlen - region->w) * 2; + + // move ptrs to the first updated pixel + srow0 += len * region->y + region->x; + dst_p += (dlen * region->y + region->x) * 2; + + for (y = region->y; y < yend; ++y, dst_p += ddst, srow0 += dsrc) + { + Uint32 *srow1; + + SCALE_(Prefetch) (srow0 + 16); + SCALE_(Prefetch) (srow0 + 32); + + if (y < h - 1) + srow1 = srow0 + len; + else + srow1 = srow0; + + SCALE_(Prefetch) (srow1 + 16); + SCALE_(Prefetch) (srow1 + 32); + + for (x = region->x; x < xend; ++x, ++srow0, ++srow1, dst_p += 2) + { + if (x < w - 1) + { // can blend directly from pixels + SCALE_BILINEAR_BLEND4 (srow0, srow1, dst_p, dlen); + } + else + { // need to make temp pixel rows + p[0] = srow0[0]; + p[1] = p[0]; + p[2] = srow1[0]; + p[3] = p[2]; + + SCALE_BILINEAR_BLEND4 (&p[0], &p[2], dst_p, dlen); + } + } + + SCALE_(Prefetch) (srow0 + dsrc); + SCALE_(Prefetch) (srow0 + dsrc + 16); + SCALE_(Prefetch) (srow1 + dsrc); + SCALE_(Prefetch) (srow1 + dsrc + 16); + } + + SCALE_(PlatDone) (); +} + diff --git a/src/libs/graphics/sdl/canvas.c b/src/libs/graphics/sdl/canvas.c new file mode 100644 index 0000000..ad7024d --- /dev/null +++ b/src/libs/graphics/sdl/canvas.c @@ -0,0 +1,2176 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "port.h" +#include <string.h> + // for memcpy() + +#include SDL_INCLUDE(SDL.h) +#include "sdl_common.h" +#include "libs/graphics/gfx_common.h" +#include "libs/graphics/tfb_draw.h" +#include "libs/graphics/cmap.h" +#include "libs/log.h" +#include "libs/memlib.h" +#include "primitives.h" +#include "palette.h" +#include "sdluio.h" +#include "rotozoom.h" +#include "options.h" +#include "types.h" + +typedef SDL_Surface *NativeCanvas; + +// BYTE x BYTE weight (mult >> 8) table +static Uint8 btable[256][256]; + +void +TFB_DrawCanvas_Initialize (void) +{ + int i, j; + for (i = 0; i < 256; ++i) + for (j = 0; j < 256; ++j) + btable[j][i] = (j * i + 0x80) >> 8; + // need error correction here +} + +const char * +TFB_DrawCanvas_GetError (void) +{ + const char *err = SDL_GetError (); + // TODO: Should we call SDL_ClearError() here so that it is not + // returned again later? + return err; +} + +static void +checkPrimitiveMode (SDL_Surface *surf, Color *color, DrawMode *mode) +{ + const SDL_PixelFormat *fmt = surf->format; + // Special case: We support DRAW_ALPHA mode to non-alpha surfaces + // for primitives via Color.a + if (mode->kind == DRAW_REPLACE && fmt->Amask == 0 && color->a != 0xff) + { + mode->kind = DRAW_ALPHA; + mode->factor = color->a; + color->a = 0xff; + } +} + +void +TFB_DrawCanvas_Line (int x1, int y1, int x2, int y2, Color color, + DrawMode mode, TFB_Canvas target) +{ + SDL_Surface *dst = target; + SDL_PixelFormat *fmt = dst->format; + Uint32 sdlColor; + RenderPixelFn plotFn; + + checkPrimitiveMode (dst, &color, &mode); + sdlColor = SDL_MapRGBA (fmt, color.r, color.g, color.b, color.a); + + plotFn = renderpixel_for (target, mode.kind); + if (!plotFn) + { + log_add (log_Warning, "ERROR: TFB_DrawCanvas_Line " + "unsupported draw mode (%d)", (int)mode.kind); + return; + } + + SDL_LockSurface (dst); + line_prim (x1, y1, x2, y2, sdlColor, plotFn, mode.factor, dst); + SDL_UnlockSurface (dst); +} + +void +TFB_DrawCanvas_Rect (RECT *rect, Color color, DrawMode mode, TFB_Canvas target) +{ + SDL_Surface *dst = target; + SDL_PixelFormat *fmt = dst->format; + Uint32 sdlColor; + SDL_Rect sr; + sr.x = rect->corner.x; + sr.y = rect->corner.y; + sr.w = rect->extent.width; + sr.h = rect->extent.height; + + checkPrimitiveMode (dst, &color, &mode); + sdlColor = SDL_MapRGBA (fmt, color.r, color.g, color.b, color.a); + + if (mode.kind == DRAW_REPLACE) + { // Standard SDL fillrect rendering + Uint32 colorkey; + if (fmt->Amask && (TFB_GetColorKey (dst, &colorkey) == 0)) + { // special case -- alpha surface with colorkey + // colorkey rects are transparent + if ((sdlColor & ~fmt->Amask) == (colorkey & ~fmt->Amask)) + sdlColor &= ~fmt->Amask; // make transparent + } + SDL_FillRect (dst, &sr, sdlColor); + } + else + { // Custom fillrect rendering + RenderPixelFn plotFn = renderpixel_for (target, mode.kind); + if (!plotFn) + { + log_add (log_Warning, "ERROR: TFB_DrawCanvas_Rect " + "unsupported draw mode (%d)", (int)mode.kind); + return; + } + + SDL_LockSurface (dst); + fillrect_prim (sr, sdlColor, plotFn, mode.factor, dst); + SDL_UnlockSurface (dst); + } +} + +static void +TFB_DrawCanvas_Blit (SDL_Surface *src, SDL_Rect *src_r, + SDL_Surface *dst, SDL_Rect *dst_r, DrawMode mode) +{ + SDL_PixelFormat *srcfmt = src->format; + + if (mode.kind == DRAW_REPLACE) + { // Standard SDL simple blit + SDL_BlitSurface (src, src_r, dst, dst_r); + } + else if (mode.kind == DRAW_ALPHA && srcfmt->Amask == 0) + { // Standard SDL surface-alpha blit + // Note that surface alpha and per-pixel alpha cannot work + // at the same time, which is why the Amask test + int hasAlpha = TFB_HasSurfaceAlphaMod (src); + assert (!hasAlpha); + // Set surface alpha temporarily + TFB_SetSurfaceAlphaMod (src, mode.factor); + SDL_BlitSurface (src, src_r, dst, dst_r); + TFB_DisableSurfaceAlphaMod (src); + } + else + { // Custom blit + SDL_Rect loc_src_r, loc_dst_r; + RenderPixelFn plotFn = renderpixel_for (dst, mode.kind); + if (!plotFn) + { + log_add (log_Warning, "ERROR: TFB_DrawCanvas_Blit " + "unsupported draw mode (%d)", (int)mode.kind); + return; + } + + if (!src_r) + { // blit whole image; generate rect + loc_src_r.x = 0; + loc_src_r.y = 0; + loc_src_r.w = src->w; + loc_src_r.h = src->h; + src_r = &loc_src_r; + } + + if (!dst_r) + { // blit to 0,0; generate rect + loc_dst_r.x = 0; + loc_dst_r.y = 0; + loc_dst_r.w = dst->w; + loc_dst_r.h = dst->h; + dst_r = &loc_dst_r; + } + + SDL_LockSurface (dst); + blt_prim (src, *src_r, plotFn, mode.factor, dst, *dst_r); + SDL_UnlockSurface (dst); + } +} + +// XXX: If a colormap is passed in, it has to have been acquired via +// TFB_GetColorMap(). We release the colormap at the end. +void +TFB_DrawCanvas_Image (TFB_Image *img, int x, int y, int scale, + int scaleMode, TFB_ColorMap *cmap, DrawMode mode, TFB_Canvas target) +{ + SDL_Rect srcRect, targetRect, *pSrcRect; + SDL_Surface *surf; + SDL_Palette *NormalPal; + + if (img == 0) + { + log_add (log_Warning, + "ERROR: TFB_DrawCanvas_Image passed null image ptr"); + return; + } + + LockMutex (img->mutex); + + NormalPal = ((SDL_Surface *)img->NormalImg)->format->palette; + // only set the new palette if it changed + if (NormalPal && cmap && img->colormap_version != cmap->version) + TFB_SetColors (img->NormalImg, cmap->palette->colors, 0, 256); + + if (scale != 0 && scale != GSCALE_IDENTITY) + { + if (scaleMode == TFB_SCALE_TRILINEAR && img->MipmapImg) + { + // only set the new palette if it changed + if (TFB_DrawCanvas_IsPaletted (img->MipmapImg) + && cmap && img->colormap_version != cmap->version) + TFB_SetColors (img->MipmapImg, cmap->palette->colors, 0, 256); + } + else if (scaleMode == TFB_SCALE_TRILINEAR && !img->MipmapImg) + { // Do bilinear scaling instead when mipmap is unavailable + scaleMode = TFB_SCALE_BILINEAR; + } + + TFB_DrawImage_FixScaling (img, scale, scaleMode); + surf = img->ScaledImg; + if (TFB_DrawCanvas_IsPaletted (surf)) + { + // We may only get a paletted scaled image if the source is + // paletted. Currently, all scaling targets are truecolor. + assert (NormalPal && NormalPal->colors); + TFB_SetColors (surf, NormalPal->colors, 0, NormalPal->ncolors); + } + + srcRect.x = 0; + srcRect.y = 0; + srcRect.w = img->extent.width; + srcRect.h = img->extent.height; + pSrcRect = &srcRect; + + targetRect.x = x - img->last_scale_hs.x; + targetRect.y = y - img->last_scale_hs.y; + } + else + { + surf = img->NormalImg; + pSrcRect = NULL; + + targetRect.x = x - img->NormalHs.x; + targetRect.y = y - img->NormalHs.y; + } + + if (cmap) + { + img->colormap_version = cmap->version; + // TODO: Technically, this is not a proper place to release a + // colormap. As it stands now, the colormap must have been + // addrefed when passed to us. + TFB_ReturnColorMap (cmap); + } + + TFB_DrawCanvas_Blit (surf, pSrcRect, target, &targetRect, mode); + UnlockMutex (img->mutex); +} + +// Assumes the source and destination surfaces are in the same format +static void +TFB_DrawCanvas_Fill (SDL_Surface *src, Uint32 fillcolor, SDL_Surface *dst) +{ + const SDL_PixelFormat *srcfmt = src->format; + SDL_PixelFormat *dstfmt = dst->format; + const int width = src->w; + const int height = src->h; + const int bpp = dstfmt->BytesPerPixel; + const int sp = src->pitch, dp = dst->pitch; + const int slen = sp / bpp, dlen = dp / bpp; + const int dsrc = slen - width, ddst = dlen - width; + Uint32 *src_p; + Uint32 *dst_p; + int x, y; + Uint32 srckey = 0, dstkey = 0; // 0 means alpha=0 too + Uint32 amask = srcfmt->Amask; + int alpha = (fillcolor & amask) >> srcfmt->Ashift; + + if (srcfmt->BytesPerPixel != 4 || dstfmt->BytesPerPixel != 4) + { + log_add (log_Warning, "TFB_DrawCanvas_Fill: Unsupported surface " + "formats: %d bytes/pixel source, %d bytes/pixel destination", + (int)srcfmt->BytesPerPixel, (int)dstfmt->BytesPerPixel); + return; + } + + // Strip the alpha channel from fillcolor because we process the + // alpha separately + fillcolor &= ~amask; + + SDL_LockSurface(src); + SDL_LockSurface(dst); + + src_p = (Uint32 *)src->pixels; + dst_p = (Uint32 *)dst->pixels; + + if (dstkey == fillcolor) + { // color collision, must switch colorkey + // new colorkey is grey (1/2,1/2,1/2) + dstkey = SDL_MapRGBA (dstfmt, 127, 127, 127, 0); + } + + if (srcfmt->Amask) + { // alpha-based fill + for (y = 0; y < height; ++y, dst_p += ddst, src_p += dsrc) + { + for (x = 0; x < width; ++x, ++src_p, ++dst_p) + { + Uint32 p = *src_p & amask; + + if (p == 0) + { // fully transparent pixel + *dst_p = dstkey; + } + else if (alpha == 0xff) + { // not for DRAW_ALPHA; use alpha chan directly + *dst_p = p | fillcolor; + } + else + { // for DRAW_ALPHA; modulate the alpha channel + p >>= srcfmt->Ashift; + p = (p * alpha) >> 8; + p <<= srcfmt->Ashift; + *dst_p = p | fillcolor; + } + } + } + } + else if (TFB_GetColorKey (src, &srckey) == 0) + { // colorkey-based fill + + for (y = 0; y < height; ++y, dst_p += ddst, src_p += dsrc) + { + for (x = 0; x < width; ++x, ++src_p, ++dst_p) + { + Uint32 p = *src_p; + + *dst_p = (p == srckey) ? dstkey : fillcolor; + } + } + } + else + { + log_add (log_Warning, "TFB_DrawCanvas_Fill: Unsupported source" + "surface format\n"); + } + + SDL_UnlockSurface(dst); + SDL_UnlockSurface(src); + + // save the colorkey (dynamic image -- not using RLE coding here) + TFB_SetColorKey (dst, dstkey, 0); + // if the filled surface is RGBA, colorkey will only be used + // when SDL_SRCALPHA flag is cleared. this allows us to blit + // the surface in different ways to diff targets +} + +void +TFB_DrawCanvas_FilledImage (TFB_Image *img, int x, int y, int scale, + int scaleMode, Color color, DrawMode mode, TFB_Canvas target) +{ + SDL_Surface *dst = target; + SDL_Rect srcRect, targetRect, *pSrcRect; + SDL_Surface *surf; + SDL_Palette *palette; + int i; + bool force_fill = false; + + if (img == 0) + { + log_add (log_Warning, + "ERROR: TFB_DrawCanvas_FilledImage passed null image ptr"); + return; + } + + checkPrimitiveMode (dst, &color, &mode); + + LockMutex (img->mutex); + + if (scale != 0 && scale != GSCALE_IDENTITY) + { + if (scaleMode == TFB_SCALE_TRILINEAR) + scaleMode = TFB_SCALE_BILINEAR; + // no point in trilinear for filled images + + if (scale != img->last_scale || scaleMode != img->last_scale_type) + force_fill = true; + + TFB_DrawImage_FixScaling (img, scale, scaleMode); + surf = img->ScaledImg; + srcRect.x = 0; + srcRect.y = 0; + srcRect.w = img->extent.width; + srcRect.h = img->extent.height; + pSrcRect = &srcRect; + + targetRect.x = x - img->last_scale_hs.x; + targetRect.y = y - img->last_scale_hs.y; + } + else + { + if (img->last_scale != 0) + { + // Make sure we remember that the last fill was from + // an unscaled image + force_fill = true; + img->last_scale = 0; + } + + surf = img->NormalImg; + pSrcRect = NULL; + + targetRect.x = x - img->NormalHs.x; + targetRect.y = y - img->NormalHs.y; + } + + palette = surf->format->palette; + if (palette) + { // set palette for fill-stamp + // Calling TFB_SetColors() results in an expensive src -> dst + // color-mapping operation for an SDL blit, following the call. + // We want to avoid that as much as possible. + + // TODO: generate a 32bpp filled image? + + SDL_Color colors[256]; + + colors[0] = ColorToNative (color); + for (i = 1; i < palette->ncolors; i++) + colors[i] = colors[0]; + + TFB_SetColors (surf, colors, 0, palette->ncolors); + // reflect the change in *actual* image palette + img->colormap_version--; + } + else + { // fill the non-transparent parts of the image with fillcolor + SDL_Surface *newfill = img->FilledImg; + SDL_PixelFormat *fillfmt; + + if (newfill && (newfill->w < surf->w || newfill->h < surf->h)) + { + TFB_DrawCanvas_Delete (newfill); + newfill = NULL; + } + + // prepare the filled image + if (!newfill) + { + newfill = SDL_CreateRGBSurface (SDL_SWSURFACE, + surf->w, surf->h, + surf->format->BitsPerPixel, + surf->format->Rmask, + surf->format->Gmask, + surf->format->Bmask, + surf->format->Amask); + force_fill = true; + } + fillfmt = newfill->format; + + if (force_fill || !sameColor (img->last_fill, color)) + { // image or fillcolor changed - regenerate + Uint32 fillColor; + + if (mode.kind == DRAW_ALPHA && fillfmt->Amask) + { // Per-pixel alpha and surface alpha will not work together + // We have to handle DRAW_ALPHA differently by modulating + // the surface alpha channel ourselves. + color.a = mode.factor; + mode.kind = DRAW_REPLACE; + } + else + { // Make sure we do not modulate the alpha channel + color.a = 0xff; + } + fillColor = SDL_MapRGBA (newfill->format, color.r, color.g, + color.b, color.a); + TFB_DrawCanvas_Fill (surf, fillColor, newfill); + // cache filled image if possible + img->last_fill = color; + } + + img->FilledImg = newfill; + surf = newfill; + } + + TFB_DrawCanvas_Blit (surf, pSrcRect, dst, &targetRect, mode); + UnlockMutex (img->mutex); +} + +void +TFB_DrawCanvas_FontChar (TFB_Char *fontChar, TFB_Image *backing, + int x, int y, DrawMode mode, TFB_Canvas target) +{ + SDL_Surface *dst = target; + SDL_Rect srcRect, targetRect; + SDL_Surface *surf; + int w, h; + + if (fontChar == 0) + { + log_add (log_Warning, "ERROR: " + "TFB_DrawCanvas_FontChar passed null char ptr"); + return; + } + if (backing == 0) + { + log_add (log_Warning, "ERROR: " + "TFB_DrawCanvas_FontChar passed null backing ptr"); + return; + } + + w = fontChar->extent.width; + h = fontChar->extent.height; + + LockMutex (backing->mutex); + + surf = backing->NormalImg; + if (surf->format->BytesPerPixel != 4 + || surf->w < w || surf->h < h) + { + log_add (log_Warning, "ERROR: " + "TFB_DrawCanvas_FontChar bad backing surface: %dx%dx%d; " + "char: %dx%d", + surf->w, surf->h, (int)surf->format->BytesPerPixel, w, h); + UnlockMutex (backing->mutex); + return; + } + + srcRect.x = 0; + srcRect.y = 0; + srcRect.w = fontChar->extent.width; + srcRect.h = fontChar->extent.height; + + targetRect.x = x - fontChar->HotSpot.x; + targetRect.y = y - fontChar->HotSpot.y; + + // transfer the alpha channel to the backing surface + SDL_LockSurface (surf); + { + int x, y; + const int sskip = fontChar->pitch - w; + const int dskip = (surf->pitch / 4) - w; + const Uint32 dmask = ~surf->format->Amask; + const int ashift = surf->format->Ashift; + Uint8 *src_p = fontChar->data; + Uint32 *dst_p = (Uint32 *)surf->pixels; + + if (mode.kind == DRAW_ALPHA) + { // Per-pixel alpha and surface alpha will not work together + // We have to handle DRAW_ALPHA differently by modulating + // the backing surface alpha channel ourselves. + // The existing backing surface alpha channel is ignored. + int alpha = mode.factor; + mode.kind = DRAW_REPLACE; + + for (y = 0; y < h; ++y, src_p += sskip, dst_p += dskip) + { + for (x = 0; x < w; ++x, ++src_p, ++dst_p) + { + Uint32 p = *dst_p & dmask; + Uint32 a = *src_p; + + // we use >> 8 instead of / 255, and it does not handle + // alpha == 255 correctly + if (alpha != 0xff) + { // modulate the alpha channel + a = (a * alpha) >> 8; + } + *dst_p = p | (a << ashift); + } + } + } + else /* if (mode.kind != DRAW_ALPHA) */ + { // Transfer the alpha channel to the backing surface + // DRAW_REPLACE + Color.a is NOT supported right now + for (y = 0; y < h; ++y, src_p += sskip, dst_p += dskip) + { + for (x = 0; x < w; ++x, ++src_p, ++dst_p) + { + *dst_p = (*dst_p & dmask) | ((Uint32)*src_p << ashift); + } + } + } + } + SDL_UnlockSurface (surf); + + TFB_DrawCanvas_Blit (surf, &srcRect, dst, &targetRect, mode); + UnlockMutex (backing->mutex); +} + +TFB_Canvas +TFB_DrawCanvas_New_TrueColor (int w, int h, BOOLEAN hasalpha) +{ + SDL_Surface *new_surf; + SDL_PixelFormat* fmt = format_conv_surf->format; + + new_surf = SDL_CreateRGBSurface (SDL_SWSURFACE, w, h, + fmt->BitsPerPixel, fmt->Rmask, fmt->Gmask, fmt->Bmask, + hasalpha ? fmt->Amask : 0); + if (!new_surf) + { + log_add (log_Fatal, "INTERNAL PANIC: Failed to create TFB_Canvas: %s", + SDL_GetError()); + exit (EXIT_FAILURE); + } + return new_surf; +} + +TFB_Canvas +TFB_DrawCanvas_New_ForScreen (int w, int h, BOOLEAN withalpha) +{ + SDL_Surface *new_surf; + SDL_PixelFormat* fmt = SDL_Screen->format; + + if (fmt->palette) + { + log_add (log_Warning, "TFB_DrawCanvas_New_ForScreen() WARNING:" + "Paletted display format will be slow"); + + new_surf = TFB_DrawCanvas_New_TrueColor (w, h, withalpha); + } + else + { + if (withalpha && fmt->Amask == 0) + fmt = format_conv_surf->format; // Screen has no alpha and we need it + + new_surf = SDL_CreateRGBSurface (SDL_SWSURFACE, w, h, + fmt->BitsPerPixel, fmt->Rmask, fmt->Gmask, fmt->Bmask, + withalpha ? fmt->Amask : 0); + } + + if (!new_surf) + { + log_add (log_Fatal, "TFB_DrawCanvas_New_ForScreen() INTERNAL PANIC:" + "Failed to create TFB_Canvas: %s", SDL_GetError()); + exit (EXIT_FAILURE); + } + return new_surf; +} + +TFB_Canvas +TFB_DrawCanvas_New_Paletted (int w, int h, Color palette[256], + int transparent_index) +{ + SDL_Surface *new_surf; + new_surf = SDL_CreateRGBSurface (SDL_SWSURFACE, w, h, 8, 0, 0, 0, 0); + if (!new_surf) + { + log_add (log_Fatal, "INTERNAL PANIC: Failed to create TFB_Canvas: %s", + SDL_GetError()); + exit (EXIT_FAILURE); + } + if (palette != NULL) + { + TFB_DrawCanvas_SetPalette (new_surf, palette); + } + if (transparent_index >= 0) + { + TFB_SetColorKey (new_surf, transparent_index, 0); + } + else + { + TFB_DisableColorKey (new_surf); + } + return new_surf; +} + +TFB_Canvas +TFB_DrawCanvas_New_ScaleTarget (TFB_Canvas canvas, TFB_Canvas oldcanvas, int type, int last_type) +{ + SDL_Surface *src = canvas; + SDL_Surface *old = oldcanvas; + SDL_Surface *newsurf = NULL; + + // For the purposes of this function, bilinear == trilinear + if (type == TFB_SCALE_TRILINEAR) + type = TFB_SCALE_BILINEAR; + if (last_type == TFB_SCALE_TRILINEAR) + last_type = TFB_SCALE_BILINEAR; + + if (old && type != last_type) + { + TFB_DrawCanvas_Delete (old); + old = NULL; + } + if (old) + return old; /* can just reuse the old one */ + + if (type == TFB_SCALE_NEAREST) + { + newsurf = SDL_CreateRGBSurface (SDL_SWSURFACE, src->w, + src->h, + src->format->BitsPerPixel, + src->format->Rmask, + src->format->Gmask, + src->format->Bmask, + src->format->Amask); + TFB_DrawCanvas_CopyTransparencyInfo (src, newsurf); + } + else + { + // The scaled image may in fact be larger by 1 pixel than the source + // because of hotspot alignment and fractional edge pixels + if (SDL_Screen->format->BitsPerPixel == 32) + newsurf = TFB_DrawCanvas_New_ForScreen (src->w + 1, src->h + 1, TRUE); + else + newsurf = TFB_DrawCanvas_New_TrueColor (src->w + 1, src->h + 1, TRUE); + } + + return newsurf; +} + +TFB_Canvas +TFB_DrawCanvas_New_RotationTarget (TFB_Canvas src_canvas, int angle) +{ + SDL_Surface *src = src_canvas; + SDL_Surface *newsurf; + EXTENT size; + + TFB_DrawCanvas_GetRotatedExtent (src_canvas, angle, &size); + + newsurf = SDL_CreateRGBSurface (SDL_SWSURFACE, + size.width, size.height, + src->format->BitsPerPixel, + src->format->Rmask, + src->format->Gmask, + src->format->Bmask, + src->format->Amask); + if (!newsurf) + { + log_add (log_Fatal, "TFB_DrawCanvas_New_RotationTarget()" + " INTERNAL PANIC: Failed to create TFB_Canvas: %s", + SDL_GetError()); + exit (EXIT_FAILURE); + } + TFB_DrawCanvas_CopyTransparencyInfo (src, newsurf); + + return newsurf; +} + +TFB_Canvas +TFB_DrawCanvas_LoadFromFile (void *dir, const char *fileName) +{ + SDL_Surface *surf = sdluio_loadImage (dir, fileName); + if (!surf) + return NULL; + + if (surf->format->BitsPerPixel < 8) + { + SDL_SetError ("unsupported image format (min 8bpp)"); + SDL_FreeSurface (surf); + surf = NULL; + } + + return surf; +} + +void +TFB_DrawCanvas_Delete (TFB_Canvas canvas) +{ + if (!canvas) + { + log_add (log_Warning, "INTERNAL PANIC: Attempted" + " to delete a NULL canvas!"); + /* Should we actually die here? */ + } + else + { + SDL_FreeSurface (canvas); + } +} + +BOOLEAN +TFB_DrawCanvas_GetFontCharData (TFB_Canvas canvas, BYTE *outData, + unsigned dataPitch) +{ + SDL_Surface *surf = canvas; + int x, y; + Uint8 r, g, b, a; + Uint32 p; + SDL_PixelFormat *fmt = surf->format; + GetPixelFn getpix; + + if (!surf || !outData) + return FALSE; + + SDL_LockSurface (surf); + + getpix = getpixel_for (surf); + + // produce an alpha-only image in internal BYTE[] format + // from the SDL surface + for (y = 0; y < surf->h; ++y) + { + BYTE *dst = outData + dataPitch * y; + + for (x = 0; x < surf->w; ++x, ++dst) + { + p = getpix (surf, x, y); + SDL_GetRGBA (p, fmt, &r, &g, &b, &a); + + if (!fmt->Amask) + { // produce alpha from intensity (Y component) + // using a fast approximation + // contributions to Y are: R=2, G=4, B=1 + a = ((r * 2) + (g * 4) + b) / 7; + } + + *dst = a; + } + } + + SDL_UnlockSurface (surf); + + return TRUE; +} + +Color * +TFB_DrawCanvas_ExtractPalette (TFB_Canvas canvas) +{ + int i; + Color *result; + SDL_Surface *surf = canvas; + SDL_Palette *palette = surf->format->palette; + + if (!palette) + return NULL; + + // There may be less colors in the surface than 256. Init to 0 first. + result = HCalloc (sizeof (Color) * 256); + assert (palette->ncolors <= 256); + for (i = 0; i < palette->ncolors; ++i) + result[i] = NativeToColor (palette->colors[i]); + + return result; +} + +TFB_Canvas +TFB_DrawCanvas_ToScreenFormat (TFB_Canvas canvas) +{ + SDL_Surface *result = TFB_DisplayFormatAlpha (canvas); + if (result == NULL) + { + log_add (log_Debug, "WARNING: Could not convert" + " sprite-canvas to display format."); + return canvas; + } + else if (result == canvas) + { // no conversion was necessary + return canvas; + } + else + { // converted + TFB_DrawCanvas_Delete (canvas); + return result; + } + + return canvas; +} + +BOOLEAN +TFB_DrawCanvas_IsPaletted (TFB_Canvas canvas) +{ + return ((SDL_Surface *)canvas)->format->palette != NULL; +} + +void +TFB_DrawCanvas_SetPalette (TFB_Canvas target, Color palette[256]) +{ + SDL_Color colors[256]; + int i; + + for (i = 0; i < 256; ++i) + colors[i] = ColorToNative (palette[i]); + + TFB_SetColors (target, colors, 0, 256); +} + +int +TFB_DrawCanvas_GetTransparentIndex (TFB_Canvas canvas) +{ + Uint32 colorkey; + if (TFB_GetColorKey (canvas, &colorkey)) + { + return colorkey; + } + return -1; +} + +void +TFB_DrawCanvas_SetTransparentIndex (TFB_Canvas canvas, int index, BOOLEAN rleaccel) +{ + if (index >= 0) + { + TFB_SetColorKey (canvas, index, rleaccel); + + if (!TFB_DrawCanvas_IsPaletted (canvas)) + { + // disables surface alpha so color key transparency actually works + TFB_DisableSurfaceAlphaMod (canvas); + } + } + else + { + TFB_DisableColorKey (canvas); + } +} + +void +TFB_DrawCanvas_CopyTransparencyInfo (TFB_Canvas src_canvas, + TFB_Canvas dst_canvas) +{ + SDL_Surface* src = src_canvas; + + if (src->format->palette) + { + int index; + index = TFB_DrawCanvas_GetTransparentIndex (src_canvas); + TFB_DrawCanvas_SetTransparentIndex (dst_canvas, index, FALSE); + } + else + { + Color color; + if (TFB_DrawCanvas_GetTransparentColor (src_canvas, &color)) + TFB_DrawCanvas_SetTransparentColor (dst_canvas, color, FALSE); + } +} + +BOOLEAN +TFB_DrawCanvas_GetTransparentColor (TFB_Canvas canvas, Color *color) +{ + Uint32 colorkey; + if (!TFB_DrawCanvas_IsPaletted (canvas) + && (TFB_GetColorKey ((SDL_Surface *)canvas, &colorkey) == 0)) + { + Uint8 ur, ug, ub; + SDL_GetRGB (colorkey, ((SDL_Surface *)canvas)->format, &ur, &ug, &ub); + color->r = ur; + color->g = ug; + color->b = ub; + color->a = 0xff; + return TRUE; + } + return FALSE; +} + +void +TFB_DrawCanvas_SetTransparentColor (TFB_Canvas canvas, Color color, + BOOLEAN rleaccel) +{ + Uint32 sdlColor; + sdlColor = SDL_MapRGBA (((SDL_Surface *)canvas)->format, + color.r, color.g, color.b, 0); + TFB_SetColorKey (canvas, sdlColor, rleaccel); + + if (!TFB_DrawCanvas_IsPaletted (canvas)) + { + // disables surface alpha so color key transparency actually works + TFB_DisableSurfaceAlphaMod (canvas); + } +} + +void +TFB_DrawCanvas_GetScaledExtent (TFB_Canvas src_canvas, HOT_SPOT* src_hs, + TFB_Canvas src_mipmap, HOT_SPOT* mm_hs, + int scale, int type, EXTENT *size, HOT_SPOT *hs) +{ + SDL_Surface *src = src_canvas; + sint32 x, y, w, h; + int frac; + + if (!src_mipmap) + { + w = src->w * scale; + h = src->h * scale; + x = src_hs->x * scale; + y = src_hs->y * scale; + } + else + { + // interpolates extents between src and mipmap to get smoother + // transition when surface changes + SDL_Surface *mipmap = src_mipmap; + int ratio = scale * 2 - GSCALE_IDENTITY; + + assert (scale >= GSCALE_IDENTITY / 2); + + w = mipmap->w * GSCALE_IDENTITY + (src->w - mipmap->w) * ratio; + h = mipmap->h * GSCALE_IDENTITY + (src->h - mipmap->h) * ratio; + + // Seems it is better to use mipmap hotspot because some + // source and mipmap images have the same dimensions! + x = mm_hs->x * GSCALE_IDENTITY + (src_hs->x - mm_hs->x) * ratio; + y = mm_hs->y * GSCALE_IDENTITY + (src_hs->y - mm_hs->y) * ratio; + } + + if (type != TFB_SCALE_NEAREST) + { + // align hotspot on an whole pixel + if (x & (GSCALE_IDENTITY - 1)) + { + frac = GSCALE_IDENTITY - (x & (GSCALE_IDENTITY - 1)); + x += frac; + w += frac; + } + if (y & (GSCALE_IDENTITY - 1)) + { + frac = GSCALE_IDENTITY - (y & (GSCALE_IDENTITY - 1)); + y += frac; + h += frac; + } + // pad the extent to accomodate fractional edge pixels + w += (GSCALE_IDENTITY - 1); + h += (GSCALE_IDENTITY - 1); + } + + size->width = w / GSCALE_IDENTITY; + size->height = h / GSCALE_IDENTITY; + hs->x = x / GSCALE_IDENTITY; + hs->y = y / GSCALE_IDENTITY; + + // Scaled image can be larger than the source by 1 pixel + // because of hotspot alignment and fractional edge pixels + assert (size->width <= src->w + 1 && size->height <= src->h + 1); + + if (!size->width && src->w) + size->width = 1; + if (!size->height && src->h) + size->height = 1; +} + +void +TFB_DrawCanvas_GetExtent (TFB_Canvas canvas, EXTENT *size) +{ + SDL_Surface *src = canvas; + + size->width = src->w; + size->height = src->h; +} + +void +TFB_DrawCanvas_Rescale_Nearest (TFB_Canvas src_canvas, TFB_Canvas dst_canvas, + int scale, HOT_SPOT* src_hs, EXTENT* size, HOT_SPOT* dst_hs) +{ + SDL_Surface *src = src_canvas; + SDL_Surface *dst = dst_canvas; + int x, y; + int fsx = 0, fsy = 0; // source fractional dx and dy increments + int ssx = 0, ssy = 0; // source fractional x and y starting points + int w, h; + + if (scale > 0) + { + TFB_DrawCanvas_GetScaledExtent (src, src_hs, NULL, NULL, scale, + TFB_SCALE_NEAREST, size, dst_hs); + + w = size->width; + h = size->height; + } + else + { + // Just go with the dst surface dimensions + w = dst->w; + h = dst->h; + } + + if (w > dst->w || h > dst->h) + { + log_add (log_Warning, "TFB_DrawCanvas_Rescale_Nearest: Tried to scale" + " image to size %d %d when dest_canvas has only" + " dimensions of %d %d! Failing.", + w, h, dst->w, dst->h); + return; + } + + if (w > 1) + fsx = ((src->w - 1) << 16) / (w - 1); + if (h > 1) + fsy = ((src->h - 1) << 16) / (h - 1); + // We start with a value in 0..0.5 range to shift the bigger + // jumps towards the center of the image + ssx = 0x6000; + ssy = 0x6000; + + SDL_LockSurface (src); + SDL_LockSurface (dst); + + if (src->format->BytesPerPixel == 1 && dst->format->BytesPerPixel == 1) + { + Uint8 *sp, *csp, *dp, *cdp; + int sx, sy; // source fractional x and y positions + + sp = csp = (Uint8 *) src->pixels; + dp = cdp = (Uint8 *) dst->pixels; + + for (y = 0, sy = ssy; y < h; ++y) + { + csp += (sy >> 16) * src->pitch; + sp = csp; + dp = cdp; + for (x = 0, sx = ssx; x < w; ++x) + { + sp += (sx >> 16); + *dp = *sp; + sx &= 0xffff; + sx += fsx; + ++dp; + } + sy &= 0xffff; + sy += fsy; + cdp += dst->pitch; + } + } + else if (src->format->BytesPerPixel == 4 && dst->format->BytesPerPixel == 4) + { + Uint32 *sp, *csp, *dp, *cdp; + int sx, sy; // source fractional x and y positions + int sgap, dgap; + + sgap = src->pitch >> 2; + dgap = dst->pitch >> 2; + + sp = csp = (Uint32 *) src->pixels; + dp = cdp = (Uint32 *) dst->pixels; + + for (y = 0, sy = ssy; y < h; ++y) + { + csp += (sy >> 16) * sgap; + sp = csp; + dp = cdp; + for (x = 0, sx = ssx; x < w; ++x) + { + sp += (sx >> 16); + *dp = *sp; + sx &= 0xffff; + sx += fsx; + ++dp; + } + sy &= 0xffff; + sy += fsy; + cdp += dgap; + } + } + else + { + log_add (log_Warning, "Tried to deal with unknown BPP: %d -> %d", + src->format->BitsPerPixel, dst->format->BitsPerPixel); + } + SDL_UnlockSurface (dst); + SDL_UnlockSurface (src); +} + +typedef union +{ + Uint32 value; + Uint8 chan[4]; + struct + { + Uint8 r, g, b, a; + } c; +} pixel_t; + +static inline Uint8 +dot_product_8_4 (pixel_t* p, int c, Uint8* v) +{ // math expanded for speed +#if 0 + return ( + (Uint32)p[0].chan[c] * v[0] + (Uint32)p[1].chan[c] * v[1] + + (Uint32)p[2].chan[c] * v[2] + (Uint32)p[3].chan[c] * v[3] + ) >> 8; +#else + // mult-table driven version + return + btable[p[0].chan[c]][v[0]] + btable[p[1].chan[c]][v[1]] + + btable[p[2].chan[c]][v[2]] + btable[p[3].chan[c]][v[3]]; +#endif +} + +static inline Uint8 +weight_product_8_4 (pixel_t* p, int c, Uint8* w) +{ // math expanded for speed + return ( + (Uint32)p[0].chan[c] * w[0] + (Uint32)p[1].chan[c] * w[1] + + (Uint32)p[2].chan[c] * w[2] + (Uint32)p[3].chan[c] * w[3] + ) / (w[0] + w[1] + w[2] + w[3]); +} + +static inline Uint8 +blend_ratio_2 (Uint8 c1, Uint8 c2, int ratio) +{ // blend 2 color values according to ratio (0..256) + // identical to proper alpha blending + return (((c1 - c2) * ratio) >> 8) + c2; +} + +static inline Uint32 +scale_read_pixel (void* ppix, SDL_PixelFormat *fmt, SDL_Color *pal, + Uint32 mask, Uint32 key) +{ + pixel_t p; + + // start off with non-present pixel + p.value = 0; + + if (pal) + { // paletted pixel; mask not used + Uint32 c = *(Uint8 *)ppix; + + if (c != key) + { + p.c.r = pal[c].r; + p.c.g = pal[c].g; + p.c.b = pal[c].b; + p.c.a = SDL_ALPHA_OPAQUE; + } + } + else + { // RGB(A) pixel; (pix & mask) != key + Uint32 c = *(Uint32 *)ppix; + + if ((c & mask) != key) + { +#if 0 + SDL_GetRGBA (c, fmt, &p.c.r, &p.c.g, &p.c.b, &p.c.a); +#else + // Assume 8 bits/channel; a safe assumption with 32bpp surfaces + p.c.r = (c >> fmt->Rshift) & 0xff; + p.c.g = (c >> fmt->Gshift) & 0xff; + p.c.b = (c >> fmt->Bshift) & 0xff; + if (fmt->Amask) + p.c.a = (c >> fmt->Ashift) & 0xff; + else + p.c.a = SDL_ALPHA_OPAQUE; + } +#endif + } + + return p.value; +} + +static inline Uint32 +scale_get_pixel (SDL_Surface *src, Uint32 mask, Uint32 key, int x, int y) +{ + SDL_Color *pal = src->format->palette? src->format->palette->colors : 0; + + if (x < 0 || x >= src->w || y < 0 || y >= src->h) + return 0; + + return scale_read_pixel ((Uint8*)src->pixels + y * src->pitch + + x * src->format->BytesPerPixel, src->format, pal, mask, key); +} + +void +TFB_DrawCanvas_Rescale_Trilinear (TFB_Canvas src_canvas, TFB_Canvas src_mipmap, + TFB_Canvas dst_canvas, int scale, HOT_SPOT* src_hs, HOT_SPOT* mm_hs, + EXTENT* size, HOT_SPOT* dst_hs) +{ + SDL_Surface *src = src_canvas; + SDL_Surface *dst = dst_canvas; + SDL_Surface *mm = src_mipmap; + SDL_PixelFormat *srcfmt = src->format; + SDL_PixelFormat *mmfmt = mm->format; + SDL_PixelFormat *dstfmt = dst->format; + SDL_Color *srcpal = srcfmt->palette? srcfmt->palette->colors : 0; + const int sbpp = srcfmt->BytesPerPixel; + const int mmbpp = mmfmt->BytesPerPixel; + const int slen = src->pitch; + const int mmlen = mm->pitch; + const int dst_has_alpha = (dstfmt->Amask != 0); + Uint32 transparent = 0; + const int alpha_threshold = dst_has_alpha ? 0 : 127; + // src v. mipmap importance factor + int ratio = scale * 2 - GSCALE_IDENTITY; + // source masks and keys + Uint32 mk0 = 0, ck0 = ~0, mk1 = 0, ck1 = ~0; + // source fractional x and y positions + int sx0, sy0, sx1, sy1; + // source fractional dx and dy increments + int fsx0 = 0, fsy0 = 0, fsx1 = 0, fsy1 = 0; + // source fractional x and y starting points + int ssx0 = 0, ssy0 = 0, ssx1 = 0, ssy1 = 0; + int x, y, w, h; + + TFB_GetColorKey (dst, &transparent); + + if (mmfmt->palette && !srcpal) + { + log_add (log_Warning, "TFB_DrawCanvas_Rescale_Trilinear: " + "Mipmap is paletted, but source is not! Failing."); + return; + } + + if (scale > 0) + { + int fw, fh; + + // Use (scale / GSCALE_IDENTITY) sizing factor + TFB_DrawCanvas_GetScaledExtent (src, src_hs, mm, mm_hs, scale, + TFB_SCALE_TRILINEAR, size, dst_hs); + + w = size->width; + h = size->height; + + fw = mm->w * GSCALE_IDENTITY + (src->w - mm->w) * ratio; + fh = mm->h * GSCALE_IDENTITY + (src->h - mm->h) * ratio; + + // This limits the effective source dimensions to 2048x2048, + // and we also lose 4 bits of precision out of 16 (no problem) + fsx0 = (src->w << 20) / fw; + fsx0 <<= 4; + fsy0 = (src->h << 20) / fh; + fsy0 <<= 4; + + fsx1 = (mm->w << 20) / fw; + fsx1 <<= 4; + fsy1 = (mm->h << 20) / fh; + fsy1 <<= 4; + + // position the hotspots directly over each other + ssx0 = (src_hs->x << 16) - fsx0 * dst_hs->x; + ssy0 = (src_hs->y << 16) - fsy0 * dst_hs->y; + + ssx1 = (mm_hs->x << 16) - fsx1 * dst_hs->x; + ssy1 = (mm_hs->y << 16) - fsy1 * dst_hs->y; + } + else + { + // Just go with the dst surface dimensions + w = dst->w; + h = dst->h; + + fsx0 = (src->w << 16) / w; + fsy0 = (src->h << 16) / h; + + fsx1 = (mm->w << 16) / w; + fsy1 = (mm->h << 16) / h; + + // give equal importance to both edges + ssx0 = (((src->w - 1) << 16) - fsx0 * (w - 1)) >> 1; + ssy0 = (((src->h - 1) << 16) - fsy0 * (h - 1)) >> 1; + + ssx1 = (((mm->w - 1) << 16) - fsx1 * (w - 1)) >> 1; + ssy1 = (((mm->h - 1) << 16) - fsy1 * (h - 1)) >> 1; + } + + if (w > dst->w || h > dst->h) + { + log_add (log_Warning, "TFB_DrawCanvas_Rescale_Trilinear: " + "Tried to scale image to size %d %d when dest_canvas" + " has only dimensions of %d %d! Failing.", + w, h, dst->w, dst->h); + return; + } + + if ((srcfmt->BytesPerPixel != 1 && srcfmt->BytesPerPixel != 4) || + (mmfmt->BytesPerPixel != 1 && mmfmt->BytesPerPixel != 4) || + (dst->format->BytesPerPixel != 4)) + { + log_add (log_Warning, "TFB_DrawCanvas_Rescale_Trilinear: " + "Tried to deal with unknown BPP: %d -> %d, mipmap %d", + srcfmt->BitsPerPixel, dst->format->BitsPerPixel, + mmfmt->BitsPerPixel); + return; + } + + // use colorkeys where appropriate + if (srcfmt->Amask) + { // alpha transparency + mk0 = srcfmt->Amask; + ck0 = 0; + } + else if (TFB_GetColorKey (src, &ck0) == 0) + { // colorkey transparency + mk0 = ~srcfmt->Amask; + ck0 &= mk0; + } + + if (mmfmt->Amask) + { // alpha transparency + mk1 = mmfmt->Amask; + ck1 = 0; + } + else if (TFB_GetColorKey (mm, &ck1) == 0) + { // colorkey transparency + mk1 = ~mmfmt->Amask; + ck1 &= mk1; + } + + SDL_LockSurface(src); + SDL_LockSurface(dst); + SDL_LockSurface(mm); + + for (y = 0, sy0 = ssy0, sy1 = ssy1; + y < h; + ++y, sy0 += fsy0, sy1 += fsy1) + { + Uint32 *dst_p = (Uint32 *) ((Uint8*)dst->pixels + y * dst->pitch); + const int py0 = (sy0 >> 16); + const int py1 = (sy1 >> 16); + Uint8 *src_a0 = (Uint8*)src->pixels + py0 * slen; + Uint8 *src_a1 = (Uint8*)mm->pixels + py1 * mmlen; + // retrieve the fractional portions of y + const Uint8 v0 = (sy0 >> 8) & 0xff; + const Uint8 v1 = (sy1 >> 8) & 0xff; + Uint8 w0[4], w1[4]; // pixel weight vectors + + for (x = 0, sx0 = ssx0, sx1 = ssx1; + x < w; + ++x, ++dst_p, sx0 += fsx0, sx1 += fsx1) + { + const int px0 = (sx0 >> 16); + const int px1 = (sx1 >> 16); + // retrieve the fractional portions of x + const Uint8 u0 = (sx0 >> 8) & 0xff; + const Uint8 u1 = (sx1 >> 8) & 0xff; + // pixels are examined and numbered in pattern + // 0 1 + // 2 3 + // the ideal pixel (4) is somewhere between these four + // and is calculated from these using weight vector (w) + // with a dot product + pixel_t p0[5], p1[5]; + Uint8 res_a; + + w0[0] = btable[255 - u0][255 - v0]; + w0[1] = btable[u0][255 - v0]; + w0[2] = btable[255 - u0][v0]; + w0[3] = btable[u0][v0]; + + w1[0] = btable[255 - u1][255 - v1]; + w1[1] = btable[u1][255 - v1]; + w1[2] = btable[255 - u1][v1]; + w1[3] = btable[u1][v1]; + + // Collect interesting pixels from src image + // Optimization: speed is criticial on larger images; + // most pixel reads fall completely inside the image + if (px0 >= 0 && px0 + 1 < src->w && py0 >= 0 && py0 + 1 < src->h) + { + Uint8 *src_p = src_a0 + px0 * sbpp; + + p0[0].value = scale_read_pixel (src_p, srcfmt, + srcpal, mk0, ck0); + p0[1].value = scale_read_pixel (src_p + sbpp, srcfmt, + srcpal, mk0, ck0); + p0[2].value = scale_read_pixel (src_p + slen, srcfmt, + srcpal, mk0, ck0); + p0[3].value = scale_read_pixel (src_p + sbpp + slen, srcfmt, + srcpal, mk0, ck0); + } + else + { + p0[0].value = scale_get_pixel (src, mk0, ck0, px0, py0); + p0[1].value = scale_get_pixel (src, mk0, ck0, px0 + 1, py0); + p0[2].value = scale_get_pixel (src, mk0, ck0, px0, py0 + 1); + p0[3].value = scale_get_pixel (src, mk0, ck0, + px0 + 1, py0 + 1); + } + + // Collect interesting pixels from mipmap image + if (px1 >= 0 && px1 + 1 < mm->w && py1 >= 0 && py1 + 1 < mm->h) + { + Uint8 *mm_p = src_a1 + px1 * mmbpp; + + p1[0].value = scale_read_pixel (mm_p, mmfmt, + srcpal, mk1, ck1); + p1[1].value = scale_read_pixel (mm_p + mmbpp, mmfmt, + srcpal, mk1, ck1); + p1[2].value = scale_read_pixel (mm_p + mmlen, mmfmt, + srcpal, mk1, ck1); + p1[3].value = scale_read_pixel (mm_p + mmbpp + mmlen, mmfmt, + srcpal, mk1, ck1); + } + else + { + p1[0].value = scale_get_pixel (mm, mk1, ck1, px1, py1); + p1[1].value = scale_get_pixel (mm, mk1, ck1, px1 + 1, py1); + p1[2].value = scale_get_pixel (mm, mk1, ck1, px1, py1 + 1); + p1[3].value = scale_get_pixel (mm, mk1, ck1, + px1 + 1, py1 + 1); + } + + p0[4].c.a = dot_product_8_4 (p0, 3, w0); + p1[4].c.a = dot_product_8_4 (p1, 3, w1); + + res_a = blend_ratio_2 (p0[4].c.a, p1[4].c.a, ratio); + + if (res_a <= alpha_threshold) + { + *dst_p = transparent; + } + else if (!dst_has_alpha) + { // RGB surface handling + p0[4].c.r = dot_product_8_4 (p0, 0, w0); + p0[4].c.g = dot_product_8_4 (p0, 1, w0); + p0[4].c.b = dot_product_8_4 (p0, 2, w0); + + p1[4].c.r = dot_product_8_4 (p1, 0, w1); + p1[4].c.g = dot_product_8_4 (p1, 1, w1); + p1[4].c.b = dot_product_8_4 (p1, 2, w1); + + p0[4].c.r = blend_ratio_2 (p0[4].c.r, p1[4].c.r, ratio); + p0[4].c.g = blend_ratio_2 (p0[4].c.g, p1[4].c.g, ratio); + p0[4].c.b = blend_ratio_2 (p0[4].c.b, p1[4].c.b, ratio); + + // TODO: we should handle alpha-blending here, but we do + // not know the destination color for blending! + + *dst_p = + (p0[4].c.r << dstfmt->Rshift) | + (p0[4].c.g << dstfmt->Gshift) | + (p0[4].c.b << dstfmt->Bshift); + } + else + { // RGBA surface handling + + // we do not want to blend with non-present pixels + // (pixels that have alpha == 0) as these will + // skew the result and make resulting alpha useless + if (p0[4].c.a != 0) + { + int i; + for (i = 0; i < 4; ++i) + if (p0[i].c.a == 0) + w0[i] = 0; + + p0[4].c.r = weight_product_8_4 (p0, 0, w0); + p0[4].c.g = weight_product_8_4 (p0, 1, w0); + p0[4].c.b = weight_product_8_4 (p0, 2, w0); + } + if (p1[4].c.a != 0) + { + int i; + for (i = 0; i < 4; ++i) + if (p1[i].c.a == 0) + w1[i] = 0; + + p1[4].c.r = weight_product_8_4 (p1, 0, w1); + p1[4].c.g = weight_product_8_4 (p1, 1, w1); + p1[4].c.b = weight_product_8_4 (p1, 2, w1); + } + + if (p0[4].c.a != 0 && p1[4].c.a != 0) + { // blend if both present + p0[4].c.r = blend_ratio_2 (p0[4].c.r, p1[4].c.r, ratio); + p0[4].c.g = blend_ratio_2 (p0[4].c.g, p1[4].c.g, ratio); + p0[4].c.b = blend_ratio_2 (p0[4].c.b, p1[4].c.b, ratio); + } + else if (p1[4].c.a != 0) + { // other pixel is present + p0[4].value = p1[4].value; + } + + // error-correct alpha to fully opaque to remove + // the often unwanted and unnecessary blending + if (res_a > 0xf8) + res_a = 0xff; + + *dst_p = + (p0[4].c.r << dstfmt->Rshift) | + (p0[4].c.g << dstfmt->Gshift) | + (p0[4].c.b << dstfmt->Bshift) | + (res_a << dstfmt->Ashift); + } + } + } + + SDL_UnlockSurface(mm); + SDL_UnlockSurface(dst); + SDL_UnlockSurface(src); +} + +void +TFB_DrawCanvas_Rescale_Bilinear (TFB_Canvas src_canvas, TFB_Canvas dst_canvas, + int scale, HOT_SPOT* src_hs, EXTENT* size, HOT_SPOT* dst_hs) +{ + SDL_Surface *src = src_canvas; + SDL_Surface *dst = dst_canvas; + SDL_PixelFormat *srcfmt = src->format; + SDL_PixelFormat *dstfmt = dst->format; + SDL_Color *srcpal = srcfmt->palette? srcfmt->palette->colors : 0; + const int sbpp = srcfmt->BytesPerPixel; + const int slen = src->pitch; + const int dst_has_alpha = (dstfmt->Amask != 0); + Uint32 srckey = 0, transparent = 0; + const int alpha_threshold = dst_has_alpha ? 0 : 127; + // source masks and keys + Uint32 mk = 0, ck = ~0; + // source fractional x and y positions + int sx, sy; + // source fractional dx and dy increments + int fsx = 0, fsy = 0; + // source fractional x and y starting points + int ssx = 0, ssy = 0; + int x, y, w, h; + + // Get destination transparent color if it exists + TFB_GetColorKey (dst, &transparent); + + if (scale > 0) + { + // Use (scale / GSCALE_IDENTITY) sizing factor + TFB_DrawCanvas_GetScaledExtent (src, src_hs, NULL, NULL, scale, + TFB_SCALE_BILINEAR, size, dst_hs); + + w = size->width; + h = size->height; + fsx = (GSCALE_IDENTITY << 16) / scale; + fsy = (GSCALE_IDENTITY << 16) / scale; + + // position the hotspots directly over each other + ssx = (src_hs->x << 16) - fsx * dst_hs->x; + ssy = (src_hs->y << 16) - fsy * dst_hs->y; + } + else + { + // Just go with the dst surface dimensions + w = dst->w; + h = dst->h; + fsx = (src->w << 16) / w; + fsy = (src->h << 16) / h; + + // give equal importance to both edges + ssx = (((src->w - 1) << 16) - fsx * (w - 1)) >> 1; + ssy = (((src->h - 1) << 16) - fsy * (h - 1)) >> 1; + } + + if (w > dst->w || h > dst->h) + { + log_add (log_Warning, "TFB_DrawCanvas_Rescale_Bilinear: " + "Tried to scale image to size %d %d when dest_canvas" + " has only dimensions of %d %d! Failing.", + w, h, dst->w, dst->h); + return; + } + + if ((srcfmt->BytesPerPixel != 1 && srcfmt->BytesPerPixel != 4) || + (dst->format->BytesPerPixel != 4)) + { + log_add (log_Warning, "TFB_DrawCanvas_Rescale_Bilinear: " + "Tried to deal with unknown BPP: %d -> %d", + srcfmt->BitsPerPixel, dst->format->BitsPerPixel); + return; + } + + // use colorkeys where appropriate + if (srcfmt->Amask) + { // alpha transparency + mk = srcfmt->Amask; + ck = 0; + } + else if (TFB_GetColorKey (src, &srckey) == 0) + { // colorkey transparency + mk = ~srcfmt->Amask; + ck = srckey & mk; + } + + SDL_LockSurface(src); + SDL_LockSurface(dst); + + for (y = 0, sy = ssy; y < h; ++y, sy += fsy) + { + Uint32 *dst_p = (Uint32 *) ((Uint8*)dst->pixels + y * dst->pitch); + const int py = (sy >> 16); + Uint8 *src_a = (Uint8*)src->pixels + py * slen; + // retrieve the fractional portions of y + const Uint8 v = (sy >> 8) & 0xff; + Uint8 weight[4]; // pixel weight vectors + + for (x = 0, sx = ssx; x < w; ++x, ++dst_p, sx += fsx) + { + const int px = (sx >> 16); + // retrieve the fractional portions of x + const Uint8 u = (sx >> 8) & 0xff; + // pixels are examined and numbered in pattern + // 0 1 + // 2 3 + // the ideal pixel (4) is somewhere between these four + // and is calculated from these using weight vector (weight) + // with a dot product + pixel_t p[5]; + + weight[0] = btable[255 - u][255 - v]; + weight[1] = btable[u][255 - v]; + weight[2] = btable[255 - u][v]; + weight[3] = btable[u][v]; + + // Collect interesting pixels from src image + // Optimization: speed is criticial on larger images; + // most pixel reads fall completely inside the image + if (px >= 0 && px + 1 < src->w && py >= 0 && py + 1 < src->h) + { + Uint8 *src_p = src_a + px * sbpp; + + p[0].value = scale_read_pixel (src_p, srcfmt, srcpal, mk, ck); + p[1].value = scale_read_pixel (src_p + sbpp, srcfmt, + srcpal, mk, ck); + p[2].value = scale_read_pixel (src_p + slen, srcfmt, + srcpal, mk, ck); + p[3].value = scale_read_pixel (src_p + sbpp + slen, srcfmt, + srcpal, mk, ck); + } + else + { + p[0].value = scale_get_pixel (src, mk, ck, px, py); + p[1].value = scale_get_pixel (src, mk, ck, px + 1, py); + p[2].value = scale_get_pixel (src, mk, ck, px, py + 1); + p[3].value = scale_get_pixel (src, mk, ck, px + 1, py + 1); + } + + p[4].c.a = dot_product_8_4 (p, 3, weight); + + if (p[4].c.a <= alpha_threshold) + { + *dst_p = transparent; + } + else if (!dst_has_alpha) + { // RGB surface handling + p[4].c.r = dot_product_8_4 (p, 0, weight); + p[4].c.g = dot_product_8_4 (p, 1, weight); + p[4].c.b = dot_product_8_4 (p, 2, weight); + + // TODO: we should handle alpha-blending here, but we do + // not know the destination color for blending! + + *dst_p = + (p[4].c.r << dstfmt->Rshift) | + (p[4].c.g << dstfmt->Gshift) | + (p[4].c.b << dstfmt->Bshift); + } + else + { // RGBA surface handling + + // we do not want to blend with non-present pixels + // (pixels that have alpha == 0) as these will + // skew the result and make resulting alpha useless + int i; + for (i = 0; i < 4; ++i) + if (p[i].c.a == 0) + weight[i] = 0; + + p[4].c.r = weight_product_8_4 (p, 0, weight); + p[4].c.g = weight_product_8_4 (p, 1, weight); + p[4].c.b = weight_product_8_4 (p, 2, weight); + + // error-correct alpha to fully opaque to remove + // the often unwanted and unnecessary blending + if (p[4].c.a > 0xf8) + p[4].c.a = 0xff; + + *dst_p = + (p[4].c.r << dstfmt->Rshift) | + (p[4].c.g << dstfmt->Gshift) | + (p[4].c.b << dstfmt->Bshift) | + (p[4].c.a << dstfmt->Ashift); + } + } + } + + SDL_UnlockSurface(dst); + SDL_UnlockSurface(src); +} + +void +TFB_DrawCanvas_Lock (TFB_Canvas canvas) +{ + SDL_Surface *surf = canvas; + SDL_LockSurface (surf); +} + +void +TFB_DrawCanvas_Unlock (TFB_Canvas canvas) +{ + SDL_Surface *surf = canvas; + SDL_UnlockSurface (surf); +} + +void +TFB_DrawCanvas_GetScreenFormat (TFB_PixelFormat *fmt) +{ + SDL_PixelFormat *sdl = SDL_Screen->format; + + if (sdl->palette) + { + log_add (log_Warning, "TFB_DrawCanvas_GetScreenFormat() WARNING:" + "Paletted display format will be slow"); + + fmt->BitsPerPixel = 32; + fmt->Rmask = 0x000000ff; + fmt->Gmask = 0x0000ff00; + fmt->Bmask = 0x00ff0000; + fmt->Amask = 0xff000000; + } + else + { + fmt->BitsPerPixel = sdl->BitsPerPixel; + fmt->Rmask = sdl->Rmask; + fmt->Gmask = sdl->Gmask; + fmt->Bmask = sdl->Bmask; + fmt->Amask = sdl->Amask; + } +} + +int +TFB_DrawCanvas_GetStride (TFB_Canvas canvas) +{ + SDL_Surface *surf = canvas; + return surf->pitch; +} + +void* +TFB_DrawCanvas_GetLine (TFB_Canvas canvas, int line) +{ + SDL_Surface *surf = canvas; + return (uint8 *)surf->pixels + surf->pitch * line; +} + +Color +TFB_DrawCanvas_GetPixel (TFB_Canvas canvas, int x, int y) +{ + SDL_Surface* surf = canvas; + Uint32 pixel; + GetPixelFn getpixel; + Color c = {0, 0, 0, 0}; + + if (x < 0 || x >= surf->w || y < 0 || y >= surf->h) + { // outside bounds, return 0 + return c; + } + + SDL_LockSurface (surf); + + getpixel = getpixel_for(surf); + pixel = (*getpixel)(surf, x, y); + SDL_GetRGBA (pixel, surf->format, &c.r, &c.g, &c.b, &c.a); + + SDL_UnlockSurface (surf); + + return c; +} + +void +TFB_DrawCanvas_Rotate (TFB_Canvas src_canvas, TFB_Canvas dst_canvas, + int angle, EXTENT size) +{ + SDL_Surface *src = src_canvas; + SDL_Surface *dst = dst_canvas; + int ret; + Color color; + + if (size.width > dst->w || size.height > dst->h) + { + log_add (log_Warning, "TFB_DrawCanvas_Rotate: Tried to rotate" + " image to size %d %d when dst_canvas has only dimensions" + " of %d %d! Failing.", + size.width, size.height, dst->w, dst->h); + return; + } + + if (TFB_DrawCanvas_GetTransparentColor (src, &color)) + { + TFB_DrawCanvas_SetTransparentColor (dst, color, FALSE); + /* fill destination with transparent color before rotating */ + SDL_FillRect(dst, NULL, SDL_MapRGBA (dst->format, + color.r, color.g, color.b, 0)); + } + + ret = rotateSurface (src, dst, angle, 0); + if (ret != 0) + { + log_add (log_Warning, "TFB_DrawCanvas_Rotate: WARNING:" + " actual rotation func returned failure\n"); + } +} + +void +TFB_DrawCanvas_GetRotatedExtent (TFB_Canvas src_canvas, int angle, EXTENT *size) +{ + int dstw, dsth; + SDL_Surface *src = src_canvas; + + rotozoomSurfaceSize (src->w, src->h, angle, 1, &dstw, &dsth); + size->height = dsth; + size->width = dstw; +} + +void +TFB_DrawCanvas_CopyRect (TFB_Canvas source, const RECT *srcRect, + TFB_Canvas target, POINT dstPt) +{ + SDL_Rect sourceRect, targetRect; + + if (source == 0 || target == 0) + { + log_add (log_Warning, + "ERROR: TFB_DrawCanvas_CopyRect passed null canvas ptr"); + return; + } + + sourceRect.x = srcRect->corner.x; + sourceRect.y = srcRect->corner.y; + sourceRect.w = srcRect->extent.width; + sourceRect.h = srcRect->extent.height; + + targetRect.x = dstPt.x; + targetRect.y = dstPt.y; + // According to SDL docs, width and height are ignored, but + // we'll set them anyway, just in case. + targetRect.w = srcRect->extent.width; + targetRect.h = srcRect->extent.height; + + SDL_BlitSurface (source, &sourceRect, target, &targetRect); +} + +void +TFB_DrawCanvas_SetClipRect (TFB_Canvas canvas, const RECT *clipRect) +{ + if (canvas == 0) + { + log_add (log_Warning, + "ERROR: TFB_DrawCanvas_SetClipRect passed null canvas ptr"); + return; + } + + if (!clipRect) + { // clipping disabled + SDL_SetClipRect (canvas, NULL); + } + else + { + SDL_Rect r; + r.x = clipRect->corner.x; + r.y = clipRect->corner.y; + r.w = clipRect->extent.width; + r.h = clipRect->extent.height; + SDL_SetClipRect (canvas, &r); + } +} + +BOOLEAN +TFB_DrawCanvas_Intersect (TFB_Canvas canvas1, POINT c1org, + TFB_Canvas canvas2, POINT c2org, const RECT *interRect) +{ + BOOLEAN ret = FALSE; + SDL_Surface *surf1 = canvas1; + SDL_Surface *surf2 = canvas2; + int x, y; + Uint32 s1key, s2key; + Uint32 s1mask, s2mask; + GetPixelFn getpixel1, getpixel2; + + SDL_LockSurface (surf1); + SDL_LockSurface (surf2); + + getpixel1 = getpixel_for (surf1); + getpixel2 = getpixel_for (surf2); + + if (surf1->format->Amask) + { // use alpha transparency info + s1mask = surf1->format->Amask; + // consider any not fully transparent pixel collidable + s1key = 0; + } + else + { // colorkey transparency + Uint32 colorkey = 0; + TFB_GetColorKey(surf1, &colorkey); + s1mask = ~surf1->format->Amask; + s1key = colorkey & s1mask; + } + + if (surf2->format->Amask) + { // use alpha transparency info + s2mask = surf2->format->Amask; + // consider any not fully transparent pixel collidable + s2key = 0; + } + else + { // colorkey transparency + Uint32 colorkey = 0; + TFB_GetColorKey(surf2, &colorkey); + s2mask = ~surf2->format->Amask; + s2key = colorkey & s2mask; + } + + // convert surface origins to pixel offsets within + c1org.x = interRect->corner.x - c1org.x; + c1org.y = interRect->corner.y - c1org.y; + c2org.x = interRect->corner.x - c2org.x; + c2org.y = interRect->corner.y - c2org.y; + + for (y = 0; y < interRect->extent.height; ++y) + { + for (x = 0; x < interRect->extent.width; ++x) + { + Uint32 p1 = getpixel1 (surf1, x + c1org.x, y + c1org.y) & s1mask; + Uint32 p2 = getpixel2 (surf2, x + c2org.x, y + c2org.y) & s2mask; + + if (p1 != s1key && p2 != s2key) + { // pixel collision + ret = TRUE; + break; + } + } + } + + SDL_UnlockSurface (surf2); + SDL_UnlockSurface (surf1); + + return ret; +} + +// Read/write the canvas pixels in a Color format understood by the core. +// The pixels array is assumed to be at least width * height large. +// The pixels array can be wider/narrower or taller/shorter than the canvas, +// and in that case, only the relevant pixels will be transfered. +static BOOLEAN +TFB_DrawCanvas_TransferColors (TFB_Canvas canvas, BOOLEAN write, + Color *pixels, int width, int height) +{ + SDL_Surface *surf = canvas; + SDL_PixelFormat *fmt; + GetPixelFn getpix; + PutPixelFn putpix; + int x, y, w, h; + + if (canvas == 0) + { + log_add (log_Warning, "ERROR: TFB_DrawCanvas_TransferColors " + "passed null canvas"); + return FALSE; + } + + fmt = surf->format; + getpix = getpixel_for (surf); + putpix = putpixel_for (surf); + + w = width < surf->w ? width : surf->w; + h = height < surf->h ? height : surf->h; + + SDL_LockSurface (surf); + + // This could be done faster if we assumed 32bpp surfaces + for (y = 0; y < h; ++y) + { + // pixels array pitch is width so as not to violate the interface + Color *c = pixels + y * width; + + for (x = 0; x < w; ++x, ++c) + { + if (write) + { // writing from data to surface + Uint32 p = SDL_MapRGBA (fmt, c->r, c->g, c->b, c->a); + putpix (surf, x, y, p); + } + else + { // reading from surface to data + Uint32 p = getpix (surf, x, y); + SDL_GetRGBA (p, fmt, &c->r, &c->g, &c->b, &c->a); + } + } + } + + SDL_UnlockSurface (surf); + + return TRUE; +} + +// Read the canvas pixels in a Color format understood by the core. +// See TFB_DrawCanvas_TransferColors() for pixels array info +BOOLEAN +TFB_DrawCanvas_GetPixelColors (TFB_Canvas canvas, Color *pixels, + int width, int height) +{ + return TFB_DrawCanvas_TransferColors (canvas, FALSE, pixels, + width, height); +} + +// Write the canvas pixels from a Color format understood by the core. +// See TFB_DrawCanvas_TransferColors() for pixels array info +BOOLEAN +TFB_DrawCanvas_SetPixelColors (TFB_Canvas canvas, const Color *pixels, + int width, int height) +{ + // unconst pixels, but it is safe -- it will not be written to + return TFB_DrawCanvas_TransferColors (canvas, TRUE, (Color *)pixels, + width, height); +} + +// Read/write the indexed canvas pixels as palette indexes. +// The data array is assumed to be at least width * height large. +// The data array can be wider/narrower or taller/shorter than the canvas, +// and in that case, only the relevant pixels will be transfered. +static BOOLEAN +TFB_DrawCanvas_TransferIndexes (TFB_Canvas canvas, BOOLEAN write, + BYTE *data, int width, int height) +{ + SDL_Surface *surf = canvas; + const SDL_PixelFormat *fmt; + int y, w, h; + + if (canvas == 0) + { + log_add (log_Warning, "ERROR: TFB_DrawCanvas_TransferIndexes " + "passed null canvas"); + return FALSE; + } + fmt = surf->format; + if (!TFB_DrawCanvas_IsPaletted (canvas) || fmt->BitsPerPixel != 8) + { + log_add (log_Warning, "ERROR: TFB_DrawCanvas_TransferIndexes " + "unimplemeted function: not an 8bpp indexed canvas"); + return FALSE; + } + + w = width < surf->w ? width : surf->w; + h = height < surf->h ? height : surf->h; + + SDL_LockSurface (surf); + + for (y = 0; y < h; ++y) + { + Uint8 *surf_p = (Uint8 *)surf->pixels + y * surf->pitch; + // pixels array pitch is width so as not to violate the interface + BYTE *data_p = data + y * width; + + if (write) + { // writing from data to surface + memcpy (surf_p, data_p, w * sizeof (BYTE)); + } + else + { // reading from surface to data + memcpy (data_p, surf_p, w * sizeof (BYTE)); + } + } + + SDL_UnlockSurface (surf); + + return TRUE; +} + +// Read the indexed canvas pixels as palette indexes. +// See TFB_DrawCanvas_TransferIndexes() for data array info. +BOOLEAN +TFB_DrawCanvas_GetPixelIndexes (TFB_Canvas canvas, BYTE *data, + int width, int height) +{ + return TFB_DrawCanvas_TransferIndexes (canvas, FALSE, data, + width, height); +} + +// Write the indexed canvas pixels as palette indexes. +// See TFB_DrawCanvas_TransferIndexes() for data array info. +BOOLEAN +TFB_DrawCanvas_SetPixelIndexes (TFB_Canvas canvas, const BYTE *data, + int width, int height) +{ + // unconst data, but it is safe -- it will not be written to + return TFB_DrawCanvas_TransferIndexes (canvas, TRUE, (BYTE *)data, + width, height); +} diff --git a/src/libs/graphics/sdl/hq2x.c b/src/libs/graphics/sdl/hq2x.c new file mode 100644 index 0000000..02dc806 --- /dev/null +++ b/src/libs/graphics/sdl/hq2x.c @@ -0,0 +1,2888 @@ +//hq2x filter +//-------------------------------------------------------------------------- +//Copyright (C) 2003 MaxSt ( maxst@hiend3d.com ) - Original version +// +//Portions Copyright (C) 2005 Alex Volkov ( codepro@usa.net ) +// Modified Oct-2-2005 + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +// Core algorithm of the HQ screen scaler +// adapted from hq2x -- www.hiend3d.com/hq2x.html +// Template +// When this file is built standalone is produces a plain C version +// Also #included by 2xscalers_mmx.c for an MMX version + +#include "libs/graphics/sdl/sdl_common.h" +#include "types.h" +#include "scalers.h" +#include "scaleint.h" +#include "2xscalers.h" + + +// Pixel blending/manipulation instructions +#define PIXEL00_0 dst_p[0] = pix[5]; +#define PIXEL00_10 dst_p[0] = Scale_Blend_31(pix[5], pix[1]); +#define PIXEL00_11 dst_p[0] = Scale_Blend_31(pix[5], pix[4]); +#define PIXEL00_12 dst_p[0] = Scale_Blend_31(pix[5], pix[2]); +#define PIXEL00_20 dst_p[0] = Scale_Blend_211(pix[5], pix[4], pix[2]); +#define PIXEL00_21 dst_p[0] = Scale_Blend_211(pix[5], pix[1], pix[2]); +#define PIXEL00_22 dst_p[0] = Scale_Blend_211(pix[5], pix[1], pix[4]); +#define PIXEL00_60 dst_p[0] = Scale_Blend_521(pix[5], pix[2], pix[4]); +#define PIXEL00_61 dst_p[0] = Scale_Blend_521(pix[5], pix[4], pix[2]); +#define PIXEL00_70 dst_p[0] = Scale_Blend_611(pix[5], pix[4], pix[2]); +#define PIXEL00_90 dst_p[0] = Scale_Blend_233(pix[5], pix[4], pix[2]); +#define PIXEL00_100 dst_p[0] = Scale_Blend_e11(pix[5], pix[4], pix[2]); +#define PIXEL01_0 dst_p[1] = pix[5]; +#define PIXEL01_10 dst_p[1] = Scale_Blend_31(pix[5], pix[3]); +#define PIXEL01_11 dst_p[1] = Scale_Blend_31(pix[5], pix[2]); +#define PIXEL01_12 dst_p[1] = Scale_Blend_31(pix[5], pix[6]); +#define PIXEL01_20 dst_p[1] = Scale_Blend_211(pix[5], pix[2], pix[6]); +#define PIXEL01_21 dst_p[1] = Scale_Blend_211(pix[5], pix[3], pix[6]); +#define PIXEL01_22 dst_p[1] = Scale_Blend_211(pix[5], pix[3], pix[2]); +#define PIXEL01_60 dst_p[1] = Scale_Blend_521(pix[5], pix[6], pix[2]); +#define PIXEL01_61 dst_p[1] = Scale_Blend_521(pix[5], pix[2], pix[6]); +#define PIXEL01_70 dst_p[1] = Scale_Blend_611(pix[5], pix[2], pix[6]); +#define PIXEL01_90 dst_p[1] = Scale_Blend_233(pix[5], pix[2], pix[6]); +#define PIXEL01_100 dst_p[1] = Scale_Blend_e11(pix[5], pix[2], pix[6]); +#define PIXEL10_0 dst_p[dlen] = pix[5]; +#define PIXEL10_10 dst_p[dlen] = Scale_Blend_31(pix[5], pix[7]); +#define PIXEL10_11 dst_p[dlen] = Scale_Blend_31(pix[5], pix[8]); +#define PIXEL10_12 dst_p[dlen] = Scale_Blend_31(pix[5], pix[4]); +#define PIXEL10_20 dst_p[dlen] = Scale_Blend_211(pix[5], pix[8], pix[4]); +#define PIXEL10_21 dst_p[dlen] = Scale_Blend_211(pix[5], pix[7], pix[4]); +#define PIXEL10_22 dst_p[dlen] = Scale_Blend_211(pix[5], pix[7], pix[8]); +#define PIXEL10_60 dst_p[dlen] = Scale_Blend_521(pix[5], pix[4], pix[8]); +#define PIXEL10_61 dst_p[dlen] = Scale_Blend_521(pix[5], pix[8], pix[4]); +#define PIXEL10_70 dst_p[dlen] = Scale_Blend_611(pix[5], pix[8], pix[4]); +#define PIXEL10_90 dst_p[dlen] = Scale_Blend_233(pix[5], pix[8], pix[4]); +#define PIXEL10_100 dst_p[dlen] = Scale_Blend_e11(pix[5], pix[8], pix[4]); +#define PIXEL11_0 dst_p[dlen + 1] = pix[5]; +#define PIXEL11_10 dst_p[dlen + 1] = Scale_Blend_31(pix[5], pix[9]); +#define PIXEL11_11 dst_p[dlen + 1] = Scale_Blend_31(pix[5], pix[6]); +#define PIXEL11_12 dst_p[dlen + 1] = Scale_Blend_31(pix[5], pix[8]); +#define PIXEL11_20 dst_p[dlen + 1] = Scale_Blend_211(pix[5], pix[6], pix[8]); +#define PIXEL11_21 dst_p[dlen + 1] = Scale_Blend_211(pix[5], pix[9], pix[8]); +#define PIXEL11_22 dst_p[dlen + 1] = Scale_Blend_211(pix[5], pix[9], pix[6]); +#define PIXEL11_60 dst_p[dlen + 1] = Scale_Blend_521(pix[5], pix[8], pix[6]); +#define PIXEL11_61 dst_p[dlen + 1] = Scale_Blend_521(pix[5], pix[6], pix[8]); +#define PIXEL11_70 dst_p[dlen + 1] = Scale_Blend_611(pix[5], pix[6], pix[8]); +#define PIXEL11_90 dst_p[dlen + 1] = Scale_Blend_233(pix[5], pix[6], pix[8]); +#define PIXEL11_100 dst_p[dlen + 1] = Scale_Blend_e11(pix[5], pix[6], pix[8]); + + +// HQ scaling to 2x +// The name expands to +// Scale_HqFilter (for plain C) +// Scale_MMX_HqFilter (for MMX) +// [others when platforms are added] +void +SCALE_(HqFilter) (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r) +{ + int x, y; + const int w = src->w, h = src->h; + int xend, yend; + int dsrc, ddst; + SDL_Rect *region = r; + SDL_Rect limits; + SDL_PixelFormat *fmt = dst->format; + const int sp = src->pitch, dp = dst->pitch; + const int bpp = fmt->BytesPerPixel; + const int slen = sp / bpp, dlen = dp / bpp; + Uint32 *src_p = (Uint32 *)src->pixels; + Uint32 *dst_p = (Uint32 *)dst->pixels; + + int prevline, nextline; + Uint32 pix[10]; + Uint32 yuv[10]; +// +----+----+----+ +// | | | | +// | p1 | p2 | p3 | +// +----+----+----+ +// | | | | +// | p4 | p5 | p6 | +// +----+----+----+ +// | | | | +// | p7 | p8 | p9 | +// +----+----+----+ + + // MMX code runs faster w/o branching + #define HQXX_DIFFYUV(p1, p2) \ + SCALE_DIFFYUV (p1, p2) + + SCALE_(PlatInit) (); + + // expand updated region if necessary + // pixels neighbooring the updated region may + // change as a result of updates + limits.x = 0; + limits.y = 0; + limits.w = src->w; + limits.h = src->h; + Scale_ExpandRect (region, 1, &limits); + + xend = region->x + region->w; + yend = region->y + region->h; + dsrc = slen - region->w; + ddst = (dlen - region->w) * 2; + + // move ptrs to the first updated pixel + src_p += slen * region->y + region->x; + dst_p += (dlen * region->y + region->x) * 2; + + for (y = region->y; y < yend; ++y, dst_p += ddst, src_p += dsrc) + { + if (y > 0) + prevline = -slen; + else + prevline = 0; + + if (y < h - 1) + nextline = slen; + else + nextline = 0; + + // prime the (tiny) sliding-window pixel arrays + pix[3] = src_p[prevline]; + pix[6] = src_p[0]; + pix[9] = src_p[nextline]; + + yuv[3] = SCALE_TOYUV (pix[3]); + yuv[6] = SCALE_TOYUV (pix[6]); + yuv[9] = SCALE_TOYUV (pix[9]); + + if (region->x > 0) + { + pix[2] = src_p[prevline - 1]; + pix[5] = src_p[-1]; + pix[8] = src_p[nextline - 1]; + + yuv[2] = SCALE_TOYUV (pix[2]); + yuv[5] = SCALE_TOYUV (pix[5]); + yuv[8] = SCALE_TOYUV (pix[8]); + } + else + { + pix[2] = pix[3]; + pix[5] = pix[6]; + pix[8] = pix[9]; + + yuv[2] = yuv[3]; + yuv[5] = yuv[6]; + yuv[8] = yuv[9]; + } + + for (x = region->x; x < xend; ++x, ++src_p, dst_p += 2) + { + int pattern = 0; + + // slide the window + pix[1] = pix[2]; + pix[4] = pix[5]; + pix[7] = pix[8]; + + yuv[1] = yuv[2]; + yuv[4] = yuv[5]; + yuv[7] = yuv[8]; + + pix[2] = pix[3]; + pix[5] = pix[6]; + pix[8] = pix[9]; + + yuv[2] = yuv[3]; + yuv[5] = yuv[6]; + yuv[8] = yuv[9]; + + if (x < w - 1) + { + pix[3] = src_p[prevline + 1]; + pix[6] = src_p[1]; + pix[9] = src_p[nextline + 1]; + + yuv[3] = SCALE_TOYUV (pix[3]); + yuv[6] = SCALE_TOYUV (pix[6]); + yuv[9] = SCALE_TOYUV (pix[9]); + } + else + { + pix[3] = pix[2]; + pix[6] = pix[5]; + pix[9] = pix[8]; + + yuv[3] = yuv[2]; + yuv[6] = yuv[5]; + yuv[9] = yuv[8]; + } + + // this runs much faster with branching removed + pattern |= HQXX_DIFFYUV (yuv[5], yuv[1]) & 0x0001; + pattern |= HQXX_DIFFYUV (yuv[5], yuv[2]) & 0x0002; + pattern |= HQXX_DIFFYUV (yuv[5], yuv[3]) & 0x0004; + pattern |= HQXX_DIFFYUV (yuv[5], yuv[4]) & 0x0008; + pattern |= HQXX_DIFFYUV (yuv[5], yuv[6]) & 0x0010; + pattern |= HQXX_DIFFYUV (yuv[5], yuv[7]) & 0x0020; + pattern |= HQXX_DIFFYUV (yuv[5], yuv[8]) & 0x0040; + pattern |= HQXX_DIFFYUV (yuv[5], yuv[9]) & 0x0080; + + switch (pattern) + { + case 0: + case 1: + case 4: + case 32: + case 128: + case 5: + case 132: + case 160: + case 33: + case 129: + case 36: + case 133: + case 164: + case 161: + case 37: + case 165: + { + PIXEL00_20 + PIXEL01_20 + PIXEL10_20 + PIXEL11_20 + break; + } + case 2: + case 34: + case 130: + case 162: + { + PIXEL00_22 + PIXEL01_21 + PIXEL10_20 + PIXEL11_20 + break; + } + case 16: + case 17: + case 48: + case 49: + { + PIXEL00_20 + PIXEL01_22 + PIXEL10_20 + PIXEL11_21 + break; + } + case 64: + case 65: + case 68: + case 69: + { + PIXEL00_20 + PIXEL01_20 + PIXEL10_21 + PIXEL11_22 + break; + } + case 8: + case 12: + case 136: + case 140: + { + PIXEL00_21 + PIXEL01_20 + PIXEL10_22 + PIXEL11_20 + break; + } + case 3: + case 35: + case 131: + case 163: + { + PIXEL00_11 + PIXEL01_21 + PIXEL10_20 + PIXEL11_20 + break; + } + case 6: + case 38: + case 134: + case 166: + { + PIXEL00_22 + PIXEL01_12 + PIXEL10_20 + PIXEL11_20 + break; + } + case 20: + case 21: + case 52: + case 53: + { + PIXEL00_20 + PIXEL01_11 + PIXEL10_20 + PIXEL11_21 + break; + } + case 144: + case 145: + case 176: + case 177: + { + PIXEL00_20 + PIXEL01_22 + PIXEL10_20 + PIXEL11_12 + break; + } + case 192: + case 193: + case 196: + case 197: + { + PIXEL00_20 + PIXEL01_20 + PIXEL10_21 + PIXEL11_11 + break; + } + case 96: + case 97: + case 100: + case 101: + { + PIXEL00_20 + PIXEL01_20 + PIXEL10_12 + PIXEL11_22 + break; + } + case 40: + case 44: + case 168: + case 172: + { + PIXEL00_21 + PIXEL01_20 + PIXEL10_11 + PIXEL11_20 + break; + } + case 9: + case 13: + case 137: + case 141: + { + PIXEL00_12 + PIXEL01_20 + PIXEL10_22 + PIXEL11_20 + break; + } + case 18: + case 50: + { + PIXEL00_22 + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_20 + } + PIXEL10_20 + PIXEL11_21 + break; + } + case 80: + case 81: + { + PIXEL00_20 + PIXEL01_22 + PIXEL10_21 + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_20 + } + break; + } + case 72: + case 76: + { + PIXEL00_21 + PIXEL01_20 + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_20 + } + PIXEL11_22 + break; + } + case 10: + case 138: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_20 + } + PIXEL01_21 + PIXEL10_22 + PIXEL11_20 + break; + } + case 66: + { + PIXEL00_22 + PIXEL01_21 + PIXEL10_21 + PIXEL11_22 + break; + } + case 24: + { + PIXEL00_21 + PIXEL01_22 + PIXEL10_22 + PIXEL11_21 + break; + } + case 7: + case 39: + case 135: + { + PIXEL00_11 + PIXEL01_12 + PIXEL10_20 + PIXEL11_20 + break; + } + case 148: + case 149: + case 180: + { + PIXEL00_20 + PIXEL01_11 + PIXEL10_20 + PIXEL11_12 + break; + } + case 224: + case 228: + case 225: + { + PIXEL00_20 + PIXEL01_20 + PIXEL10_12 + PIXEL11_11 + break; + } + case 41: + case 169: + case 45: + { + PIXEL00_12 + PIXEL01_20 + PIXEL10_11 + PIXEL11_20 + break; + } + case 22: + case 54: + { + PIXEL00_22 + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_20 + PIXEL11_21 + break; + } + case 208: + case 209: + { + PIXEL00_20 + PIXEL01_22 + PIXEL10_21 + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 104: + case 108: + { + PIXEL00_21 + PIXEL01_20 + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_22 + break; + } + case 11: + case 139: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_21 + PIXEL10_22 + PIXEL11_20 + break; + } + case 19: + case 51: + { + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL00_11 + PIXEL01_10 + } + else + { + PIXEL00_60 + PIXEL01_90 + } + PIXEL10_20 + PIXEL11_21 + break; + } + case 146: + case 178: + { + PIXEL00_22 + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_10 + PIXEL11_12 + } + else + { + PIXEL01_90 + PIXEL11_61 + } + PIXEL10_20 + break; + } + case 84: + case 85: + { + PIXEL00_20 + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL01_11 + PIXEL11_10 + } + else + { + PIXEL01_60 + PIXEL11_90 + } + PIXEL10_21 + break; + } + case 112: + case 113: + { + PIXEL00_20 + PIXEL01_22 + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL10_12 + PIXEL11_10 + } + else + { + PIXEL10_61 + PIXEL11_90 + } + break; + } + case 200: + case 204: + { + PIXEL00_21 + PIXEL01_20 + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_10 + PIXEL11_11 + } + else + { + PIXEL10_90 + PIXEL11_60 + } + break; + } + case 73: + case 77: + { + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL00_12 + PIXEL10_10 + } + else + { + PIXEL00_61 + PIXEL10_90 + } + PIXEL01_20 + PIXEL11_22 + break; + } + case 42: + case 170: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_10 + PIXEL10_11 + } + else + { + PIXEL00_90 + PIXEL10_60 + } + PIXEL01_21 + PIXEL11_20 + break; + } + case 14: + case 142: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_10 + PIXEL01_12 + } + else + { + PIXEL00_90 + PIXEL01_61 + } + PIXEL10_22 + PIXEL11_20 + break; + } + case 67: + { + PIXEL00_11 + PIXEL01_21 + PIXEL10_21 + PIXEL11_22 + break; + } + case 70: + { + PIXEL00_22 + PIXEL01_12 + PIXEL10_21 + PIXEL11_22 + break; + } + case 28: + { + PIXEL00_21 + PIXEL01_11 + PIXEL10_22 + PIXEL11_21 + break; + } + case 152: + { + PIXEL00_21 + PIXEL01_22 + PIXEL10_22 + PIXEL11_12 + break; + } + case 194: + { + PIXEL00_22 + PIXEL01_21 + PIXEL10_21 + PIXEL11_11 + break; + } + case 98: + { + PIXEL00_22 + PIXEL01_21 + PIXEL10_12 + PIXEL11_22 + break; + } + case 56: + { + PIXEL00_21 + PIXEL01_22 + PIXEL10_11 + PIXEL11_21 + break; + } + case 25: + { + PIXEL00_12 + PIXEL01_22 + PIXEL10_22 + PIXEL11_21 + break; + } + case 26: + case 31: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_22 + PIXEL11_21 + break; + } + case 82: + case 214: + { + PIXEL00_22 + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_21 + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 88: + case 248: + { + PIXEL00_21 + PIXEL01_22 + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 74: + case 107: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_21 + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_22 + break; + } + case 27: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_10 + PIXEL10_22 + PIXEL11_21 + break; + } + case 86: + { + PIXEL00_22 + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_21 + PIXEL11_10 + break; + } + case 216: + { + PIXEL00_21 + PIXEL01_22 + PIXEL10_10 + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 106: + { + PIXEL00_10 + PIXEL01_21 + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_22 + break; + } + case 30: + { + PIXEL00_10 + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_22 + PIXEL11_21 + break; + } + case 210: + { + PIXEL00_22 + PIXEL01_10 + PIXEL10_21 + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 120: + { + PIXEL00_21 + PIXEL01_22 + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_10 + break; + } + case 75: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_21 + PIXEL10_10 + PIXEL11_22 + break; + } + case 29: + { + PIXEL00_12 + PIXEL01_11 + PIXEL10_22 + PIXEL11_21 + break; + } + case 198: + { + PIXEL00_22 + PIXEL01_12 + PIXEL10_21 + PIXEL11_11 + break; + } + case 184: + { + PIXEL00_21 + PIXEL01_22 + PIXEL10_11 + PIXEL11_12 + break; + } + case 99: + { + PIXEL00_11 + PIXEL01_21 + PIXEL10_12 + PIXEL11_22 + break; + } + case 57: + { + PIXEL00_12 + PIXEL01_22 + PIXEL10_11 + PIXEL11_21 + break; + } + case 71: + { + PIXEL00_11 + PIXEL01_12 + PIXEL10_21 + PIXEL11_22 + break; + } + case 156: + { + PIXEL00_21 + PIXEL01_11 + PIXEL10_22 + PIXEL11_12 + break; + } + case 226: + { + PIXEL00_22 + PIXEL01_21 + PIXEL10_12 + PIXEL11_11 + break; + } + case 60: + { + PIXEL00_21 + PIXEL01_11 + PIXEL10_11 + PIXEL11_21 + break; + } + case 195: + { + PIXEL00_11 + PIXEL01_21 + PIXEL10_21 + PIXEL11_11 + break; + } + case 102: + { + PIXEL00_22 + PIXEL01_12 + PIXEL10_12 + PIXEL11_22 + break; + } + case 153: + { + PIXEL00_12 + PIXEL01_22 + PIXEL10_22 + PIXEL11_12 + break; + } + case 58: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_11 + PIXEL11_21 + break; + } + case 83: + { + PIXEL00_11 + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_21 + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 92: + { + PIXEL00_21 + PIXEL01_11 + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 202: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + PIXEL01_21 + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + PIXEL11_11 + break; + } + case 78: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + PIXEL01_12 + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + PIXEL11_22 + break; + } + case 154: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_22 + PIXEL11_12 + break; + } + case 114: + { + PIXEL00_22 + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_12 + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 89: + { + PIXEL00_12 + PIXEL01_22 + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 90: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 55: + case 23: + { + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL00_11 + PIXEL01_0 + } + else + { + PIXEL00_60 + PIXEL01_90 + } + PIXEL10_20 + PIXEL11_21 + break; + } + case 182: + case 150: + { + PIXEL00_22 + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_0 + PIXEL11_12 + } + else + { + PIXEL01_90 + PIXEL11_61 + } + PIXEL10_20 + break; + } + case 213: + case 212: + { + PIXEL00_20 + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL01_11 + PIXEL11_0 + } + else + { + PIXEL01_60 + PIXEL11_90 + } + PIXEL10_21 + break; + } + case 241: + case 240: + { + PIXEL00_20 + PIXEL01_22 + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL10_12 + PIXEL11_0 + } + else + { + PIXEL10_61 + PIXEL11_90 + } + break; + } + case 236: + case 232: + { + PIXEL00_21 + PIXEL01_20 + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_0 + PIXEL11_11 + } + else + { + PIXEL10_90 + PIXEL11_60 + } + break; + } + case 109: + case 105: + { + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL00_12 + PIXEL10_0 + } + else + { + PIXEL00_61 + PIXEL10_90 + } + PIXEL01_20 + PIXEL11_22 + break; + } + case 171: + case 43: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_0 + PIXEL10_11 + } + else + { + PIXEL00_90 + PIXEL10_60 + } + PIXEL01_21 + PIXEL11_20 + break; + } + case 143: + case 15: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_0 + PIXEL01_12 + } + else + { + PIXEL00_90 + PIXEL01_61 + } + PIXEL10_22 + PIXEL11_20 + break; + } + case 124: + { + PIXEL00_21 + PIXEL01_11 + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_10 + break; + } + case 203: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_21 + PIXEL10_10 + PIXEL11_11 + break; + } + case 62: + { + PIXEL00_10 + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_11 + PIXEL11_21 + break; + } + case 211: + { + PIXEL00_11 + PIXEL01_10 + PIXEL10_21 + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 118: + { + PIXEL00_22 + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_12 + PIXEL11_10 + break; + } + case 217: + { + PIXEL00_12 + PIXEL01_22 + PIXEL10_10 + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 110: + { + PIXEL00_10 + PIXEL01_12 + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_22 + break; + } + case 155: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_10 + PIXEL10_22 + PIXEL11_12 + break; + } + case 188: + { + PIXEL00_21 + PIXEL01_11 + PIXEL10_11 + PIXEL11_12 + break; + } + case 185: + { + PIXEL00_12 + PIXEL01_22 + PIXEL10_11 + PIXEL11_12 + break; + } + case 61: + { + PIXEL00_12 + PIXEL01_11 + PIXEL10_11 + PIXEL11_21 + break; + } + case 157: + { + PIXEL00_12 + PIXEL01_11 + PIXEL10_22 + PIXEL11_12 + break; + } + case 103: + { + PIXEL00_11 + PIXEL01_12 + PIXEL10_12 + PIXEL11_22 + break; + } + case 227: + { + PIXEL00_11 + PIXEL01_21 + PIXEL10_12 + PIXEL11_11 + break; + } + case 230: + { + PIXEL00_22 + PIXEL01_12 + PIXEL10_12 + PIXEL11_11 + break; + } + case 199: + { + PIXEL00_11 + PIXEL01_12 + PIXEL10_21 + PIXEL11_11 + break; + } + case 220: + { + PIXEL00_21 + PIXEL01_11 + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 158: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_22 + PIXEL11_12 + break; + } + case 234: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + PIXEL01_21 + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_11 + break; + } + case 242: + { + PIXEL00_22 + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_12 + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 59: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_11 + PIXEL11_21 + break; + } + case 121: + { + PIXEL00_12 + PIXEL01_22 + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 87: + { + PIXEL00_11 + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_21 + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 79: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_12 + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + PIXEL11_22 + break; + } + case 122: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 94: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 218: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 91: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 229: + { + PIXEL00_20 + PIXEL01_20 + PIXEL10_12 + PIXEL11_11 + break; + } + case 167: + { + PIXEL00_11 + PIXEL01_12 + PIXEL10_20 + PIXEL11_20 + break; + } + case 173: + { + PIXEL00_12 + PIXEL01_20 + PIXEL10_11 + PIXEL11_20 + break; + } + case 181: + { + PIXEL00_20 + PIXEL01_11 + PIXEL10_20 + PIXEL11_12 + break; + } + case 186: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_11 + PIXEL11_12 + break; + } + case 115: + { + PIXEL00_11 + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_12 + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 93: + { + PIXEL00_12 + PIXEL01_11 + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 206: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + PIXEL01_12 + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + PIXEL11_11 + break; + } + case 205: + case 201: + { + PIXEL00_12 + PIXEL01_20 + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + PIXEL11_11 + break; + } + case 174: + case 46: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + PIXEL01_12 + PIXEL10_11 + PIXEL11_20 + break; + } + case 179: + case 147: + { + PIXEL00_11 + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_20 + PIXEL11_12 + break; + } + case 117: + case 116: + { + PIXEL00_20 + PIXEL01_11 + PIXEL10_12 + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 189: + { + PIXEL00_12 + PIXEL01_11 + PIXEL10_11 + PIXEL11_12 + break; + } + case 231: + { + PIXEL00_11 + PIXEL01_12 + PIXEL10_12 + PIXEL11_11 + break; + } + case 126: + { + PIXEL00_10 + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_10 + break; + } + case 219: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_10 + PIXEL10_10 + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 125: + { + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL00_12 + PIXEL10_0 + } + else + { + PIXEL00_61 + PIXEL10_90 + } + PIXEL01_11 + PIXEL11_10 + break; + } + case 221: + { + PIXEL00_12 + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL01_11 + PIXEL11_0 + } + else + { + PIXEL01_60 + PIXEL11_90 + } + PIXEL10_10 + break; + } + case 207: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_0 + PIXEL01_12 + } + else + { + PIXEL00_90 + PIXEL01_61 + } + PIXEL10_10 + PIXEL11_11 + break; + } + case 238: + { + PIXEL00_10 + PIXEL01_12 + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_0 + PIXEL11_11 + } + else + { + PIXEL10_90 + PIXEL11_60 + } + break; + } + case 190: + { + PIXEL00_10 + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_0 + PIXEL11_12 + } + else + { + PIXEL01_90 + PIXEL11_61 + } + PIXEL10_11 + break; + } + case 187: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_0 + PIXEL10_11 + } + else + { + PIXEL00_90 + PIXEL10_60 + } + PIXEL01_10 + PIXEL11_12 + break; + } + case 243: + { + PIXEL00_11 + PIXEL01_10 + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL10_12 + PIXEL11_0 + } + else + { + PIXEL10_61 + PIXEL11_90 + } + break; + } + case 119: + { + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL00_11 + PIXEL01_0 + } + else + { + PIXEL00_60 + PIXEL01_90 + } + PIXEL10_12 + PIXEL11_10 + break; + } + case 237: + case 233: + { + PIXEL00_12 + PIXEL01_20 + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_100 + } + PIXEL11_11 + break; + } + case 175: + case 47: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_100 + } + PIXEL01_12 + PIXEL10_11 + PIXEL11_20 + break; + } + case 183: + case 151: + { + PIXEL00_11 + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_100 + } + PIXEL10_20 + PIXEL11_12 + break; + } + case 245: + case 244: + { + PIXEL00_20 + PIXEL01_11 + PIXEL10_12 + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_100 + } + break; + } + case 250: + { + PIXEL00_10 + PIXEL01_10 + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 123: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_10 + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_10 + break; + } + case 95: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_10 + PIXEL11_10 + break; + } + case 222: + { + PIXEL00_10 + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_10 + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 252: + { + PIXEL00_21 + PIXEL01_11 + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_100 + } + break; + } + case 249: + { + PIXEL00_12 + PIXEL01_22 + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_100 + } + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 235: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_21 + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_100 + } + PIXEL11_11 + break; + } + case 111: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_100 + } + PIXEL01_12 + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_22 + break; + } + case 63: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_100 + } + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_11 + PIXEL11_21 + break; + } + case 159: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_100 + } + PIXEL10_22 + PIXEL11_12 + break; + } + case 215: + { + PIXEL00_11 + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_100 + } + PIXEL10_21 + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 246: + { + PIXEL00_22 + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_12 + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_100 + } + break; + } + case 254: + { + PIXEL00_10 + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_100 + } + break; + } + case 253: + { + PIXEL00_12 + PIXEL01_11 + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_100 + } + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_100 + } + break; + } + case 251: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_10 + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_100 + } + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 239: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_100 + } + PIXEL01_12 + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_100 + } + PIXEL11_11 + break; + } + case 127: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_100 + } + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_10 + break; + } + case 191: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_100 + } + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_100 + } + PIXEL10_11 + PIXEL11_12 + break; + } + case 223: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_100 + } + PIXEL10_10 + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 247: + { + PIXEL00_11 + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_100 + } + PIXEL10_12 + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_100 + } + break; + } + case 255: + { + if (HQXX_DIFFYUV (yuv[4], yuv[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_100 + } + if (HQXX_DIFFYUV (yuv[2], yuv[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_100 + } + if (HQXX_DIFFYUV (yuv[8], yuv[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_100 + } + if (HQXX_DIFFYUV (yuv[6], yuv[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_100 + } + break; + } + } + } + } + + SCALE_(PlatDone) (); +} + diff --git a/src/libs/graphics/sdl/nearest2x.c b/src/libs/graphics/sdl/nearest2x.c new file mode 100644 index 0000000..42e6813 --- /dev/null +++ b/src/libs/graphics/sdl/nearest2x.c @@ -0,0 +1,207 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +// Core algorithm of the BiLinear screen scaler +// Template +// When this file is built standalone is produces a plain C version +// Also #included by 2xscalers_mmx.c for an MMX version + +#include "libs/graphics/sdl/sdl_common.h" +#include "types.h" +#include "scalers.h" +#include "scaleint.h" +#include "2xscalers.h" + +// Nearest Neighbor scaling to 2x +// The name expands to +// Scale_Nearest (for plain C) +// Scale_MMX_Nearest (for MMX) +// Scale_SSE_Nearest (for SSE) +// [others when platforms are added] +void +SCALE_(Nearest) (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r) +{ + int y; + const int rw = r->w, rh = r->h; + const int sp = src->pitch, dp = dst->pitch; + const int bpp = dst->format->BytesPerPixel; + const int slen = sp / bpp, dlen = dp / bpp; + const int dsrc = slen-rw, ddst = (dlen-rw) * 2; + + Uint32 *src_p = (Uint32 *)src->pixels; + Uint32 *dst_p = (Uint32 *)dst->pixels; + + // guard asm code against such atrocities + if (rw == 0 || rh == 0) + return; + + SCALE_(PlatInit) (); + + // move ptrs to the first updated pixel + src_p += slen * r->y + r->x; + dst_p += (dlen * r->y + r->x) * 2; + +#if defined(MMX_ASM) && defined(MSVC_ASM) + // Just about everything has to be done in asm for MSVC + // to actually take advantage of asm here + // MSVC does not support beautiful GCC-like asm templates + + y = rh; + __asm + { + // setup vars + mov esi, src_p + mov edi, dst_p + + PREFETCH (esi + 0x40) + PREFETCH (esi + 0x80) + PREFETCH (esi + 0xc0) + + mov edx, dlen + lea edx, [edx * 4] + mov eax, dsrc + lea eax, [eax * 4] + mov ebx, ddst + lea ebx, [ebx * 4] + + mov ecx, rw + loop_y: + test ecx, 1 + jz even_x + + // one-pixel transfer + movd mm1, [esi] + punpckldq mm1, mm1 // pix1 | pix1 -> mm1 + add esi, 4 + MOVNTQ (edi, mm1) + add edi, 8 + MOVNTQ (edi - 8 + edx, mm1) + + even_x: + shr ecx, 1 // x = rw / 2 + jz end_x // rw was 1 + + loop_x: + // two-pixel transfer + movq mm1, [esi] + movq mm2, mm1 + PREFETCH (esi + 0x100) + punpckldq mm1, mm1 // pix1 | pix1 -> mm1 + add esi, 8 + MOVNTQ (edi, mm1) + punpckhdq mm2, mm2 // pix2 | pix2 -> mm2 + MOVNTQ (edi + edx, mm1) + add edi, 16 + MOVNTQ (edi - 8, mm2) + MOVNTQ (edi - 8 + edx, mm2) + + dec ecx + jnz loop_x + + end_x: + // try to prefetch as early as possible to have it on time + PREFETCH (esi + eax) + + mov ecx, rw + add esi, eax + + PREFETCH (esi + 0x40) + PREFETCH (esi + 0x80) + PREFETCH (esi + 0xc0) + + add edi, ebx + + dec y + jnz loop_y + } + +#elif defined(MMX_ASM) && defined(GCC_ASM) + + SCALE_(Prefetch) (src_p + 16); + SCALE_(Prefetch) (src_p + 32); + SCALE_(Prefetch) (src_p + 48); + + for (y = rh; y; --y) + { + int x = rw; + + if (x & 1) + { // one-pixel transfer + __asm__ ( + "movd (%0), %%mm1 \n\t" + "punpckldq %%mm1, %%mm1 \n\t" + MOVNTQ (%%mm1, (%1)) "\n\t" + MOVNTQ (%%mm1, (%1,%2)) "\n\t" + + : /* nothing */ + : /*0*/"r" (src_p), /*1*/"r" (dst_p), /*2*/"r" (dlen*sizeof(Uint32)) + ); + + ++src_p; + dst_p += 2; + --x; + } + + for (x >>= 1; x; --x, src_p += 2, dst_p += 4) + { // two-pixel transfer + __asm__ ( + "movq (%0), %%mm1 \n\t" + "movq %%mm1, %%mm2 \n\t" + PREFETCH (0x100(%0)) "\n\t" + "punpckldq %%mm1, %%mm1 \n\t" + MOVNTQ (%%mm1, (%1)) "\n\t" + MOVNTQ (%%mm1, (%1,%2)) "\n\t" + "punpckhdq %%mm2, %%mm2 \n\t" + MOVNTQ (%%mm2, 8(%1)) "\n\t" + MOVNTQ (%%mm2, 8(%1,%2)) "\n\t" + + : /* nothing */ + : /*0*/"r" (src_p), /*1*/"r" (dst_p), /*2*/"r" (dlen*sizeof(Uint32)) + ); + } + + src_p += dsrc; + // try to prefetch as early as possible to have it on time + SCALE_(Prefetch) (src_p); + + dst_p += ddst; + + SCALE_(Prefetch) (src_p + 16); + SCALE_(Prefetch) (src_p + 32); + SCALE_(Prefetch) (src_p + 48); + } + +#else + // Plain C version + for (y = 0; y < rh; ++y) + { + int x; + for (x = 0; x < rw; ++x, ++src_p, dst_p += 2) + { + Uint32 pix = *src_p; + dst_p[0] = pix; + dst_p[1] = pix; + dst_p[dlen] = pix; + dst_p[dlen + 1] = pix; + } + dst_p += ddst; + src_p += dsrc; + } +#endif + + SCALE_(PlatDone) (); +} + diff --git a/src/libs/graphics/sdl/opengl.c b/src/libs/graphics/sdl/opengl.c new file mode 100644 index 0000000..d8fe33f --- /dev/null +++ b/src/libs/graphics/sdl/opengl.c @@ -0,0 +1,575 @@ +//Copyright Paul Reiche, Fred Ford. 1992-2002 + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_OPENGL + +#include "libs/graphics/sdl/opengl.h" +#include "libs/graphics/bbox.h" +#include "scalers.h" +#include "options.h" +#include "libs/log.h" + +#if SDL_MAJOR_VERSION == 1 + +typedef struct _gl_screeninfo { + SDL_Surface *scaled; + GLuint texture; + BOOLEAN dirty, active; + SDL_Rect updated; +} TFB_GL_SCREENINFO; + +static TFB_GL_SCREENINFO GL_Screens[TFB_GFX_NUMSCREENS]; + +static int ScreenFilterMode; + +static TFB_ScaleFunc scaler = NULL; +static BOOLEAN first_init = TRUE; + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN +#define R_MASK 0xff000000 +#define G_MASK 0x00ff0000 +#define B_MASK 0x0000ff00 +#define A_MASK 0x000000ff +#else +#define R_MASK 0x000000ff +#define G_MASK 0x0000ff00 +#define B_MASK 0x00ff0000 +#define A_MASK 0xff000000 +#endif + +static void TFB_GL_Preprocess (int force_full_redraw, int transition_amount, int fade_amount); +static void TFB_GL_Postprocess (void); +static void TFB_GL_UploadTransitionScreen (void); +static void TFB_GL_Scaled_ScreenLayer (SCREEN screen, Uint8 a, SDL_Rect *rect); +static void TFB_GL_Unscaled_ScreenLayer (SCREEN screen, Uint8 a, SDL_Rect *rect); +static void TFB_GL_ColorLayer (Uint8 r, Uint8 g, Uint8 b, Uint8 a, SDL_Rect *rect); + +static TFB_GRAPHICS_BACKEND opengl_scaled_backend = { + TFB_GL_Preprocess, + TFB_GL_Postprocess, + TFB_GL_UploadTransitionScreen, + TFB_GL_Scaled_ScreenLayer, + TFB_GL_ColorLayer }; + +static TFB_GRAPHICS_BACKEND opengl_unscaled_backend = { + TFB_GL_Preprocess, + TFB_GL_Postprocess, + TFB_GL_UploadTransitionScreen, + TFB_GL_Unscaled_ScreenLayer, + TFB_GL_ColorLayer }; + + +static int +AttemptColorDepth (int flags, int width, int height, int bpp) +{ + SDL_Surface *SDL_Video; + int videomode_flags; + ScreenColorDepth = bpp; + ScreenWidthActual = width; + ScreenHeightActual = height; + + switch (bpp) { + case 15: + SDL_GL_SetAttribute (SDL_GL_RED_SIZE, 5); + SDL_GL_SetAttribute (SDL_GL_GREEN_SIZE, 5); + SDL_GL_SetAttribute (SDL_GL_BLUE_SIZE, 5); + break; + + case 16: + SDL_GL_SetAttribute (SDL_GL_RED_SIZE, 5); + SDL_GL_SetAttribute (SDL_GL_GREEN_SIZE, 6); + SDL_GL_SetAttribute (SDL_GL_BLUE_SIZE, 5); + break; + + case 24: + SDL_GL_SetAttribute (SDL_GL_RED_SIZE, 8); + SDL_GL_SetAttribute (SDL_GL_GREEN_SIZE, 8); + SDL_GL_SetAttribute (SDL_GL_BLUE_SIZE, 8); + break; + + case 32: + SDL_GL_SetAttribute (SDL_GL_RED_SIZE, 8); + SDL_GL_SetAttribute (SDL_GL_GREEN_SIZE, 8); + SDL_GL_SetAttribute (SDL_GL_BLUE_SIZE, 8); + break; + default: + break; + } + + SDL_GL_SetAttribute (SDL_GL_DEPTH_SIZE, 0); + SDL_GL_SetAttribute (SDL_GL_DOUBLEBUFFER, 1); + + videomode_flags = SDL_OPENGL; + if (flags & TFB_GFXFLAGS_FULLSCREEN) + videomode_flags |= SDL_FULLSCREEN; + videomode_flags |= SDL_ANYFORMAT; + + SDL_Video = SDL_SetVideoMode (ScreenWidthActual, ScreenHeightActual, + bpp, videomode_flags); + if (SDL_Video == NULL) + { + log_add (log_Error, "Couldn't set OpenGL %ix%ix%i video mode: %s", + ScreenWidthActual, ScreenHeightActual, bpp, + SDL_GetError ()); + return -1; + } + else + { + log_add (log_Info, "Set the resolution to: %ix%ix%i" + " (surface reports %ix%ix%i)", + width, height, bpp, + SDL_GetVideoSurface()->w, SDL_GetVideoSurface()->h, + SDL_GetVideoSurface()->format->BitsPerPixel); + + log_add (log_Info, "OpenGL renderer: %s version: %s", + glGetString (GL_RENDERER), glGetString (GL_VERSION)); + } + return 0; +} + +int +TFB_GL_ConfigureVideo (int driver, int flags, int width, int height, int togglefullscreen) +{ + int i, texture_width, texture_height; + GraphicsDriver = driver; + + if (AttemptColorDepth (flags, width, height, 32) && + AttemptColorDepth (flags, width, height, 24) && + AttemptColorDepth (flags, width, height, 16)) + { + log_add (log_Error, "Couldn't set any OpenGL %ix%i video mode!", + width, height); + return -1; + } + + if (!togglefullscreen) + { + if (format_conv_surf) + SDL_FreeSurface (format_conv_surf); + format_conv_surf = SDL_CreateRGBSurface (SDL_SWSURFACE, 0, 0, 32, + R_MASK, G_MASK, B_MASK, A_MASK); + if (format_conv_surf == NULL) + { + log_add (log_Error, "Couldn't create format_conv_surf: %s", + SDL_GetError()); + return -1; + } + + for (i = 0; i < TFB_GFX_NUMSCREENS; i++) + { + if (0 != SDL1_ReInit_Screen (&SDL_Screens[i], format_conv_surf, + ScreenWidth, ScreenHeight)) + return -1; + } + + SDL_Screen = SDL_Screens[0]; + TransitionScreen = SDL_Screens[2]; + + if (first_init) + { + for (i = 0; i < TFB_GFX_NUMSCREENS; i++) + { + GL_Screens[i].scaled = NULL; + GL_Screens[i].dirty = TRUE; + GL_Screens[i].active = TRUE; + } + GL_Screens[1].active = FALSE; + first_init = FALSE; + } + } + + if (GfxFlags & TFB_GFXFLAGS_SCALE_SOFT_ONLY) + { + if (!togglefullscreen) + { + for (i = 0; i < TFB_GFX_NUMSCREENS; i++) + { + if (!GL_Screens[i].active) + continue; + if (0 != SDL1_ReInit_Screen (&GL_Screens[i].scaled, format_conv_surf, + ScreenWidth * 2, ScreenHeight * 2)) + return -1; + } + scaler = Scale_PrepPlatform (flags, SDL_Screen->format); + } + + texture_width = 1024; + texture_height = 512; + + graphics_backend = &opengl_scaled_backend; + } + else + { + texture_width = 512; + texture_height = 256; + + scaler = NULL; + graphics_backend = &opengl_unscaled_backend; + } + + + if (GfxFlags & TFB_GFXFLAGS_SCALE_ANY) + ScreenFilterMode = GL_LINEAR; + else + ScreenFilterMode = GL_NEAREST; + + glViewport (0, 0, ScreenWidthActual, ScreenHeightActual); + glClearColor (0,0,0,0); + glClear (GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + SDL_GL_SwapBuffers (); + glClear (GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + glDisable (GL_DITHER); + glDepthMask(GL_FALSE); + + for (i = 0; i < TFB_GFX_NUMSCREENS; i++) + { + if (!GL_Screens[i].active) + continue; + glGenTextures (1, &GL_Screens[i].texture); + glBindTexture (GL_TEXTURE_2D, GL_Screens[i].texture); + glPixelStorei (GL_UNPACK_ALIGNMENT, 1); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB, texture_width, texture_height, + 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + } + + return 0; +} + +int +TFB_GL_InitGraphics (int driver, int flags, int width, int height) +{ + char VideoName[256]; + + log_add (log_Info, "Initializing SDL with OpenGL support."); + + SDL_VideoDriverName (VideoName, sizeof (VideoName)); + log_add (log_Info, "SDL driver used: %s", VideoName); + log_add (log_Info, "SDL initialized."); + log_add (log_Info, "Initializing Screen."); + + ScreenWidth = 320; + ScreenHeight = 240; + + if (TFB_GL_ConfigureVideo (driver, flags, width, height, 0)) + { + log_add (log_Fatal, "Could not initialize video: " + "no fallback at start of program!"); + exit (EXIT_FAILURE); + } + + // Initialize scalers (let them precompute whatever) + Scale_Init (); + + return 0; +} + +void +TFB_GL_UninitGraphics (void) +{ + int i; + + for (i = 0; i < TFB_GFX_NUMSCREENS; i++) { + UnInit_Screen (&GL_Screens[i].scaled); + } +} + +static void +TFB_GL_UploadTransitionScreen (void) +{ + GL_Screens[TFB_SCREEN_TRANSITION].updated.x = 0; + GL_Screens[TFB_SCREEN_TRANSITION].updated.y = 0; + GL_Screens[TFB_SCREEN_TRANSITION].updated.w = ScreenWidth; + GL_Screens[TFB_SCREEN_TRANSITION].updated.h = ScreenHeight; + GL_Screens[TFB_SCREEN_TRANSITION].dirty = TRUE; +} + +static void +TFB_GL_ScanLines (void) +{ + int y; + + glDisable (GL_TEXTURE_2D); + glEnable (GL_BLEND); + glBlendFunc (GL_DST_COLOR, GL_ZERO); + glColor3f (0.85f, 0.85f, 0.85f); + for (y = 0; y < ScreenHeightActual; y += 2) + { + glBegin (GL_LINES); + glVertex2i (0, y); + glVertex2i (ScreenWidthActual, y); + glEnd (); + } + + glBlendFunc (GL_DST_COLOR, GL_ONE); + glColor3f (0.2f, 0.2f, 0.2f); + for (y = 1; y < ScreenHeightActual; y += 2) + { + glBegin (GL_LINES); + glVertex2i (0, y); + glVertex2i (ScreenWidthActual, y); + glEnd (); + } +} + +static void +TFB_GL_DrawQuad (SDL_Rect *r) +{ + BOOLEAN keep_aspect_ratio = optKeepAspectRatio; + int x1 = 0, y1 = 0, x2 = ScreenWidthActual, y2 = ScreenHeightActual; + int sx = 0, sy = 0; + int sw, sh; + float sx_multiplier = 1; + float sy_multiplier = 1; + + if (keep_aspect_ratio) + { + float threshold = 0.75f; + float ratio = ScreenHeightActual / (float)ScreenWidthActual; + + if (ratio > threshold) + { + // screen is narrower than 4:3 + int height = (int)(ScreenWidthActual * threshold); + y1 = (ScreenHeightActual - height) / 2; + y2 = ScreenHeightActual - y1; + + if (r != NULL) + { + sx_multiplier = ScreenWidthActual / (float)ScreenWidth; + sy_multiplier = height / (float)ScreenHeight; + sx = (int)(r->x * sx_multiplier); + sy = (int)(((ScreenHeight - (r->y + r->h)) * sy_multiplier) + y1); + } + } + else if (ratio < threshold) + { + // screen is wider than 4:3 + int width = (int)(ScreenHeightActual / threshold); + x1 = (ScreenWidthActual - width) / 2; + x2 = ScreenWidthActual - x1; + + if (r != NULL) + { + sx_multiplier = width / (float)ScreenWidth; + sy_multiplier = ScreenHeightActual / (float)ScreenHeight; + sx = (int)((r->x * sx_multiplier) + x1); + sy = (int)((ScreenHeight - (r->y + r->h)) * sy_multiplier); + } + } + else + { + // screen is 4:3 + keep_aspect_ratio = 0; + } + } + + if (r != NULL) + { + if (!keep_aspect_ratio) + { + sx_multiplier = ScreenWidthActual / (float)ScreenWidth; + sy_multiplier = ScreenHeightActual / (float)ScreenHeight; + sx = (int)(r->x * sx_multiplier); + sy = (int)((ScreenHeight - (r->y + r->h)) * sy_multiplier); + } + sw = (int)(r->w * sx_multiplier); + sh = (int)(r->h * sy_multiplier); + glScissor (sx, sy, sw, sh); + glEnable (GL_SCISSOR_TEST); + } + + glBegin (GL_TRIANGLE_FAN); + glTexCoord2f (0, 0); + glVertex2i (x1, y1); + glTexCoord2f (ScreenWidth / 512.0f, 0); + glVertex2i (x2, y1); + glTexCoord2f (ScreenWidth / 512.0f, ScreenHeight / 256.0f); + glVertex2i (x2, y2); + glTexCoord2f (0, ScreenHeight / 256.0f); + glVertex2i (x1, y2); + glEnd (); + if (r != NULL) + { + glDisable (GL_SCISSOR_TEST); + } +} + +static void +TFB_GL_Preprocess (int force_full_redraw, int transition_amount, int fade_amount) +{ + glMatrixMode (GL_PROJECTION); + glLoadIdentity (); + glOrtho (0,ScreenWidthActual,ScreenHeightActual, 0, -1, 1); + glMatrixMode (GL_MODELVIEW); + glLoadIdentity (); + if (optKeepAspectRatio) + glClear (GL_COLOR_BUFFER_BIT); + + (void) transition_amount; + (void) fade_amount; + + if (force_full_redraw == TFB_REDRAW_YES) + { + GL_Screens[TFB_SCREEN_MAIN].updated.x = 0; + GL_Screens[TFB_SCREEN_MAIN].updated.y = 0; + GL_Screens[TFB_SCREEN_MAIN].updated.w = ScreenWidth; + GL_Screens[TFB_SCREEN_MAIN].updated.h = ScreenHeight; + GL_Screens[TFB_SCREEN_MAIN].dirty = TRUE; + } + else if (TFB_BBox.valid) + { + GL_Screens[TFB_SCREEN_MAIN].updated.x = TFB_BBox.region.corner.x; + GL_Screens[TFB_SCREEN_MAIN].updated.y = TFB_BBox.region.corner.y; + GL_Screens[TFB_SCREEN_MAIN].updated.w = TFB_BBox.region.extent.width; + GL_Screens[TFB_SCREEN_MAIN].updated.h = TFB_BBox.region.extent.height; + GL_Screens[TFB_SCREEN_MAIN].dirty = TRUE; + } +} + +static void +TFB_GL_Unscaled_ScreenLayer (SCREEN screen, Uint8 a, SDL_Rect *rect) +{ + glBindTexture (GL_TEXTURE_2D, GL_Screens[screen].texture); + glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + if (GL_Screens[screen].dirty) + { + int PitchWords = SDL_Screens[screen]->pitch / 4; + glPixelStorei (GL_UNPACK_ROW_LENGTH, PitchWords); + /* Matrox OpenGL drivers do not handle GL_UNPACK_SKIP_* + correctly */ + glPixelStorei (GL_UNPACK_SKIP_ROWS, 0); + glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0); + SDL_LockSurface (SDL_Screens[screen]); + glTexSubImage2D (GL_TEXTURE_2D, 0, GL_Screens[screen].updated.x, + GL_Screens[screen].updated.y, + GL_Screens[screen].updated.w, + GL_Screens[screen].updated.h, + GL_RGBA, GL_UNSIGNED_BYTE, + (Uint32 *)SDL_Screens[screen]->pixels + + (GL_Screens[screen].updated.y * PitchWords + + GL_Screens[screen].updated.x)); + SDL_UnlockSurface (SDL_Screens[screen]); + GL_Screens[screen].dirty = FALSE; + } + + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, ScreenFilterMode); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, ScreenFilterMode); + glEnable (GL_TEXTURE_2D); + + if (a == 255) + { + glDisable (GL_BLEND); + glColor4f (1, 1, 1, 1); + } + else + { + float a_f = a / 255.0f; + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable (GL_BLEND); + glColor4f (1, 1, 1, a_f); + } + + TFB_GL_DrawQuad (rect); +} + +static void +TFB_GL_Scaled_ScreenLayer (SCREEN screen, Uint8 a, SDL_Rect *rect) +{ + glBindTexture (GL_TEXTURE_2D, GL_Screens[screen].texture); + glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + if (GL_Screens[screen].dirty) + { + int PitchWords = GL_Screens[screen].scaled->pitch / 4; + scaler (SDL_Screens[screen], GL_Screens[screen].scaled, &GL_Screens[screen].updated); + glPixelStorei (GL_UNPACK_ROW_LENGTH, PitchWords); + + /* Matrox OpenGL drivers do not handle GL_UNPACK_SKIP_* + correctly */ + glPixelStorei (GL_UNPACK_SKIP_ROWS, 0); + glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0); + SDL_LockSurface (GL_Screens[screen].scaled); + glTexSubImage2D (GL_TEXTURE_2D, 0, GL_Screens[screen].updated.x * 2, + GL_Screens[screen].updated.y * 2, + GL_Screens[screen].updated.w * 2, + GL_Screens[screen].updated.h * 2, + GL_RGBA, GL_UNSIGNED_BYTE, + (Uint32 *)GL_Screens[screen].scaled->pixels + + (GL_Screens[screen].updated.y * 2 * PitchWords + + GL_Screens[screen].updated.x * 2)); + SDL_UnlockSurface (GL_Screens[screen].scaled); + GL_Screens[screen].dirty = FALSE; + } + + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, ScreenFilterMode); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, ScreenFilterMode); + glEnable (GL_TEXTURE_2D); + + if (a == 255) + { + glDisable (GL_BLEND); + glColor4f (1, 1, 1, 1); + } + else + { + float a_f = a / 255.0f; + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable (GL_BLEND); + glColor4f (1, 1, 1, a_f); + } + + TFB_GL_DrawQuad (rect); +} + +static void +TFB_GL_ColorLayer (Uint8 r, Uint8 g, Uint8 b, Uint8 a, SDL_Rect *rect) +{ + float r_f = r / 255.0f; + float g_f = g / 255.0f; + float b_f = b / 255.0f; + float a_f = a / 255.0f; + glColor4f(r_f, g_f, b_f, a_f); + + glDisable (GL_TEXTURE_2D); + if (a != 255) + { + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable (GL_BLEND); + } + else + { + glDisable (GL_BLEND); + } + + TFB_GL_DrawQuad (rect); +} + +static void +TFB_GL_Postprocess (void) +{ + if (GfxFlags & TFB_GFXFLAGS_SCANLINES) + TFB_GL_ScanLines (); + + SDL_GL_SwapBuffers (); +} + +#endif +#endif diff --git a/src/libs/graphics/sdl/opengl.h b/src/libs/graphics/sdl/opengl.h new file mode 100644 index 0000000..6550bca --- /dev/null +++ b/src/libs/graphics/sdl/opengl.h @@ -0,0 +1,89 @@ +//Copyright Paul Reiche, Fred Ford. 1992-2002 + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef OPENGL_H +#define OPENGL_H + +#include "libs/graphics/sdl/sdl_common.h" +#if SDL_MAJOR_VERSION == 1 + +int TFB_GL_InitGraphics (int driver, int flags, int width, int height); +void TFB_GL_UninitGraphics (void); +int TFB_GL_ConfigureVideo (int driver, int flags, int width, int height, int togglefullscreen); + +#ifdef HAVE_OPENGL +#ifdef WIN32 + +#ifdef _MSC_VER +#pragma comment (lib, "opengl32.lib") +#pragma comment (lib, "glu32.lib") +#endif + +/* To avoid including windows.h, + Win32's <GL/gl.h> needs APIENTRY and WINGDIAPI defined properly. */ + +#ifndef APIENTRY +#define GLUT_APIENTRY_DEFINED +#if __MINGW32__ || (_MSC_VER >= 800) || defined(_STDCALL_SUPPORTED) +#define APIENTRY __stdcall +#else +#define APIENTRY +#endif +#endif + +#ifndef WINAPI +#define GLUT_WINAPI_DEFINED +#if __MINGW32__ || (_MSC_VER >= 800) || defined(_STDCALL_SUPPORTED) +#define WINAPI __stdcall +#else +#define WINAPI +#endif +#endif + +/* This is from Win32's <winnt.h> */ +#ifndef CALLBACK +#if (defined(_M_MRX000) || defined(_M_IX86) || defined(_M_ALPHA) || defined(_M_PPC)) && !defined(MIDL_PASS) +#define CALLBACK __stdcall +#else +#define CALLBACK +#endif +#endif + +/* This is from Win32's <wingdi.h> and <winnt.h> */ +#ifndef WINGDIAPI +#define GLUT_WINGDIAPI_DEFINED +#define WINGDIAPI __declspec(dllimport) +#endif + +/* This is from Win32's <ctype.h> */ +#ifndef LIBS_GRAPHICS_SDL_OPENGL_H_ +typedef unsigned short wchar_t; +#define LIBS_GRAPHICS_SDL_OPENGL_H_ +#endif + +#include "GL/glu.h" + +#else /* !defined(WIN32) */ + +#include "port.h" +#include SDL_INCLUDE(SDL_opengl.h) + +#endif /* WIN32 */ +#endif /* SDL_MAJOR_VERSION == 1 */ +#endif /* HAVE_OPENGL */ +#endif diff --git a/src/libs/graphics/sdl/palette.c b/src/libs/graphics/sdl/palette.c new file mode 100644 index 0000000..18f4418 --- /dev/null +++ b/src/libs/graphics/sdl/palette.c @@ -0,0 +1,47 @@ +/* + * Copyright 2009 Alex Volkov <codepro@usa.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "palette.h" +#include "libs/memlib.h" +#include "libs/log.h" + +NativePalette * +AllocNativePalette (void) +{ + return HCalloc (sizeof (NativePalette)); +} + +void +FreeNativePalette (NativePalette *palette) +{ + HFree (palette); +} + +void +SetNativePaletteColor (NativePalette *palette, int index, Color color) +{ + assert (index < NUMBER_OF_PLUTVALS); + palette->colors[index] = ColorToNative (color); +} + +Color +GetNativePaletteColor (NativePalette *palette, int index) +{ + assert (index < NUMBER_OF_PLUTVALS); + return NativeToColor (palette->colors[index]); +} diff --git a/src/libs/graphics/sdl/palette.h b/src/libs/graphics/sdl/palette.h new file mode 100644 index 0000000..2cefe7c --- /dev/null +++ b/src/libs/graphics/sdl/palette.h @@ -0,0 +1,57 @@ +/* + * Copyright 2009 Alex Volkov <codepro@usa.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef PALETTE_H_INCL__ +#define PALETTE_H_INCL__ + +#include "port.h" +#include SDL_INCLUDE(SDL.h) +#include "libs/graphics/cmap.h" + +struct NativePalette +{ + SDL_Color colors[NUMBER_OF_PLUTVALS]; +}; + +static inline Color +NativeToColor (SDL_Color native) +{ + Color color; + color.r = native.r; + color.g = native.g; + color.b = native.b; + color.a = 0xff; // fully opaque + return color; +} + +static inline SDL_Color +ColorToNative (Color color) +{ + SDL_Color native; + native.r = color.r; + native.g = color.g; + native.b = color.b; +#if SDL_MAJOR_VERSION == 1 + native.unused = 0; +#else + native.a = color.a; +#endif + return native; +} + +#endif /* PALETTE_H_INCL__ */ diff --git a/src/libs/graphics/sdl/png2sdl.c b/src/libs/graphics/sdl/png2sdl.c new file mode 100644 index 0000000..1717886 --- /dev/null +++ b/src/libs/graphics/sdl/png2sdl.c @@ -0,0 +1,300 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * This code is adapted from IMG_png.c in the SDL2_image library, which is + * available under the zlib license and is (c) 1997-2019 Sam Lantinga. The + * code itself is credited to Philippe Lavoie as the original author. It + * also shares some heritage with libpng's own example.c file, and + * ultimately inherits the GPL2+ license from the rest of UQM. + * + * Differences from SDL2_image are: + * - It is PNG-only + * - It directly links the relevant version of libpng at compile time + * - It always uses libpng and will never forward to alternative + * libraries such as ImageIO.framework. This means that palette + * information will always be preserved, as UQM requires. + * - It locks the surface as the API demands rather than using + * volatility markers + * - Palette assignment is done through the API rather than by + * directly editing the format contents + */ + +#include "png2sdl.h" +#include <png.h> + +/* Link function between SDL_RWops and PNG's data source */ +static void +png_read_data(png_structp ctx, png_bytep area, png_size_t size) +{ + SDL_RWops *src = (SDL_RWops *)png_get_io_ptr (ctx); + SDL_RWread (src, area, size, 1); +} + +SDL_Surface * +TFB_png_to_sdl (SDL_RWops *src) +{ + Sint64 start; + const char *error; + SDL_Surface *surface; + png_structp png_ptr; + png_infop info_ptr; + png_uint_32 width, height; + int bit_depth, color_type, interlace_type, num_channels; + Uint32 Rmask, Gmask, Bmask, Amask; + png_bytep *row_pointers; + int row, i; + int ckey = -1; + png_color_16 *transv; + + if (!src) + { + /* The error message has been set in SDL_RWFromFile */ + return NULL; + } + start = SDL_RWtell (src); + + /* Initialize the data we will clean up when we're done */ + error = NULL; + png_ptr = NULL; + info_ptr = NULL; + row_pointers = NULL; + surface = NULL; + + /* Create the PNG loading context structure */ + png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, + NULL, NULL, NULL); + if (png_ptr == NULL) + { + error = "Couldn't allocate memory for PNG file"; + goto done; + } + + /* Allocate/initialize the memory for image information */ + info_ptr = png_create_info_struct (png_ptr); + if (info_ptr == NULL) + { + error = "Couldn't create image information for PNG file"; + goto done; + } + + /* Set error handling */ + if (setjmp (png_jmpbuf (png_ptr))) + { + error = "Error reading the PNG file."; + goto done; + } + + /* Set up the input control */ + png_set_read_fn (png_ptr, src, png_read_data); + + /* Read PNG header info */ + png_read_info (png_ptr, info_ptr); + png_get_IHDR (png_ptr, info_ptr, &width, &height, &bit_depth, + &color_type, &interlace_type, NULL, NULL); + + + /* Configure the decode based on what we know of the image + * already: strip 16 bit color down to 8 bit, automatically + * deinterlace, expand grayscale images or those with more + * than one transparent color or any translucent colors into + * full RGB or RGBA, and expand 1, 2, or 4-bpp paletted + * images to 8bpp. */ + png_set_strip_16 (png_ptr); + png_set_interlace_handling (png_ptr); + png_set_packing (png_ptr); + if (color_type == PNG_COLOR_TYPE_GRAY) + { + png_set_expand (png_ptr); + } + if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS)) + { + int num_trans; + Uint8 *trans; + png_get_tRNS (png_ptr, info_ptr, &trans, &num_trans, &transv); + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + /* Check if all tRNS entries are opaque except one */ + int j, t = -1; + for (j = 0; j < num_trans; j++) + { + if (trans[j] == 0) + { + if (t >= 0) + { + break; + } + t = j; + } + else if (trans[j] != 255) + { + break; + } + } + if (j == num_trans) + { + /* exactly one transparent index */ + ckey = t; + } + else + { + /* more than one transparent index, or translucency */ + png_set_expand (png_ptr); + } + } + else + { + ckey = 0; /* actual value will be set later */ + } + } + + if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + png_set_gray_to_rgb (png_ptr); + } + + /* Register our changes with the reading machinery and refresh + * our ancillary data about the image */ + png_read_update_info (png_ptr, info_ptr); + png_get_IHDR (png_ptr, info_ptr, &width, &height, &bit_depth, + &color_type, &interlace_type, NULL, NULL); + + /* Allocate the SDL surface to hold the image */ + Rmask = Gmask = Bmask = Amask = 0; + num_channels = png_get_channels (png_ptr, info_ptr); + if (num_channels >= 3) + { +#if SDL_BYTEORDER == SDL_LIL_ENDIAN + Rmask = 0x000000FF; + Gmask = 0x0000FF00; + Bmask = 0x00FF0000; + Amask = (num_channels == 4) ? 0xFF000000 : 0; +#else + int s = (num_channels == 4) ? 0 : 8; + Rmask = 0xFF000000 >> s; + Gmask = 0x00FF0000 >> s; + Bmask = 0x0000FF00 >> s; + Amask = 0x000000FF >> s; +#endif + } + surface = SDL_CreateRGBSurface (SDL_SWSURFACE, width, height, + bit_depth * num_channels, + Rmask, Gmask, Bmask, Amask); + if (surface == NULL) + { + error = SDL_GetError (); + goto done; + } + + if (ckey != -1) + { + if (color_type != PNG_COLOR_TYPE_PALETTE) + { + /* FIXME: Should these be truncated or shifted down? */ + ckey = SDL_MapRGB(surface->format, + (Uint8)transv->red, + (Uint8)transv->green, + (Uint8)transv->blue); + } +#if SDL_MAJOR_VERSION >= 2 + SDL_SetColorKey (surface, SDL_TRUE, ckey); +#else + SDL_SetColorKey (surface, SDL_SRCCOLORKEY, ckey); +#endif + } + + SDL_LockSurface (surface); + /* Create the array of pointers to image data */ + row_pointers = (png_bytep *)SDL_malloc (sizeof (png_bytep) * height); + if (!row_pointers) + { + error = "Out of memory"; + goto done; + } + for (row = 0; row < (int)height; row++) + { + row_pointers[row] = (png_bytep) + (Uint8 *)surface->pixels + row*surface->pitch; + } + + /* Read the entire image in one go */ + png_read_image (png_ptr, row_pointers); + SDL_UnlockSurface (surface); + + /* and we're done! (png_read_end() can be omitted if no + * processing of post-IDAT text/time/etc. is desired) + * In some cases it can't read PNGs created by some popular + * programs (ACDSEE), we do not want to process comments, so + * we omit png_read_end */ + + /* Load the palette, if any */ + if (surface->format->palette) + { + SDL_Color palette[256]; + int png_num_palette; + png_colorp png_palette; + png_get_PLTE (png_ptr, info_ptr, &png_palette, &png_num_palette); + if (color_type == PNG_COLOR_TYPE_GRAY) + { + png_num_palette = 256; + for (i = 0; i < 256; i++) + { + palette[i].r = (Uint8)i; + palette[i].g = (Uint8)i; + palette[i].b = (Uint8)i; + } + } + else if (png_num_palette > 0) + { + for (i = 0; i < png_num_palette; ++i) + { + palette[i].b = png_palette[i].blue; + palette[i].g = png_palette[i].green; + palette[i].r = png_palette[i].red; + } + } +#if SDL_MAJOR_VERSION >= 2 + SDL_SetPaletteColors (surface->format->palette, palette, + 0, png_num_palette); +#else + SDL_SetPalette (surface, SDL_LOGPAL, palette, + 0, png_num_palette); +#endif + } + +done: /* Clean up and return */ + if (png_ptr) + { + png_destroy_read_struct (&png_ptr, + info_ptr ? &info_ptr : (png_infopp)0, + (png_infopp)0); + } + if (row_pointers) + { + SDL_free (row_pointers); + } + if (error) + { + SDL_RWseek(src, start, RW_SEEK_SET); + if (surface) + { + SDL_FreeSurface (surface); + surface = NULL; + } + fprintf (stderr, "%s", error); + } + return surface; +} diff --git a/src/libs/graphics/sdl/png2sdl.h b/src/libs/graphics/sdl/png2sdl.h new file mode 100644 index 0000000..c0a9ec3 --- /dev/null +++ b/src/libs/graphics/sdl/png2sdl.h @@ -0,0 +1,24 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef PNG2SDL_H_ +#define PNG2SDL_H_ + +#include <SDL.h> + +SDL_Surface *TFB_png_to_sdl (SDL_RWops *src); + +#endif diff --git a/src/libs/graphics/sdl/primitives.c b/src/libs/graphics/sdl/primitives.c new file mode 100644 index 0000000..6dd539a --- /dev/null +++ b/src/libs/graphics/sdl/primitives.c @@ -0,0 +1,633 @@ +//Copyright Paul Reiche, Fred Ford. 1992-2002 + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "port.h" +#include "sdl_common.h" +#include "primitives.h" + + +// Pixel drawing routines + +static Uint32 +getpixel_8(SDL_Surface *surface, int x, int y) +{ + /* Here p is the address to the pixel we want to retrieve */ + Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x; + return *p; +} + +static void +putpixel_8(SDL_Surface *surface, int x, int y, Uint32 pixel) +{ + /* Here p is the address to the pixel we want to set */ + Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * 1; + *p = pixel; +} + +static Uint32 +getpixel_16(SDL_Surface *surface, int x, int y) +{ + /* Here p is the address to the pixel we want to retrieve */ + Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * 2; + return *(Uint16 *)p; +} + +static void +putpixel_16(SDL_Surface *surface, int x, int y, Uint32 pixel) +{ + /* Here p is the address to the pixel we want to set */ + Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * 2; + *(Uint16 *)p = pixel; +} + +static Uint32 +getpixel_24_be(SDL_Surface *surface, int x, int y) +{ + /* Here p is the address to the pixel we want to retrieve */ + Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * 3; + return p[0] << 16 | p[1] << 8 | p[2]; +} + +static void +putpixel_24_be(SDL_Surface *surface, int x, int y, Uint32 pixel) +{ + /* Here p is the address to the pixel we want to set */ + Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * 3; + p[0] = (pixel >> 16) & 0xff; + p[1] = (pixel >> 8) & 0xff; + p[2] = pixel & 0xff; +} + +static Uint32 +getpixel_24_le(SDL_Surface *surface, int x, int y) +{ + /* Here p is the address to the pixel we want to retrieve */ + Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * 3; + return p[0] | p[1] << 8 | p[2] << 16; +} + +static void +putpixel_24_le(SDL_Surface *surface, int x, int y, Uint32 pixel) +{ + /* Here p is the address to the pixel we want to set */ + Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * 3; + p[0] = pixel & 0xff; + p[1] = (pixel >> 8) & 0xff; + p[2] = (pixel >> 16) & 0xff; +} + +static Uint32 +getpixel_32(SDL_Surface *surface, int x, int y) +{ + /* Here p is the address to the pixel we want to retrieve */ + Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * 4; + return *(Uint32 *)p; +} + +static void +putpixel_32(SDL_Surface *surface, int x, int y, Uint32 pixel) +{ + /* Here p is the address to the pixel we want to set */ + Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * 4; + *(Uint32 *)p = pixel; +} + +GetPixelFn +getpixel_for(SDL_Surface *surface) +{ + int bpp = surface->format->BytesPerPixel; + switch (bpp) { + case 1: + return &getpixel_8; + case 2: + return &getpixel_16; + case 3: + if (SDL_BYTEORDER == SDL_BIG_ENDIAN) { + return &getpixel_24_be; + } else { + return &getpixel_24_le; + } + case 4: + return &getpixel_32; + } + return NULL; +} + +PutPixelFn +putpixel_for(SDL_Surface *surface) +{ + int bpp = surface->format->BytesPerPixel; + switch (bpp) { + case 1: + return &putpixel_8; + case 2: + return &putpixel_16; + case 3: + if (SDL_BYTEORDER == SDL_BIG_ENDIAN) { + return &putpixel_24_be; + } else { + return &putpixel_24_le; + } + case 4: + return &putpixel_32; + } + return NULL; +} + +static void +renderpixel_replace(SDL_Surface *surface, int x, int y, Uint32 pixel, + int factor) +{ + (void) factor; // ignored + putpixel_32(surface, x, y, pixel); +} + +static inline Uint8 +clip_channel(int c) +{ + if (c < 0) + c = 0; + else if (c > 255) + c = 255; + return c; +} + +static inline Uint8 +modulated_sum(Uint8 dc, Uint8 sc, int factor) +{ + // We use >> 8 instead of / 255 because it is faster, but it does + // not work 100% correctly. It should be safe because this should + // not be called for factor==255 + int b = dc + ((sc * factor) >> 8); + return clip_channel(b); +} + +static inline Uint8 +alpha_blend(Uint8 dc, Uint8 sc, int alpha) +{ + // We use >> 8 instead of / 255 because it is faster, but it does + // not work 100% correctly. It should be safe because this should + // not be called for alpha==255 + // No need to clip since we should never get values outside of 0..255 + // range, unless alpha is over 255, which is not supported. + return (((sc - dc) * alpha) >> 8) + dc; +} + +// Assumes 8 bits/channel, a safe assumption for 32bpp surfaces +#define UNPACK_PIXEL_32(p, fmt, r, g, b) \ + do { \ + (r) = ((p) >> (fmt)->Rshift) & 0xff; \ + (g) = ((p) >> (fmt)->Gshift) & 0xff; \ + (b) = ((p) >> (fmt)->Bshift) & 0xff; \ + } while (0) + +// Assumes the channels already clipped to 8 bits +static inline Uint32 +PACK_PIXEL_32(const SDL_PixelFormat *fmt, + Uint8 r, Uint8 g, Uint8 b) +{ + return ((Uint32)r << fmt->Rshift) | ((Uint32)g << fmt->Gshift) + | ((Uint32)b << fmt->Bshift); +} + +static void +renderpixel_additive(SDL_Surface *surface, int x, int y, Uint32 pixel, + int factor) +{ + const SDL_PixelFormat *fmt = surface->format; + Uint32 *p; + Uint32 sp; + Uint8 sr, sg, sb; + int r, g, b; + + p = (Uint32 *) ((Uint8 *)surface->pixels + y * surface->pitch + x * 4); + sp = *p; + UNPACK_PIXEL_32(sp, fmt, sr, sg, sb); + UNPACK_PIXEL_32(pixel, fmt, r, g, b); + + // TODO: We may need a special case for factor == -ADDITIVE_FACTOR_1 too, + // but it is not important enough right now to care ;) + if (factor == ADDITIVE_FACTOR_1) + { // no need to modulate the 'pixel', and modulation does not + // work correctly with factor==255 anyway + sr = clip_channel(sr + r); + sg = clip_channel(sg + g); + sb = clip_channel(sb + b); + } + else + { + sr = modulated_sum(sr, r, factor); + sg = modulated_sum(sg, g, factor); + sb = modulated_sum(sb, b, factor); + } + + *p = PACK_PIXEL_32(fmt, sr, sg, sb); +} + +static void +renderpixel_alpha(SDL_Surface *surface, int x, int y, Uint32 pixel, + int factor) +{ + const SDL_PixelFormat *fmt = surface->format; + Uint32 *p; + Uint32 sp; + Uint8 sr, sg, sb; + int r, g, b; + + if (factor == FULLY_OPAQUE_ALPHA) + { // alpha == 255 is equivalent to 'replace' and blending does not + // work correctly anyway because we use >> 8 instead of / 255 + putpixel_32(surface, x, y, pixel); + return; + } + + p = (Uint32 *) ((Uint8 *)surface->pixels + y * surface->pitch + x * 4); + sp = *p; + UNPACK_PIXEL_32(sp, fmt, sr, sg, sb); + UNPACK_PIXEL_32(pixel, fmt, r, g, b); + sr = alpha_blend(sr, r, factor); + sg = alpha_blend(sg, g, factor); + sb = alpha_blend(sb, b, factor); + *p = PACK_PIXEL_32(fmt, sr, sg, sb); +} + +RenderPixelFn +renderpixel_for(SDL_Surface *surface, RenderKind kind) +{ + const SDL_PixelFormat *fmt = surface->format; + + // The only supported rendering is to 32bpp surfaces + if (fmt->BytesPerPixel != 4) + return NULL; + + // Rendering other than REPLACE is not supported on RGBA surfaces + if (fmt->Amask != 0 && kind != renderReplace) + return NULL; + + switch (kind) + { + case renderReplace: + return &renderpixel_replace; + case renderAdditive: + return &renderpixel_additive; + case renderAlpha: + return &renderpixel_alpha; + } + // should not ever get here + return NULL; +} + +/* Line drawing routine + * Adapted from Paul Heckbert's implementation of Bresenham's algorithm, + * 3 Sep 85; taken from Graphics Gems I */ + +void +line_prim(int x1, int y1, int x2, int y2, Uint32 color, RenderPixelFn plot, + int factor, SDL_Surface *dst) +{ + int d, x, y, ax, ay, sx, sy, dx, dy; + SDL_Rect clip_r; + + SDL_GetClipRect (dst, &clip_r); + if (!clip_line (&x1, &y1, &x2, &y2, &clip_r)) + return; // line is completely outside clipping rectangle + + dx = x2-x1; + ax = ((dx < 0) ? -dx : dx) << 1; + sx = (dx < 0) ? -1 : 1; + dy = y2-y1; + ay = ((dy < 0) ? -dy : dy) << 1; + sy = (dy < 0) ? -1 : 1; + + x = x1; + y = y1; + if (ax > ay) { + d = ay - (ax >> 1); + for (;;) { + (*plot)(dst, x, y, color, factor); + if (x == x2) + return; + if (d >= 0) { + y += sy; + d -= ax; + } + x += sx; + d += ay; + } + } else { + d = ax - (ay >> 1); + for (;;) { + (*plot)(dst, x, y, color, factor); + if (y == y2) + return; + if (d >= 0) { + x += sx; + d -= ay; + } + y += sy; + d += ax; + } + } +} + + +// Clips line against rectangle using Cohen-Sutherland algorithm + +enum {C_TOP = 0x1, C_BOTTOM = 0x2, C_RIGHT = 0x4, C_LEFT = 0x8}; + +static int +compute_code (float x, float y, float xmin, float ymin, float xmax, float ymax) +{ + int c = 0; + if (y > ymax) + c |= C_TOP; + else if (y < ymin) + c |= C_BOTTOM; + if (x > xmax) + c |= C_RIGHT; + else if (x < xmin) + c |= C_LEFT; + return c; +} + +int +clip_line (int *lx1, int *ly1, int *lx2, int *ly2, const SDL_Rect *r) +{ + int C0, C1, C; + float x, y, x0, y0, x1, y1, xmin, ymin, xmax, ymax; + + x0 = (float)*lx1; + y0 = (float)*ly1; + x1 = (float)*lx2; + y1 = (float)*ly2; + + xmin = (float)r->x; + ymin = (float)r->y; + xmax = (float)r->x + r->w - 1; + ymax = (float)r->y + r->h - 1; + + C0 = compute_code (x0, y0, xmin, ymin, xmax, ymax); + C1 = compute_code (x1, y1, xmin, ymin, xmax, ymax); + + for (;;) { + /* trivial accept: both ends in rectangle */ + if ((C0 | C1) == 0) + { + *lx1 = (int)x0; + *ly1 = (int)y0; + *lx2 = (int)x1; + *ly2 = (int)y1; + return 1; + } + + /* trivial reject: both ends on the external side of the rectangle */ + if ((C0 & C1) != 0) + return 0; + + /* normal case: clip end outside rectangle */ + C = C0 ? C0 : C1; + if (C & C_TOP) + { + x = x0 + (x1 - x0) * (ymax - y0) / (y1 - y0); + y = ymax; + } + else if (C & C_BOTTOM) + { + x = x0 + (x1 - x0) * (ymin - y0) / (y1 - y0); + y = ymin; + } + else if (C & C_RIGHT) + { + x = xmax; + y = y0 + (y1 - y0) * (xmax - x0) / (x1 - x0); + } + else + { + x = xmin; + y = y0 + (y1 - y0) * (xmin - x0) / (x1 - x0); + } + + /* set new end point and iterate */ + if (C == C0) + { + x0 = x; y0 = y; + C0 = compute_code (x0, y0, xmin, ymin, xmax, ymax); + } + else + { + x1 = x; y1 = y; + C1 = compute_code (x1, y1, xmin, ymin, xmax, ymax); + } + } +} + +void +fillrect_prim(SDL_Rect r, Uint32 color, RenderPixelFn plot, int factor, + SDL_Surface *dst) +{ + int x, y; + int x1, y1; + SDL_Rect clip_r; + + SDL_GetClipRect (dst, &clip_r); + if (!clip_rect (&r, &clip_r)) + return; // rect is completely outside clipping rectangle + + // TODO: calculate destination pointer directly instead of + // using the plot(x,y) version + x1 = r.x + r.w; + y1 = r.y + r.h; + for (y = r.y; y < y1; ++y) + { + for (x = r.x; x < x1; ++x) + plot(dst, x, y, color, factor); + } +} + +// clip the rectangle against the clip rectangle +int +clip_rect(SDL_Rect *r, const SDL_Rect *clip_r) +{ + // NOTE: the following clipping code is copied in part + // from SDL-1.2.4 sources + int dx, dy; + int w = r->w; + int h = r->h; + // SDL_Rect.w and .h are unsigned, we need signed + + dx = clip_r->x - r->x; + if (dx > 0) + { + w -= dx; + r->x += dx; + } + dx = r->x + w - clip_r->x - clip_r->w; + if (dx > 0) + w -= dx; + + dy = clip_r->y - r->y; + if (dy > 0) + { + h -= dy; + r->y += dy; + } + dy = r->y + h - clip_r->y - clip_r->h; + if (dy > 0) + h -= dy; + + if (w <= 0 || h <= 0) + { + r->w = 0; + r->h = 0; + return 0; + } + + r->w = w; + r->h = h; + return 1; +} + +void +blt_prim(SDL_Surface *src, SDL_Rect src_r, RenderPixelFn plot, int factor, + SDL_Surface *dst, SDL_Rect dst_r) +{ + SDL_PixelFormat *srcfmt = src->format; + SDL_Palette *srcpal = srcfmt->palette; + SDL_PixelFormat *dstfmt = dst->format; + Uint32 mask = 0; + Uint32 key = ~0; + GetPixelFn getpix = getpixel_for(src); + SDL_Rect clip_r; + int x, y; + + SDL_GetClipRect (dst, &clip_r); + if (!clip_blt_rects (&src_r, &dst_r, &clip_r)) + return; // rect is completely outside clipping rectangle + + if (src_r.x >= src->w || src_r.y >= src->h) + return; // rect is completely outside source bounds + + if (src_r.x + src_r.w > src->w) + src_r.w = src->w - src_r.x; + if (src_r.y + src_r.h > src->h) + src_r.h = src->h - src_r.y; + + // use colorkeys where appropriate + if (srcfmt->Amask) + { // alpha transparency + mask = srcfmt->Amask; + key = 0; + } + else if (TFB_GetColorKey (src, &key) == 0) + { + mask = ~0; + } + // TODO: calculate the source and destination pointers directly + // instead of using the plot(x,y) version + for (y = 0; y < src_r.h; ++y) + { + for (x = 0; x < src_r.w; ++x) + { + Uint8 r, g, b, a; + Uint32 p; + + p = getpix(src, src_r.x + x, src_r.y + y); + if (srcpal) + { // source is paletted, colorkey does not use mask + if (p == key) + continue; // transparent pixel + } + else + { // source is RGB(A), colorkey uses mask + if ((p & mask) == key) + continue; // transparent pixel + } + + // convert pixel format to destination + SDL_GetRGBA(p, srcfmt, &r, &g, &b, &a); + // TODO: handle source pixel alpha; plot() should probably + // get a source alpha parameter + p = SDL_MapRGBA(dstfmt, r, g, b, a); + + plot(dst, dst_r.x + x, dst_r.y + y, p, factor); + } + } +} + +// clip the source and destination rectangles against the clip rectangle +int +clip_blt_rects(SDL_Rect *src_r, SDL_Rect *dst_r, const SDL_Rect *clip_r) +{ + // NOTE: the following clipping code is copied in part + // from SDL-1.2.4 sources + int w, h; + int dx, dy; + + // clip the source rectangle to the source surface + w = src_r->w; + if (src_r->x < 0) + { + w += src_r->x; + dst_r->x -= src_r->x; + src_r->x = 0; + } + + h = src_r->h; + if (src_r->y < 0) + { + h += src_r->y; + dst_r->y -= src_r->y; + src_r->y = 0; + } + + // clip the destination rectangle against the clip rectangle, + // minding the source rectangle in the process + dx = clip_r->x - dst_r->x; + if (dx > 0) + { + w -= dx; + dst_r->x += dx; + src_r->x += dx; + } + dx = dst_r->x + w - clip_r->x - clip_r->w; + if (dx > 0) + w -= dx; + + dy = clip_r->y - dst_r->y; + if (dy > 0) + { + h -= dy; + dst_r->y += dy; + src_r->y += dy; + } + dy = dst_r->y + h - clip_r->y - clip_r->h; + if (dy > 0) + h -= dy; + + if (w <= 0 || h <= 0) + { + src_r->w = 0; + src_r->h = 0; + return 0; + } + + src_r->w = w; + src_r->h = h; + return 1; +} + diff --git a/src/libs/graphics/sdl/primitives.h b/src/libs/graphics/sdl/primitives.h new file mode 100644 index 0000000..9d8aff2 --- /dev/null +++ b/src/libs/graphics/sdl/primitives.h @@ -0,0 +1,62 @@ +//Copyright Paul Reiche, Fred Ford. 1992-2002 + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef PRIMITIVES_H +#define PRIMITIVES_H + +/* Function types for the pixel functions */ + +typedef Uint32 (*GetPixelFn)(SDL_Surface *, int x, int y); +// 'pixel' is in destination surface format +typedef void (*PutPixelFn)(SDL_Surface *, int x, int y, Uint32 pixel); + +GetPixelFn getpixel_for(SDL_Surface *surface); +PutPixelFn putpixel_for(SDL_Surface *surface); + +// This currently matches gfxlib.h:DrawKind for simplicity +typedef enum +{ + renderReplace = 0, + renderAdditive, + renderAlpha, +} RenderKind; + +#define FULLY_OPAQUE_ALPHA 255 +#define ADDITIVE_FACTOR_1 255 + +// 'pixel' is in destination surface format +// See gfxlib.h:DrawKind for 'factor' spec +typedef void (*RenderPixelFn)(SDL_Surface *, int x, int y, Uint32 pixel, + int factor); + +RenderPixelFn renderpixel_for(SDL_Surface *surface, RenderKind); + +void line_prim(int x1, int y1, int x2, int y2, Uint32 color, + RenderPixelFn plot, int factor, SDL_Surface *dst); +void fillrect_prim(SDL_Rect r, Uint32 color, + RenderPixelFn plot, int factor, SDL_Surface *dst); +void blt_prim(SDL_Surface *src, SDL_Rect src_r, + RenderPixelFn plot, int factor, + SDL_Surface *dst, SDL_Rect dst_r); + +int clip_line(int *lx1, int *ly1, int *lx2, int *ly2, const SDL_Rect *clip_r); +int clip_rect(SDL_Rect *r, const SDL_Rect *clip_r); +int clip_blt_rects(SDL_Rect *src_r, SDL_Rect *dst_r, const SDL_Rect *clip_r); + + +#endif /* PRIMITIVES_H */ diff --git a/src/libs/graphics/sdl/pure.c b/src/libs/graphics/sdl/pure.c new file mode 100644 index 0000000..df4a329 --- /dev/null +++ b/src/libs/graphics/sdl/pure.c @@ -0,0 +1,474 @@ +//Copyright Paul Reiche, Fred Ford. 1992-2002 +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "pure.h" +#include "libs/graphics/bbox.h" +#include "scalers.h" +#include "libs/log.h" + +#if SDL_MAJOR_VERSION == 1 + +static SDL_Surface *SDL_Video = NULL; +static SDL_Surface *fade_color_surface = NULL; +static SDL_Surface *fade_temp = NULL; +static SDL_Surface *scaled_display = NULL; + +static TFB_ScaleFunc scaler = NULL; + +static Uint32 fade_color; + +static void TFB_Pure_Scaled_Preprocess (int force_full_redraw, int transition_amount, int fade_amount); +static void TFB_Pure_Scaled_Postprocess (void); +static void TFB_Pure_Unscaled_Preprocess (int force_full_redraw, int transition_amount, int fade_amount); +static void TFB_Pure_Unscaled_Postprocess (void); +static void TFB_Pure_UploadTransitionScreen (void); +static void TFB_Pure_ScreenLayer (SCREEN screen, Uint8 a, SDL_Rect *rect); +static void TFB_Pure_ColorLayer (Uint8 r, Uint8 g, Uint8 b, Uint8 a, SDL_Rect *rect); + +static TFB_GRAPHICS_BACKEND pure_scaled_backend = { + TFB_Pure_Scaled_Preprocess, + TFB_Pure_Scaled_Postprocess, + TFB_Pure_UploadTransitionScreen, + TFB_Pure_ScreenLayer, + TFB_Pure_ColorLayer }; + +static TFB_GRAPHICS_BACKEND pure_unscaled_backend = { + TFB_Pure_Unscaled_Preprocess, + TFB_Pure_Unscaled_Postprocess, + TFB_Pure_UploadTransitionScreen, + TFB_Pure_ScreenLayer, + TFB_Pure_ColorLayer }; + +// We cannot rely on SDL_DisplayFormatAlpha() anymore. It can return +// formats that we do not expect (SDL v1.2.14 on Mac OSX). Mac likes +// ARGB surfaces, but SDL_DisplayFormatAlpha thinks that only RGBA are fast. +// This is a generic replacement that gives what we want. +static void +CalcAlphaFormat (const SDL_PixelFormat* video, SDL_PixelFormat* ours) +{ + int valid = 0; + + // We use 32-bit surfaces internally + ours->BitsPerPixel = 32; + + // Try to get as close to the video format as possible + if (video->BitsPerPixel == 15 || video->BitsPerPixel == 16) + { // At least match the channel order + ours->Rshift = video->Rshift / 5 * 8; + ours->Gshift = video->Gshift / 5 * 8; + ours->Bshift = video->Bshift / 5 * 8; + valid = 1; + } + else if (video->BitsPerPixel == 24 || video->BitsPerPixel == 32) + { + // We can only use channels aligned on byte boundary + if (video->Rshift % 8 == 0 && video->Gshift % 8 == 0 + && video->Bshift % 8 == 0) + { // Match RGB in video + ours->Rshift = video->Rshift; + ours->Gshift = video->Gshift; + ours->Bshift = video->Bshift; + valid = 1; + } + } + + if (valid) + { // For alpha, use the unoccupied byte + ours->Ashift = 48 - (ours->Rshift + ours->Gshift + ours->Bshift); + // Set channels according to byte positions + ours->Rmask = 0xff << ours->Rshift; + ours->Gmask = 0xff << ours->Gshift; + ours->Bmask = 0xff << ours->Bshift; + ours->Amask = 0xff << ours->Ashift; + return; + } + + // Fallback case. It does not matter what we set, but SDL likes + // Alpha to be the highest. + ours->Rmask = 0x000000ff; + ours->Gmask = 0x0000ff00; + ours->Bmask = 0x00ff0000; + ours->Amask = 0xff000000; +} + +int +TFB_Pure_ConfigureVideo (int driver, int flags, int width, int height, int togglefullscreen) +{ + int i, videomode_flags; + SDL_PixelFormat conv_fmt; + + GraphicsDriver = driver; + + // must use SDL_SWSURFACE, HWSURFACE doesn't work properly + // with fades/scaling + if (width == 320 && height == 240) + { + videomode_flags = SDL_SWSURFACE; + ScreenWidthActual = 320; + ScreenHeightActual = 240; + graphics_backend = &pure_unscaled_backend; + } + else + { + videomode_flags = SDL_SWSURFACE; + ScreenWidthActual = 640; + ScreenHeightActual = 480; + graphics_backend = &pure_scaled_backend; + + if (width != 640 || height != 480) + log_add (log_Error, "Screen resolution of %dx%d not supported " + "under pure SDL, using 640x480", width, height); + } + + videomode_flags |= SDL_ANYFORMAT; + if (flags & TFB_GFXFLAGS_FULLSCREEN) + videomode_flags |= SDL_FULLSCREEN; + + /* We'll ask for a 32bpp frame, but it doesn't really matter, because we've set + SDL_ANYFORMAT */ + SDL_Video = SDL_SetVideoMode (ScreenWidthActual, ScreenHeightActual, + 32, videomode_flags); + + if (SDL_Video == NULL) + { + log_add (log_Error, "Couldn't set %ix%i video mode: %s", + ScreenWidthActual, ScreenHeightActual, + SDL_GetError ()); + return -1; + } + else + { + const SDL_Surface *video = SDL_GetVideoSurface (); + const SDL_PixelFormat* fmt = video->format; + + ScreenColorDepth = fmt->BitsPerPixel; + log_add (log_Info, "Set the resolution to: %ix%ix%i", + video->w, video->h, ScreenColorDepth); + log_add (log_Info, " Video: R %08x, G %08x, B %08x, A %08x", + fmt->Rmask, fmt->Gmask, fmt->Bmask, fmt->Amask); + + if (togglefullscreen) + { + // NOTE: We cannot change the format_conv_surf now because we + // have already loaded lots of graphics and changing it now + // will only lead to chaos. + // Just check if channel order has changed significantly + CalcAlphaFormat (fmt, &conv_fmt); + fmt = format_conv_surf->format; + if (conv_fmt.Rmask != fmt->Rmask || conv_fmt.Bmask != fmt->Bmask) + log_add (log_Warning, "Warning: pixel format has changed " + "significantly. Rendering will be slow."); + return 0; + } + } + + // Create a 32bpp surface in a compatible format which will supply + // the format information to all other surfaces used in the game + if (format_conv_surf) + { + SDL_FreeSurface (format_conv_surf); + format_conv_surf = NULL; + } + CalcAlphaFormat (SDL_Video->format, &conv_fmt); + format_conv_surf = SDL_CreateRGBSurface (SDL_SWSURFACE, 0, 0, + conv_fmt.BitsPerPixel, conv_fmt.Rmask, conv_fmt.Gmask, + conv_fmt.Bmask, conv_fmt.Amask); + if (!format_conv_surf) + { + log_add (log_Error, "Couldn't create format_conv_surf: %s", + SDL_GetError()); + return -1; + } + else + { + const SDL_PixelFormat* fmt = format_conv_surf->format; + log_add (log_Info, " Internal: R %08x, G %08x, B %08x, A %08x", + fmt->Rmask, fmt->Gmask, fmt->Bmask, fmt->Amask); + } + + for (i = 0; i < TFB_GFX_NUMSCREENS; i++) + { + if (0 != SDL1_ReInit_Screen (&SDL_Screens[i], format_conv_surf, + ScreenWidth, ScreenHeight)) + return -1; + } + + SDL_Screen = SDL_Screens[0]; + TransitionScreen = SDL_Screens[2]; + + if (0 != SDL1_ReInit_Screen (&fade_color_surface, format_conv_surf, + ScreenWidth, ScreenHeight)) + return -1; + fade_color = SDL_MapRGB (fade_color_surface->format, 0, 0, 0); + SDL_FillRect (fade_color_surface, NULL, fade_color); + + if (0 != SDL1_ReInit_Screen (&fade_temp, format_conv_surf, + ScreenWidth, ScreenHeight)) + return -1; + + if (ScreenWidthActual > ScreenWidth || ScreenHeightActual > ScreenHeight) + { + if (0 != SDL1_ReInit_Screen (&scaled_display, format_conv_surf, + ScreenWidthActual, ScreenHeightActual)) + return -1; + + scaler = Scale_PrepPlatform (flags, SDL_Screen->format); + } + else + { // no need to scale + scaler = NULL; + } + + return 0; +} + +int +TFB_Pure_InitGraphics (int driver, int flags, const char *renderer, int width, int height) +{ + char VideoName[256]; + + log_add (log_Info, "Initializing Pure-SDL graphics."); + + SDL_VideoDriverName (VideoName, sizeof (VideoName)); + log_add (log_Info, "SDL driver used: %s", VideoName); + (void) renderer; + // The "renderer" argument is ignored by SDL1. To control how SDL1 + // gets its pixmap, set the environment variable SDL_VIDEODRIVER. + // For Linux: x11 (default), dga, fbcon, directfb, svgalib, + // ggi, aalib + // For Windows: directx (default), windib + + log_add (log_Info, "SDL initialized."); + log_add (log_Info, "Initializing Screen."); + + ScreenWidth = 320; + ScreenHeight = 240; + + if (TFB_Pure_ConfigureVideo (driver, flags, width, height, 0)) + { + log_add (log_Fatal, "Could not initialize video: " + "no fallback at start of program!"); + exit (EXIT_FAILURE); + } + + // Initialize scalers (let them precompute whatever) + Scale_Init (); + + return 0; +} + +void +TFB_Pure_UninitGraphics (void) +{ + UnInit_Screen (&scaled_display); + UnInit_Screen (&fade_color_surface); + UnInit_Screen (&fade_temp); +} + +static void +ScanLines (SDL_Surface *dst, SDL_Rect *r) +{ + const int rw = r->w * 2; + const int rh = r->h * 2; + SDL_PixelFormat *fmt = dst->format; + const int pitch = dst->pitch; + const int len = pitch / fmt->BytesPerPixel; + int ddst; + Uint32 *p = (Uint32 *) dst->pixels; + int x, y; + + p += len * (r->y * 2) + (r->x * 2); + ddst = len + len - rw; + + for (y = rh; y; y -= 2, p += ddst) + { + for (x = rw; x; --x, ++p) + { + // we ignore the lower bits as the difference + // of 1 in 255 is negligible + *p = ((*p >> 1) & 0x7f7f7f7f) + ((*p >> 2) & 0x3f3f3f3f); + } + } +} + +static SDL_Surface *backbuffer = NULL, *scalebuffer = NULL; +static SDL_Rect updated; + +static void +TFB_Pure_Scaled_Preprocess (int force_full_redraw, int transition_amount, int fade_amount) +{ + if (force_full_redraw != TFB_REDRAW_NO) + { + updated.x = updated.y = 0; + updated.w = ScreenWidth; + updated.h = ScreenHeight; + } + else + { + updated.x = TFB_BBox.region.corner.x; + updated.y = TFB_BBox.region.corner.y; + updated.w = TFB_BBox.region.extent.width; + updated.h = TFB_BBox.region.extent.height; + } + + if (transition_amount == 255 && fade_amount == 255) + backbuffer = SDL_Screens[TFB_SCREEN_MAIN]; + else + backbuffer = fade_temp; + + // we can scale directly onto SDL_Video if video is compatible + if (SDL_Video->format->BitsPerPixel == SDL_Screen->format->BitsPerPixel + && SDL_Video->format->Rmask == SDL_Screen->format->Rmask + && SDL_Video->format->Bmask == SDL_Screen->format->Bmask) + scalebuffer = SDL_Video; + else + scalebuffer = scaled_display; + +} + +static void +TFB_Pure_Unscaled_Preprocess (int force_full_redraw, int transition_amount, int fade_amount) +{ + if (force_full_redraw != TFB_REDRAW_NO) + { + updated.x = updated.y = 0; + updated.w = ScreenWidth; + updated.h = ScreenHeight; + } + else + { + updated.x = TFB_BBox.region.corner.x; + updated.y = TFB_BBox.region.corner.y; + updated.w = TFB_BBox.region.extent.width; + updated.h = TFB_BBox.region.extent.height; + } + + backbuffer = SDL_Video; + (void)transition_amount; + (void)fade_amount; +} + +static void +TFB_Pure_Scaled_Postprocess (void) +{ + SDL_LockSurface (scalebuffer); + SDL_LockSurface (backbuffer); + + if (scaler) + scaler (backbuffer, scalebuffer, &updated); + + if (GfxFlags & TFB_GFXFLAGS_SCANLINES) + ScanLines (scalebuffer, &updated); + + SDL_UnlockSurface (backbuffer); + SDL_UnlockSurface (scalebuffer); + + updated.x *= 2; + updated.y *= 2; + updated.w *= 2; + updated.h *= 2; + if (scalebuffer != SDL_Video) + SDL_BlitSurface (scalebuffer, &updated, SDL_Video, &updated); + + SDL_UpdateRects (SDL_Video, 1, &updated); +} + +static void +TFB_Pure_Unscaled_Postprocess (void) +{ + SDL_UpdateRect (SDL_Video, updated.x, updated.y, + updated.w, updated.h); +} + +static void +TFB_Pure_UploadTransitionScreen (void) +{ + /* This is a no-op in SDL1 Pure mode */ +} + +static void +TFB_Pure_ScreenLayer (SCREEN screen, Uint8 a, SDL_Rect *rect) +{ + if (SDL_Screens[screen] == backbuffer) + return; + SDL_SetAlpha (SDL_Screens[screen], SDL_SRCALPHA, a); + SDL_BlitSurface (SDL_Screens[screen], rect, backbuffer, rect); +} + +static void +TFB_Pure_ColorLayer (Uint8 r, Uint8 g, Uint8 b, Uint8 a, SDL_Rect *rect) +{ + Uint32 col = SDL_MapRGB (fade_color_surface->format, r, g, b); + if (col != fade_color) + { + fade_color = col; + SDL_FillRect (fade_color_surface, NULL, fade_color); + } + SDL_SetAlpha (fade_color_surface, SDL_SRCALPHA, a); + SDL_BlitSurface (fade_color_surface, rect, backbuffer, rect); +} + +void +Scale_PerfTest (void) +{ + TimeCount TimeStart, TimeIn; + TimeCount Now = 0; + SDL_Rect updated = {0, 0, ScreenWidth, ScreenHeight}; + int i; + + if (!scaler) + { + log_add (log_Error, "No scaler configured! " + "Run with larger resolution, please"); + return; + } + if (!scaled_display) + { + log_add (log_Error, "Run scaler performance tests " + "in Pure mode, please"); + return; + } + + SDL_LockSurface (SDL_Screen); + SDL_LockSurface (scaled_display); + + TimeStart = TimeIn = SDL_GetTicks (); + + for (i = 1; i < 1001; ++i) // run for 1000 frames + { + scaler (SDL_Screen, scaled_display, &updated); + + if (GfxFlags & TFB_GFXFLAGS_SCANLINES) + ScanLines (scaled_display, &updated); + + if (i % 100 == 0) + { + Now = SDL_GetTicks (); + log_add (log_Debug, "%03d(%04u) ", 100*1000 / (Now - TimeIn), + Now - TimeIn); + TimeIn = Now; + } + } + + log_add (log_Debug, "Full frames scaled: %d; over %u ms; %d fps\n", + (i - 1), Now - TimeStart, i * 1000 / (Now - TimeStart)); + + SDL_UnlockSurface (scaled_display); + SDL_UnlockSurface (SDL_Screen); +} + +#endif diff --git a/src/libs/graphics/sdl/pure.h b/src/libs/graphics/sdl/pure.h new file mode 100644 index 0000000..50cf06f --- /dev/null +++ b/src/libs/graphics/sdl/pure.h @@ -0,0 +1,29 @@ +//Copyright Paul Reiche, Fred Ford. 1992-2002 + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef PURE_H +#define PURE_H + +#include "libs/graphics/sdl/sdl_common.h" + +int TFB_Pure_InitGraphics (int driver, int flags, const char *renderer, int width, int height); +void TFB_Pure_UninitGraphics (void); +int TFB_Pure_ConfigureVideo (int driver, int flags, int width, int height, int togglefullscreen); +void Scale_PerfTest (void); + +#endif diff --git a/src/libs/graphics/sdl/rotozoom.c b/src/libs/graphics/sdl/rotozoom.c new file mode 100644 index 0000000..9f22769 --- /dev/null +++ b/src/libs/graphics/sdl/rotozoom.c @@ -0,0 +1,1038 @@ +/* + + rotozoom.c - rotozoomer for 32bit or 8bit surfaces + LGPL (c) A. Schiffler + + Note by sc2 developers: + Taken from SDL_gfx library and modified, original code can be downloaded + from http://www.ferzkopp.net/Software/SDL_gfx-2.0/ + +*/ + +#include <stdlib.h> +#include <string.h> +#include "sdl_common.h" +#include "libs/memlib.h" +#include "port.h" +#include "rotozoom.h" + +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) + + + +/* + + 32bit Zoomer with optional anti-aliasing by bilinear interpolation. + + Zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface. + +*/ + +int zoomSurfaceRGBA(SDL_Surface * src, SDL_Surface * dst, int smooth) +{ + int x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy, ex, ey, t1, t2, sstep; + tColorRGBA *c00, *c01, *c10, *c11; + tColorRGBA *sp, *csp, *dp; + int sgap, dgap; + + /* + * Variable setup + */ + if (smooth) { + /* + * For interpolation: assume source dimension is one pixel + */ + /* + * smaller to avoid overflow on right and bottom edge. + */ + sx = (int) (65536.0 * (float) (src->w - 1) / (float) dst->w); + sy = (int) (65536.0 * (float) (src->h - 1) / (float) dst->h); + } else { + sx = (int) (65536.0 * (float) src->w / (float) dst->w); + sy = (int) (65536.0 * (float) src->h / (float) dst->h); + } + + /* + * Allocate memory for row increments + */ + +#ifndef __SYMBIAN32__ + if ((sax = (int *) alloca((dst->w + 1) * sizeof(Uint32))) == NULL) + return (-1); + if ((say = (int *) alloca((dst->h + 1) * sizeof(Uint32))) == NULL) + return (-1); +#else + if ((sax = (int *) HMalloc((dst->w + 1) * sizeof(Uint32))) == NULL) + return (-1); + if ((say = (int *) HMalloc((dst->h + 1) * sizeof(Uint32))) == NULL) + { + HFree(sax); + return (-1); + } +#endif + + + /* + * Precalculate row increments + */ + csx = 0; + csax = sax; + for (x = 0; x <= dst->w; x++) { + *csax = csx; + csax++; + csx &= 0xffff; + csx += sx; + } + csy = 0; + csay = say; + for (y = 0; y <= dst->h; y++) { + *csay = csy; + csay++; + csy &= 0xffff; + csy += sy; + } + + /* + * Pointer setup + */ + sp = csp = (tColorRGBA *) src->pixels; + dp = (tColorRGBA *) dst->pixels; + sgap = src->pitch - src->w * 4; + dgap = dst->pitch - dst->w * 4; + + /* + * Switch between interpolating and non-interpolating code + */ + if (smooth) { + + /* + * Interpolating Zoom + */ + + /* + * Scan destination + */ + csay = say; + for (y = 0; y < dst->h; y++) { + /* + * Setup color source pointers + */ + c00 = csp; + c01 = csp; + c01++; + c10 = (tColorRGBA *) ((Uint8 *) csp + src->pitch); + c11 = c10; + c11++; + csax = sax; + for (x = 0; x < dst->w; x++) { + + /* + * Interpolate colors + */ + ex = (*csax & 0xffff); + ey = (*csay & 0xffff); + t1 = ((((c01->r - c00->r) * ex) >> 16) + c00->r) & 0xff; + t2 = ((((c11->r - c10->r) * ex) >> 16) + c10->r) & 0xff; + dp->r = (((t2 - t1) * ey) >> 16) + t1; + t1 = ((((c01->g - c00->g) * ex) >> 16) + c00->g) & 0xff; + t2 = ((((c11->g - c10->g) * ex) >> 16) + c10->g) & 0xff; + dp->g = (((t2 - t1) * ey) >> 16) + t1; + t1 = ((((c01->b - c00->b) * ex) >> 16) + c00->b) & 0xff; + t2 = ((((c11->b - c10->b) * ex) >> 16) + c10->b) & 0xff; + dp->b = (((t2 - t1) * ey) >> 16) + t1; + t1 = ((((c01->a - c00->a) * ex) >> 16) + c00->a) & 0xff; + t2 = ((((c11->a - c10->a) * ex) >> 16) + c10->a) & 0xff; + dp->a = (((t2 - t1) * ey) >> 16) + t1; + + /* + * Advance source pointers + */ + csax++; + sstep = (*csax >> 16); + c00 += sstep; + c01 += sstep; + c10 += sstep; + c11 += sstep; + /* + * Advance destination pointer + */ + dp++; + } + /* + * Advance source pointer + */ + csay++; + csp = (tColorRGBA *) ((Uint8 *) csp + (*csay >> 16) * src->pitch); + /* + * Advance destination pointers + */ + dp = (tColorRGBA *) ((Uint8 *) dp + dgap); + } + + } else { + + /* + * Non-Interpolating Zoom + */ + + csay = say; + for (y = 0; y < dst->h; y++) { + sp = csp; + csax = sax; + for (x = 0; x < dst->w; x++) { + /* + * Draw + */ + *dp = *sp; + /* + * Advance source pointers + */ + csax++; + sp += (*csax >> 16); + /* + * Advance destination pointer + */ + dp++; + } + /* + * Advance source pointer + */ + csay++; + csp = (tColorRGBA *) ((Uint8 *) csp + (*csay >> 16) * src->pitch); + /* + * Advance destination pointers + */ + dp = (tColorRGBA *) ((Uint8 *) dp + dgap); + } + + } + +#ifdef __SYMBIAN32__ + HFree(sax); + HFree(say); +#endif + + return (0); +} + +/* + + 8bit Zoomer without smoothing. + + Zoomes 8bit palette/Y 'src' surface to 'dst' surface. + +*/ + +static +int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst) +{ + Uint32 sx, sy, *sax, *say, *csax, *csay, csx, csy; + int x, y; + Uint8 *sp, *dp, *csp; + int dgap; + + /* + * Variable setup + */ + sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w); + sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h); + + /* + * Allocate memory for row increments + */ +#ifndef __SYMBIAN32__ + if ((sax = (Uint32 *) alloca(dst->w * sizeof(Uint32))) == NULL) + return (-1); + if ((say = (Uint32 *) alloca(dst->h * sizeof(Uint32))) == NULL) + return (-1); +#else + if ((sax = (Uint32 *) HMalloc(dst->w * sizeof(Uint32))) == NULL) + return (-1); + if ((say = (Uint32 *) HMalloc(dst->h * sizeof(Uint32))) == NULL) + { + HFree(sax); + return (-1); + } +#endif + + /* + * Precalculate row increments + */ + csx = 0; + csax = sax; + for (x = 0; x < dst->w; x++) { + csx += sx; + *csax = (csx >> 16); + csx &= 0xffff; + csax++; + } + csy = 0; + csay = say; + for (y = 0; y < dst->h; y++) { + csy += sy; + *csay = (csy >> 16); + csy &= 0xffff; + csay++; + } + + csx = 0; + csax = sax; + for (x = 0; x < dst->w; x++) { + csx += (*csax); + csax++; + } + csy = 0; + csay = say; + for (y = 0; y < dst->h; y++) { + csy += (*csay); + csay++; + } + + /* + * Pointer setup + */ + sp = csp = (Uint8 *) src->pixels; + dp = (Uint8 *) dst->pixels; + dgap = dst->pitch - dst->w; + + /* + * Draw + */ + csay = say; + for (y = 0; y < dst->h; y++) { + csax = sax; + sp = csp; + for (x = 0; x < dst->w; x++) { + /* + * Draw + */ + *dp = *sp; + /* + * Advance source pointers + */ + sp += (*csax); + csax++; + /* + * Advance destination pointer + */ + dp++; + } + /* + * Advance source pointer (for row) + */ + csp += ((*csay) * src->pitch); + csay++; + /* + * Advance destination pointers + */ + dp += dgap; + } + +#ifdef __SYMBIAN32__ + HFree(sax); + HFree(say); +#endif + + return (0); +} + +/* + + 32bit Rotozoomer with optional anti-aliasing by bilinear interpolation. + + Rotates and zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface. + +*/ + +static +void transformSurfaceRGBA(SDL_Surface * src, SDL_Surface * dst, int cx, int cy, int isin, int icos, int smooth) +{ + int x, y, t1, t2, dx, dy, xd, yd, sdx, sdy, ax, ay, ex, ey, sw, sh; + tColorRGBA c00, c01, c10, c11; + tColorRGBA *pc, *sp; + int gap; + + /* + * Variable setup + */ + xd = ((src->w - dst->w) << 15); + yd = ((src->h - dst->h) << 15); + ax = (cx << 16) - (icos * cx); + ay = (cy << 16) - (isin * cx); + sw = src->w - 1; + sh = src->h - 1; + pc = dst->pixels; + gap = dst->pitch - dst->w * 4; + + /* + * Switch between interpolating and non-interpolating code + */ + if (smooth) { + for (y = 0; y < dst->h; y++) { + dy = cy - y; + sdx = (ax + (isin * dy)) + xd; + sdy = (ay - (icos * dy)) + yd; + for (x = 0; x < dst->w; x++) { + dx = (sdx >> 16); + dy = (sdy >> 16); + if ((dx >= -1) && (dy >= -1) && (dx < src->w) && (dy < src->h)) { + if ((dx >= 0) && (dy >= 0) && (dx < sw) && (dy < sh)) { + sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy); + sp += dx; + c00 = *sp; + sp += 1; + c01 = *sp; + sp = (tColorRGBA *) ((Uint8 *) sp + src->pitch); + sp -= 1; + c10 = *sp; + sp += 1; + c11 = *sp; + } else if ((dx == sw) && (dy == sh)) { + sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy); + sp += dx; + c00 = *sp; + c01 = *sp; + c10 = *sp; + c11 = *sp; + } else if ((dx == -1) && (dy == -1)) { + sp = (tColorRGBA *) (src->pixels); + c00 = *sp; + c01 = *sp; + c10 = *sp; + c11 = *sp; + } else if ((dx == -1) && (dy == sh)) { + sp = (tColorRGBA *) (src->pixels); + sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy); + c00 = *sp; + c01 = *sp; + c10 = *sp; + c11 = *sp; + } else if ((dx == sw) && (dy == -1)) { + sp = (tColorRGBA *) (src->pixels); + sp += dx; + c00 = *sp; + c01 = *sp; + c10 = *sp; + c11 = *sp; + } else if (dx == -1) { + sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy); + c00 = *sp; + c01 = *sp; + c10 = *sp; + sp = (tColorRGBA *) ((Uint8 *) sp + src->pitch); + c11 = *sp; + } else if (dy == -1) { + sp = (tColorRGBA *) (src->pixels); + sp += dx; + c00 = *sp; + c01 = *sp; + c10 = *sp; + sp += 1; + c11 = *sp; + } else if (dx == sw) { + sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy); + sp += dx; + c00 = *sp; + c01 = *sp; + sp = (tColorRGBA *) ((Uint8 *) sp + src->pitch); + c10 = *sp; + c11 = *sp; + } else if (dy == sh) { + sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy); + sp += dx; + c00 = *sp; + sp += 1; + c01 = *sp; + c10 = *sp; + c11 = *sp; + } + /* + * Interpolate colors + */ + ex = (sdx & 0xffff); + ey = (sdy & 0xffff); + t1 = ((((c01.r - c00.r) * ex) >> 16) + c00.r) & 0xff; + t2 = ((((c11.r - c10.r) * ex) >> 16) + c10.r) & 0xff; + pc->r = (((t2 - t1) * ey) >> 16) + t1; + t1 = ((((c01.g - c00.g) * ex) >> 16) + c00.g) & 0xff; + t2 = ((((c11.g - c10.g) * ex) >> 16) + c10.g) & 0xff; + pc->g = (((t2 - t1) * ey) >> 16) + t1; + t1 = ((((c01.b - c00.b) * ex) >> 16) + c00.b) & 0xff; + t2 = ((((c11.b - c10.b) * ex) >> 16) + c10.b) & 0xff; + pc->b = (((t2 - t1) * ey) >> 16) + t1; + t1 = ((((c01.a - c00.a) * ex) >> 16) + c00.a) & 0xff; + t2 = ((((c11.a - c10.a) * ex) >> 16) + c10.a) & 0xff; + pc->a = (((t2 - t1) * ey) >> 16) + t1; + } + sdx += icos; + sdy += isin; + pc++; + } + pc = (tColorRGBA *) ((Uint8 *) pc + gap); + } + } else { + for (y = 0; y < dst->h; y++) { + dy = cy - y; + sdx = (ax + (isin * dy)) + xd; + sdy = (ay - (icos * dy)) + yd; + for (x = 0; x < dst->w; x++) { + dx = (short) (sdx >> 16); + dy = (short) (sdy >> 16); + if ((dx >= 0) && (dy >= 0) && (dx < src->w) && (dy < src->h)) { + sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy); + sp += dx; + *pc = *sp; + } + sdx += icos; + sdy += isin; + pc++; + } + pc = (tColorRGBA *) ((Uint8 *) pc + gap); + } + } +} + +/* + + 8bit Rotozoomer without smoothing + + Rotates and zoomes 8bit palette/Y 'src' surface to 'dst' surface. + +*/ + +static +void transformSurfaceY(SDL_Surface * src, SDL_Surface * dst, int cx, int cy, int isin, int icos) +{ + int x, y, dx, dy, xd, yd, sdx, sdy, ax, ay, sw, sh; + tColorY *pc, *sp; + int gap; + Uint32 colorkey = 0; + + /* + * Variable setup + */ + xd = ((src->w - dst->w) << 15); + yd = ((src->h - dst->h) << 15); + ax = (cx << 16) - (icos * cx); + ay = (cy << 16) - (isin * cx); + sw = src->w - 1; + sh = src->h - 1; + pc = dst->pixels; + gap = dst->pitch - dst->w; + /* + * Clear surface to colorkey + */ + TFB_GetColorKey (src, &colorkey); + memset(pc, (unsigned char) (colorkey & 0xff), dst->pitch * dst->h); + /* + * Iterate through destination surface + */ + for (y = 0; y < dst->h; y++) { + dy = cy - y; + sdx = (ax + (isin * dy)) + xd; + sdy = (ay - (icos * dy)) + yd; + for (x = 0; x < dst->w; x++) { + dx = (short) (sdx >> 16); + dy = (short) (sdy >> 16); + if ((dx >= 0) && (dy >= 0) && (dx < src->w) && (dy < src->h)) { + sp = (tColorY *) (src->pixels); + sp += (src->pitch * dy + dx); + *pc = *sp; + } + sdx += icos; + sdy += isin; + pc++; + } + pc += gap; + } +} + +/* + + rotozoomSurface() + + Rotates and zoomes a 32bit or 8bit 'src' surface to newly created 'dst' surface. + 'angle' is the rotation in degrees. 'zoom' a scaling factor. If 'smooth' is 1 + then the destination 32bit surface is anti-aliased. If the surface is not 8bit + or 32bit RGBA/ABGR it will be converted into a 32bit RGBA format on the fly. + +*/ + +#define VALUE_LIMIT 0.001 + + +/* Local rotozoom-size function with trig result return */ + +static +void rotozoomSurfaceSizeTrig(int width, int height, double angle, double zoom, int *dstwidth, int *dstheight, + double *canglezoom, double *sanglezoom) +{ + double x, y, cx, cy, sx, sy; + double radangle; + int dstwidthhalf, dstheighthalf; + + /* + * Determine destination width and height by rotating a centered source box + */ + radangle = angle * (M_PI / 180.0); + *sanglezoom = sin(radangle); + *canglezoom = cos(radangle); + *sanglezoom *= zoom; + *canglezoom *= zoom; + x = width / 2; + y = height / 2; + cx = *canglezoom * x; + cy = *canglezoom * y; + sx = *sanglezoom * x; + sy = *sanglezoom * y; + dstwidthhalf = MAX(ceil(fabs(cx) + fabs(sy)), 1); + dstheighthalf = MAX(ceil(fabs(sx) + fabs(cy)), 1); + *dstwidth = 2 * dstwidthhalf; + *dstheight = 2 * dstheighthalf; +} + + +/* Publically available rotozoom-size function */ + +void rotozoomSurfaceSize(int width, int height, double angle, double zoom, int *dstwidth, int *dstheight) +{ + double dummy_sanglezoom, dummy_canglezoom; + + rotozoomSurfaceSizeTrig(width, height, angle, zoom, dstwidth, dstheight, &dummy_sanglezoom, &dummy_canglezoom); +} + + +/* Publically available rotozoom function */ + +SDL_Surface *rotozoomSurface(SDL_Surface * src, double angle, double zoom, int smooth) +{ + SDL_Surface *rz_src; + SDL_Surface *rz_dst; + double zoominv; + double sanglezoom, canglezoom, sanglezoominv, canglezoominv; + int dstwidthhalf, dstwidth, dstheighthalf, dstheight; + int is32bit; + int i, src_converted; + + /* + * Sanity check + */ + if (src == NULL) + return (NULL); + + /* + * Determine if source surface is 32bit or 8bit + */ + is32bit = (src->format->BitsPerPixel == 32); + if ((is32bit) || (src->format->BitsPerPixel == 8)) { + /* + * Use source surface 'as is' + */ + rz_src = src; + src_converted = 0; + } else { + /* + * New source surface is 32bit with a defined RGBA ordering + */ + rz_src = + SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000); + SDL_BlitSurface(src, NULL, rz_src, NULL); + src_converted = 1; + is32bit = 1; + } + + /* + * Sanity check zoom factor + */ + if (zoom < VALUE_LIMIT) { + zoom = VALUE_LIMIT; + } + zoominv = 65536.0 / (zoom * zoom); + + /* + * Check if we have a rotozoom or just a zoom + */ + if (fabs(angle) > VALUE_LIMIT) { + + /* + * Angle!=0: full rotozoom + */ + /* + * ----------------------- + */ + + /* Determine target size */ + rotozoomSurfaceSizeTrig(rz_src->w, rz_src->h, angle, zoom, &dstwidth, &dstheight, &canglezoom, &sanglezoom); + + /* + * Calculate target factors from sin/cos and zoom + */ + sanglezoominv = sanglezoom; + canglezoominv = canglezoom; + sanglezoominv *= zoominv; + canglezoominv *= zoominv; + + /* Calculate half size */ + dstwidthhalf = dstwidth / 2; + dstheighthalf = dstheight / 2; + + /* + * Alloc space to completely contain the rotated surface + */ + rz_dst = NULL; + if (is32bit) { + /* + * Target surface is 32bit with source RGBA/ABGR ordering + */ + rz_dst = + SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 32, + rz_src->format->Rmask, rz_src->format->Gmask, + rz_src->format->Bmask, rz_src->format->Amask); + } else { + /* + * Target surface is 8bit + */ + rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 8, 0, 0, 0, 0); + } + + /* + * Lock source surface + */ + SDL_LockSurface(rz_src); + /* + * Check which kind of surface we have + */ + if (is32bit) { + /* + * Call the 32bit transformation routine to do the rotation (using alpha) + */ + transformSurfaceRGBA(rz_src, rz_dst, dstwidthhalf, dstheighthalf, + (int) (sanglezoominv), (int) (canglezoominv), smooth); + /* + * Turn on source-alpha support + */ + TFB_SetSurfaceAlphaMod (rz_dst, 255); + } else { + /* + * Copy palette and colorkey info + */ + Uint32 srckey = 0; + TFB_GetColorKey (rz_src, &srckey); + for (i = 0; i < rz_src->format->palette->ncolors; i++) { + rz_dst->format->palette->colors[i] = rz_src->format->palette->colors[i]; + } + rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors; + /* + * Call the 8bit transformation routine to do the rotation + */ + transformSurfaceY(rz_src, rz_dst, dstwidthhalf, dstheighthalf, + (int) (sanglezoominv), (int) (canglezoominv)); + TFB_SetColorKey(rz_dst, srckey, 1); + } + /* + * Unlock source surface + */ + SDL_UnlockSurface(rz_src); + + } else { + + /* + * Angle=0: Just a zoom + */ + /* + * -------------------- + */ + + /* + * Calculate target size + */ + zoomSurfaceSize(rz_src->w, rz_src->h, zoom, zoom, &dstwidth, &dstheight); + + /* + * Alloc space to completely contain the zoomed surface + */ + rz_dst = NULL; + if (is32bit) { + /* + * Target surface is 32bit with source RGBA/ABGR ordering + */ + rz_dst = + SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 32, + rz_src->format->Rmask, rz_src->format->Gmask, + rz_src->format->Bmask, rz_src->format->Amask); + } else { + /* + * Target surface is 8bit + */ + rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 8, 0, 0, 0, 0); + } + + /* + * Lock source surface + */ + SDL_LockSurface(rz_src); + /* + * Check which kind of surface we have + */ + if (is32bit) { + /* + * Call the 32bit transformation routine to do the zooming (using alpha) + */ + zoomSurfaceRGBA(rz_src, rz_dst, smooth); + /* + * Turn on source-alpha support + */ + TFB_SetSurfaceAlphaMod (rz_dst, 255); + } else { + /* + * Copy palette and colorkey info + */ + Uint32 srckey = 0; + TFB_GetColorKey (rz_src, &srckey); + for (i = 0; i < rz_src->format->palette->ncolors; i++) { + rz_dst->format->palette->colors[i] = rz_src->format->palette->colors[i]; + } + rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors; + /* + * Call the 8bit transformation routine to do the zooming + */ + zoomSurfaceY(rz_src, rz_dst); + TFB_SetColorKey(rz_dst, srckey, 1); + } + /* + * Unlock source surface + */ + SDL_UnlockSurface(rz_src); + } + + /* + * Cleanup temp surface + */ + if (src_converted) { + SDL_FreeSurface(rz_src); + } + + /* + * Return destination surface + */ + return (rz_dst); +} + +/* Publically available rotate function */ + +int rotateSurface(SDL_Surface *src, SDL_Surface *dst, double angle, int smooth) +{ + double zoominv; + double sanglezoom, canglezoom, sanglezoominv, canglezoominv; + int dstwidthhalf, dstwidth, dstheighthalf, dstheight; + int is32bit; + int i; + + /* Sanity check */ + if (!src || !dst) + return -1; + if (src->format->BitsPerPixel != dst->format->BitsPerPixel) + return -1; + + /* Determine if source surface is 32bit or 8bit */ + is32bit = (src->format->BitsPerPixel == 32); + + zoominv = 65536.0; + + /* Check if we have to rotate anything */ + if (fabs(angle) <= VALUE_LIMIT) + { + SDL_BlitSurface(src, NULL, dst, NULL); + return 0; + } + + /* Determine target size */ + rotozoomSurfaceSizeTrig(src->w, src->h, angle, 1, &dstwidth, &dstheight, &canglezoom, &sanglezoom); + + /* + * Calculate target factors from sin/cos and zoom + */ + sanglezoominv = sanglezoom; + canglezoominv = canglezoom; + sanglezoominv *= zoominv; + canglezoominv *= zoominv; + + /* Calculate half size */ + dstwidthhalf = dstwidth / 2; + dstheighthalf = dstheight / 2; + + /* Check if the rotated surface will fit destination */ + if (dst->w < dstwidth || dst->h < dstheight) + return -1; + + /* Lock source surface */ + SDL_LockSurface(src); + SDL_LockSurface(dst); + /* Check which kind of surface we have */ + if (is32bit) + { /* Call the 32bit transformation routine to do the rotation (using alpha) */ + transformSurfaceRGBA(src, dst, dstwidthhalf, dstheighthalf, + (int) (sanglezoominv), (int) (canglezoominv), smooth); + } + else + { + /* Copy palette info */ + for (i = 0; i < src->format->palette->ncolors; i++) + dst->format->palette->colors[i] = src->format->palette->colors[i]; + dst->format->palette->ncolors = src->format->palette->ncolors; + /* Call the 8bit transformation routine to do the rotation */ + transformSurfaceY(src, dst, dstwidthhalf, dstheighthalf, + (int) (sanglezoominv), (int) (canglezoominv)); + } + /* Unlock source surface */ + SDL_UnlockSurface(dst); + SDL_UnlockSurface(src); + + return 0; +} + +/* + + zoomSurface() + + Zoomes a 32bit or 8bit 'src' surface to newly created 'dst' surface. + 'zoomx' and 'zoomy' are scaling factors for width and height. If 'smooth' is 1 + then the destination 32bit surface is anti-aliased. If the surface is not 8bit + or 32bit RGBA/ABGR it will be converted into a 32bit RGBA format on the fly. + +*/ + +#define VALUE_LIMIT 0.001 + +void zoomSurfaceSize(int width, int height, double zoomx, double zoomy, int *dstwidth, int *dstheight) +{ + /* + * Sanity check zoom factors + */ + if (zoomx < VALUE_LIMIT) { + zoomx = VALUE_LIMIT; + } + if (zoomy < VALUE_LIMIT) { + zoomy = VALUE_LIMIT; + } + + /* + * Calculate target size + */ + *dstwidth = (int) ((double) width * zoomx); + *dstheight = (int) ((double) height * zoomy); + if (*dstwidth < 1) { + *dstwidth = 1; + } + if (*dstheight < 1) { + *dstheight = 1; + } +} + +SDL_Surface *zoomSurface(SDL_Surface * src, double zoomx, double zoomy, int smooth) +{ + SDL_Surface *rz_src; + SDL_Surface *rz_dst; + int dstwidth, dstheight; + int is32bit; + int i, src_converted; + + /* + * Sanity check + */ + if (src == NULL) + return (NULL); + + /* + * Determine if source surface is 32bit or 8bit + */ + is32bit = (src->format->BitsPerPixel == 32); + if ((is32bit) || (src->format->BitsPerPixel == 8)) { + /* + * Use source surface 'as is' + */ + rz_src = src; + src_converted = 0; + } else { + /* + * New source surface is 32bit with a defined RGBA ordering + */ + rz_src = + SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000); + SDL_BlitSurface(src, NULL, rz_src, NULL); + src_converted = 1; + is32bit = 1; + } + + /* Get size if target */ + zoomSurfaceSize(rz_src->w, rz_src->h, zoomx, zoomy, &dstwidth, &dstheight); + + /* + * Alloc space to completely contain the zoomed surface + */ + rz_dst = NULL; + if (is32bit) { + /* + * Target surface is 32bit with source RGBA/ABGR ordering + */ + rz_dst = + SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 32, + rz_src->format->Rmask, rz_src->format->Gmask, + rz_src->format->Bmask, rz_src->format->Amask); + } else { + /* + * Target surface is 8bit + */ + rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 8, 0, 0, 0, 0); + } + + /* + * Lock source surface + */ + SDL_LockSurface(rz_src); + /* + * Check which kind of surface we have + */ + if (is32bit) { + /* + * Call the 32bit transformation routine to do the zooming (using alpha) + */ + zoomSurfaceRGBA(rz_src, rz_dst, smooth); + /* + * Turn on source-alpha support + */ + TFB_SetSurfaceAlphaMod (rz_dst, 255); + } else { + /* + * Copy palette and colorkey info + */ + Uint32 srckey = 0; + TFB_GetColorKey (rz_src, &srckey); + for (i = 0; i < rz_src->format->palette->ncolors; i++) { + rz_dst->format->palette->colors[i] = rz_src->format->palette->colors[i]; + } + rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors; + /* + * Call the 8bit transformation routine to do the zooming + */ + zoomSurfaceY(rz_src, rz_dst); + TFB_SetColorKey(rz_dst, srckey, 0); + } + /* + * Unlock source surface + */ + SDL_UnlockSurface(rz_src); + + /* + * Cleanup temp surface + */ + if (src_converted) { + SDL_FreeSurface(rz_src); + } + + /* + * Return destination surface + */ + return (rz_dst); +} + diff --git a/src/libs/graphics/sdl/rotozoom.h b/src/libs/graphics/sdl/rotozoom.h new file mode 100644 index 0000000..728fd42 --- /dev/null +++ b/src/libs/graphics/sdl/rotozoom.h @@ -0,0 +1,96 @@ +/* + + rotozoom.h - rotozoomer for 32bit or 8bit surfaces + LGPL (c) A. Schiffler + + Note by sc2 developers: + Taken from SDL_gfx library and modified, original code can be downloaded + from http://www.ferzkopp.net/Software/SDL_gfx-2.0/ + +*/ + + +#ifndef ROTOZOOM_H +#define ROTOZOOM_H + +#include <math.h> +#ifndef M_PI +#define M_PI 3.141592654 +#endif + +#include "port.h" +#include SDL_INCLUDE(SDL.h) + + +/* ---- Defines */ + +#define SMOOTHING_OFF 0 +#define SMOOTHING_ON 1 + +/* ---- Structures */ + +typedef struct tColorRGBA { + Uint8 r; + Uint8 g; + Uint8 b; + Uint8 a; +} tColorRGBA; + +typedef struct tColorY { + Uint8 y; +} tColorY; + + +/* ---- Prototypes */ + +/* + zoomSurfaceRGBA() + + Zoom the src surface into dst. The zoom amount is determined + by the dimensions of src and dst + +*/ +int zoomSurfaceRGBA(SDL_Surface * src, SDL_Surface * dst, int smooth); + +/* + + rotozoomSurface() + + Rotates and zoomes a 32bit or 8bit 'src' surface to newly created 'dst' surface. + 'angle' is the rotation in degrees. 'zoom' a scaling factor. If 'smooth' is 1 + then the destination 32bit surface is anti-aliased. If the surface is not 8bit + or 32bit RGBA/ABGR it will be converted into a 32bit RGBA format on the fly. + +*/ + +SDL_Surface *rotozoomSurface(SDL_Surface * src, double angle, double zoom, + int smooth); + +int rotateSurface(SDL_Surface * src, SDL_Surface * dst, double angle, + int smooth); + +/* Returns the size of the target surface for a rotozoomSurface() call */ + +void rotozoomSurfaceSize(int width, int height, double angle, double zoom, + int *dstwidth, int *dstheight); + +/* + + zoomSurface() + + Zoomes a 32bit or 8bit 'src' surface to newly created 'dst' surface. + 'zoomx' and 'zoomy' are scaling factors for width and height. If 'smooth' is 1 + then the destination 32bit surface is anti-aliased. If the surface is not 8bit + or 32bit RGBA/ABGR it will be converted into a 32bit RGBA format on the fly. + +*/ + +SDL_Surface *zoomSurface(SDL_Surface * src, double zoomx, double zoomy, + int smooth); + +/* Returns the size of the target surface for a zoomSurface() call */ + +void zoomSurfaceSize(int width, int height, double zoomx, double zoomy, + int *dstwidth, int *dstheight); + +#endif diff --git a/src/libs/graphics/sdl/scaleint.h b/src/libs/graphics/sdl/scaleint.h new file mode 100644 index 0000000..e54de80 --- /dev/null +++ b/src/libs/graphics/sdl/scaleint.h @@ -0,0 +1,433 @@ +/* + * Copyright (C) 2005 Alex Volkov (codepro@usa.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +// Scalers Internals + +#ifndef SCALEINT_H_ +#define SCALEINT_H_ + +#include "libs/graphics/sdl/sdl_common.h" +#include "types.h" + + +// Plain C names +#define SCALE_(name) Scale ## _ ## name + +// These are defaults +#define SCALE_GETPIX(p) ( *(Uint32 *)(p) ) +#define SCALE_SETPIX(p, c) ( *(Uint32 *)(p) = (c) ) + +// Plain C defaults +#define SCALE_CMPRGB(p1, p2) \ + SCALE_(GetRGBDelta) (fmt, p1, p2) + +#define SCALE_TOYUV(p) \ + SCALE_(RGBtoYUV) (fmt, p) + +#define SCALE_CMPYUV(p1, p2, toler) \ + SCALE_(CmpYUV) (fmt, p1, p2, toler) + +#define SCALE_DIFFYUV(p1, p2) \ + SCALE_(DiffYUV) (p1, p2) +#define SCALE_DIFFYUV_TY 0x40 +#define SCALE_DIFFYUV_TU 0x12 +#define SCALE_DIFFYUV_TV 0x0c + +#define SCALE_GETY(p) \ + SCALE_(GetPixY) (fmt, p) + +#define SCALE_BILINEAR_BLEND4(r0, r1, dst, dlen) \ + SCALE_(Blend_bilinear) (r0, r1, dst, dlen) + +#define NO_PREFETCH 0 +#define INTEL_PREFETCH 1 +#define AMD_PREFETCH 2 + +typedef enum +{ + YUV_XFORM_R = 0, + YUV_XFORM_G = 1, + YUV_XFORM_B = 2, + YUV_XFORM_Y = 0, + YUV_XFORM_U = 1, + YUV_XFORM_V = 2 +} RGB_YUV_INDEX; + +extern const int YUV_matrix[3][3]; + +// pre-computed transformations for 8 bits per channel +extern int RGB_to_YUV[/*RGB*/ 3][/*YUV*/ 3][ /*mult-res*/ 256]; +extern sint16 dRGB_to_dYUV[/*RGB*/ 3][/*YUV*/ 3][ /*mult-res*/ 512]; + +typedef Uint32 YUV_VECTOR; +// pre-computed transformations for RGB555 +extern YUV_VECTOR RGB15_to_YUV[0x8000]; + + +// Platform+Scaler function lookups +// +typedef struct +{ + int flag; + TFB_ScaleFunc func; +} Scale_FuncDef_t; + + +// expands the given rectangle in all directions by 'expansion' +// guarded by 'limits' +extern void Scale_ExpandRect (SDL_Rect* rect, int expansion, + const SDL_Rect* limits); + + +// Standard plain C versions of support functions + +// Initialize various platform-specific features +static inline void +SCALE_(PlatInit) (void) +{ +} + +// Finish with various platform-specific features +static inline void +SCALE_(PlatDone) (void) +{ +} + +#if 0 +static inline void +SCALE_(Prefetch) (const void* p) +{ + /* no-op in pure C */ + (void)p; +} +#else +# define Scale_Prefetch(p) +#endif + +// compute the RGB distance squared between 2 pixels +// Plain C version +static inline int +SCALE_(GetRGBDelta) (const SDL_PixelFormat* fmt, Uint32 pix1, Uint32 pix2) +{ + int c; + int delta; + + c = ((pix1 >> fmt->Rshift) & 0xff) - ((pix2 >> fmt->Rshift) & 0xff); + delta = c * c; + + c = ((pix1 >> fmt->Gshift) & 0xff) - ((pix2 >> fmt->Gshift) & 0xff); + delta += c * c; + + c = ((pix1 >> fmt->Bshift) & 0xff) - ((pix2 >> fmt->Bshift) & 0xff); + delta += c * c; + + return delta; +} + +// retrieve the Y (intensity) component of pixel's YUV +// Plain C version +static inline int +SCALE_(GetPixY) (const SDL_PixelFormat* fmt, Uint32 pix) +{ + Uint32 r, g, b; + + r = (pix >> fmt->Rshift) & 0xff; + g = (pix >> fmt->Gshift) & 0xff; + b = (pix >> fmt->Bshift) & 0xff; + + return RGB_to_YUV [YUV_XFORM_R][YUV_XFORM_Y][r] + + RGB_to_YUV [YUV_XFORM_G][YUV_XFORM_Y][g] + + RGB_to_YUV [YUV_XFORM_B][YUV_XFORM_Y][b]; +} + +static inline YUV_VECTOR +SCALE_(RGBtoYUV) (const SDL_PixelFormat* fmt, Uint32 pix) +{ + return RGB15_to_YUV[ + (((pix >> (fmt->Rshift + 3)) & 0x1f) << 10) | + (((pix >> (fmt->Gshift + 3)) & 0x1f) << 5) | + (((pix >> (fmt->Bshift + 3)) & 0x1f) ) + ]; +} + +// compare 2 pixels with respect to their YUV representations +// tolerance set by toler arg +// returns true: close; false: distant (-gt toler) +// Plain C version +static inline bool +SCALE_(CmpYUV) (const SDL_PixelFormat* fmt, Uint32 pix1, Uint32 pix2, int toler) +#if 1 +{ + int dr, dg, db; + int delta; + + dr = ((pix1 >> fmt->Rshift) & 0xff) - ((pix2 >> fmt->Rshift) & 0xff) + 255; + dg = ((pix1 >> fmt->Gshift) & 0xff) - ((pix2 >> fmt->Gshift) & 0xff) + 255; + db = ((pix1 >> fmt->Bshift) & 0xff) - ((pix2 >> fmt->Bshift) & 0xff) + 255; + + // compute Y delta + delta = abs (dRGB_to_dYUV [YUV_XFORM_R][YUV_XFORM_Y][dr] + + dRGB_to_dYUV [YUV_XFORM_G][YUV_XFORM_Y][dg] + + dRGB_to_dYUV [YUV_XFORM_B][YUV_XFORM_Y][db]); + if (delta > toler) + return false; + + // compute U delta + delta += abs (dRGB_to_dYUV [YUV_XFORM_R][YUV_XFORM_U][dr] + + dRGB_to_dYUV [YUV_XFORM_G][YUV_XFORM_U][dg] + + dRGB_to_dYUV [YUV_XFORM_B][YUV_XFORM_U][db]); + if (delta > toler) + return false; + + // compute V delta + delta += abs (dRGB_to_dYUV [YUV_XFORM_R][YUV_XFORM_V][dr] + + dRGB_to_dYUV [YUV_XFORM_G][YUV_XFORM_V][dg] + + dRGB_to_dYUV [YUV_XFORM_B][YUV_XFORM_V][db]); + + return delta <= toler; +} +#else +{ + int delta; + Uint32 yuv1, yuv2; + + yuv1 = RGB15_to_YUV[ + (((pix1 >> (fmt->Rshift + 3)) & 0x1f) << 10) | + (((pix1 >> (fmt->Gshift + 3)) & 0x1f) << 5) | + (((pix1 >> (fmt->Bshift + 3)) & 0x1f) ) + ]; + + yuv2 = RGB15_to_YUV[ + (((pix2 >> (fmt->Rshift + 3)) & 0x1f) << 10) | + (((pix2 >> (fmt->Gshift + 3)) & 0x1f) << 5) | + (((pix2 >> (fmt->Bshift + 3)) & 0x1f) ) + ]; + + // compute Y delta + delta = abs ((yuv1 & 0xff0000) - (yuv2 & 0xff0000)) >> 16; + if (delta > toler) + return false; + + // compute U delta + delta += abs ((yuv1 & 0x00ff00) - (yuv2 & 0x00ff00)) >> 8; + if (delta > toler) + return false; + + // compute V delta + delta += abs ((yuv1 & 0x0000ff) - (yuv2 & 0x0000ff)); + + return delta <= toler; +} +#endif + +// Check if 2 pixels are different with respect to their +// YUV representations +// returns 0: close; ~0: distant +static inline int +SCALE_(DiffYUV) (Uint32 yuv1, Uint32 yuv2) +{ + // non-branching version -- assumes 2's complement integers + // delta math only needs 25 bits and we have 32 available; + // only interested in the sign bits after subtraction + sint32 delta, ret; + + if (yuv1 == yuv2) + return 0; + + // compute Y delta + delta = abs ((yuv1 & 0xff0000) - (yuv2 & 0xff0000)); + ret = (SCALE_DIFFYUV_TY << 16) - delta; // save sign bit + + // compute U delta + delta = abs ((yuv1 & 0x00ff00) - (yuv2 & 0x00ff00)); + ret |= (SCALE_DIFFYUV_TU << 8) - delta; // save sign bit + + // compute V delta + delta = abs ((yuv1 & 0x0000ff) - (yuv2 & 0x0000ff)); + ret |= SCALE_DIFFYUV_TV - delta; // save sign bit + + return (ret >> 31); +} + +// blends two pixels with 1:1 ratio +static inline Uint32 +SCALE_(Blend_11) (Uint32 pix1, Uint32 pix2) +{ + /* (pix1 + pix2) >> 1 */ + return + /* lower bits can be safely ignored - the error is minimal + expression that calcs them is left for posterity + (pix1 & pix2 & low_mask) + + */ + ((pix1 & 0xfefefefe) >> 1) + ((pix2 & 0xfefefefe) >> 1); +} + +// blends four pixels with 1:1:1:1 ratio +static inline Uint32 +SCALE_(Blend_1111) (Uint32 pix1, Uint32 pix2, + Uint32 pix3, Uint32 pix4) +{ + /* (pix1 + pix2 + pix3 + pix4) >> 2 */ + return + /* lower bits can be safely ignored - the error is minimal + expression that calcs them is left for posterity + ((((pix1 & low_mask) + (pix2 & low_mask) + + (pix3 & low_mask) + (pix4 & low_mask) + ) >> 2) & low_mask) + + */ + ((pix1 & 0xfcfcfcfc) >> 2) + ((pix2 & 0xfcfcfcfc) >> 2) + + ((pix3 & 0xfcfcfcfc) >> 2) + ((pix4 & 0xfcfcfcfc) >> 2); +} + +// blends pixels with 3:1 ratio +static inline Uint32 +Scale_Blend_31 (Uint32 pix1, Uint32 pix2) +{ + /* (pix1 * 3 + pix2) / 4 */ + /* lower bits can be safely ignored - the error is minimal */ + return ((pix1 & 0xfefefefe) >> 1) + ((pix1 & 0xfcfcfcfc) >> 2) + + ((pix2 & 0xfcfcfcfc) >> 2); +} + +// blends pixels with 2:1:1 ratio +static inline Uint32 +Scale_Blend_211 (Uint32 pix1, Uint32 pix2, Uint32 pix3) +{ + /* (pix1 * 2 + pix2 + pix3) / 4 */ + /* lower bits can be safely ignored - the error is minimal */ + return ((pix1 & 0xfefefefe) >> 1) + + ((pix2 & 0xfcfcfcfc) >> 2) + + ((pix3 & 0xfcfcfcfc) >> 2); +} + +// blends pixels with 5:2:1 ratio +static inline Uint32 +Scale_Blend_521 (Uint32 pix1, Uint32 pix2, Uint32 pix3) +{ + /* (pix1 * 5 + pix2 * 2 + pix3) / 8 */ + /* lower bits can be safely ignored - the error is minimal */ + return ((pix1 & 0xfefefefe) >> 1) + ((pix1 & 0xf8f8f8f8) >> 3) + + ((pix2 & 0xfcfcfcfc) >> 2) + + ((pix3 & 0xf8f8f8f8) >> 3) + + 0x02020202 /* half-error */; +} + +// blends pixels with 6:1:1 ratio +static inline Uint32 +Scale_Blend_611 (Uint32 pix1, Uint32 pix2, Uint32 pix3) +{ + /* (pix1 * 6 + pix2 + pix3) / 8 */ + /* lower bits can be safely ignored - the error is minimal */ + return ((pix1 & 0xfefefefe) >> 1) + ((pix1 & 0xfcfcfcfc) >> 2) + + ((pix2 & 0xf8f8f8f8) >> 3) + + ((pix3 & 0xf8f8f8f8) >> 3) + + 0x02020202 /* half-error */; +} + +// blends pixels with 2:3:3 ratio +static inline Uint32 +Scale_Blend_233 (Uint32 pix1, Uint32 pix2, Uint32 pix3) +{ + /* (pix1 * 2 + pix2 * 3 + pix3 * 3) / 8 */ + /* lower bits can be safely ignored - the error is minimal */ + return ((pix1 & 0xfcfcfcfc) >> 2) + + ((pix2 & 0xfcfcfcfc) >> 2) + ((pix2 & 0xf8f8f8f8) >> 3) + + ((pix3 & 0xfcfcfcfc) >> 2) + ((pix3 & 0xf8f8f8f8) >> 3) + + 0x02020202 /* half-error */; +} + +// blends pixels with 14:1:1 ratio +static inline Uint32 +Scale_Blend_e11 (Uint32 pix1, Uint32 pix2, Uint32 pix3) +{ + /* (pix1 * 14 + pix2 + pix3) >> 4 */ + /* lower bits can be safely ignored - the error is minimal */ + return ((pix1 & 0xfefefefe) >> 1) + ((pix1 & 0xfcfcfcfc) >> 2) + + ((pix1 & 0xf8f8f8f8) >> 3) + + ((pix2 & 0xf0f0f0f0) >> 4) + + ((pix3 & 0xf0f0f0f0) >> 4) + + 0x03030303 /* half-error */; +} + +// Halfs the pixel's intensity +static inline Uint32 +SCALE_(HalfPixel) (Uint32 pix) +{ + return ((pix & 0xfefefefe) >> 1); +} + + +// Bilinear weighted blend of four pixels +// Function produces 4 blended pixels and writes them +// out to the surface (in 2x2 matrix) +// Pixels are computed using expanded weight matrix like so: +// ('sp' - source pixel, 'dp' - destination pixel) +// dp[0] = (9*sp[0] + 3*sp[1] + 3*sp[2] + 1*sp[3]) / 16 +// dp[1] = (3*sp[0] + 9*sp[1] + 1*sp[2] + 3*sp[3]) / 16 +// dp[2] = (3*sp[0] + 1*sp[1] + 9*sp[2] + 3*sp[3]) / 16 +// dp[3] = (1*sp[0] + 3*sp[1] + 3*sp[2] + 9*sp[3]) / 16 +static inline void +SCALE_(Blend_bilinear) (const Uint32* row0, const Uint32* row1, + Uint32* dst_p, Uint32 dlen) +{ + // We loose some lower bits here and try to compensate for + // that by adding half-error values. + // In general, the error is minimal (+-7) + // The >>4 reduction is achieved gradually +# define BL_PACKED_HALF(p) \ + (((p) & 0xfefefefe) >> 1) +# define BL_SUM(p1, p2) \ + (BL_PACKED_HALF(p1) + BL_PACKED_HALF(p2)) +# define BL_HALF_ERR 0x01010101 +# define BL_SUM_WERR(p1, p2) \ + (BL_PACKED_HALF(p1) + BL_PACKED_HALF(p2) + BL_HALF_ERR) + + Uint32 sum1111, sum1331, sum3113; + + // cache p[0] + 3*(p[1] + p[2]) + p[3] in sum1331 + // cache p[1] + 3*(p[0] + p[3]) + p[2] in sum3113 + sum1331 = BL_SUM (row0[1], row1[0]); + sum3113 = BL_SUM (row0[0], row1[1]); + + // cache p[0] + p[1] + p[2] + p[3] in sum1111 + sum1111 = BL_SUM_WERR (sum1331, sum3113); + + sum1331 = BL_SUM_WERR (sum1331, sum1111); + sum1331 = BL_PACKED_HALF (sum1331); + sum3113 = BL_SUM_WERR (sum3113, sum1111); + sum3113 = BL_PACKED_HALF (sum3113); + + // pixel 0 math -- (9*p[0] + 3*(p[1] + p[2]) + p[3]) / 16 + dst_p[0] = BL_PACKED_HALF (row0[0]) + sum1331; + + // pixel 1 math -- (9*p[1] + 3*(p[0] + p[3]) + p[2]) / 16 + dst_p[1] = BL_PACKED_HALF (row0[1]) + sum3113; + + // pixel 2 math -- (9*p[2] + 3*(p[0] + p[3]) + p[1]) / 16 + dst_p[dlen] = BL_PACKED_HALF (row1[0]) + sum3113; + + // pixel 3 math -- (9*p[3] + 3*(p[1] + p[2]) + p[0]) / 16 + dst_p[dlen + 1] = BL_PACKED_HALF (row1[1]) + sum1331; + +# undef BL_PACKED_HALF +# undef BL_SUM +# undef BL_HALF_ERR +# undef BL_SUM_WERR +} + +#endif /* SCALEINT_H_ */ diff --git a/src/libs/graphics/sdl/scalemmx.h b/src/libs/graphics/sdl/scalemmx.h new file mode 100644 index 0000000..69c83fe --- /dev/null +++ b/src/libs/graphics/sdl/scalemmx.h @@ -0,0 +1,793 @@ +/* + * Copyright (C) 2005 Alex Volkov (codepro@usa.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef SCALEMMX_H_ +#define SCALEMMX_H_ + +#if !defined(SCALE_) +# error Please define SCALE_(name) before including scalemmx.h +#endif + +#if !defined(MSVC_ASM) && !defined(GCC_ASM) +# error Please define target assembler (MSVC_ASM, GCC_ASM) before including scalemmx.h +#endif + +// MMX defaults (no Format param) +#undef SCALE_CMPRGB +#define SCALE_CMPRGB(p1, p2) \ + SCALE_(GetRGBDelta) (p1, p2) + +#undef SCALE_TOYUV +#define SCALE_TOYUV(p) \ + SCALE_(RGBtoYUV) (p) + +#undef SCALE_CMPYUV +#define SCALE_CMPYUV(p1, p2, toler) \ + SCALE_(CmpYUV) (p1, p2, toler) + +#undef SCALE_GETY +#define SCALE_GETY(p) \ + SCALE_(GetPixY) (p) + +// MMX transformation multipliers +extern Uint64 mmx_888to555_mult; +extern Uint64 mmx_Y_mult; +extern Uint64 mmx_U_mult; +extern Uint64 mmx_V_mult; +extern Uint64 mmx_YUV_threshold; + +#define USE_YUV_LOOKUP + +#if defined(MSVC_ASM) +// MSVC inline assembly versions + +#if defined(USE_MOVNTQ) +# define MOVNTQ(addr, val) movntq [addr], val +#else +# define MOVNTQ(addr, val) movq [addr], val +#endif + +#if USE_PREFETCH == INTEL_PREFETCH +// using Intel SSE non-temporal prefetch +# define PREFETCH(addr) prefetchnta [addr] +# define HAVE_PREFETCH +#elif USE_PREFETCH == AMD_PREFETCH +// using AMD 3DNOW! prefetch +# define PREFETCH(addr) prefetch [addr] +# define HAVE_PREFETCH +#else +// no prefetch -- too bad for poor MMX-only souls +# define PREFETCH(addr) +# undef HAVE_PREFETCH +#endif + +#if defined(_MSC_VER) && (_MSC_VER >= 1300) +# pragma warning( disable : 4799 ) +#endif + +static inline void +SCALE_(PlatInit) (void) +{ + __asm + { + // mm0 will be kept == 0 throughout + // 0 is needed for bytes->words unpack instructions + pxor mm0, mm0 + } +} + +static inline void +SCALE_(PlatDone) (void) +{ + // finish with MMX registers and yield them to FPU + __asm + { + emms + } +} + +#if defined(HAVE_PREFETCH) +static inline void +SCALE_(Prefetch) (const void* p) +{ + __asm + { + mov eax, p + PREFETCH (eax) + } +} + +#else /* Not HAVE_PREFETCH */ + +static inline void +SCALE_(Prefetch) (const void* p) +{ + (void)p; // silence compiler + /* no-op */ +} + +#endif /* HAVE_PREFETCH */ + +// compute the RGB distance squared between 2 pixels +static inline int +SCALE_(GetRGBDelta) (Uint32 pix1, Uint32 pix2) +{ + __asm + { + // load pixels + movd mm1, pix1 + punpcklbw mm1, mm0 + movd mm2, pix2 + punpcklbw mm2, mm0 + // get the difference between RGBA components + psubw mm1, mm2 + // squared and sumed + pmaddwd mm1, mm1 + // finish suming the squares + movq mm2, mm1 + punpckhdq mm2, mm0 + paddd mm1, mm2 + // store result + movd eax, mm1 + } +} + +// retrieve the Y (intensity) component of pixel's YUV +static inline int +SCALE_(GetPixY) (Uint32 pix) +{ + __asm + { + // load pixel + movd mm1, pix + punpcklbw mm1, mm0 + // process + pmaddwd mm1, mmx_Y_mult // RGB * Yvec + movq mm2, mm1 // finish suming + punpckhdq mm2, mm0 // ditto + paddd mm1, mm2 // ditto + // store result + movd eax, mm1 + shr eax, 14 + } +} + +#ifdef USE_YUV_LOOKUP + +// convert pixel RGB vector into YUV representation vector +static inline YUV_VECTOR +SCALE_(RGBtoYUV) (Uint32 pix) +{ + __asm + { + // convert RGB888 to 555 + movd mm1, pix + punpcklbw mm1, mm0 + psrlw mm1, 3 // 8->5 bit + pmaddwd mm1, mmx_888to555_mult // shuffle into the right channel order + movq mm2, mm1 // finish shuffling + punpckhdq mm2, mm0 // ditto + por mm1, mm2 // ditto + + // lookup the YUV vector + movd eax, mm1 + mov eax, [RGB15_to_YUV + eax * 4] + } +} + +// compare 2 pixels with respect to their YUV representations +// tolerance set by toler arg +// returns true: close; false: distant (-gt toler) +static inline bool +SCALE_(CmpYUV) (Uint32 pix1, Uint32 pix2, int toler) +{ + __asm + { + // convert RGB888 to 555 + movd mm1, pix1 + punpcklbw mm1, mm0 + psrlw mm1, 3 // 8->5 bit + movd mm3, pix2 + punpcklbw mm3, mm0 + psrlw mm3, 3 // 8->5 bit + pmaddwd mm1, mmx_888to555_mult // shuffle into the right channel order + movq mm2, mm1 // finish shuffling + pmaddwd mm3, mmx_888to555_mult // shuffle into the right channel order + movq mm4, mm3 // finish shuffling + punpckhdq mm2, mm0 // ditto + por mm1, mm2 // ditto + punpckhdq mm4, mm0 // ditto + por mm3, mm4 // ditto + + // lookup the YUV vector + movd eax, mm1 + movd edx, mm3 + movd mm1, [RGB15_to_YUV + eax * 4] + movq mm4, mm1 + movd mm2, [RGB15_to_YUV + edx * 4] + + // get abs difference between YUV components +#ifdef USE_PSADBW + // we can use PSADBW and save us some grief + psadbw mm1, mm2 + movd edx, mm1 +#else + // no PSADBW -- have to do it the hard way + psubusb mm1, mm2 + psubusb mm2, mm4 + por mm1, mm2 + + // sum the differences + // XXX: technically, this produces a MAX diff of 510 + // but we do not need anything bigger, currently + movq mm2, mm1 + psrlq mm2, 8 + paddusb mm1, mm2 + psrlq mm2, 8 + paddusb mm1, mm2 + movd edx, mm1 + and edx, 0xff +#endif /* USE_PSADBW */ + xor eax, eax + shl edx, 1 + cmp edx, toler + // store result + setle al + } +} + +#else /* Not USE_YUV_LOOKUP */ + +// convert pixel RGB vector into YUV representation vector +static inline YUV_VECTOR +SCALE_(RGBtoYUV) (Uint32 pix) +{ + __asm + { + movd mm1, pix + punpcklbw mm1, mm0 + + movq mm2, mm1 + + // Y vector multiply + pmaddwd mm1, mmx_Y_mult + movq mm4, mm1 + punpckhdq mm4, mm0 + punpckldq mm1, mm0 // clear out the high dword + paddd mm1, mm4 + psrad mm1, 15 + + movq mm3, mm2 + + // U vector multiply + pmaddwd mm2, mmx_U_mult + psrad mm2, 10 + + // V vector multiply + pmaddwd mm3, mmx_V_mult + psrad mm3, 10 + + // load (1|1|1|1) into mm4 + pcmpeqw mm4, mm4 + psrlw mm4, 15 + + packssdw mm3, mm2 + pmaddwd mm3, mm4 + psrad mm3, 5 + + // load (64|64) into mm4 + punpcklwd mm4, mm0 + pslld mm4, 6 + paddd mm3, mm4 + + packssdw mm3, mm1 + packuswb mm3, mm0 + + movd eax, mm3 + } +} + +// compare 2 pixels with respect to their YUV representations +// tolerance set by toler arg +// returns true: close; false: distant (-gt toler) +static inline bool +SCALE_(CmpYUV) (Uint32 pix1, Uint32 pix2, int toler) +{ + __asm + { + movd mm1, pix1 + punpcklbw mm1, mm0 + movd mm2, pix2 + punpcklbw mm2, mm0 + + psubw mm1, mm2 + movq mm2, mm1 + + // Y vector multiply + pmaddwd mm1, mmx_Y_mult + movq mm4, mm1 + punpckhdq mm4, mm0 + paddd mm1, mm4 + // abs() + movq mm4, mm1 + psrad mm4, 31 + pxor mm4, mm1 + psubd mm1, mm4 + + movq mm3, mm2 + + // U vector multiply + pmaddwd mm2, mmx_U_mult + movq mm4, mm2 + punpckhdq mm4, mm0 + paddd mm2, mm4 + // abs() + movq mm4, mm2 + psrad mm4, 31 + pxor mm4, mm2 + psubd mm2, mm4 + + paddd mm1, mm2 + + // V vector multiply + pmaddwd mm3, mmx_V_mult + movq mm4, mm3 + punpckhdq mm3, mm0 + paddd mm3, mm4 + // abs() + movq mm4, mm3 + psrad mm4, 31 + pxor mm4, mm3 + psubd mm3, mm4 + + paddd mm1, mm3 + + movd edx, mm1 + xor eax, eax + shr edx, 14 + cmp edx, toler + // store result + setle al + } +} + +#endif /* USE_YUV_LOOKUP */ + +// Check if 2 pixels are different with respect to their +// YUV representations +// returns 0: close; ~0: distant +static inline int +SCALE_(DiffYUV) (Uint32 yuv1, Uint32 yuv2) +{ + __asm + { + // load YUV pixels + movd mm1, yuv1 + movq mm4, mm1 + movd mm2, yuv2 + // abs difference between channels + psubusb mm1, mm2 + psubusb mm2, mm4 + por mm1, mm2 + // compare to threshold + psubusb mm1, mmx_YUV_threshold + + movd edx, mm1 + // transform eax to 0 or ~0 + xor eax, eax + or edx, edx + setz al + dec eax + } +} + +// bilinear weighted blend of four pixels +// MSVC asm version +static inline void +SCALE_(Blend_bilinear) (const Uint32* row0, const Uint32* row1, + Uint32* dst_p, Uint32 dlen) +{ + __asm + { + // EL0: setup vars + mov ebx, row0 // EL0 + + // EL0: load pixels + movq mm1, [ebx] // EL0 + movq mm2, mm1 // EL0: p[1] -> mm2 + PREFETCH (ebx + 0x80) + punpckhbw mm2, mm0 // EL0: p[1] -> mm2 + mov ebx, row1 + punpcklbw mm1, mm0 // EL0: p[0] -> mm1 + movq mm3, [ebx] + movq mm4, mm3 // EL0: p[3] -> mm4 + movq mm6, mm2 // EL1.1: p[1] -> mm6 + PREFETCH (ebx + 0x80) + punpcklbw mm3, mm0 // EL0: p[2] -> mm3 + movq mm5, mm1 // EL1.1: p[0] -> mm5 + punpckhbw mm4, mm0 // EL0: p[3] -> mm4 + + mov edi, dst_p // EL0 + + // EL1: cache p[0] + 3*(p[1] + p[2]) + p[3] in mm6 + paddw mm6, mm3 // EL1.2: p[1] + p[2] -> mm6 + // EL1: cache p[0] + p[1] + p[2] + p[3] in mm7 + movq mm7, mm6 // EL1.3: p[1] + p[2] -> mm7 + // EL1: cache p[1] + 3*(p[0] + p[3]) + p[2] in mm5 + paddw mm5, mm4 // EL1.2: p[0] + p[3] -> mm5 + psllw mm6, 1 // EL1.4: 2*(p[1] + p[2]) -> mm6 + paddw mm7, mm5 // EL1.4: sum(p[]) -> mm7 + psllw mm5, 1 // EL1.5: 2*(p[0] + p[3]) -> mm5 + paddw mm6, mm7 // EL1.5: p[0] + 3*(p[1] + p[2]) + p[3] -> mm6 + paddw mm5, mm7 // EL1.6: p[1] + 3*(p[0] + p[3]) + p[2] -> mm5 + + // EL2: pixel 0 math -- (9*p[0] + 3*(p[1] + p[2]) + p[3]) / 16 + psllw mm1, 3 // EL2.1: 8*p[0] -> mm1 + paddw mm1, mm6 // EL2.2: 9*p[0] + 3*(p[1] + p[2]) + p[3] -> mm1 + psrlw mm1, 4 // EL2.3: sum[0]/16 -> mm1 + + mov edx, dlen // EL0 + + // EL3: pixel 1 math -- (9*p[1] + 3*(p[0] + p[3]) + p[2]) / 16 + psllw mm2, 3 // EL3.1: 8*p[1] -> mm2 + paddw mm2, mm5 // EL3.2: 9*p[1] + 3*(p[0] + p[3]) + p[2] -> mm2 + psrlw mm2, 4 // EL3.3: sum[1]/16 -> mm5 + + // EL2/3: store pixels 0 & 1 + packuswb mm1, mm2 // EL2/3: pack into bytes + MOVNTQ (edi, mm1) // EL2/3: store 2 pixels + + // EL4: pixel 2 math -- (9*p[2] + 3*(p[0] + p[3]) + p[1]) / 16 + psllw mm3, 3 // EL4.1: 8*p[2] -> mm3 + paddw mm3, mm5 // EL4.2: 9*p[2] + 3*(p[0] + p[3]) + p[1] -> mm3 + psrlw mm3, 4 // EL4.3: sum[2]/16 -> mm3 + + // EL5: pixel 3 math -- (9*p[3] + 3*(p[1] + p[2]) + p[0]) / 16 + psllw mm4, 3 // EL5.1: 8*p[3] -> mm4 + paddw mm4, mm6 // EL5.2: 9*p[3] + 3*(p[1] + p[2]) + p[0] -> mm4 + psrlw mm4, 4 // EL5.3: sum[3]/16 -> mm4 + + // EL4/5: store pixels 2 & 3 + packuswb mm3, mm4 // EL4/5: pack into bytes + MOVNTQ (edi + edx*4, mm3) // EL4/5: store 2 pixels + } +} +// End MSVC_ASM + +#elif defined(GCC_ASM) +// GCC inline assembly versions + +#if defined(USE_MOVNTQ) +# define MOVNTQ(val, addr) "movntq " #val "," #addr +#else +# define MOVNTQ(val, addr) "movq " #val "," #addr +#endif + +#if USE_PREFETCH == INTEL_PREFETCH +// using Intel SSE non-temporal prefetch +# define PREFETCH(addr) "prefetchnta " #addr +#elif USE_PREFETCH == AMD_PREFETCH +// using AMD 3DNOW! prefetch +# define PREFETCH(addr) "prefetch " #addr +#else +// no prefetch -- too bad for poor MMX-only souls +# define PREFETCH(addr) +#endif + +#if defined(__x86_64__) +# define A_REG "rax" +# define D_REG "rdx" +# define CLR_UPPER32(r) "xor " "%%" r "," "%%" r +#else +# define A_REG "eax" +# define D_REG "edx" +# define CLR_UPPER32(r) +#endif + +static inline void +SCALE_(PlatInit) (void) +{ + __asm__ ( + // mm0 will be kept == 0 throughout + // 0 is needed for bytes->words unpack instructions + "pxor %%mm0, %%mm0 \n\t" + + : /* nothing */ + : /* nothing */ + ); +} + +static inline void +SCALE_(PlatDone) (void) +{ + // finish with MMX registers and yield them to FPU + __asm__ ( + "emms \n\t" + : /* nothing */ : /* nothing */ + ); +} + +static inline void +SCALE_(Prefetch) (const void* p) +{ + __asm__ __volatile__ ("" PREFETCH (%0) : /*nothing*/ : "m" (p) ); +} + +// compute the RGB distance squared between 2 pixels +static inline int +SCALE_(GetRGBDelta) (Uint32 pix1, Uint32 pix2) +{ + int res; + + __asm__ ( + // load pixels + "movd %1, %%mm1 \n\t" + "punpcklbw %%mm0, %%mm1 \n\t" + "movd %2, %%mm2 \n\t" + "punpcklbw %%mm0, %%mm2 \n\t" + // get the difference between RGBA components + "psubw %%mm2, %%mm1 \n\t" + // squared and sumed + "pmaddwd %%mm1, %%mm1 \n\t" + // finish suming the squares + "movq %%mm1, %%mm2 \n\t" + "punpckhdq %%mm0, %%mm2 \n\t" + "paddd %%mm2, %%mm1 \n\t" + // store result + "movd %%mm1, %0 \n\t" + + : /*0*/"=rm" (res) + : /*1*/"rm" (pix1), /*2*/"rm" (pix2) + ); + + return res; +} + +// retrieve the Y (intensity) component of pixel's YUV +static inline int +SCALE_(GetPixY) (Uint32 pix) +{ + int ret; + + __asm__ ( + // load pixel + "movd %1, %%mm1 \n\t" + "punpcklbw %%mm0, %%mm1 \n\t" + // process + "pmaddwd %2, %%mm1 \n\t" // R,G,B * Yvec + "movq %%mm1, %%mm2 \n\t" // finish suming + "punpckhdq %%mm0, %%mm2 \n\t" // ditto + "paddd %%mm2, %%mm1 \n\t" // ditto + // store index + "movd %%mm1, %0 \n\t" + + : /*0*/"=r" (ret) + : /*1*/"rm" (pix), /*2*/"m" (mmx_Y_mult) + ); + return ret >> 14; +} + +#ifdef USE_YUV_LOOKUP + +// convert pixel RGB vector into YUV representation vector +static inline YUV_VECTOR +SCALE_(RGBtoYUV) (Uint32 pix) +{ + int i; + + __asm__ ( + // convert RGB888 to 555 + "movd %1, %%mm1 \n\t" + "punpcklbw %%mm0, %%mm1 \n\t" + "psrlw $3, %%mm1 \n\t" // 8->5 bit + "pmaddwd %2, %%mm1 \n\t" // shuffle into the right channel order + "movq %%mm1, %%mm2 \n\t" // finish shuffling + "punpckhdq %%mm0, %%mm2 \n\t" // ditto + "por %%mm2, %%mm1 \n\t" // ditto + "movd %%mm1, %0 \n\t" + + : /*0*/"=rm" (i) + : /*1*/"rm" (pix), /*2*/"m" (mmx_888to555_mult) + ); + return RGB15_to_YUV[i]; +} + +// compare 2 pixels with respect to their YUV representations +// tolerance set by toler arg +// returns true: close; false: distant (-gt toler) +static inline bool +SCALE_(CmpYUV) (Uint32 pix1, Uint32 pix2, int toler) +{ + int delta; + + __asm__ ( + "movd %1, %%mm1 \n\t" + "movd %2, %%mm3 \n\t" + + // convert RGB888 to 555 + // this is somewhat parallelized + "punpcklbw %%mm0, %%mm1 \n\t" + CLR_UPPER32 (A_REG) "\n\t" + "psrlw $3, %%mm1 \n\t" // 8->5 bit + "punpcklbw %%mm0, %%mm3 \n\t" + "psrlw $3, %%mm3 \n\t" // 8->5 bit + "pmaddwd %4, %%mm1 \n\t" // shuffle into the right channel order + "movq %%mm1, %%mm2 \n\t" // finish shuffling + "pmaddwd %4, %%mm3 \n\t" // shuffle into the right channel order + CLR_UPPER32 (D_REG) "\n\t" + "movq %%mm3, %%mm4 \n\t" // finish shuffling + "punpckhdq %%mm0, %%mm2 \n\t" // ditto + "por %%mm2, %%mm1 \n\t" // ditto + "punpckhdq %%mm0, %%mm4 \n\t" // ditto + "por %%mm4, %%mm3 \n\t" // ditto + + // lookup the YUV vector + "movd %%mm1, %%eax \n\t" + "movd %%mm3, %%edx \n\t" + "movd (%3, %%" A_REG ", 4), %%mm1 \n\t" + "movq %%mm1, %%mm4 \n\t" + "movd (%3, %%" D_REG ", 4), %%mm2 \n\t" + + // get abs difference between YUV components +#ifdef USE_PSADBW + // we can use PSADBW and save us some grief + "psadbw %%mm2, %%mm1 \n\t" + "movd %%mm1, %0 \n\t" +#else + // no PSADBW -- have to do it the hard way + "psubusb %%mm2, %%mm1 \n\t" + "psubusb %%mm4, %%mm2 \n\t" + "por %%mm2, %%mm1 \n\t" + + // sum the differences + // technically, this produces a MAX diff of 510 + // but we do not need anything bigger, currently + "movq %%mm1, %%mm2 \n\t" + "psrlq $8, %%mm2 \n\t" + "paddusb %%mm2, %%mm1 \n\t" + "psrlq $8, %%mm2 \n\t" + "paddusb %%mm2, %%mm1 \n\t" + // store intermediate delta + "movd %%mm1, %0 \n\t" + "andl $0xff, %0 \n\t" +#endif /* USE_PSADBW */ + : /*0*/"=rm" (delta) + : /*1*/"rm" (pix1), /*2*/"rm" (pix2), + /*3*/ "r" (RGB15_to_YUV), + /*4*/"m" (mmx_888to555_mult) + : "%" A_REG, "%" D_REG, "cc" + ); + + return (delta << 1) <= toler; +} + +#endif /* USE_YUV_LOOKUP */ + +// Check if 2 pixels are different with respect to their +// YUV representations +// returns 0: close; ~0: distant +static inline int +SCALE_(DiffYUV) (Uint32 yuv1, Uint32 yuv2) +{ + sint32 ret; + + __asm__ ( + // load YUV pixels + "movd %1, %%mm1 \n\t" + "movq %%mm1, %%mm4 \n\t" + "movd %2, %%mm2 \n\t" + // abs difference between channels + "psubusb %%mm2, %%mm1 \n\t" + "psubusb %%mm4, %%mm2 \n\t" + CLR_UPPER32(D_REG) "\n\t" + "por %%mm2, %%mm1 \n\t" + // compare to threshold + "psubusb %3, %%mm1 \n\t" + + "movd %%mm1, %%edx \n\t" + // transform eax to 0 or ~0 + "xor %%" A_REG ", %%" A_REG "\n\t" + "or %%" D_REG ", %%" D_REG "\n\t" + "setz %%al \n\t" + "dec %%" A_REG " \n\t" + + : /*0*/"=a" (ret) + : /*1*/"rm" (yuv1), /*2*/"rm" (yuv2), + /*3*/"m" (mmx_YUV_threshold) + : "%" D_REG, "cc" + ); + return ret; +} + +// Bilinear weighted blend of four pixels +// Function produces 4 blended pixels (in 2x2 matrix) and writes them +// out to the surface +// Last version +static inline void +SCALE_(Blend_bilinear) (const Uint32* row0, const Uint32* row1, + Uint32* dst_p, Uint32 dlen) +{ + __asm__ ( + // EL0: load pixels + "movq %0, %%mm1 \n\t" // EL0 + "movq %%mm1, %%mm2 \n\t" // EL0: p[1] -> mm2 + PREFETCH (0x80%0) "\n\t" + "punpckhbw %%mm0, %%mm2 \n\t" // EL0: p[1] -> mm2 + "punpcklbw %%mm0, %%mm1 \n\t" // EL0: p[0] -> mm1 + "movq %1, %%mm3 \n\t" + "movq %%mm3, %%mm4 \n\t" // EL0: p[3] -> mm4 + "movq %%mm2, %%mm6 \n\t" // EL1.1: p[1] -> mm6 + PREFETCH (0x80%1) "\n\t" + "punpcklbw %%mm0, %%mm3 \n\t" // EL0: p[2] -> mm3 + "movq %%mm1, %%mm5 \n\t" // EL1.1: p[0] -> mm5 + "punpckhbw %%mm0, %%mm4 \n\t" // EL0: p[3] -> mm4 + + // EL1: cache p[0] + 3*(p[1] + p[2]) + p[3] in mm6 + "paddw %%mm3, %%mm6 \n\t" // EL1.2: p[1] + p[2] -> mm6 + // EL1: cache p[0] + p[1] + p[2] + p[3] in mm7 + "movq %%mm6, %%mm7 \n\t" // EL1.3: p[1] + p[2] -> mm7 + // EL1: cache p[1] + 3*(p[0] + p[3]) + p[2] in mm5 + "paddw %%mm4, %%mm5 \n\t" // EL1.2: p[0] + p[3] -> mm5 + "psllw $1, %%mm6 \n\t" // EL1.4: 2*(p[1] + p[2]) -> mm6 + "paddw %%mm5, %%mm7 \n\t" // EL1.4: sum(p[]) -> mm7 + "psllw $1, %%mm5 \n\t" // EL1.5: 2*(p[0] + p[3]) -> mm5 + "paddw %%mm7, %%mm6 \n\t" // EL1.5: p[0] + 3*(p[1] + p[2]) + p[3] -> mm6 + "paddw %%mm7, %%mm5 \n\t" // EL1.6: p[1] + 3*(p[0] + p[3]) + p[2] -> mm5 + + // EL2: pixel 0 math -- (9*p[0] + 3*(p[1] + p[2]) + p[3]) / 16 + "psllw $3, %%mm1 \n\t" // EL2.1: 8*p[0] -> mm1 + "paddw %%mm6, %%mm1 \n\t" // EL2.2: 9*p[0] + 3*(p[1] + p[2]) + p[3] -> mm1 + "psrlw $4, %%mm1 \n\t" // EL2.3: sum[0]/16 -> mm1 + + // EL3: pixel 1 math -- (9*p[1] + 3*(p[0] + p[3]) + p[2]) / 16 + "psllw $3, %%mm2 \n\t" // EL3.1: 8*p[1] -> mm2 + "paddw %%mm5, %%mm2 \n\t" // EL3.2: 9*p[1] + 3*(p[0] + p[3]) + p[2] -> mm5 + "psrlw $4, %%mm2 \n\t" // EL3.3: sum[1]/16 -> mm5 + + // EL2/4: store pixels 0 & 1 + "packuswb %%mm2, %%mm1 \n\t" // EL2/4: pack into bytes + MOVNTQ (%%mm1, (%2)) "\n\t" // EL2/4: store 2 pixels + + // EL4: pixel 2 math -- (9*p[2] + 3*(p[0] + p[3]) + p[1]) / 16 + "psllw $3, %%mm3 \n\t" // EL4.1: 8*p[2] -> mm3 + "paddw %%mm5, %%mm3 \n\t" // EL4.2: 9*p[2] + 3*(p[0] + p[3]) + p[1] -> mm3 + "psrlw $4, %%mm3 \n\t" // EL4.3: sum[2]/16 -> mm3 + + // EL5: pixel 3 math -- (9*p[3] + 3*(p[1] + p[2]) + p[0]) / 16 + "psllw $3, %%mm4 \n\t" // EL5.1: 8*p[3] -> mm4 + "paddw %%mm6, %%mm4 \n\t" // EL5.2: 9*p[3] + 3*(p[1] + p[2]) + p[0] -> mm4 + "psrlw $4, %%mm4 \n\t" // EL5.3: sum[3]/16 -> mm4 + + // EL4/5: store pixels 2 & 3 + "packuswb %%mm4, %%mm3 \n\t" // EL4/5: pack into bytes + MOVNTQ (%%mm3, (%2,%3,4)) "\n\t" // EL4/5: store 2 pixels + + : /* nothing */ + : /*0*/"m" (*row0), /*1*/"m" (*row1), /*2*/"r" (dst_p), + /*3*/"r" ((unsigned long)dlen) /* 'long' is for proper reg alloc on amd64 */ + : "memory" + ); +} + +#undef A_REG +#undef D_REG +#undef CLR_UPPER32 + +#endif // GCC_ASM + +#endif /* SCALEMMX_H_ */ diff --git a/src/libs/graphics/sdl/scalers.c b/src/libs/graphics/sdl/scalers.c new file mode 100644 index 0000000..751dae3 --- /dev/null +++ b/src/libs/graphics/sdl/scalers.c @@ -0,0 +1,289 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "types.h" +#include "libs/graphics/sdl/sdl_common.h" +#include "libs/platform.h" +#include "libs/log.h" +#include "scalers.h" +#include "scaleint.h" +#include "2xscalers.h" +#ifdef USE_PLATFORM_ACCEL +# ifndef __APPLE__ + // MacOS X framework has no SDL_cpuinfo.h for some reason +# include SDL_INCLUDE(SDL_cpuinfo.h) +# endif +# ifdef MMX_ASM +# include "2xscalers_mmx.h" +# endif /* MMX_ASM */ +#endif /* USE_PLATFORM_ACCEL */ + +#if SDL_MAJOR_VERSION == 1 +#define SDL_HasMMX SDL_HasMMXExt +#endif + +typedef enum +{ + SCALEPLAT_NULL = PLATFORM_NULL, + SCALEPLAT_C = PLATFORM_C, + SCALEPLAT_MMX = PLATFORM_MMX, + SCALEPLAT_SSE = PLATFORM_SSE, + SCALEPLAT_3DNOW = PLATFORM_3DNOW, + SCALEPLAT_ALTIVEC = PLATFORM_ALTIVEC, + + SCALEPLAT_C_RGBA, + SCALEPLAT_C_BGRA, + SCALEPLAT_C_ARGB, + SCALEPLAT_C_ABGR, + +} Scale_PlatType_t; + + +// RGB -> YUV transformation +// the RGB vector is multiplied by the transformation matrix +// to get the YUV vector +#if 0 +// original table -- not used +const int YUV_matrix[3][3] = +{ + /* Y U V */ + /* R */ {0.2989, -0.1687, 0.5000}, + /* G */ {0.5867, -0.3312, -0.4183}, + /* B */ {0.1144, 0.5000, -0.0816} +}; +#else +// scaled up by a 2^14 factor, with Y doubled +const int YUV_matrix[3][3] = +{ + /* Y U V */ + /* R */ { 9794, -2764, 8192}, + /* G */ {19224, -5428, -6853}, + /* B */ { 3749, 8192, -1339} +}; +#endif + +// pre-computed transformations for 8 bits per channel +int RGB_to_YUV[/*RGB*/ 3][/*YUV*/ 3][ /*mult-res*/ 256]; +sint16 dRGB_to_dYUV[/*RGB*/ 3][/*YUV*/ 3][ /*mult-res*/ 512]; + +// pre-computed transformations for RGB555 +YUV_VECTOR RGB15_to_YUV[0x8000]; + +PLATFORM_TYPE force_platform = PLATFORM_NULL; +Scale_PlatType_t Scale_Platform = SCALEPLAT_NULL; + + +// pre-compute the RGB->YUV transformations +void +Scale_Init (void) +{ + int i1, i2, i3; + + for (i1 = 0; i1 < 3; i1++) // enum R,G,B + for (i2 = 0; i2 < 3; i2++) // enum Y,U,V + for (i3 = 0; i3 < 256; i3++) // enum possible channel vals + { + RGB_to_YUV[i1][i2][i3] = + (YUV_matrix[i1][i2] * i3) >> 14; + } + + for (i1 = 0; i1 < 3; i1++) // enum R,G,B + for (i2 = 0; i2 < 3; i2++) // enum Y,U,V + for (i3 = -255; i3 < 256; i3++) // enum possible channel delta vals + { + dRGB_to_dYUV[i1][i2][i3 + 255] = + (YUV_matrix[i1][i2] * i3) >> 14; + } + + for (i1 = 0; i1 < 32; ++i1) + for (i2 = 0; i2 < 32; ++i2) + for (i3 = 0; i3 < 32; ++i3) + { + int y, u, v; + // adding upper bits halved for error correction + int r = (i1 << 3) | (i1 >> 3); + int g = (i2 << 3) | (i2 >> 3); + int b = (i3 << 3) | (i3 >> 3); + + y = ( r * YUV_matrix[YUV_XFORM_R][YUV_XFORM_Y] + + g * YUV_matrix[YUV_XFORM_G][YUV_XFORM_Y] + + b * YUV_matrix[YUV_XFORM_B][YUV_XFORM_Y] + ) >> 15; // we dont need Y doubled, need Y to fit 8 bits + + // U and V are half the importance of Y + u = 64+(( r * YUV_matrix[YUV_XFORM_R][YUV_XFORM_U] + + g * YUV_matrix[YUV_XFORM_G][YUV_XFORM_U] + + b * YUV_matrix[YUV_XFORM_B][YUV_XFORM_U] + ) >> 15); // halved + + v = 64+(( r * YUV_matrix[YUV_XFORM_R][YUV_XFORM_V] + + g * YUV_matrix[YUV_XFORM_G][YUV_XFORM_V] + + b * YUV_matrix[YUV_XFORM_B][YUV_XFORM_V] + ) >> 15); // halved + + RGB15_to_YUV[(i1 << 10) | (i2 << 5) | i3] = (y << 16) | (u << 8) | v; + } +} + + +// expands the given rectangle in all directions by 'expansion' +// guarded by 'limits' +void +Scale_ExpandRect (SDL_Rect* rect, int expansion, const SDL_Rect* limits) +{ + if (rect->x - expansion >= limits->x) + { + rect->w += expansion; + rect->x -= expansion; + } + else + { + rect->w += rect->x - limits->x; + rect->x = limits->x; + } + + if (rect->y - expansion >= limits->y) + { + rect->h += expansion; + rect->y -= expansion; + } + else + { + rect->h += rect->y - limits->y; + rect->y = limits->y; + } + + if (rect->x + rect->w + expansion <= limits->w) + rect->w += expansion; + else + rect->w = limits->w - rect->x; + + if (rect->y + rect->h + expansion <= limits->h) + rect->h += expansion; + else + rect->h = limits->h - rect->y; +} + + +// Platform+Scaler function lookups + +typedef struct +{ + Scale_PlatType_t platform; + const Scale_FuncDef_t* funcdefs; +} Scale_PlatDef_t; + + +static const Scale_PlatDef_t +Scale_PlatDefs[] = +{ +#if defined(MMX_ASM) + {SCALEPLAT_SSE, Scale_SSE_Functions}, + {SCALEPLAT_3DNOW, Scale_3DNow_Functions}, + {SCALEPLAT_MMX, Scale_MMX_Functions}, +#endif /* MMX_ASM */ + // Default + {SCALEPLAT_NULL, Scale_C_Functions} +}; + + +TFB_ScaleFunc +Scale_PrepPlatform (int flags, const SDL_PixelFormat* fmt) +{ + const Scale_PlatDef_t* pdef; + const Scale_FuncDef_t* fdef; + + (void)flags; + + Scale_Platform = SCALEPLAT_NULL; + + // first match wins + // add better platform techs to the top +#ifdef MMX_ASM + if ( (!force_platform && (SDL_HasSSE () || SDL_HasMMX ())) + || force_platform == PLATFORM_SSE) + { + log_add (log_Info, "Screen scalers are using SSE/MMX-Ext/MMX code"); + Scale_Platform = SCALEPLAT_SSE; + + Scale_SSE_PrepPlatform (fmt); + } + else + if ( (!force_platform && SDL_HasAltiVec ()) + || force_platform == PLATFORM_ALTIVEC) + { + log_add (log_Info, "Screen scalers would use AltiVec code " + "if someone actually wrote it"); + //Scale_Platform = SCALEPLAT_ALTIVEC; + } + else + if ( (!force_platform && SDL_Has3DNow ()) + || force_platform == PLATFORM_3DNOW) + { + log_add (log_Info, "Screen scalers are using 3DNow/MMX code"); + Scale_Platform = SCALEPLAT_3DNOW; + + Scale_3DNow_PrepPlatform (fmt); + } + else + if ( (!force_platform && SDL_HasMMX ()) + || force_platform == PLATFORM_MMX) + { + log_add (log_Info, "Screen scalers are using MMX code"); + Scale_Platform = SCALEPLAT_MMX; + + Scale_MMX_PrepPlatform (fmt); + } +#endif + + if (Scale_Platform == SCALEPLAT_NULL) + { // Plain C versions + if (fmt->Rmask == 0xff000000 && fmt->Bmask == 0x0000ff00) + Scale_Platform = SCALEPLAT_C_RGBA; + else if (fmt->Rmask == 0x00ff0000 && fmt->Bmask == 0x000000ff) + Scale_Platform = SCALEPLAT_C_ARGB; + else if (fmt->Rmask == 0x0000ff00 && fmt->Bmask == 0xff000000) + Scale_Platform = SCALEPLAT_C_BGRA; + else if (fmt->Rmask == 0x000000ff && fmt->Bmask == 0x00ff0000) + Scale_Platform = SCALEPLAT_C_ABGR; + else + { // use slowest default + log_add (log_Warning, "Scale_PrepPlatform(): unknown masks " + "(Red %08x, Blue %08x)", fmt->Rmask, fmt->Bmask); + Scale_Platform = SCALEPLAT_C; + } + + if (Scale_Platform == SCALEPLAT_C) + log_add (log_Info, "Screen scalers are using slow generic C code"); + else + log_add (log_Info, "Screen scalers are using optimized C code"); + } + + // Lookup the scaling function + // First find the right platform + for (pdef = Scale_PlatDefs; + pdef->platform != Scale_Platform && pdef->platform != SCALEPLAT_NULL; + ++pdef) + ; + // Next find the right function + for (fdef = pdef->funcdefs; + (flags & fdef->flag) != fdef->flag; + ++fdef) + ; + + return fdef->func; +} + diff --git a/src/libs/graphics/sdl/scalers.h b/src/libs/graphics/sdl/scalers.h new file mode 100644 index 0000000..cd36fe5 --- /dev/null +++ b/src/libs/graphics/sdl/scalers.h @@ -0,0 +1,27 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef SCALERS_H_ +#define SCALERS_H_ + +void Scale_Init (void); + +typedef void (* TFB_ScaleFunc) (SDL_Surface *src, SDL_Surface *dst, + SDL_Rect *r); + +TFB_ScaleFunc Scale_PrepPlatform (int flags, const SDL_PixelFormat* fmt); + +#endif /* SCALERS_H_ */ diff --git a/src/libs/graphics/sdl/sdl1_common.c b/src/libs/graphics/sdl/sdl1_common.c new file mode 100644 index 0000000..5c675e1 --- /dev/null +++ b/src/libs/graphics/sdl/sdl1_common.c @@ -0,0 +1,247 @@ +//Copyright Paul Reiche, Fred Ford. 1992-2002 + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "sdl_common.h" +#include "opengl.h" +#include "pure.h" +#include "primitives.h" +#include "options.h" +#include "uqmversion.h" +#include "libs/graphics/drawcmd.h" +#include "libs/graphics/dcqueue.h" +#include "libs/graphics/cmap.h" +#include "libs/input/sdl/input.h" + // for ProcessInputEvent() +#include "libs/graphics/bbox.h" +#include "port.h" +#include "libs/uio.h" +#include "libs/log.h" +#include "libs/memlib.h" +#include "libs/vidlib.h" + +#if SDL_MAJOR_VERSION == 1 + +static void TFB_PreQuit (void); + +void +TFB_PreInit (void) +{ + log_add (log_Info, "Initializing base SDL functionality."); + log_add (log_Info, "Using SDL version %d.%d.%d (compiled with " + "%d.%d.%d)", SDL_Linked_Version ()->major, + SDL_Linked_Version ()->minor, SDL_Linked_Version ()->patch, + SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL); +#if 0 + if (SDL_Linked_Version ()->major != SDL_MAJOR_VERSION || + SDL_Linked_Version ()->minor != SDL_MINOR_VERSION || + SDL_Linked_Version ()->patch != SDL_PATCHLEVEL) { + log_add (log_Warning, "The used SDL library is not the same version " + "as the one used to compile The Ur-Quan Masters with! " + "If you experience any crashes, this would be an excellent " + "suspect."); + } +#endif + + if ((SDL_Init (SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE) == -1)) + { + log_add (log_Fatal, "Could not initialize SDL: %s.", SDL_GetError ()); + exit (EXIT_FAILURE); + } + + atexit (TFB_PreQuit); +} + +static void +TFB_PreQuit (void) +{ + SDL_Quit (); +} + +int +TFB_ReInitGraphics (int driver, int flags, int width, int height) +{ + int result; + int togglefullscreen = 0; + char caption[200]; + + if (GfxFlags == (flags ^ TFB_GFXFLAGS_FULLSCREEN) && + driver == GraphicsDriver && + width == ScreenWidthActual && height == ScreenHeightActual) + { + togglefullscreen = 1; + } + + GfxFlags = flags; + + if (driver == TFB_GFXDRIVER_SDL_OPENGL) + { +#ifdef HAVE_OPENGL + result = TFB_GL_ConfigureVideo (driver, flags, width, height, + togglefullscreen); +#else + driver = TFB_GFXDRIVER_SDL_PURE; + log_add (log_Warning, "OpenGL support not compiled in," + " so using pure SDL driver"); + result = TFB_Pure_ConfigureVideo (driver, flags, width, height, + togglefullscreen); +#endif + } + else + { + result = TFB_Pure_ConfigureVideo (driver, flags, width, height, + togglefullscreen); + } + + sprintf (caption, "The Ur-Quan Masters v%d.%d.%d%s", + UQM_MAJOR_VERSION, UQM_MINOR_VERSION, + UQM_PATCH_VERSION, UQM_EXTRA_VERSION); + SDL_WM_SetCaption (caption, NULL); + + if (flags & TFB_GFXFLAGS_FULLSCREEN) + SDL_ShowCursor (SDL_DISABLE); + else + SDL_ShowCursor (SDL_ENABLE); + + return result; +} + +bool +TFB_SetGamma (float gamma) +{ + return (SDL_SetGamma (gamma, gamma, gamma) == 0); +} + +int +TFB_HasSurfaceAlphaMod (SDL_Surface *surface) +{ + if (!surface) + { + return 0; + } + return (surface->flags & SDL_SRCALPHA) ? 1 : 0; +} + +int +TFB_GetSurfaceAlphaMod (SDL_Surface *surface, Uint8 *alpha) +{ + if (!surface || !surface->format || !alpha) + { + return -1; + } + if (surface->flags & SDL_SRCALPHA) + { + *alpha = surface->format->alpha; + } + else + { + *alpha = 255; + } + return 0; +} + +int +TFB_SetSurfaceAlphaMod (SDL_Surface *surface, Uint8 alpha) +{ + if (!surface) + { + return -1; + } + return SDL_SetAlpha (surface, SDL_SRCALPHA, alpha); +} + +int +TFB_DisableSurfaceAlphaMod (SDL_Surface *surface) +{ + if (!surface) + { + return -1; + } + return SDL_SetAlpha (surface, 0, 255); +} + +int +TFB_GetColorKey (SDL_Surface *surface, Uint32 *key) +{ + if (surface && surface->format && key && + (surface->flags & SDL_SRCCOLORKEY)) + { + *key = surface->format->colorkey; + return 0; + } + return -1; +} + +int +TFB_SetColorKey (SDL_Surface *surface, Uint32 key, int rleaccel) +{ + if (!surface) + { + return -1; + } + return SDL_SetColorKey (surface, SDL_SRCCOLORKEY | (rleaccel ? SDL_RLEACCEL : 0), key); +} + +int +TFB_DisableColorKey (SDL_Surface *surface) +{ + if (!surface) + { + return -1; + } + return SDL_SetColorKey (surface, 0, 0); +} + +int +TFB_SetColors (SDL_Surface *surface, SDL_Color *colors, int firstcolor, int ncolors) +{ + return SDL_SetColors (surface, colors, firstcolor, ncolors); +} + +int +TFB_SupportsHardwareScaling (void) +{ +#ifdef HAVE_OPENGL + return 1; +#else + return 0; +#endif +} + +static SDL_Surface * +Create_Screen (SDL_Surface *templat, int w, int h) +{ + SDL_Surface *newsurf = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, + templat->format->BitsPerPixel, + templat->format->Rmask, templat->format->Gmask, + templat->format->Bmask, 0); + if (newsurf == 0) { + log_add (log_Error, "Couldn't create screen buffes: %s", + SDL_GetError()); + } + return newsurf; +} + +int +SDL1_ReInit_Screen (SDL_Surface **screen, SDL_Surface *templat, int w, int h) +{ + UnInit_Screen (screen); + *screen = Create_Screen (templat, w, h); + + return *screen == 0 ? -1 : 0; +} +#endif diff --git a/src/libs/graphics/sdl/sdl2_common.c b/src/libs/graphics/sdl/sdl2_common.c new file mode 100644 index 0000000..3eaf7af --- /dev/null +++ b/src/libs/graphics/sdl/sdl2_common.c @@ -0,0 +1,222 @@ +//Copyright Paul Reiche, Fred Ford. 1992-2002 + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "sdl_common.h" +#include "opengl.h" +#include "pure.h" +#include "primitives.h" +#include "options.h" +#include "uqmversion.h" +#include "libs/graphics/drawcmd.h" +#include "libs/graphics/dcqueue.h" +#include "libs/graphics/cmap.h" +#include "libs/input/sdl/input.h" + // for ProcessInputEvent() +#include "libs/graphics/bbox.h" +#include "port.h" +#include "libs/uio.h" +#include "libs/log.h" +#include "libs/memlib.h" +#include "libs/vidlib.h" + +#if SDL_MAJOR_VERSION > 1 + +static void TFB_PreQuit (void); + +void +TFB_PreInit (void) +{ + SDL_version compiled, linked; + SDL_VERSION(&compiled); + SDL_GetVersion(&linked); + log_add (log_Info, "Initializing base SDL functionality."); + log_add (log_Info, "Using SDL version %d.%d.%d (compiled with " + "%d.%d.%d)", linked.major, linked.minor, linked.patch, + compiled.major, compiled.minor, compiled.patch); +#if 0 + if (compiled.major != linked.major || compiled.minor != linked.minor || + compiled.patch != linked.patch) + { + log_add (log_Warning, "The used SDL library is not the same version " + "as the one used to compile The Ur-Quan Masters with! " + "If you experience any crashes, this would be an excellent " + "suspect."); + } +#endif + + if ((SDL_Init (SDL_INIT_VIDEO) == -1)) + { + log_add (log_Fatal, "Could not initialize SDL: %s.", SDL_GetError ()); + exit (EXIT_FAILURE); + } + + atexit (TFB_PreQuit); +} + +static void +TFB_PreQuit (void) +{ + SDL_Quit (); +} + +int +TFB_ReInitGraphics (int driver, int flags, int width, int height) +{ + int result; + int togglefullscreen = 0; + + if (GfxFlags == (flags ^ TFB_GFXFLAGS_FULLSCREEN) && + driver == GraphicsDriver && + width == ScreenWidthActual && height == ScreenHeightActual) + { + togglefullscreen = 1; + } + + GfxFlags = flags; + + result = TFB_Pure_ConfigureVideo (TFB_GFXDRIVER_SDL_PURE, flags, + width, height, togglefullscreen); + + if (flags & TFB_GFXFLAGS_FULLSCREEN) + SDL_ShowCursor (SDL_DISABLE); + else + SDL_ShowCursor (SDL_ENABLE); + + return result; +} + +bool +TFB_SetGamma (float gamma) +{ + log_add (log_Warning, "Custom gamma correction is not available in the SDL2 engine."); + return 0; +} + +int +TFB_HasSurfaceAlphaMod (SDL_Surface *surface) +{ + SDL_BlendMode blend_mode; + if (!surface) + { + return 0; + } + if (SDL_GetSurfaceBlendMode (surface, &blend_mode) != 0) + { + return 0; + } + return blend_mode == SDL_BLENDMODE_BLEND; +} + +int +TFB_GetSurfaceAlphaMod (SDL_Surface *surface, Uint8 *alpha) +{ + SDL_BlendMode blend_mode; + if (!surface || !alpha) + { + return -1; + } + if (SDL_GetSurfaceBlendMode (surface, &blend_mode) == 0) + { + if (blend_mode == SDL_BLENDMODE_BLEND) + { + return SDL_GetSurfaceAlphaMod (surface, alpha); + } + } + *alpha = 255; + return 0; +} + +int +TFB_SetSurfaceAlphaMod (SDL_Surface *surface, Uint8 alpha) +{ + int result; + if (!surface) + { + return -1; + } + result = SDL_SetSurfaceBlendMode (surface, SDL_BLENDMODE_BLEND); + if (result == 0) + { + result = SDL_SetSurfaceAlphaMod (surface, alpha); + } + return result; +} + +int +TFB_DisableSurfaceAlphaMod (SDL_Surface *surface) +{ + if (!surface) + { + return -1; + } + SDL_SetSurfaceAlphaMod (surface, 255); + return SDL_SetSurfaceBlendMode (surface, SDL_BLENDMODE_NONE); +} + +int +TFB_GetColorKey (SDL_Surface *surface, Uint32 *key) +{ + if (!surface || !key) + { + return -1; + } + return SDL_GetColorKey (surface, key); +} + +int +TFB_SetColorKey (SDL_Surface *surface, Uint32 key, int rleaccel) +{ + if (!surface) + { + return -1; + } + SDL_SetSurfaceRLE (surface, rleaccel); + return SDL_SetColorKey (surface, SDL_TRUE, key); +} + +int +TFB_DisableColorKey (SDL_Surface *surface) +{ + if (!surface) + { + return -1; + } + return SDL_SetColorKey (surface, SDL_FALSE, 0); +} + +int +TFB_SetColors (SDL_Surface *surface, SDL_Color *colors, int firstcolor, int ncolors) +{ + if (!surface || !colors || !surface->format || !surface->format->palette) + { + return 0; + } + if (SDL_SetPaletteColors (surface->format->palette, colors, firstcolor, ncolors) == 0) + { + // SDL2's success code is opposite from SDL1's SDL_SetColors + return 1; + } + return 0; +} + +int +TFB_SupportsHardwareScaling (void) +{ + return 1; +} +#endif diff --git a/src/libs/graphics/sdl/sdl2_pure.c b/src/libs/graphics/sdl/sdl2_pure.c new file mode 100644 index 0000000..c6503db --- /dev/null +++ b/src/libs/graphics/sdl/sdl2_pure.c @@ -0,0 +1,465 @@ +//Copyright Paul Reiche, Fred Ford. 1992-2002 +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "pure.h" +#include "libs/graphics/bbox.h" +#include "libs/log.h" +#include "scalers.h" +#include "uqmversion.h" + +#if SDL_MAJOR_VERSION > 1 + +typedef struct tfb_sdl2_screeninfo_s { + SDL_Surface *scaled; + SDL_Texture *texture; + BOOLEAN dirty, active; + SDL_Rect updated; +} TFB_SDL2_SCREENINFO; + +static TFB_SDL2_SCREENINFO SDL2_Screens[TFB_GFX_NUMSCREENS]; + +static SDL_Window *window = NULL; +static SDL_Renderer *renderer = NULL; +static const char *rendererBackend = NULL; + +static int ScreenFilterMode; + +static TFB_ScaleFunc scaler = NULL; + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN +#define A_MASK 0xff000000 +#define B_MASK 0x00ff0000 +#define G_MASK 0x0000ff00 +#define R_MASK 0x000000ff +#else +#define A_MASK 0x000000ff +#define B_MASK 0x0000ff00 +#define G_MASK 0x00ff0000 +#define R_MASK 0xff000000 +#endif + +static void TFB_SDL2_Preprocess (int force_full_redraw, int transition_amount, int fade_amount); +static void TFB_SDL2_Postprocess (void); +static void TFB_SDL2_UploadTransitionScreen (void); +static void TFB_SDL2_Scaled_ScreenLayer (SCREEN screen, Uint8 a, SDL_Rect *rect); +static void TFB_SDL2_Unscaled_ScreenLayer (SCREEN screen, Uint8 a, SDL_Rect *rect); +static void TFB_SDL2_ColorLayer (Uint8 r, Uint8 g, Uint8 b, Uint8 a, SDL_Rect *rect); + +static TFB_GRAPHICS_BACKEND sdl2_scaled_backend = { + TFB_SDL2_Preprocess, + TFB_SDL2_Postprocess, + TFB_SDL2_UploadTransitionScreen, + TFB_SDL2_Scaled_ScreenLayer, + TFB_SDL2_ColorLayer }; + +static TFB_GRAPHICS_BACKEND sdl2_unscaled_backend = { + TFB_SDL2_Preprocess, + TFB_SDL2_Postprocess, + TFB_SDL2_UploadTransitionScreen, + TFB_SDL2_Unscaled_ScreenLayer, + TFB_SDL2_ColorLayer }; + +static SDL_Surface * +Create_Screen (int w, int h) +{ + SDL_Surface *newsurf = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, + 32, R_MASK, G_MASK, B_MASK, 0); + if (newsurf == 0) + { + log_add (log_Error, "Couldn't create screen buffers: %s", + SDL_GetError()); + } + return newsurf; +} + +static int +ReInit_Screen (SDL_Surface **screen, int w, int h) +{ + if (*screen) + SDL_FreeSurface (*screen); + *screen = Create_Screen (w, h); + + return *screen == 0 ? -1 : 0; +} + +static int +FindBestRenderDriver (void) +{ + int i, n; + if (!rendererBackend) { + /* If the user has no preference, just let SDL2 choose */ + return -1; + } + n = SDL_GetNumRenderDrivers (); + log_add (log_Info, "Searching for render driver \"%s\".", rendererBackend); + + for (i = 0; i < n; i++) { + SDL_RendererInfo info; + if (SDL_GetRenderDriverInfo (i, &info) < 0) { + continue; + } + if (!strcmp(info.name, rendererBackend)) { + return i; + } + log_add (log_Info, "Skipping render driver \"%s\"", info.name); + } + /* We did not find any accelerated drivers that weren't D3D9. + * Return -1 to ask SDL2 to do its best. */ + log_add (log_Info, "Render driver \"%s\" not available, using system default", rendererBackend); + return -1; +} + +int +TFB_Pure_ConfigureVideo (int driver, int flags, int width, int height, int togglefullscreen) +{ + int i; + GraphicsDriver = driver; + (void) togglefullscreen; + if (window == NULL) + { + SDL_RendererInfo info; + char caption[200]; + + sprintf (caption, "The Ur-Quan Masters v%d.%d.%d%s", + UQM_MAJOR_VERSION, UQM_MINOR_VERSION, + UQM_PATCH_VERSION, UQM_EXTRA_VERSION); + window = SDL_CreateWindow (caption, + SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + width, height, 0); + if (flags & TFB_GFXFLAGS_FULLSCREEN) + { + /* If we create the window fullscreen, it will have + * no icon if and when it becomes windowed. */ + SDL_SetWindowFullscreen (window, SDL_WINDOW_FULLSCREEN_DESKTOP); + } + if (!window) + { + return -1; + } + renderer = SDL_CreateRenderer (window, FindBestRenderDriver (), 0); + if (!renderer) + { + return -1; + } + if (SDL_GetRendererInfo (renderer, &info) == 0) + { + log_add (log_Info, "SDL2 renderer '%s' selected.\n", info.name); + } + else + { + log_add (log_Info, "SDL2 renderer had no name."); + } + SDL_RenderSetLogicalSize (renderer, ScreenWidth, ScreenHeight); + for (i = 0; i < TFB_GFX_NUMSCREENS; i++) + { + SDL2_Screens[i].scaled = NULL; + SDL2_Screens[i].texture = NULL; + SDL2_Screens[i].dirty = TRUE; + SDL2_Screens[i].active = TRUE; + if (0 != ReInit_Screen (&SDL_Screens[i], ScreenWidth, ScreenHeight)) + { + return -1; + } + } + SDL2_Screens[1].active = FALSE; + SDL_Screen = SDL_Screens[0]; + TransitionScreen = SDL_Screens[2]; + format_conv_surf = SDL_CreateRGBSurface(SDL_SWSURFACE, 0, 0, + 32, R_MASK, G_MASK, B_MASK, A_MASK); + if (!format_conv_surf) + { + return -1; + } + } + else + { + if (flags & TFB_GFXFLAGS_FULLSCREEN) + { + SDL_SetWindowFullscreen (window, SDL_WINDOW_FULLSCREEN_DESKTOP); + } + else + { + SDL_SetWindowFullscreen (window, 0); + SDL_SetWindowSize (window, width, height); + } + } + + if (GfxFlags & TFB_GFXFLAGS_SCALE_ANY) + { + /* Linear scaling */ + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); + } + else + { + /* Nearest-neighbor scaling */ + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); + } + + if (GfxFlags & TFB_GFXFLAGS_SCALE_SOFT_ONLY) + { + for (i = 0; i < TFB_GFX_NUMSCREENS; i++) + { + if (!SDL2_Screens[i].active) + { + continue; + } + if (0 != ReInit_Screen(&SDL2_Screens[i].scaled, + ScreenWidth * 2, ScreenHeight * 2)) + { + return -1; + } + if (SDL2_Screens[i].texture) + { + SDL_DestroyTexture (SDL2_Screens[i].texture); + SDL2_Screens[i].texture = NULL; + } + SDL2_Screens[i].texture = SDL_CreateTexture (renderer, SDL_PIXELFORMAT_RGBX8888, SDL_TEXTUREACCESS_STREAMING, ScreenWidth * 2, ScreenHeight * 2); + SDL_LockSurface (SDL2_Screens[i].scaled); + SDL_UpdateTexture (SDL2_Screens[i].texture, NULL, SDL2_Screens[i].scaled->pixels, SDL2_Screens[i].scaled->pitch); + SDL_UnlockSurface (SDL2_Screens[i].scaled); + } + scaler = Scale_PrepPlatform (flags, SDL2_Screens[0].scaled->format); + graphics_backend = &sdl2_scaled_backend; + } + else + { + for (i = 0; i < TFB_GFX_NUMSCREENS; i++) + { + if (SDL2_Screens[i].scaled) + { + SDL_FreeSurface (SDL2_Screens[i].scaled); + SDL2_Screens[i].scaled = NULL; + } + if (SDL2_Screens[i].texture) + { + SDL_DestroyTexture (SDL2_Screens[i].texture); + SDL2_Screens[i].texture = NULL; + } + SDL2_Screens[i].texture = SDL_CreateTexture (renderer, SDL_PIXELFORMAT_RGBX8888, SDL_TEXTUREACCESS_STREAMING, ScreenWidth, ScreenHeight); + SDL_LockSurface (SDL_Screens[i]); + SDL_UpdateTexture (SDL2_Screens[i].texture, NULL, SDL_Screens[i]->pixels, SDL_Screens[i]->pitch); + SDL_UnlockSurface (SDL_Screens[i]); + } + scaler = NULL; + graphics_backend = &sdl2_unscaled_backend; + } + + /* We succeeded, so alter the screen size to our new sizes */ + ScreenWidthActual = width; + ScreenHeightActual = height; + + return 0; +} + +int +TFB_Pure_InitGraphics (int driver, int flags, const char *renderer, int width, int height) +{ + log_add (log_Info, "Initializing SDL."); + log_add (log_Info, "SDL initialized."); + log_add (log_Info, "Initializing Screen."); + + ScreenWidth = 320; + ScreenHeight = 240; + rendererBackend = renderer; + + if (TFB_Pure_ConfigureVideo (driver, flags, width, height, 0)) + { + log_add (log_Fatal, "Could not initialize video: %s", + SDL_GetError ()); + exit (EXIT_FAILURE); + } + + /* Initialize scalers (let them precompute whatever) */ + Scale_Init (); + + return 0; +} + +void +TFB_Pure_UninitGraphics (void) +{ + if (renderer) { + SDL_DestroyRenderer (renderer); + } + if (window) { + SDL_DestroyWindow (window); + } +} + +static void +TFB_SDL2_UploadTransitionScreen (void) +{ + SDL2_Screens[TFB_SCREEN_TRANSITION].updated.x = 0; + SDL2_Screens[TFB_SCREEN_TRANSITION].updated.y = 0; + SDL2_Screens[TFB_SCREEN_TRANSITION].updated.w = ScreenWidth; + SDL2_Screens[TFB_SCREEN_TRANSITION].updated.h = ScreenHeight; + SDL2_Screens[TFB_SCREEN_TRANSITION].dirty = TRUE; +} + +static void +TFB_SDL2_UpdateTexture (SDL_Texture *dest, SDL_Surface *src, SDL_Rect *rect) +{ + char *srcBytes; + SDL_LockSurface (src); + srcBytes = src->pixels; + if (rect) + { + /* SDL2 screen surfaces are always 32bpp */ + srcBytes += (src->pitch * rect->y) + (rect->x * 4); + } + /* 2020-08-02: At time of writing, the documentation for + * SDL_UpdateTexture states this: "If the texture is intended to be + * updated often, it is preferred to create the texture as streaming + * and use [SDL_LockTexture and SDL_UnlockTexture]." Unfortunately, + * SDL_LockTexture will corrupt driver-space memory in the 32-bit + * Direct3D 9 driver on Intel Integrated graphics chips, resulting + * in an immediate crash with no detectable errors from the API up + * to that point. + * + * We also cannot simply forbid the Direct3D driver outright, because + * pre-Windows 10 machines appear to fail to initialize D3D11 even + * while claiming to support it. + * + * These bugs may be fixed in the future, but in the meantime we + * rely on this allegedly slower but definitely more reliable + * function. */ + SDL_UpdateTexture (dest, rect, srcBytes, src->pitch); + SDL_UnlockSurface (src); +} + +static void +TFB_SDL2_ScanLines (void) +{ + int y; + SDL_SetRenderDrawColor (renderer, 0, 0, 0, 64); + SDL_SetRenderDrawBlendMode (renderer, SDL_BLENDMODE_BLEND); + SDL_RenderSetLogicalSize (renderer, ScreenWidth * 2, ScreenHeight * 2); + for (y = 0; y < ScreenHeight * 2; y += 2) + { + SDL_RenderDrawLine (renderer, 0, y, ScreenWidth * 2 - 1, y); + } + SDL_RenderSetLogicalSize (renderer, ScreenWidth, ScreenHeight); +} + +static void +TFB_SDL2_Preprocess (int force_full_redraw, int transition_amount, int fade_amount) +{ + (void) transition_amount; + (void) fade_amount; + + if (force_full_redraw == TFB_REDRAW_YES) + { + SDL2_Screens[TFB_SCREEN_MAIN].updated.x = 0; + SDL2_Screens[TFB_SCREEN_MAIN].updated.y = 0; + SDL2_Screens[TFB_SCREEN_MAIN].updated.w = ScreenWidth; + SDL2_Screens[TFB_SCREEN_MAIN].updated.h = ScreenHeight; + SDL2_Screens[TFB_SCREEN_MAIN].dirty = TRUE; + } + else if (TFB_BBox.valid) + { + SDL2_Screens[TFB_SCREEN_MAIN].updated.x = TFB_BBox.region.corner.x; + SDL2_Screens[TFB_SCREEN_MAIN].updated.y = TFB_BBox.region.corner.y; + SDL2_Screens[TFB_SCREEN_MAIN].updated.w = TFB_BBox.region.extent.width; + SDL2_Screens[TFB_SCREEN_MAIN].updated.h = TFB_BBox.region.extent.height; + SDL2_Screens[TFB_SCREEN_MAIN].dirty = TRUE; + } + + SDL_SetRenderDrawBlendMode (renderer, SDL_BLENDMODE_NONE); + SDL_SetRenderDrawColor (renderer, 0, 0, 0, 255); + SDL_RenderClear (renderer); +} + +static void +TFB_SDL2_Unscaled_ScreenLayer (SCREEN screen, Uint8 a, SDL_Rect *rect) +{ + SDL_Texture *texture = SDL2_Screens[screen].texture; + if (SDL2_Screens[screen].dirty) + { + TFB_SDL2_UpdateTexture (texture, SDL_Screens[screen], &SDL2_Screens[screen].updated); + } + if (a == 255) + { + SDL_SetTextureBlendMode (texture, SDL_BLENDMODE_NONE); + } + else + { + SDL_SetTextureBlendMode (texture, SDL_BLENDMODE_BLEND); + SDL_SetTextureAlphaMod (texture, a); + } + SDL_RenderCopy (renderer, texture, rect, rect); +} + +static void +TFB_SDL2_Scaled_ScreenLayer (SCREEN screen, Uint8 a, SDL_Rect *rect) +{ + SDL_Texture *texture = SDL2_Screens[screen].texture; + SDL_Rect srcRect, *pSrcRect = NULL; + if (SDL2_Screens[screen].dirty) + { + SDL_Surface *src = SDL2_Screens[screen].scaled; + SDL_Rect scaled_update = SDL2_Screens[screen].updated; + scaler (SDL_Screens[screen], src, &SDL2_Screens[screen].updated); + scaled_update.x *= 2; + scaled_update.y *= 2; + scaled_update.w *= 2; + scaled_update.h *= 2; + TFB_SDL2_UpdateTexture (texture, src, &scaled_update); + } + if (a == 255) + { + SDL_SetTextureBlendMode (texture, SDL_BLENDMODE_NONE); + } + else + { + SDL_SetTextureBlendMode (texture, SDL_BLENDMODE_BLEND); + SDL_SetTextureAlphaMod (texture, a); + } + /* The texture has twice the resolution when scaled, but the + * screen's logical resolution has not changed, so the clip + * rectangle does not need to be scaled. The *source* clip + * rect, however, must be scaled to match. */ + if (rect) + { + srcRect = *rect; + srcRect.x *= 2; + srcRect.y *= 2; + srcRect.w *= 2; + srcRect.h *= 2; + pSrcRect = &srcRect; + } + SDL_RenderCopy (renderer, texture, pSrcRect, rect); +} + +static void +TFB_SDL2_ColorLayer (Uint8 r, Uint8 g, Uint8 b, Uint8 a, SDL_Rect *rect) +{ + SDL_SetRenderDrawBlendMode (renderer, a == 255 ? SDL_BLENDMODE_NONE + : SDL_BLENDMODE_BLEND); + SDL_SetRenderDrawColor (renderer, r, g, b, a); + SDL_RenderFillRect (renderer, rect); +} + +static void +TFB_SDL2_Postprocess (void) +{ + if (GfxFlags & TFB_GFXFLAGS_SCANLINES) + TFB_SDL2_ScanLines (); + + SDL_RenderPresent (renderer); +} + +#endif diff --git a/src/libs/graphics/sdl/sdl_common.c b/src/libs/graphics/sdl/sdl_common.c new file mode 100644 index 0000000..b699bf8 --- /dev/null +++ b/src/libs/graphics/sdl/sdl_common.c @@ -0,0 +1,308 @@ +//Copyright Paul Reiche, Fred Ford. 1992-2002 + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "sdl_common.h" +#include "opengl.h" +#include "pure.h" +#include "primitives.h" +#include "options.h" +#include "uqmversion.h" +#include "libs/graphics/drawcmd.h" +#include "libs/graphics/dcqueue.h" +#include "libs/graphics/cmap.h" +#include "libs/input/sdl/input.h" + // for ProcessInputEvent() +#include "libs/graphics/bbox.h" +#include "port.h" +#include "libs/uio.h" +#include "libs/log.h" +#include "libs/memlib.h" +#include "libs/vidlib.h" + +SDL_Surface *SDL_Screen; +SDL_Surface *TransitionScreen; + +SDL_Surface *SDL_Screens[TFB_GFX_NUMSCREENS]; + +SDL_Surface *format_conv_surf = NULL; + +static volatile BOOLEAN abortFlag = FALSE; + +int GfxFlags = 0; + +TFB_GRAPHICS_BACKEND *graphics_backend = NULL; + +volatile int QuitPosted = 0; +volatile int GameActive = 1; // Track the SDL_ACTIVEEVENT state SDL_APPACTIVE + +int +TFB_InitGraphics (int driver, int flags, const char *renderer, int width, int height) +{ + int result, i; + char caption[200]; + + /* Null out screen pointers the first time */ + for (i = 0; i < TFB_GFX_NUMSCREENS; i++) + { + SDL_Screens[i] = NULL; + } + + GfxFlags = flags; + + if (driver == TFB_GFXDRIVER_SDL_OPENGL) + { +#ifdef HAVE_OPENGL + result = TFB_GL_InitGraphics (driver, flags, width, height); +#else + driver = TFB_GFXDRIVER_SDL_PURE; + log_add (log_Warning, "OpenGL support not compiled in," + " so using pure SDL driver"); + result = TFB_Pure_InitGraphics (driver, flags, renderer, width, height); +#endif + } + else + { + result = TFB_Pure_InitGraphics (driver, flags, renderer, width, height); + } + +#if SDL_MAJOR_VERSION == 1 + /* Other versions do this when setting up the window */ + sprintf (caption, "The Ur-Quan Masters v%d.%d.%d%s", + UQM_MAJOR_VERSION, UQM_MINOR_VERSION, + UQM_PATCH_VERSION, UQM_EXTRA_VERSION); + SDL_WM_SetCaption (caption, NULL); +#endif + + if (flags & TFB_GFXFLAGS_FULLSCREEN) + SDL_ShowCursor (SDL_DISABLE); + + Init_DrawCommandQueue (); + + TFB_DrawCanvas_Initialize (); + + return 0; +} + +void +TFB_UninitGraphics (void) +{ + int i; + + Uninit_DrawCommandQueue (); + + for (i = 0; i < TFB_GFX_NUMSCREENS; i++) + UnInit_Screen (&SDL_Screens[i]); + + TFB_Pure_UninitGraphics (); +#ifdef HAVE_OPENGL + TFB_GL_UninitGraphics (); +#endif + + UnInit_Screen (&format_conv_surf); +} + +void +TFB_ProcessEvents () +{ + SDL_Event Event; + + while (SDL_PollEvent (&Event) > 0) + { + /* Run through the InputEvent filter. */ + ProcessInputEvent (&Event); + /* Handle graphics and exposure events. */ + switch (Event.type) { +#if 0 /* Currently disabled in mainline */ + case SDL_ACTIVEEVENT: /* Lose/gain visibility or focus */ + /* Up to three different state changes can occur in one event. */ + /* Here, disregard least significant change (mouse focus). */ + // This controls the automatic sleep/pause when minimized. + // On small displays (e.g. mobile devices), APPINPUTFOCUS would + // be an appropriate substitution for APPACTIVE: + // if (Event.active.state & SDL_APPINPUTFOCUS) + if (Event.active.state & SDL_APPACTIVE) + GameActive = Event.active.gain; + break; + case SDL_VIDEORESIZE: /* User resized video mode */ + // TODO + break; +#endif + case SDL_QUIT: + QuitPosted = 1; + break; +#if SDL_MAJOR_VERSION == 1 + case SDL_VIDEOEXPOSE: /* Screen needs to be redrawn */ + TFB_SwapBuffers (TFB_REDRAW_EXPOSE); + break; +#else + case SDL_WINDOWEVENT: + if (Event.window.event == SDL_WINDOWEVENT_EXPOSED) + { + /* Screen needs to be redrawn */ + TFB_SwapBuffers (TFB_REDRAW_EXPOSE); + } + break; +#endif + default: + break; + } + } +} + +static BOOLEAN system_box_active = 0; +static SDL_Rect system_box; + +void +SetSystemRect (const RECT *r) +{ + system_box_active = TRUE; + system_box.x = r->corner.x; + system_box.y = r->corner.y; + system_box.w = r->extent.width; + system_box.h = r->extent.height; +} + +void +ClearSystemRect (void) +{ + system_box_active = FALSE; +} + +void +TFB_SwapBuffers (int force_full_redraw) +{ + static int last_fade_amount = 255, last_transition_amount = 255; + static int fade_amount = 255, transition_amount = 255; + + fade_amount = GetFadeAmount (); + transition_amount = TransitionAmount; + + if (force_full_redraw == TFB_REDRAW_NO && !TFB_BBox.valid && + fade_amount == 255 && transition_amount == 255 && + last_fade_amount == 255 && last_transition_amount == 255) + return; + + if (force_full_redraw == TFB_REDRAW_NO && + (fade_amount != 255 || transition_amount != 255 || + last_fade_amount != 255 || last_transition_amount != 255)) + force_full_redraw = TFB_REDRAW_FADING; + + last_fade_amount = fade_amount; + last_transition_amount = transition_amount; + + graphics_backend->preprocess (force_full_redraw, transition_amount, + fade_amount); + graphics_backend->screen (TFB_SCREEN_MAIN, 255, NULL); + + if (transition_amount != 255) + { + SDL_Rect r; + r.x = TransitionClipRect.corner.x; + r.y = TransitionClipRect.corner.y; + r.w = TransitionClipRect.extent.width; + r.h = TransitionClipRect.extent.height; + graphics_backend->screen (TFB_SCREEN_TRANSITION, + 255 - transition_amount, &r); + } + + if (fade_amount != 255) + { + if (fade_amount < 255) + { + graphics_backend->color (0, 0, 0, 255 - fade_amount, NULL); + } + else + { + graphics_backend->color (255, 255, 255, + fade_amount - 255, NULL); + } + } + + if (system_box_active) + { + graphics_backend->screen (TFB_SCREEN_MAIN, 255, &system_box); + } + + graphics_backend->postprocess (); +} + +/* Probably ought to clean this away at some point. */ +SDL_Surface * +TFB_DisplayFormatAlpha (SDL_Surface *surface) +{ + SDL_Surface* newsurf; + SDL_PixelFormat* dstfmt; + const SDL_PixelFormat* srcfmt = surface->format; + + // figure out what format to use (alpha/no alpha) + if (surface->format->Amask) + dstfmt = format_conv_surf->format; + else + dstfmt = SDL_Screen->format; + + if (srcfmt->BytesPerPixel == dstfmt->BytesPerPixel && + srcfmt->Rmask == dstfmt->Rmask && + srcfmt->Gmask == dstfmt->Gmask && + srcfmt->Bmask == dstfmt->Bmask && + srcfmt->Amask == dstfmt->Amask) + return surface; // no conversion needed + + newsurf = SDL_ConvertSurface (surface, dstfmt, surface->flags); + // Colorkeys and surface-level alphas cannot work at the same time, + // so we need to disable one of them + if (TFB_HasColorKey (surface) && newsurf && + TFB_HasColorKey (newsurf) && + TFB_HasSurfaceAlphaMod (newsurf)) + { + TFB_DisableSurfaceAlphaMod (newsurf); + } + + return newsurf; +} + +// This function should only be called from the graphics thread, +// like from a TFB_DrawCommand_Callback command. +TFB_Canvas +TFB_GetScreenCanvas (SCREEN screen) +{ + return SDL_Screens[screen]; +} + +void +TFB_UploadTransitionScreen (void) +{ + graphics_backend->uploadTransitionScreen (); +} + +int +TFB_HasColorKey (SDL_Surface *surface) +{ + Uint32 key; + return TFB_GetColorKey (surface, &key) == 0; +} + +void +UnInit_Screen (SDL_Surface **screen) +{ + if (*screen == NULL) { + return; + } + + SDL_FreeSurface (*screen); + *screen = NULL; +} diff --git a/src/libs/graphics/sdl/sdl_common.h b/src/libs/graphics/sdl/sdl_common.h new file mode 100644 index 0000000..76d1cb6 --- /dev/null +++ b/src/libs/graphics/sdl/sdl_common.h @@ -0,0 +1,63 @@ +//Copyright Paul Reiche, Fred Ford. 1992-2002 + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef SDL_COMMON_H +#define SDL_COMMON_H + +#include "port.h" +#include SDL_INCLUDE(SDL.h) + +#include "../gfxintrn.h" +#include "libs/graphics/tfb_draw.h" +#include "libs/graphics/gfx_common.h" + +// The Graphics Backend vtable +typedef struct _tfb_graphics_backend { + void (*preprocess) (int force_redraw, int transition_amount, int fade_amount); + void (*postprocess) (void); + void (*uploadTransitionScreen) (void); + void (*screen) (SCREEN screen, Uint8 alpha, SDL_Rect *rect); + void (*color) (Uint8 r, Uint8 g, Uint8 b, Uint8 a, SDL_Rect *rect); +} TFB_GRAPHICS_BACKEND; + +extern TFB_GRAPHICS_BACKEND *graphics_backend; + +extern SDL_Surface *SDL_Screen; +extern SDL_Surface *TransitionScreen; + +extern SDL_Surface *SDL_Screens[TFB_GFX_NUMSCREENS]; + +extern SDL_Surface *format_conv_surf; + +SDL_Surface* TFB_DisplayFormatAlpha (SDL_Surface *surface); +int TFB_HasSurfaceAlphaMod (SDL_Surface *surface); +int TFB_GetSurfaceAlphaMod (SDL_Surface *surface, Uint8 *alpha); +int TFB_SetSurfaceAlphaMod (SDL_Surface *surface, Uint8 alpha); +int TFB_DisableSurfaceAlphaMod (SDL_Surface *surface); +int TFB_HasColorKey (SDL_Surface *surface); +int TFB_GetColorKey (SDL_Surface *surface, Uint32 *key); +int TFB_SetColorKey (SDL_Surface *surface, Uint32 key, int rleaccel); +int TFB_DisableColorKey (SDL_Surface *surface); +int TFB_SetColors (SDL_Surface *surface, SDL_Color *colors, int firstcolor, int ncolors); + +#if SDL_MAJOR_VERSION == 1 +int SDL1_ReInit_Screen (SDL_Surface **screen, SDL_Surface *templat, int w, int h); +#endif +void UnInit_Screen (SDL_Surface **screen); + +#endif diff --git a/src/libs/graphics/sdl/sdluio.c b/src/libs/graphics/sdl/sdluio.c new file mode 100644 index 0000000..5e4554d --- /dev/null +++ b/src/libs/graphics/sdl/sdluio.c @@ -0,0 +1,153 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "sdluio.h" + +#include "port.h" +#include "libs/uio.h" +#include SDL_INCLUDE(SDL.h) +#include SDL_INCLUDE(SDL_error.h) +#include SDL_INCLUDE(SDL_rwops.h) +#include "libs/memlib.h" +#include "png2sdl.h" +#include <errno.h> +#include <string.h> + + +static SDL_RWops *sdluio_makeRWops (uio_Stream *stream); + +#if 0 +// For use for initialisation, using structure assignment. +static SDL_RWops sdluio_templateRWops = +{ + .seek = sdluio_seek, + .read = sdluio_read, + .write = sdluio_write, + .close = sdluio_close, +}; +#endif + +SDL_Surface * +sdluio_loadImage (uio_DirHandle *dir, const char *fileName) { + uio_Stream *stream; + SDL_RWops *rwops; + SDL_Surface *result = NULL; + + stream = uio_fopen (dir, fileName, "rb"); + if (stream == NULL) + { + SDL_SetError ("Couldn't open '%s': %s", fileName, + strerror(errno)); + return NULL; + } + rwops = sdluio_makeRWops (stream); + if (rwops) { + result = TFB_png_to_sdl (rwops); + SDL_RWclose (rwops); + } + return result; +} + +#if SDL_MAJOR_VERSION == 1 +int +sdluio_seek (SDL_RWops *context, int offset, int whence) +#else +Sint64 +sdluio_seek (SDL_RWops *context, Sint64 offset, int whence) +#endif +{ + if (uio_fseek ((uio_Stream *) context->hidden.unknown.data1, offset, + whence) == -1) + { + SDL_SetError ("Error seeking in uio_Stream: %s", + strerror(errno)); + return -1; + } + return uio_ftell ((uio_Stream *) context->hidden.unknown.data1); +} + +#if SDL_MAJOR_VERSION == 1 +int +sdluio_read (SDL_RWops *context, void *ptr, int size, int maxnum) +#else +size_t +sdluio_read (SDL_RWops *context, void *ptr, size_t size, size_t maxnum) +#endif +{ + size_t numRead; + + numRead = uio_fread (ptr, (size_t) size, (size_t) maxnum, + (uio_Stream *) context->hidden.unknown.data1); + if (numRead == 0 && uio_ferror ((uio_Stream *) + context->hidden.unknown.data1)) + { + SDL_SetError ("Error reading from uio_Stream: %s", + strerror(errno)); + return 0; + } + return (int) numRead; +} + +#if SDL_MAJOR_VERSION == 1 +int +sdluio_write (SDL_RWops *context, const void *ptr, int size, int num) +#else +size_t +sdluio_write (SDL_RWops *context, const void *ptr, size_t size, size_t num) +#endif +{ + size_t numWritten; + + numWritten = uio_fwrite (ptr, (size_t) size, (size_t) num, + (uio_Stream *) context->hidden.unknown.data1); + if (numWritten == 0 && uio_ferror ((uio_Stream *) + context->hidden.unknown.data1)) + { + SDL_SetError ("Error writing to uio_Stream: %s", + strerror(errno)); + return 0; + } + return (size_t) numWritten; +} + +int +sdluio_close (SDL_RWops *context) { + int result; + + result = uio_fclose ((uio_Stream *) context->hidden.unknown.data1); + HFree (context); + return result; +} + +static SDL_RWops * +sdluio_makeRWops (uio_Stream *stream) { + SDL_RWops *result; + + result = HMalloc (sizeof (SDL_RWops)); +#if 0 + *(struct SDL_RWops *) result = sdluio_templateRWops; + // structure assignment +#endif + result->seek = sdluio_seek; + result->read = sdluio_read; + result->write = sdluio_write; + result->close = sdluio_close; + result->hidden.unknown.data1 = stream; + return result; +} + + + diff --git a/src/libs/graphics/sdl/sdluio.h b/src/libs/graphics/sdl/sdluio.h new file mode 100644 index 0000000..0f8c701 --- /dev/null +++ b/src/libs/graphics/sdl/sdluio.h @@ -0,0 +1,39 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef LIBS_GRAPHICS_SDL_SDLUIO_H_ +#define LIBS_GRAPHICS_SDL_SDLUIO_H_ + +#include "port.h" +#include "libs/uio.h" +#include SDL_INCLUDE(SDL.h) +#include SDL_INCLUDE(SDL_rwops.h) + +SDL_Surface *sdluio_loadImage (uio_DirHandle *dir, const char *fileName); +#if SDL_MAJOR_VERSION == 1 +int sdluio_seek (SDL_RWops *context, int offset, int whence); +int sdluio_read (SDL_RWops *context, void *ptr, int size, int maxnum); +int sdluio_write (SDL_RWops *context, const void *ptr, int size, int num); +#else +Sint64 sdlui_seek (SDL_RWops *context, Sint64 offset, int whence); +size_t sdlui_read (SDL_RWops *context, void *ptr, size_t size, size_t maxnum); +size_t sdlui_write (SDL_RWops *contex, const void *ptr, size_t size, size_t num); +#endif +int sdluio_close (SDL_RWops *context); + + +#endif /* LIBS_GRAPHICS_SDL_SDLUIO_H_ */ + diff --git a/src/libs/graphics/sdl/triscan2x.c b/src/libs/graphics/sdl/triscan2x.c new file mode 100644 index 0000000..830dc7c --- /dev/null +++ b/src/libs/graphics/sdl/triscan2x.c @@ -0,0 +1,155 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +// Core algorithm of the Triscan screen scaler (based on Scale2x) +// (for scale2x please see http://scale2x.sf.net) +// Template +// When this file is built standalone is produces a plain C version +// Also #included by 2xscalers_mmx.c for an MMX version + +#include "libs/graphics/sdl/sdl_common.h" +#include "types.h" +#include "scalers.h" +#include "scaleint.h" +#include "2xscalers.h" + + +// Triscan scaling to 2x +// derivative of scale2x -- scale2x.sf.net +// The name expands to either +// Scale_TriScanFilter (for plain C) or +// Scale_MMX_TriScanFilter (for MMX) +// [others when platforms are added] +void +SCALE_(TriScanFilter) (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r) +{ + int x, y; + const int w = src->w, h = src->h; + int xend, yend; + int dsrc, ddst; + SDL_Rect *region = r; + SDL_Rect limits; + SDL_PixelFormat *fmt = dst->format; + const int sp = src->pitch, dp = dst->pitch; + const int bpp = fmt->BytesPerPixel; + const int slen = sp / bpp, dlen = dp / bpp; + // for clarity purposes, the 'pixels' array here is transposed + Uint32 pixels[3][3]; + Uint32 *src_p = (Uint32 *)src->pixels; + Uint32 *dst_p = (Uint32 *)dst->pixels; + + int prevline, nextline; + + // these macros are for clarity; they make the current pixel (0,0) + // and allow to access pixels in all directions + #define PIX(x, y) (pixels[1 + (x)][1 + (y)]) + + #define TRISCAN_YUV_MED 100 + // medium tolerance pixel comparison + #define TRISCAN_CMPYUV(p1, p2) \ + (PIX p1 == PIX p2 || SCALE_CMPYUV (PIX p1, PIX p2, TRISCAN_YUV_MED)) + + + SCALE_(PlatInit) (); + + // expand updated region if necessary + // pixels neighbooring the updated region may + // change as a result of updates + limits.x = 0; + limits.y = 0; + limits.w = src->w; + limits.h = src->h; + Scale_ExpandRect (region, 1, &limits); + + xend = region->x + region->w; + yend = region->y + region->h; + dsrc = slen - region->w; + ddst = (dlen - region->w) * 2; + + // move ptrs to the first updated pixel + src_p += slen * region->y + region->x; + dst_p += (dlen * region->y + region->x) * 2; + + for (y = region->y; y < yend; ++y, dst_p += ddst, src_p += dsrc) + { + if (y > 0) + prevline = -slen; + else + prevline = 0; + + if (y < h - 1) + nextline = slen; + else + nextline = 0; + + // prime the (tiny) sliding-window pixel arrays + PIX( 1, 0) = src_p[0]; + + if (region->x > 0) + PIX( 0, 0) = src_p[-1]; + else + PIX( 0, 0) = PIX( 1, 0); + + for (x = region->x; x < xend; ++x, ++src_p, dst_p += 2) + { + // slide the window + PIX(-1, 0) = PIX( 0, 0); + + PIX( 0, -1) = src_p[prevline]; + PIX( 0, 0) = PIX( 1, 0); + PIX( 0, 1) = src_p[nextline]; + + if (x < w - 1) + PIX( 1, 0) = src_p[1]; + else + PIX( 1, 0) = PIX( 0, 0); + + if (!TRISCAN_CMPYUV (( 0, -1), ( 0, 1)) && + !TRISCAN_CMPYUV ((-1, 0), ( 1, 0))) + { + if (TRISCAN_CMPYUV ((-1, 0), ( 0, -1))) + dst_p[0] = Scale_Blend_11 (PIX(-1, 0), PIX(0, -1)); + else + dst_p[0] = PIX(0, 0); + + if (TRISCAN_CMPYUV (( 1, 0), ( 0, -1))) + dst_p[1] = Scale_Blend_11 (PIX(1, 0), PIX(0, -1)); + else + dst_p[1] = PIX(0, 0); + + if (TRISCAN_CMPYUV ((-1, 0), ( 0, 1))) + dst_p[dlen] = Scale_Blend_11 (PIX(-1, 0), PIX(0, 1)); + else + dst_p[dlen] = PIX(0, 0); + + if (TRISCAN_CMPYUV (( 1, 0), ( 0, 1))) + dst_p[dlen+1] = Scale_Blend_11 (PIX(1, 0), PIX(0, 1)); + else + dst_p[dlen+1] = PIX(0, 0); + } + else + { + dst_p[0] = PIX(0, 0); + dst_p[1] = PIX(0, 0); + dst_p[dlen] = PIX(0, 0); + dst_p[dlen+1] = PIX(0, 0); + } + } + } + + SCALE_(PlatDone) (); +} + diff --git a/src/libs/graphics/tfb_draw.c b/src/libs/graphics/tfb_draw.c new file mode 100644 index 0000000..1ac3c34 --- /dev/null +++ b/src/libs/graphics/tfb_draw.c @@ -0,0 +1,493 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "gfx_common.h" +#include "tfb_draw.h" +#include "drawcmd.h" +#include "libs/log.h" +#include "libs/memlib.h" + + +static const HOT_SPOT NullHs = {0, 0}; + +void +TFB_DrawScreen_Line (int x1, int y1, int x2, int y2, Color color, + DrawMode mode, SCREEN dest) +{ + TFB_DrawCommand DC; + + DC.Type = TFB_DRAWCOMMANDTYPE_LINE; + DC.data.line.x1 = x1; + DC.data.line.y1 = y1; + DC.data.line.x2 = x2; + DC.data.line.y2 = y2; + DC.data.line.color = color; + DC.data.line.drawMode = mode; + DC.data.line.destBuffer = dest; + + TFB_EnqueueDrawCommand (&DC); +} + +void +TFB_DrawScreen_Rect (RECT *rect, Color color, DrawMode mode, SCREEN dest) +{ + RECT locRect; + TFB_DrawCommand DC; + + if (!rect) + { + locRect.corner.x = locRect.corner.y = 0; + locRect.extent.width = ScreenWidth; + locRect.extent.height = ScreenHeight; + rect = &locRect; + } + + DC.Type = TFB_DRAWCOMMANDTYPE_RECTANGLE; + DC.data.rect.rect = *rect; + DC.data.rect.color = color; + DC.data.rect.drawMode = mode; + DC.data.rect.destBuffer = dest; + + TFB_EnqueueDrawCommand (&DC); +} + +void +TFB_DrawScreen_Image (TFB_Image *img, int x, int y, int scale, + int scaleMode, TFB_ColorMap *cmap, DrawMode mode, SCREEN dest) +{ + TFB_DrawCommand DC; + + DC.Type = TFB_DRAWCOMMANDTYPE_IMAGE; + DC.data.image.image = img; + DC.data.image.colormap = cmap; + DC.data.image.x = x; + DC.data.image.y = y; + DC.data.image.scale = (scale == GSCALE_IDENTITY) ? 0 : scale; + DC.data.image.scaleMode = scaleMode; + DC.data.image.drawMode = mode; + DC.data.image.destBuffer = dest; + + TFB_EnqueueDrawCommand (&DC); +} + +void +TFB_DrawScreen_FilledImage (TFB_Image *img, int x, int y, int scale, + int scaleMode, Color color, DrawMode mode, SCREEN dest) +{ + TFB_DrawCommand DC; + + DC.Type = TFB_DRAWCOMMANDTYPE_FILLEDIMAGE; + DC.data.filledimage.image = img; + DC.data.filledimage.x = x; + DC.data.filledimage.y = y; + DC.data.filledimage.scale = (scale == GSCALE_IDENTITY) ? 0 : scale; + DC.data.filledimage.scaleMode = scaleMode; + DC.data.filledimage.color = color; + DC.data.filledimage.drawMode = mode; + DC.data.filledimage.destBuffer = dest; + + TFB_EnqueueDrawCommand (&DC); +} + +void +TFB_DrawScreen_FontChar (TFB_Char *fontChar, TFB_Image *backing, + int x, int y, DrawMode mode, SCREEN dest) +{ + TFB_DrawCommand DC; + + DC.Type = TFB_DRAWCOMMANDTYPE_FONTCHAR; + DC.data.fontchar.fontchar = fontChar; + DC.data.fontchar.backing = backing; + DC.data.fontchar.x = x; + DC.data.fontchar.y = y; + DC.data.fontchar.drawMode = mode; + DC.data.fontchar.destBuffer = dest; + + TFB_EnqueueDrawCommand (&DC); +} + +void +TFB_DrawScreen_CopyToImage (TFB_Image *img, const RECT *r, SCREEN src) +{ + TFB_DrawCommand DC; + + DC.Type = TFB_DRAWCOMMANDTYPE_COPYTOIMAGE; + DC.data.copytoimage.rect = *r; + DC.data.copytoimage.image = img; + DC.data.copytoimage.srcBuffer = src; + + TFB_EnqueueDrawCommand (&DC); +} + +void +TFB_DrawScreen_Copy (const RECT *r, SCREEN src, SCREEN dest) +{ + RECT locRect; + TFB_DrawCommand DC; + + if (!r) + { + locRect.corner.x = locRect.corner.y = 0; + locRect.extent.width = ScreenWidth; + locRect.extent.height = ScreenHeight; + r = &locRect; + } + + DC.Type = TFB_DRAWCOMMANDTYPE_COPY; + DC.data.copy.rect = *r; + DC.data.copy.srcBuffer = src; + DC.data.copy.destBuffer = dest; + + TFB_EnqueueDrawCommand (&DC); +} + +void +TFB_DrawScreen_SetMipmap (TFB_Image *img, TFB_Image *mmimg, int hotx, int hoty) +{ + TFB_DrawCommand DC; + + DC.Type = TFB_DRAWCOMMANDTYPE_SETMIPMAP; + DC.data.setmipmap.image = img; + DC.data.setmipmap.mipmap = mmimg; + DC.data.setmipmap.hotx = hotx; + DC.data.setmipmap.hoty = hoty; + + TFB_EnqueueDrawCommand (&DC); +} + +void +TFB_DrawScreen_DeleteImage (TFB_Image *img) +{ + if (img) + { + TFB_DrawCommand DC; + + DC.Type = TFB_DRAWCOMMANDTYPE_DELETEIMAGE; + DC.data.deleteimage.image = img; + + TFB_EnqueueDrawCommand (&DC); + } +} + +void +TFB_DrawScreen_DeleteData (void *data) + // data must be a result of HXalloc() call +{ + if (data) + { + TFB_DrawCommand DC; + + DC.Type = TFB_DRAWCOMMANDTYPE_DELETEDATA; + DC.data.deletedata.data = data; + + TFB_EnqueueDrawCommand (&DC); + } +} + +void +TFB_DrawScreen_WaitForSignal (void) +{ + TFB_DrawCommand DrawCommand; + Semaphore s; + s = GetMyThreadLocal ()->flushSem; + DrawCommand.Type = TFB_DRAWCOMMANDTYPE_SENDSIGNAL; + DrawCommand.data.sendsignal.sem = s; + Lock_DCQ (1); + TFB_BatchReset (); + TFB_EnqueueDrawCommand (&DrawCommand); + Unlock_DCQ(); + SetSemaphore (s); +} + +void +TFB_DrawScreen_ReinitVideo (int driver, int flags, int width, int height) +{ + TFB_DrawCommand DrawCommand; + DrawCommand.Type = TFB_DRAWCOMMANDTYPE_REINITVIDEO; + DrawCommand.data.reinitvideo.driver = driver; + DrawCommand.data.reinitvideo.flags = flags; + DrawCommand.data.reinitvideo.width = width; + DrawCommand.data.reinitvideo.height = height; + TFB_EnqueueDrawCommand (&DrawCommand); +} + +void +TFB_DrawScreen_Callback (void (*callback) (void *arg), void *arg) +{ + TFB_DrawCommand DrawCommand; + DrawCommand.Type = TFB_DRAWCOMMANDTYPE_CALLBACK; + DrawCommand.data.callback.callback = callback; + DrawCommand.data.callback.arg = arg; + TFB_EnqueueDrawCommand(&DrawCommand); +} + +void +TFB_DrawImage_Line (int x1, int y1, int x2, int y2, Color color, + DrawMode mode, TFB_Image *target) +{ + LockMutex (target->mutex); + TFB_DrawCanvas_Line (x1, y1, x2, y2, color, mode, target->NormalImg); + target->dirty = TRUE; + UnlockMutex (target->mutex); +} + +void +TFB_DrawImage_Rect (RECT *rect, Color color, DrawMode mode, TFB_Image *target) +{ + LockMutex (target->mutex); + TFB_DrawCanvas_Rect (rect, color, mode, target->NormalImg); + target->dirty = TRUE; + UnlockMutex (target->mutex); +} + +void +TFB_DrawImage_Image (TFB_Image *img, int x, int y, int scale, + int scaleMode, TFB_ColorMap *cmap, DrawMode mode, TFB_Image *target) +{ + LockMutex (target->mutex); + TFB_DrawCanvas_Image (img, x, y, scale, scaleMode, cmap, + mode, target->NormalImg); + target->dirty = TRUE; + UnlockMutex (target->mutex); +} + +void +TFB_DrawImage_FilledImage (TFB_Image *img, int x, int y, int scale, + int scaleMode, Color color, DrawMode mode, TFB_Image *target) +{ + LockMutex (target->mutex); + TFB_DrawCanvas_FilledImage (img, x, y, scale, scaleMode, color, + mode, target->NormalImg); + target->dirty = TRUE; + UnlockMutex (target->mutex); +} + +void +TFB_DrawImage_FontChar (TFB_Char *fontChar, TFB_Image *backing, + int x, int y, DrawMode mode, TFB_Image *target) +{ + LockMutex (target->mutex); + TFB_DrawCanvas_FontChar (fontChar, backing, x, y, mode, target->NormalImg); + target->dirty = TRUE; + UnlockMutex (target->mutex); +} + + +TFB_Image * +TFB_DrawImage_New (TFB_Canvas canvas) +{ + TFB_Image *img = HMalloc (sizeof (TFB_Image)); + img->mutex = CreateMutex ("image lock", SYNC_CLASS_VIDEO); + img->ScaledImg = NULL; + img->MipmapImg = NULL; + img->FilledImg = NULL; + img->colormap_index = -1; + img->colormap_version = 0; + img->NormalHs = NullHs; + img->MipmapHs = NullHs; + img->last_scale_hs = NullHs; + img->last_scale_type = -1; + img->last_scale = 0; + img->dirty = FALSE; + TFB_DrawCanvas_GetExtent (canvas, &img->extent); + + if (TFB_DrawCanvas_IsPaletted (canvas)) + { + img->NormalImg = canvas; + } + else + { + img->NormalImg = TFB_DrawCanvas_ToScreenFormat (canvas); + } + + return img; +} + +TFB_Image* +TFB_DrawImage_CreateForScreen (int w, int h, BOOLEAN withalpha) +{ + TFB_Image* img = HMalloc (sizeof (TFB_Image)); + img->mutex = CreateMutex ("image lock", SYNC_CLASS_VIDEO); + img->ScaledImg = NULL; + img->MipmapImg = NULL; + img->FilledImg = NULL; + img->colormap_index = -1; + img->colormap_version = 0; + img->NormalHs = NullHs; + img->MipmapHs = NullHs; + img->last_scale_hs = NullHs; + img->last_scale_type = -1; + img->last_scale = 0; + img->extent.width = w; + img->extent.height = h; + + img->NormalImg = TFB_DrawCanvas_New_ForScreen (w, h, withalpha); + + return img; +} + +TFB_Image * +TFB_DrawImage_New_Rotated (TFB_Image *img, int angle) +{ + TFB_Canvas dst; + EXTENT size; + TFB_Image* newimg; + + /* sanity check */ + if (!img->NormalImg) + { + log_add (log_Warning, "TFB_DrawImage_New_Rotated: " + "source canvas is NULL! Failing."); + return NULL; + } + + TFB_DrawCanvas_GetRotatedExtent (img->NormalImg, angle, &size); + dst = TFB_DrawCanvas_New_RotationTarget (img->NormalImg, angle); + if (!dst) + { + log_add (log_Warning, "TFB_DrawImage_New_Rotated: " + "rotation target canvas not created! Failing."); + return NULL; + } + TFB_DrawCanvas_Rotate (img->NormalImg, dst, angle, size); + + newimg = TFB_DrawImage_New (dst); + return newimg; +} + +void +TFB_DrawImage_SetMipmap (TFB_Image *img, TFB_Image *mmimg, int hotx, int hoty) +{ + bool imgpal; + bool mmpal; + + if (!img || !mmimg) + return; + + LockMutex (img->mutex); + LockMutex (mmimg->mutex); + + // Either both images must be using the same colormap, or mipmap image + // must not be paletted. This restriction is due to the current + // implementation of fill-stamp, which replaces the palette with + // fill color. + imgpal = TFB_DrawCanvas_IsPaletted (img->NormalImg); + mmpal = TFB_DrawCanvas_IsPaletted (mmimg->NormalImg); + if (!mmpal || (mmpal && imgpal && + img->colormap_index == mmimg->colormap_index)) + { + img->MipmapImg = mmimg->NormalImg; + img->MipmapHs.x = hotx; + img->MipmapHs.y = hoty; + } + else + { + img->MipmapImg = NULL; + } + + UnlockMutex (mmimg->mutex); + UnlockMutex (img->mutex); +} + +void +TFB_DrawImage_Delete (TFB_Image *image) +{ + if (image == 0) + { + log_add (log_Warning, "INTERNAL ERROR: Tried to delete a null image!"); + /* Should we die here? */ + return; + } + LockMutex (image->mutex); + + TFB_DrawCanvas_Delete (image->NormalImg); + + if (image->ScaledImg) + { + TFB_DrawCanvas_Delete (image->ScaledImg); + image->ScaledImg = 0; + } + + if (image->FilledImg) + { + TFB_DrawCanvas_Delete (image->FilledImg); + image->FilledImg = 0; + } + + UnlockMutex (image->mutex); + DestroyMutex (image->mutex); + + HFree (image); +} + +void +TFB_DrawImage_FixScaling (TFB_Image *image, int target, int type) +{ + if (image->dirty || !image->ScaledImg || + target != image->last_scale || + type != image->last_scale_type) + { + image->dirty = FALSE; + image->ScaledImg = TFB_DrawCanvas_New_ScaleTarget (image->NormalImg, + image->ScaledImg, type, image->last_scale_type); + + if (type == TFB_SCALE_NEAREST) + TFB_DrawCanvas_Rescale_Nearest (image->NormalImg, + image->ScaledImg, target, &image->NormalHs, + &image->extent, &image->last_scale_hs); + else if (type == TFB_SCALE_BILINEAR) + TFB_DrawCanvas_Rescale_Bilinear (image->NormalImg, + image->ScaledImg, target, &image->NormalHs, + &image->extent, &image->last_scale_hs); + else + TFB_DrawCanvas_Rescale_Trilinear (image->NormalImg, + image->MipmapImg, image->ScaledImg, target, + &image->NormalHs, &image->MipmapHs, + &image->extent, &image->last_scale_hs); + + image->last_scale_type = type; + image->last_scale = target; + } +} + +BOOLEAN +TFB_DrawImage_Intersect (TFB_Image *img1, POINT img1org, + TFB_Image *img2, POINT img2org, const RECT *interRect) +{ + BOOLEAN ret; + + LockMutex (img1->mutex); + LockMutex (img2->mutex); + ret = TFB_DrawCanvas_Intersect (img1->NormalImg, img1org, + img2->NormalImg, img2org, interRect); + UnlockMutex (img2->mutex); + UnlockMutex (img1->mutex); + + return ret; +} + +void +TFB_DrawImage_CopyRect (TFB_Image *source, const RECT *srcRect, + TFB_Image *target, POINT dstPt) +{ + LockMutex (source->mutex); + LockMutex (target->mutex); + TFB_DrawCanvas_CopyRect (source->NormalImg, srcRect, + target->NormalImg, dstPt); + target->dirty = TRUE; + UnlockMutex (target->mutex); + UnlockMutex (source->mutex); +} diff --git a/src/libs/graphics/tfb_draw.h b/src/libs/graphics/tfb_draw.h new file mode 100644 index 0000000..11de5b0 --- /dev/null +++ b/src/libs/graphics/tfb_draw.h @@ -0,0 +1,199 @@ +//Copyright Paul Reiche, Fred Ford. 1992-2002 + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef TFB_DRAW_H +#define TFB_DRAW_H + +#include "libs/threadlib.h" + + +typedef void *TFB_Canvas; + +typedef enum { + TFB_SCREEN_MAIN, + TFB_SCREEN_EXTRA, + TFB_SCREEN_TRANSITION, + + TFB_GFX_NUMSCREENS +} SCREEN; + +#include "libs/graphics/gfx_common.h" +#include "libs/graphics/cmap.h" + +typedef struct tfb_image +{ + TFB_Canvas NormalImg; + TFB_Canvas ScaledImg; + TFB_Canvas MipmapImg; + TFB_Canvas FilledImg; + int colormap_index; + int colormap_version; + HOT_SPOT NormalHs; + HOT_SPOT MipmapHs; + HOT_SPOT last_scale_hs; + int last_scale; + int last_scale_type; + Color last_fill; + EXTENT extent; + Mutex mutex; + BOOLEAN dirty; +} TFB_Image; + +typedef struct tfb_char +{ + EXTENT extent; + EXTENT disp; + // Display extent + HOT_SPOT HotSpot; + BYTE* data; + DWORD pitch; + // Pitch is for storing all chars of a page + // in one rectangular pixel matrix +} TFB_Char; + +// we do not support paletted format for now +typedef struct tfb_pixelformat +{ + int BitsPerPixel; + int BytesPerPixel; + DWORD Rmask, Gmask, Bmask, Amask; + DWORD Rshift, Gshift, Bshift, Ashift; + DWORD Rloss, Gloss, Bloss, Aloss; +} TFB_PixelFormat; + +// Drawing commands + +void TFB_DrawScreen_Line (int x1, int y1, int x2, int y2, Color color, + DrawMode, SCREEN dest); +void TFB_DrawScreen_Rect (RECT *rect, Color, DrawMode, SCREEN dest); +void TFB_DrawScreen_Image (TFB_Image *img, int x, int y, int scale, + int scaleMode, TFB_ColorMap *, DrawMode, SCREEN dest); +void TFB_DrawScreen_Copy (const RECT *r, SCREEN src, SCREEN dest); +void TFB_DrawScreen_FilledImage (TFB_Image *img, int x, int y, int scale, + int scaleMode, Color, DrawMode, SCREEN dest); +void TFB_DrawScreen_FontChar (TFB_Char *, TFB_Image *backing, int x, int y, + DrawMode, SCREEN dest); + +void TFB_DrawScreen_CopyToImage (TFB_Image *img, const RECT *r, SCREEN src); +void TFB_DrawScreen_SetMipmap (TFB_Image *img, TFB_Image *mmimg, int hotx, + int hoty); +void TFB_DrawScreen_DeleteImage (TFB_Image *img); +void TFB_DrawScreen_DeleteData (void *); +void TFB_DrawScreen_WaitForSignal (void); +void TFB_DrawScreen_ReinitVideo (int driver, int flags, int width, int height); +void TFB_DrawScreen_Callback (void (*callback) (void *arg), void *arg); + +TFB_Image *TFB_DrawImage_New (TFB_Canvas canvas); +TFB_Image *TFB_DrawImage_CreateForScreen (int w, int h, BOOLEAN withalpha); +TFB_Image *TFB_DrawImage_New_Rotated (TFB_Image *img, int angle); +void TFB_DrawImage_SetMipmap (TFB_Image *img, TFB_Image *mmimg, int hotx, + int hoty); +void TFB_DrawImage_Delete (TFB_Image *image); +void TFB_DrawImage_FixScaling (TFB_Image *image, int target, int type); +BOOLEAN TFB_DrawImage_Intersect (TFB_Image *img1, POINT img1org, + TFB_Image *img2, POINT img2org, const RECT *interRect); +void TFB_DrawImage_CopyRect (TFB_Image *source, const RECT *srcRect, + TFB_Image *target, POINT dstPt); + +void TFB_DrawImage_Line (int x1, int y1, int x2, int y2, Color color, + DrawMode, TFB_Image *target); +void TFB_DrawImage_Rect (RECT *rect, Color, DrawMode, TFB_Image *target); +void TFB_DrawImage_Image (TFB_Image *img, int x, int y, int scale, + int scaleMode, TFB_ColorMap *, DrawMode, TFB_Image *target); +void TFB_DrawImage_FilledImage (TFB_Image *img, int x, int y, int scale, + int scaleMode, Color, DrawMode, TFB_Image *target); +void TFB_DrawImage_FontChar (TFB_Char *, TFB_Image *backing, int x, int y, + DrawMode, TFB_Image *target); + +TFB_Canvas TFB_DrawCanvas_LoadFromFile (void *dir, const char *fileName); +TFB_Canvas TFB_DrawCanvas_New_TrueColor (int w, int h, BOOLEAN hasalpha); +TFB_Canvas TFB_DrawCanvas_New_ForScreen (int w, int h, BOOLEAN withalpha); +TFB_Canvas TFB_DrawCanvas_New_Paletted (int w, int h, Color palette[256], + int transparent_index); +TFB_Canvas TFB_DrawCanvas_New_ScaleTarget (TFB_Canvas canvas, + TFB_Canvas oldcanvas, int type, int last_type); +TFB_Canvas TFB_DrawCanvas_New_RotationTarget (TFB_Canvas src, int angle); +TFB_Canvas TFB_DrawCanvas_ToScreenFormat (TFB_Canvas canvas); +BOOLEAN TFB_DrawCanvas_IsPaletted (TFB_Canvas canvas); +void TFB_DrawCanvas_Rescale_Nearest (TFB_Canvas src, TFB_Canvas dst, + int scale, HOT_SPOT* src_hs, EXTENT* size, HOT_SPOT* dst_hs); +void TFB_DrawCanvas_Rescale_Bilinear (TFB_Canvas src, TFB_Canvas dst, + int scale, HOT_SPOT* src_hs, EXTENT* size, HOT_SPOT* dst_hs); +void TFB_DrawCanvas_Rescale_Trilinear (TFB_Canvas src, TFB_Canvas mipmap, + TFB_Canvas dst, int scale, HOT_SPOT* src_hs, HOT_SPOT* mm_hs, + EXTENT* size, HOT_SPOT* dst_hs); +void TFB_DrawCanvas_GetScaledExtent (TFB_Canvas src_canvas, HOT_SPOT* src_hs, + TFB_Canvas src_mipmap, HOT_SPOT* mm_hs, + int scale, int type, EXTENT *size, HOT_SPOT *hs); +void TFB_DrawCanvas_Rotate (TFB_Canvas src, TFB_Canvas dst, int angle, + EXTENT size); +void TFB_DrawCanvas_GetRotatedExtent (TFB_Canvas src, int angle, EXTENT *size); +void TFB_DrawCanvas_GetExtent (TFB_Canvas canvas, EXTENT *size); +void TFB_DrawCanvas_SetClipRect (TFB_Canvas canvas, const RECT *clipRect); + +void TFB_DrawCanvas_Delete (TFB_Canvas canvas); + +void TFB_DrawCanvas_Line (int x1, int y1, int x2, int y2, Color color, + DrawMode, TFB_Canvas target); +void TFB_DrawCanvas_Rect (RECT *rect, Color, DrawMode, TFB_Canvas target); +void TFB_DrawCanvas_Image (TFB_Image *img, int x, int y, int scale, + int scaleMode, TFB_ColorMap *, DrawMode, TFB_Canvas target); +void TFB_DrawCanvas_FilledImage (TFB_Image *img, int x, int y, int scale, + int scaleMode, Color, DrawMode, TFB_Canvas target); +void TFB_DrawCanvas_FontChar (TFB_Char *, TFB_Image *backing, int x, int y, + DrawMode, TFB_Canvas target); +void TFB_DrawCanvas_CopyRect (TFB_Canvas source, const RECT *srcRect, + TFB_Canvas target, POINT dstPt); + +BOOLEAN TFB_DrawCanvas_GetFontCharData (TFB_Canvas canvas, BYTE *outData, + unsigned dataPitch); +Color *TFB_DrawCanvas_ExtractPalette (TFB_Canvas canvas); +void TFB_DrawCanvas_SetPalette (TFB_Canvas target, Color palette[256]); +int TFB_DrawCanvas_GetTransparentIndex (TFB_Canvas canvas); +void TFB_DrawCanvas_SetTransparentIndex (TFB_Canvas canvas, int i, + BOOLEAN rleaccel); +BOOLEAN TFB_DrawCanvas_GetTransparentColor (TFB_Canvas canvas, + Color *color); +void TFB_DrawCanvas_SetTransparentColor (TFB_Canvas canvas, + Color color, BOOLEAN rleaccel); +void TFB_DrawCanvas_CopyTransparencyInfo (TFB_Canvas src, TFB_Canvas dst); +void TFB_DrawCanvas_Initialize (void); +void TFB_DrawCanvas_Lock (TFB_Canvas canvas); +void TFB_DrawCanvas_Unlock (TFB_Canvas canvas); +void TFB_DrawCanvas_GetScreenFormat (TFB_PixelFormat *fmt); +int TFB_DrawCanvas_GetStride (TFB_Canvas canvas); +void *TFB_DrawCanvas_GetLine (TFB_Canvas canvas, int line); +Color TFB_DrawCanvas_GetPixel (TFB_Canvas canvas, int x, int y); +BOOLEAN TFB_DrawCanvas_Intersect (TFB_Canvas canvas1, POINT c1org, + TFB_Canvas canvas2, POINT c2org, const RECT *interRect); + +BOOLEAN TFB_DrawCanvas_GetPixelColors (TFB_Canvas, Color *pixels, + int width, int height); +BOOLEAN TFB_DrawCanvas_SetPixelColors (TFB_Canvas, const Color *pixels, + int width, int height); +BOOLEAN TFB_DrawCanvas_GetPixelIndexes (TFB_Canvas, BYTE *data, + int width, int height); +BOOLEAN TFB_DrawCanvas_SetPixelIndexes (TFB_Canvas, const BYTE *data, + int width, int height); + +const char *TFB_DrawCanvas_GetError (void); + +TFB_Canvas TFB_GetScreenCanvas (SCREEN screen); + +#endif + diff --git a/src/libs/graphics/tfb_prim.c b/src/libs/graphics/tfb_prim.c new file mode 100644 index 0000000..f8a2df4 --- /dev/null +++ b/src/libs/graphics/tfb_prim.c @@ -0,0 +1,237 @@ +// Copyright Michael Martin, 2003 + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* The original Primitive routines do various elaborate checks to + * ensure we're within bounds for the clipping. Since clipping is + * handled by the underlying TFB_Canvas implementation, we need not + * worry about this. */ + +#include "gfxintrn.h" +#include "gfx_common.h" +#include "tfb_draw.h" +#include "tfb_prim.h" +#include "cmap.h" +#include "libs/log.h" + +void +TFB_Prim_Point (POINT *p, Color color, DrawMode mode, POINT ctxOrigin) +{ + RECT r; + + // The caller must scale the origin! + r.corner.x = p->x + ctxOrigin.x; + r.corner.y = p->y + ctxOrigin.y; + r.extent.width = r.extent.height = 1; + + if (_CurFramePtr->Type == SCREEN_DRAWABLE) + TFB_DrawScreen_Rect (&r, color, mode, TFB_SCREEN_MAIN); + else + TFB_DrawImage_Rect (&r, color, mode, _CurFramePtr->image); +} + +void +TFB_Prim_Rect (RECT *r, Color color, DrawMode mode, POINT ctxOrigin) +{ + RECT arm; + int gscale; + + // XXX: Rect prim scaling is currently unused + // We scale the rect size just to be consistent with stamp prim, + // which does same. The caller must scale the origin! + gscale = GetGraphicScale (); + arm = *r; + arm.extent.width = r->extent.width; + arm.extent.height = 1; + TFB_Prim_FillRect (&arm, color, mode, ctxOrigin); + arm.extent.height = r->extent.height; + arm.extent.width = 1; + TFB_Prim_FillRect (&arm, color, mode, ctxOrigin); + // rounding error correction here + arm.corner.x += ((r->extent.width * gscale + (GSCALE_IDENTITY >> 1)) + / GSCALE_IDENTITY) - 1; + TFB_Prim_FillRect (&arm, color, mode, ctxOrigin); + arm.corner.x = r->corner.x; + arm.corner.y += ((r->extent.height * gscale + (GSCALE_IDENTITY >> 1)) + / GSCALE_IDENTITY) - 1; + arm.extent.width = r->extent.width; + arm.extent.height = 1; + TFB_Prim_FillRect (&arm, color, mode, ctxOrigin); +} + +void +TFB_Prim_FillRect (RECT *r, Color color, DrawMode mode, POINT ctxOrigin) +{ + RECT rect; + int gscale; + + rect.corner.x = r->corner.x + ctxOrigin.x; + rect.corner.y = r->corner.y + ctxOrigin.y; + rect.extent.width = r->extent.width; + rect.extent.height = r->extent.height; + + // XXX: Rect prim scaling is currently unused + // We scale the rect size just to be consistent with stamp prim, + // which does same. The caller must scale the origin! + gscale = GetGraphicScale (); + if (gscale != GSCALE_IDENTITY) + { // rounding error correction here + rect.extent.width = (rect.extent.width * gscale + + (GSCALE_IDENTITY >> 1)) / GSCALE_IDENTITY; + rect.extent.height = (rect.extent.height * gscale + + (GSCALE_IDENTITY >> 1)) / GSCALE_IDENTITY; + } + + if (_CurFramePtr->Type == SCREEN_DRAWABLE) + TFB_DrawScreen_Rect (&rect, color, mode, TFB_SCREEN_MAIN); + else + TFB_DrawImage_Rect (&rect, color, mode, _CurFramePtr->image); +} + +void +TFB_Prim_Line (LINE *line, Color color, DrawMode mode, POINT ctxOrigin) +{ + int x1, y1, x2, y2; + + // The caller must scale the origins! + x1=line->first.x + ctxOrigin.x; + y1=line->first.y + ctxOrigin.y; + x2=line->second.x + ctxOrigin.x; + y2=line->second.y + ctxOrigin.y; + + if (_CurFramePtr->Type == SCREEN_DRAWABLE) + TFB_DrawScreen_Line (x1, y1, x2, y2, color, mode, TFB_SCREEN_MAIN); + else + TFB_DrawImage_Line (x1, y1, x2, y2, color, mode, _CurFramePtr->image); +} + +void +TFB_Prim_Stamp (STAMP *stmp, DrawMode mode, POINT ctxOrigin) +{ + int x, y; + FRAME SrcFramePtr; + TFB_Image *img; + TFB_ColorMap *cmap = NULL; + + SrcFramePtr = stmp->frame; + if (!SrcFramePtr) + { + log_add (log_Warning, "TFB_Prim_Stamp: Tried to draw a NULL frame" + " (Stamp address = %p)", (void *) stmp); + return; + } + img = SrcFramePtr->image; + + if (!img) + { + log_add (log_Warning, "Non-existent image to TFB_Prim_Stamp()"); + return; + } + + LockMutex (img->mutex); + + img->NormalHs = SrcFramePtr->HotSpot; + // We scale the image size here, but the caller must scale the origin! + x = stmp->origin.x + ctxOrigin.x; + y = stmp->origin.y + ctxOrigin.y; + + if (TFB_DrawCanvas_IsPaletted(img->NormalImg) && img->colormap_index != -1) + { + // returned cmap is addrefed, must release later + cmap = TFB_GetColorMap (img->colormap_index); + } + + UnlockMutex (img->mutex); + + if (_CurFramePtr->Type == SCREEN_DRAWABLE) + { + TFB_DrawScreen_Image (img, x, y, GetGraphicScale (), + GetGraphicScaleMode (), cmap, mode, TFB_SCREEN_MAIN); + } + else + { + TFB_DrawImage_Image (img, x, y, GetGraphicScale (), + GetGraphicScaleMode (), cmap, mode, _CurFramePtr->image); + } +} + +void +TFB_Prim_StampFill (STAMP *stmp, Color color, DrawMode mode, POINT ctxOrigin) +{ + int x, y; + FRAME SrcFramePtr; + TFB_Image *img; + + SrcFramePtr = stmp->frame; + if (!SrcFramePtr) + { + log_add (log_Warning, "TFB_Prim_StampFill: Tried to draw a NULL frame" + " (Stamp address = %p)", (void *) stmp); + return; + } + img = SrcFramePtr->image; + + if (!img) + { + log_add (log_Warning, "Non-existent image to TFB_Prim_StampFill()"); + return; + } + + LockMutex (img->mutex); + + img->NormalHs = SrcFramePtr->HotSpot; + // We scale the image size here, but the caller must scale the origin! + x = stmp->origin.x + ctxOrigin.x; + y = stmp->origin.y + ctxOrigin.y; + + UnlockMutex (img->mutex); + + if (_CurFramePtr->Type == SCREEN_DRAWABLE) + { + TFB_DrawScreen_FilledImage (img, x, y, GetGraphicScale (), + GetGraphicScaleMode (), color, mode, TFB_SCREEN_MAIN); + } + else + { + TFB_DrawImage_FilledImage (img, x, y, GetGraphicScale (), + GetGraphicScaleMode (), color, mode, _CurFramePtr->image); + } +} + +void +TFB_Prim_FontChar (POINT charOrigin, TFB_Char *fontChar, TFB_Image *backing, + DrawMode mode, POINT ctxOrigin) +{ + int x, y; + + // Text prim does not scale + x = charOrigin.x + ctxOrigin.x; + y = charOrigin.y + ctxOrigin.y; + + if (_CurFramePtr->Type == SCREEN_DRAWABLE) + { + TFB_DrawScreen_FontChar (fontChar, backing, x, y, mode, + TFB_SCREEN_MAIN); + } + else + { + TFB_DrawImage_FontChar (fontChar, backing, x, y, mode, + _CurFramePtr->image); + } +} + +// Text rendering is in font.c, under the name _text_blt diff --git a/src/libs/graphics/tfb_prim.h b/src/libs/graphics/tfb_prim.h new file mode 100644 index 0000000..6fcd546 --- /dev/null +++ b/src/libs/graphics/tfb_prim.h @@ -0,0 +1,30 @@ +// Copyright Michael Martin, 2003 + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "libs/gfxlib.h" +#include "tfb_draw.h" + + +void TFB_Prim_Line (LINE *, Color, DrawMode, POINT ctxOrigin); +void TFB_Prim_Point (POINT *, Color, DrawMode, POINT ctxOrigin); +void TFB_Prim_Rect (RECT *, Color, DrawMode, POINT ctxOrigin); +void TFB_Prim_FillRect (RECT *, Color, DrawMode, POINT ctxOrigin); +void TFB_Prim_Stamp (STAMP *, DrawMode, POINT ctxOrigin); +void TFB_Prim_StampFill (STAMP *, Color, DrawMode, POINT ctxOrigin); +void TFB_Prim_FontChar (POINT charOrigin, TFB_Char *fontChar, + TFB_Image *backing, DrawMode, POINT ctxOrigin); diff --git a/src/libs/graphics/widgets.c b/src/libs/graphics/widgets.c new file mode 100644 index 0000000..0c444bc --- /dev/null +++ b/src/libs/graphics/widgets.c @@ -0,0 +1,941 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "gfx_common.h" +#include "widgets.h" +#include "libs/strlib.h" + +WIDGET *widget_focus = NULL; + +/* Some basic color defines */ +#define WIDGET_ACTIVE_COLOR \ + BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x00), 0x0E) +#define WIDGET_INACTIVE_COLOR \ + BUILD_COLOR (MAKE_RGB15 (0x18, 0x18, 0x1F), 0x00) +#define WIDGET_INACTIVE_SELECTED_COLOR \ + BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x1F), 0x0F) +#define WIDGET_CURSOR_COLOR \ + BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x00), 0x00) +#define WIDGET_DIALOG_COLOR \ + BUILD_COLOR (MAKE_RGB15 (0x14, 0x14, 0x14), 0x07) +#define WIDGET_DIALOG_TEXT_COLOR \ + BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x00), 0x00) + +static Color win_bg_clr = + BUILD_COLOR (MAKE_RGB15_INIT (0x18, 0x18, 0x1F), 0x00); +static Color win_medium_clr = + BUILD_COLOR (MAKE_RGB15_INIT (0x10, 0x10, 0x18), 0x00); +static Color win_dark_clr = + BUILD_COLOR (MAKE_RGB15_INIT (0x08, 0x08, 0x10), 0x00); + +static FONT cur_font; + +void +DrawShadowedBox (RECT *r, Color bg, Color dark, Color medium) +{ + RECT t; + Color oldcolor; + + BatchGraphics (); + + t.corner.x = r->corner.x - 2; + t.corner.y = r->corner.y - 2; + t.extent.width = r->extent.width + 4; + t.extent.height = r->extent.height + 4; + oldcolor = SetContextForeGroundColor (dark); + DrawFilledRectangle (&t); + + t.corner.x += 2; + t.corner.y += 2; + t.extent.width -= 2; + t.extent.height -= 2; + SetContextForeGroundColor (medium); + DrawFilledRectangle (&t); + + t.corner.x -= 1; + t.corner.y += r->extent.height + 1; + t.extent.height = 1; + DrawFilledRectangle (&t); + + t.corner.x += r->extent.width + 2; + t.corner.y -= r->extent.height + 2; + t.extent.width = 1; + DrawFilledRectangle (&t); + + SetContextForeGroundColor (bg); + DrawFilledRectangle (r); + + SetContextForeGroundColor (oldcolor); + UnbatchGraphics (); +} + +// windowRect, if not NULL, will be filled with the dimensions of the +// window drawn. +void +DrawLabelAsWindow (WIDGET_LABEL *label, RECT *windowRect) +{ + Color oldfg = SetContextForeGroundColor (WIDGET_DIALOG_TEXT_COLOR); + FONT oldfont = 0; + FRAME oldFontEffect = SetContextFontEffect (NULL); + RECT r; + TEXT t; + int i, win_w, win_h; + + if (cur_font) + oldfont = SetContextFont (cur_font); + + /* Compute the dimensions of the label */ + win_h = label->height ((WIDGET *)label) + 16; + win_w = 0; + for (i = 0; i < label->line_count; i++) + { + int len = utf8StringCount (label->lines[i]); + if (len > win_w) + { + win_w = len; + } + } + win_w = (win_w * 6) + 16; + + BatchGraphics (); + r.corner.x = (ScreenWidth - win_w) >> 1; + r.corner.y = (ScreenHeight - win_h) >> 1; + r.extent.width = win_w; + r.extent.height = win_h; + DrawShadowedBox (&r, win_bg_clr, win_dark_clr, win_medium_clr); + + t.baseline.x = r.corner.x + (r.extent.width >> 1); + t.baseline.y = r.corner.y + 16; + for (i = 0; i < label->line_count; i++) + { + t.pStr = label->lines[i]; + t.align = ALIGN_CENTER; + t.CharCount = (COUNT)~0; + font_DrawText (&t); + t.baseline.y += 8; + } + + UnbatchGraphics (); + + SetContextFontEffect (oldFontEffect); + if (oldfont) + SetContextFont (oldfont); + SetContextForeGroundColor (oldfg); + + if (windowRect != NULL) { + // Add the outer border added by DrawShadowedBox. + // XXX: It may be nicer to add a border size parameter to + // DrawShadowedBox, instead of assuming 2 here. + windowRect->corner.x = r.corner.x - 2; + windowRect->corner.y = r.corner.y - 2; + windowRect->extent.width = r.extent.width + 4; + windowRect->extent.height = r.extent.height + 4; + } +} + +void +Widget_SetWindowColors (Color bg, Color dark, Color medium) +{ + win_bg_clr = bg; + win_dark_clr = dark; + win_medium_clr = medium; +} + +FONT +Widget_SetFont (FONT newFont) +{ + FONT oldFont = cur_font; + cur_font = newFont; + return oldFont; +} + +static void +Widget_DrawToolTips (int numlines, const char **tips) +{ + RECT r; + FONT oldfont = 0; + FRAME oldFontEffect = SetContextFontEffect (NULL); + Color oldtext = SetContextForeGroundColor (WIDGET_INACTIVE_SELECTED_COLOR); + TEXT t; + int i; + + if (cur_font) + oldfont = SetContextFont (cur_font); + + r.corner.x = 2; + r.corner.y = 2; + r.extent.width = ScreenWidth - 4; + r.extent.height = ScreenHeight - 4; + + t.align = ALIGN_CENTER; + t.CharCount = ~0; + t.baseline.x = r.corner.x + (r.extent.width >> 1); + t.baseline.y = r.corner.y + (r.extent.height - 8 - 8 * numlines); + + for (i = 0; i < numlines; i++) + { + t.pStr = tips[i]; + font_DrawText(&t); + t.baseline.y += 8; + } + + SetContextFontEffect (oldFontEffect); + if (oldfont) + SetContextFont (oldfont); + SetContextForeGroundColor (oldtext); +} + +void +Widget_DrawMenuScreen (WIDGET *_self, int x, int y) +{ + RECT r; + Color title, oldtext; + Color inactive, default_color, selected; + FONT oldfont = 0; + FRAME oldFontEffect = SetContextFontEffect (NULL); + TEXT t; + int widget_index, height, widget_y; + + WIDGET_MENU_SCREEN *self = (WIDGET_MENU_SCREEN *)_self; + + if (cur_font) + oldfont = SetContextFont (cur_font); + + r.corner.x = 2; + r.corner.y = 2; + r.extent.width = ScreenWidth - 4; + r.extent.height = ScreenHeight - 4; + + title = WIDGET_INACTIVE_SELECTED_COLOR; + selected = WIDGET_ACTIVE_COLOR; + inactive = WIDGET_INACTIVE_COLOR; + default_color = title; + + DrawStamp (&self->bgStamp); + + oldtext = SetContextForeGroundColor (title); + t.baseline.x = r.corner.x + (r.extent.width >> 1); + t.baseline.y = r.corner.y + 8; + t.pStr = self->title; + t.align = ALIGN_CENTER; + t.CharCount = ~0; + font_DrawText (&t); + t.baseline.y += 8; + t.pStr = self->subtitle; + font_DrawText (&t); + + height = 0; + for (widget_index = 0; widget_index < self->num_children; widget_index++) + { + WIDGET *child = self->child[widget_index]; + height += (*child->height)(child); + height += 8; /* spacing */ + } + + height -= 8; + + widget_y = (ScreenHeight - height) >> 1; + for (widget_index = 0; widget_index < self->num_children; widget_index++) + { + WIDGET *c = self->child[widget_index]; + (*c->draw)(c, 0, widget_y); + widget_y += (*c->height)(c) + 8; + } + + SetContextFontEffect (oldFontEffect); + if (oldfont) + SetContextFont (oldfont); + SetContextForeGroundColor (oldtext); + + (void) x; + (void) y; +} + +void +Widget_DrawChoice (WIDGET *_self, int x, int y) +{ + WIDGET_CHOICE *self = (WIDGET_CHOICE *)_self; + Color oldtext; + Color inactive, default_color, selected; + FONT oldfont = 0; + FRAME oldFontEffect = SetContextFontEffect (NULL); + TEXT t; + int i, home_x, home_y; + + if (cur_font) + oldfont = SetContextFont (cur_font); + + default_color = WIDGET_INACTIVE_SELECTED_COLOR; + selected = WIDGET_ACTIVE_COLOR; + inactive = WIDGET_INACTIVE_COLOR; + + t.baseline.x = x; + t.baseline.y = y; + t.align = ALIGN_LEFT; + t.CharCount = ~0; + t.pStr = self->category; + if (widget_focus == _self) + { + oldtext = SetContextForeGroundColor (selected); + } + else + { + oldtext = SetContextForeGroundColor (default_color); + } + font_DrawText (&t); + + home_x = t.baseline.x + 3 * (ScreenWidth / ((self->maxcolumns + 1) * 2)); + home_y = t.baseline.y; + t.align = ALIGN_CENTER; + for (i = 0; i < self->numopts; i++) + { + t.baseline.x = home_x + ((i % 3) * + (ScreenWidth / (self->maxcolumns + 1))); + t.baseline.y = home_y + (8 * (i / 3)); + t.pStr = self->options[i].optname; + if ((widget_focus == _self) && + (self->highlighted == i)) + { + SetContextForeGroundColor (selected); + Widget_DrawToolTips (3, self->options[i].tooltip); + } + else if (i == self->selected) + { + SetContextForeGroundColor (default_color); + } + else + { + SetContextForeGroundColor (inactive); + } + font_DrawText (&t); + } + SetContextFontEffect (oldFontEffect); + if (oldfont) + SetContextFont (oldfont); + SetContextForeGroundColor (oldtext); +} + +void +Widget_DrawButton (WIDGET *_self, int x, int y) +{ + WIDGET_BUTTON *self = (WIDGET_BUTTON *)_self; + Color oldtext; + Color inactive, selected; + FONT oldfont = 0; + FRAME oldFontEffect = SetContextFontEffect (NULL); + TEXT t; + + if (cur_font) + oldfont = SetContextFont (cur_font); + + selected = WIDGET_ACTIVE_COLOR; + inactive = WIDGET_INACTIVE_COLOR; + + t.baseline.x = 160; + t.baseline.y = y; + t.align = ALIGN_CENTER; + t.CharCount = ~0; + t.pStr = self->name; + if (widget_focus == _self) + { + Widget_DrawToolTips (3, self->tooltip); + oldtext = SetContextForeGroundColor (selected); + } + else + { + oldtext = SetContextForeGroundColor (inactive); + } + font_DrawText (&t); + SetContextFontEffect (oldFontEffect); + if (oldfont) + SetContextFont (oldfont); + SetContextForeGroundColor (oldtext); + (void) x; +} + +void +Widget_DrawLabel (WIDGET *_self, int x, int y) +{ + WIDGET_LABEL *self = (WIDGET_LABEL *)_self; + Color oldtext = SetContextForeGroundColor (WIDGET_INACTIVE_SELECTED_COLOR); + FONT oldfont = 0; + FRAME oldFontEffect = SetContextFontEffect (NULL); + TEXT t; + int i; + + if (cur_font) + oldfont = SetContextFont (cur_font); + + t.baseline.x = 160; + t.baseline.y = y; + t.align = ALIGN_CENTER; + t.CharCount = ~0; + + for (i = 0; i < self->line_count; i++) + { + t.pStr = self->lines[i]; + font_DrawText (&t); + t.baseline.y += 8; + } + SetContextFontEffect (oldFontEffect); + if (oldfont) + SetContextFont (oldfont); + SetContextForeGroundColor (oldtext); + (void) x; +} + +void +Widget_DrawSlider(WIDGET *_self, int x, int y) +{ + WIDGET_SLIDER *self = (WIDGET_SLIDER *)_self; + Color oldtext; + Color inactive, default_color, selected; + FONT oldfont = 0; + FRAME oldFontEffect = SetContextFontEffect (NULL); + TEXT t; + RECT r; + int tick = (ScreenWidth - x) / 8; + + if (cur_font) + oldfont = SetContextFont (cur_font); + + default_color = WIDGET_INACTIVE_SELECTED_COLOR; + selected = WIDGET_ACTIVE_COLOR; + inactive = WIDGET_INACTIVE_COLOR; + + t.baseline.x = x; + t.baseline.y = y; + t.align = ALIGN_LEFT; + t.CharCount = ~0; + t.pStr = self->category; + if (widget_focus == _self) + { + Widget_DrawToolTips (3, self->tooltip); + oldtext = SetContextForeGroundColor (selected); + } + else + { + oldtext = SetContextForeGroundColor (default_color); + } + font_DrawText (&t); + + r.corner.x = t.baseline.x + 3 * tick; + r.corner.y = t.baseline.y - 4; + r.extent.height = 2; + r.extent.width = 3 * tick; + DrawFilledRectangle (&r); + + r.extent.width = 3; + r.extent.height = 8; + r.corner.y = t.baseline.y - 7; + r.corner.x = t.baseline.x + 3 * tick + (3 * tick * (self->value - self->min) / + (self->max - self->min)) - 1; + DrawFilledRectangle (&r); + + (*self->draw_value)(self, t.baseline.x + 7 * tick, t.baseline.y); + + SetContextFontEffect (oldFontEffect); + if (oldfont) + SetContextFont (oldfont); + SetContextForeGroundColor (oldtext); +} + +void +Widget_Slider_DrawValue (WIDGET_SLIDER *self, int x, int y) +{ + TEXT t; + char buffer[16]; + + sprintf (buffer, "%d", self->value); + + t.baseline.x = x; + t.baseline.y = y; + t.align = ALIGN_CENTER; + t.CharCount = ~0; + t.pStr = buffer; + + font_DrawText (&t); +} + +void +Widget_DrawTextEntry (WIDGET *_self, int x, int y) +{ + WIDGET_TEXTENTRY *self = (WIDGET_TEXTENTRY *)_self; + Color oldtext; + Color inactive, default_color, selected; + FONT oldfont = 0; + FRAME oldFontEffect = SetContextFontEffect (NULL); + TEXT t; + + if (cur_font) + oldfont = SetContextFont (cur_font); + + default_color = WIDGET_INACTIVE_SELECTED_COLOR; + selected = WIDGET_ACTIVE_COLOR; + inactive = WIDGET_INACTIVE_COLOR; + + BatchGraphics (); + + t.baseline.x = x; + t.baseline.y = y; + t.align = ALIGN_LEFT; + t.CharCount = ~0; + t.pStr = self->category; + if (widget_focus == _self) + { + oldtext = SetContextForeGroundColor (selected); + } + else + { + oldtext = SetContextForeGroundColor (default_color); + } + font_DrawText (&t); + + /* Force string termination */ + self->value[WIDGET_TEXTENTRY_WIDTH-1] = 0; + + t.baseline.y = y; + t.CharCount = utf8StringCount (self->value); + t.pStr = self->value; + + if (!(self->state & WTE_EDITING)) + { // normal or selected state + t.baseline.x = 160; + t.align = ALIGN_CENTER; + + if (widget_focus == _self) + { + oldtext = SetContextForeGroundColor (selected); + } + else + { + oldtext = SetContextForeGroundColor (inactive); + } + font_DrawText (&t); + } + else + { // editing state + COUNT i; + RECT text_r; + BYTE char_deltas[WIDGET_TEXTENTRY_WIDTH]; + BYTE *pchar_deltas; + RECT r; + SIZE leading; + + t.baseline.x = 90; + t.align = ALIGN_LEFT; + + // calc background box dimensions + // XXX: this may need some tuning, especially if a + // different font is used. The font 'leading' values + // are not what they should be. +#define BOX_VERT_OFFSET 2 + GetContextFontLeading (&leading); + r.corner.x = t.baseline.x - 1; + r.corner.y = t.baseline.y - leading + BOX_VERT_OFFSET; + r.extent.width = ScreenWidth - r.corner.x - 10; + r.extent.height = leading + 2; + + TextRect (&t, &text_r, char_deltas); +#if 0 + // XXX: this should potentially be used in ChangeCallback + if ((text_r.extent.width + 2) >= r.extent.width) + { // the text does not fit the input box size and so + // will not fit when displayed later + UnbatchGraphics (); + // disallow the change + return (FALSE); + } +#endif + + oldtext = SetContextForeGroundColor (selected); + DrawFilledRectangle (&r); + + // calculate the cursor position and draw it + pchar_deltas = char_deltas; + for (i = self->cursor_pos; i > 0; --i) + r.corner.x += (SIZE)*pchar_deltas++; + if (self->cursor_pos < t.CharCount) /* cursor mid-line */ + --r.corner.x; + if (self->state & WTE_BLOCKCUR) + { // Use block cursor for keyboardless systems + if (self->cursor_pos == t.CharCount) + { // cursor at end-line -- use insertion point + r.extent.width = 1; + } + else if (self->cursor_pos + 1 == t.CharCount) + { // extra pixel for last char margin + r.extent.width = (SIZE)*pchar_deltas + 2; + } + else + { // normal mid-line char + r.extent.width = (SIZE)*pchar_deltas + 1; + } + } + else + { // Insertion point cursor + r.extent.width = 1; + } + // position cursor within input field rect + ++r.corner.x; + ++r.corner.y; + r.extent.height -= 2; + SetContextForeGroundColor (WIDGET_CURSOR_COLOR); + DrawFilledRectangle (&r); + + SetContextForeGroundColor (inactive); + font_DrawText (&t); + } + + UnbatchGraphics (); + SetContextFontEffect (oldFontEffect); + if (oldfont) + SetContextFont (oldfont); + SetContextForeGroundColor (oldtext); +} + +void +Widget_DrawControlEntry (WIDGET *_self, int x, int y) +{ + WIDGET_CONTROLENTRY *self = (WIDGET_CONTROLENTRY *)_self; + Color oldtext; + Color inactive, default_color, selected; + FONT oldfont = 0; + FRAME oldFontEffect = SetContextFontEffect (NULL); + TEXT t; + int i, home_x, home_y; + + if (cur_font) + oldfont = SetContextFont (cur_font); + + default_color = WIDGET_INACTIVE_SELECTED_COLOR; + selected = WIDGET_ACTIVE_COLOR; + inactive = WIDGET_INACTIVE_COLOR; + + t.baseline.x = x; + t.baseline.y = y; + t.align = ALIGN_LEFT; + t.CharCount = ~0; + t.pStr = self->category; + if (widget_focus == _self) + { + oldtext = SetContextForeGroundColor (selected); + } + else + { + oldtext = SetContextForeGroundColor (default_color); + } + font_DrawText (&t); + + // 3 * ScreenWidth / ((self->maxcolumns + 1) * 2)) as per CHOICE, but only two options. + home_x = t.baseline.x + (ScreenWidth / 2); + home_y = t.baseline.y; + t.align = ALIGN_CENTER; + for (i = 0; i < 2; i++) + { + t.baseline.x = home_x + ((i % 3) * (ScreenWidth / 3)); // self->maxcolumns + 1 as per CHOICE. + t.baseline.y = home_y + (8 * (i / 3)); + t.pStr = self->controlname[i]; + if (!t.pStr[0]) + { + t.pStr = "---"; + } + if ((widget_focus == _self) && + (self->highlighted == i)) + { + SetContextForeGroundColor (selected); + } + else + { + SetContextForeGroundColor (default_color); + } + font_DrawText (&t); + } + SetContextFontEffect (oldFontEffect); + if (oldfont) + SetContextFont (oldfont); + SetContextForeGroundColor (oldtext); +} + +int +Widget_HeightChoice (WIDGET *_self) +{ + return ((((WIDGET_CHOICE *)_self)->numopts + 2) / 3) * 8; +} + +int +Widget_HeightFullScreen (WIDGET *_self) +{ + (void)_self; + return ScreenHeight; +} + +int +Widget_HeightOneLine (WIDGET *_self) +{ + (void)_self; + return 8; +} + +int +Widget_HeightLabel (WIDGET *_self) +{ + WIDGET_LABEL *self = (WIDGET_LABEL *)_self; + return self->line_count * 8; +} + +int +Widget_WidthFullScreen (WIDGET *_self) +{ + (void)_self; + return ScreenWidth; +} + +int +Widget_ReceiveFocusSimple (WIDGET *_self, int event) +{ + widget_focus = _self; + (void)event; + return TRUE; +} + +int +Widget_ReceiveFocusChoice (WIDGET *_self, int event) +{ + WIDGET_CHOICE *self = (WIDGET_CHOICE *)_self; + widget_focus = _self; + self->highlighted = self->selected; + (void)event; + return TRUE; +} + +int +Widget_ReceiveFocusControlEntry (WIDGET *_self, int event) +{ + WIDGET_CONTROLENTRY *self = (WIDGET_CONTROLENTRY *)_self; + int oldval = 0; + if (widget_focus->tag == WIDGET_TYPE_CONTROLENTRY) + { + oldval = ((WIDGET_CONTROLENTRY *)widget_focus)->highlighted; + } + widget_focus = _self; + self->highlighted = oldval; + (void)event; + return TRUE; +} + +int +Widget_ReceiveFocusMenuScreen (WIDGET *_self, int event) +{ + WIDGET_MENU_SCREEN *self = (WIDGET_MENU_SCREEN *)_self; + int x, last_x, dx; + for (x = 0; x < self->num_children; x++) + { + self->child[x]->parent = _self; + } + if (event == WIDGET_EVENT_UP) + { + x = self->num_children - 1; + dx = -1; + last_x = -1; + } + else if (event == WIDGET_EVENT_DOWN) + { + x = 0; + dx = 1; + last_x = self->num_children; + } + else + { + /* Leave highlighted value the same */ + WIDGET *child = self->child[self->highlighted]; + child->receiveFocus (child, event); + return TRUE; + } + for ( ; x != last_x; x += dx) + { + WIDGET *child = self->child[x]; + if ((*child->receiveFocus)(child, event)) + { + self->highlighted = x; + return TRUE; + } + } + return FALSE; +} + +int +Widget_ReceiveFocusRefuseFocus (WIDGET *self, int event) +{ + (void)self; + (void)event; + return FALSE; +} + +int +Widget_HandleEventIgnoreAll (WIDGET *self, int event) +{ + (void)event; + (void)self; + return FALSE; +} + +int +Widget_HandleEventChoice (WIDGET *_self, int event) +{ + WIDGET_CHOICE *self = (WIDGET_CHOICE *)_self; + switch (event) + { + case WIDGET_EVENT_LEFT: + self->highlighted -= 1; + if (self->highlighted < 0) + self->highlighted = self->numopts - 1; + return TRUE; + case WIDGET_EVENT_RIGHT: + self->highlighted += 1; + if (self->highlighted >= self->numopts) + self->highlighted = 0; + return TRUE; + case WIDGET_EVENT_SELECT: + { + int oldval = self->selected; + self->selected = self->highlighted; + if (self->onChange) + { + (*(self->onChange))(self, oldval); + } + return TRUE; + } + default: + return FALSE; + } +} + +int +Widget_HandleEventSlider (WIDGET *_self, int event) +{ + WIDGET_SLIDER *self = (WIDGET_SLIDER *)_self; + switch (event) + { + case WIDGET_EVENT_LEFT: + self->value -= self->step; + if (self->value < self->min) + self->value = self->min; + return TRUE; + case WIDGET_EVENT_RIGHT: + self->value += self->step; + if (self->value > self->max) + self->value = self->max; + return TRUE; + default: + return FALSE; + } +} + +int +Widget_HandleEventMenuScreen (WIDGET *_self, int event) +{ + WIDGET_MENU_SCREEN *self = (WIDGET_MENU_SCREEN *)_self; + int x, last_x, dx; + switch (event) + { + case WIDGET_EVENT_UP: + dx = -1; + break; + case WIDGET_EVENT_DOWN: + dx = 1; + break; + case WIDGET_EVENT_CANCEL: + /* On cancel, shift focus to last element and send a SELECT. */ + self->highlighted = self->num_children - 1; + widget_focus = self->child[self->highlighted]; + return (widget_focus->handleEvent)(widget_focus, WIDGET_EVENT_SELECT); + default: + return FALSE; + } + last_x = self->highlighted; + x = self->highlighted + dx; + while (x != last_x) + { + WIDGET *child; + if (x == -1) + x = self->num_children - 1; + if (x == self->num_children) + x = 0; + child = self->child[x]; + if ((*child->receiveFocus)(child, event)) + { + self->highlighted = x; + return TRUE; + } + x += dx; + } + return FALSE; +} + +int +Widget_HandleEventTextEntry (WIDGET *_self, int event) +{ + WIDGET_TEXTENTRY *self = (WIDGET_TEXTENTRY *)_self; + if (event == WIDGET_EVENT_SELECT) { + if (!self->handleEventSelect) + return FALSE; + return (*self->handleEventSelect)(self); + } + return FALSE; +} + +int +Widget_HandleEventControlEntry (WIDGET *_self, int event) +{ + WIDGET_CONTROLENTRY *self = (WIDGET_CONTROLENTRY *)_self; + if (event == WIDGET_EVENT_SELECT) + { + if (self->onChange) + { + (self->onChange)(self); + return TRUE; + } + } + if (event == WIDGET_EVENT_DELETE) + { + if (self->onDelete) + { + (self->onDelete)(self); + return TRUE; + } + } + if ((event == WIDGET_EVENT_RIGHT) || + (event == WIDGET_EVENT_LEFT)) + { + self->highlighted = 1-self->highlighted; + return TRUE; + } + return FALSE; +} + +int +Widget_Event (int event) +{ + WIDGET *widget = widget_focus; + while (widget != NULL) + { + if ((*widget->handleEvent)(widget, event)) + return TRUE; + widget = widget->parent; + } + return FALSE; +} diff --git a/src/libs/graphics/widgets.h b/src/libs/graphics/widgets.h new file mode 100644 index 0000000..4548e35 --- /dev/null +++ b/src/libs/graphics/widgets.h @@ -0,0 +1,222 @@ +// Copyright Michael Martin, 2004. + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef LIBS_GRAPHICS_WIDGETS_H_ +#define LIBS_GRAPHICS_WIDGETS_H_ + +#include "libs/gfxlib.h" + +enum { + WIDGET_EVENT_UP, + WIDGET_EVENT_DOWN, + WIDGET_EVENT_LEFT, + WIDGET_EVENT_RIGHT, + WIDGET_EVENT_SELECT, + WIDGET_EVENT_CANCEL, + WIDGET_EVENT_DELETE, + NUM_WIDGET_EVENTS +}; + +typedef enum { + WIDGET_TYPE_MENU_SCREEN, + WIDGET_TYPE_CHOICE, + WIDGET_TYPE_BUTTON, + WIDGET_TYPE_LABEL, + WIDGET_TYPE_SLIDER, + WIDGET_TYPE_TEXTENTRY, + WIDGET_TYPE_CONTROLENTRY, + NUM_WIDGET_TYPES +} WIDGET_TYPE; + +#define WIDGET_TEXTENTRY_WIDTH 50 +#define WIDGET_CONTROLENTRY_WIDTH 16 + +typedef struct _widget { + WIDGET_TYPE tag; + struct _widget *parent; + int (*handleEvent)(struct _widget *self, int event); + int (*receiveFocus)(struct _widget *self, int event); + void (*draw)(struct _widget *self, int x, int y); + int (*height)(struct _widget *self); + int (*width)(struct _widget *self); +} WIDGET; + +typedef struct _widget_menu_screen { + WIDGET_TYPE tag; + struct _widget *parent; + int (*handleEvent)(struct _widget *self, int event); + int (*receiveFocus)(struct _widget *self, int event); + void (*draw)(struct _widget *self, int x, int y); + int (*height)(struct _widget *self); + int (*width)(struct _widget *self); + const char *title; + const char *subtitle; + STAMP bgStamp; + int num_children; + struct _widget **child; + int highlighted; +} WIDGET_MENU_SCREEN; + +typedef struct { + const char *optname; + const char *tooltip[3]; +} CHOICE_OPTION; + +typedef struct _widget_choice { + WIDGET_TYPE tag; + struct _widget *parent; + int (*handleEvent)(struct _widget *self, int event); + int (*receiveFocus)(struct _widget *self, int event); + void (*draw)(struct _widget *self, int x, int y); + int (*height)(struct _widget *self); + int (*width)(struct _widget *self); + const char *category; + int numopts; + int maxcolumns; + CHOICE_OPTION *options; + int selected, highlighted; + void (*onChange)(struct _widget_choice *self, int oldval); +} WIDGET_CHOICE; + +typedef struct _widget_button { + WIDGET_TYPE tag; + struct _widget *parent; + int (*handleEvent)(struct _widget *self, int event); + int (*receiveFocus)(struct _widget *self, int event); + void (*draw)(struct _widget *self, int x, int y); + int (*height)(struct _widget *self); + int (*width)(struct _widget *self); + const char *name; + const char *tooltip[3]; +} WIDGET_BUTTON; + +typedef struct _widget_label { + WIDGET_TYPE tag; + struct _widget *parent; + int (*handleEvent)(struct _widget *self, int event); + int (*receiveFocus)(struct _widget *self, int event); + void (*draw)(struct _widget *self, int x, int y); + int (*height)(struct _widget *self); + int (*width)(struct _widget *self); + int line_count; + const char **lines; +} WIDGET_LABEL; + +typedef struct _widget_slider { + WIDGET_TYPE tag; + struct _widget *parent; + int (*handleEvent)(struct _widget *self, int event); + int (*receiveFocus)(struct _widget *self, int event); + void (*draw)(struct _widget *self, int x, int y); + int (*height)(struct _widget *self); + int (*width)(struct _widget *self); + void (*draw_value)(struct _widget_slider *self, int x, int y); + int min, max, step; + int value; + const char *category; + const char *tooltip[3]; +} WIDGET_SLIDER; + +typedef enum { + WTE_NORMAL = 0, + WTE_EDITING, + WTE_BLOCKCUR, + +} WIDGET_TEXTENTRY_STATE; + +typedef struct _widget_textentry { + WIDGET_TYPE tag; + struct _widget *parent; + int (*handleEvent)(struct _widget *self, int event); + int (*receiveFocus)(struct _widget *self, int event); + void (*draw)(struct _widget *self, int x, int y); + int (*height)(struct _widget *self); + int (*width)(struct _widget *self); + int (*handleEventSelect)(struct _widget_textentry *self); + // handleEventSelect is an overridable callback event + // called by the default handleEvent implementation + // can be NULL, in which case SELECT is ignored + void (*onChange)(struct _widget_textentry *self); + const char *category; + char value[WIDGET_TEXTENTRY_WIDTH]; + int maxlen; + WIDGET_TEXTENTRY_STATE state; + int cursor_pos; +} WIDGET_TEXTENTRY; + +typedef struct _widget_controlentry { + WIDGET_TYPE tag; + struct _widget *parent; + int (*handleEvent)(struct _widget *self, int event); + int (*receiveFocus)(struct _widget *self, int event); + void (*draw)(struct _widget *self, int x, int y); + int (*height)(struct _widget *self); + int (*width)(struct _widget *self); + void (*onChange)(struct _widget_controlentry *self); + void (*onDelete)(struct _widget_controlentry *self); + const char *category; + int controlindex; + int highlighted; + char controlname[2][WIDGET_CONTROLENTRY_WIDTH]; +} WIDGET_CONTROLENTRY; + +void DrawShadowedBox (RECT *r, Color bg, Color dark, Color medium); +void DrawLabelAsWindow (WIDGET_LABEL *label, RECT *windowRect); +void Widget_SetWindowColors (Color bg, Color dark, Color medium); +FONT Widget_SetFont (FONT newFont); + + +int Widget_Event (int event); + +/* Methods for filling in widgets with */ + +int Widget_ReceiveFocusMenuScreen (WIDGET *_self, int event); +int Widget_ReceiveFocusChoice (WIDGET *_self, int event); +int Widget_ReceiveFocusSimple (WIDGET *_self, int event); +int Widget_ReceiveFocusSlider (WIDGET *_self, int event); +int Widget_ReceiveFocusControlEntry (WIDGET *_self, int event); +int Widget_ReceiveFocusRefuseFocus (WIDGET *_self, int event); + +int Widget_HandleEventMenuScreen (WIDGET *_self, int event); +int Widget_HandleEventChoice (WIDGET *_self, int event); +int Widget_HandleEventSlider (WIDGET *_self, int event); +int Widget_HandleEventTextEntry (WIDGET *_self, int event); +int Widget_HandleEventControlEntry (WIDGET *_self, int event); +int Widget_HandleEventIgnoreAll (WIDGET *_self, int event); + +int Widget_HeightChoice (WIDGET *_self); +int Widget_HeightFullScreen (WIDGET *_self); +int Widget_HeightOneLine (WIDGET *_self); +int Widget_HeightLabel (WIDGET *_self); + +int Widget_WidthFullScreen (WIDGET *_self); + +void Widget_DrawMenuScreen (WIDGET *_self, int x, int y); +void Widget_DrawChoice (WIDGET *_self, int x, int y); +void Widget_DrawButton (WIDGET *_self, int x, int y); +void Widget_DrawLabel (WIDGET *_self, int x, int y); +void Widget_DrawSlider (WIDGET *_self, int x, int y); +void Widget_DrawTextEntry (WIDGET *_self, int x, int y); +void Widget_DrawControlEntry (WIDGET *_self, int x, int y); + +void Widget_Slider_DrawValue (WIDGET_SLIDER *self, int x, int y); + +/* Other implementations will need these values */ +extern WIDGET *widget_focus; + +#endif /* LIBS_GRAPHICS_WIDGETS_H_ */ |