Image scaling wrt hotspot; re-added bilinear; minor gfx refactoring

git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@2827 8092fc87-c524-0410-9efc-e669fe64eaf9
This commit is contained in:
avolkov
2007-08-01 03:25:08 +00:00
parent 2dcea171b5
commit 8669bbaf99
13 changed files with 604 additions and 291 deletions
+3
View File
@@ -1,4 +1,7 @@
Changes towards version 0.7: Changes towards version 0.7:
- Scaling images with respect to their hotspots: stabilizes compound
Melee objects; re-added bilinear Melee scaler; zooming planet uses
bilinear; fixes bug #685 - Alex
- Added --keepaspectratio to keep correct aspect ratio when using - Added --keepaspectratio to keep correct aspect ratio when using
custom resolutions in OpenGL mode - Mika custom resolutions in OpenGL mode - Mika
- Add /var/tmp as possible location for temporary files. Don't try - Add /var/tmp as possible location for temporary files. Don't try
+4
View File
@@ -19,6 +19,7 @@
#include "battle.h" #include "battle.h"
#include "controls.h" #include "controls.h"
#include "options.h"
#include "init.h" #include "init.h"
#include "intel.h" #include "intel.h"
#ifdef NETPLAY #ifdef NETPLAY
@@ -422,6 +423,9 @@ Battle (void)
battle_counter[0] = CountLinks (&race_q[0]); battle_counter[0] = CountLinks (&race_q[0]);
battle_counter[1] = CountLinks (&race_q[1]); battle_counter[1] = CountLinks (&race_q[1]);
if (optMeleeScale != TFB_SCALE_STEP)
SetGraphicScaleMode (optMeleeScale);
setupBattleInputOrder (); setupBattleInputOrder ();
#ifdef NETPLAY #ifdef NETPLAY
initBattleInputBuffers (); initBattleInputBuffers ();
+8 -4
View File
@@ -585,20 +585,23 @@ DoPresentation (void *pIS)
int cargs; int cargs;
int draw_what; int draw_what;
int index, x, y, scale, angle; int index, x, y, scale, angle;
int scale_mode;
char ImgName[16]; char ImgName[16];
int OldScale; int old_scale, old_mode;
STAMP s; STAMP s;
if (1 == sscanf (pStr, "%15s", ImgName) if (1 == sscanf (pStr, "%15s", ImgName)
&& strcmp (strupr (ImgName), "SIS") == 0) && strcmp (strupr (ImgName), "SIS") == 0)
{ {
draw_what = PRES_DRAW_SIS; draw_what = PRES_DRAW_SIS;
scale_mode = TFB_SCALE_NEAREST;
cargs = sscanf (pStr, "%*s %d %d %d %d", cargs = sscanf (pStr, "%*s %d %d %d %d",
&x, &y, &scale, &angle) + 1; &x, &y, &scale, &angle) + 1;
} }
else else
{ {
draw_what = PRES_DRAW_INDEX; draw_what = PRES_DRAW_INDEX;
scale_mode = TFB_SCALE_BILINEAR;
cargs = sscanf (pStr, "%d %d %d %d %d", cargs = sscanf (pStr, "%d %d %d %d %d",
&index, &x, &y, &scale, &angle); &index, &x, &y, &scale, &angle);
} }
@@ -646,11 +649,12 @@ DoPresentation (void *pIS)
s.origin.y = y; s.origin.y = y;
if (!pPIS->Batched) if (!pPIS->Batched)
LockMutex (GraphicsLock); LockMutex (GraphicsLock);
OldScale = GetGraphicScale (); old_mode = SetGraphicScaleMode (scale_mode);
SetGraphicScale (scale); old_scale = SetGraphicScale (scale);
SetContextClipping (TRUE); SetContextClipping (TRUE);
DrawStamp (&s); DrawStamp (&s);
SetGraphicScale (OldScale); SetGraphicScale (old_scale);
SetGraphicScaleMode (old_mode);
if (!pPIS->Batched) if (!pPIS->Batched)
UnlockMutex (GraphicsLock); UnlockMutex (GraphicsLock);
} }
@@ -34,6 +34,9 @@ int ScreenColorDepth;
int GraphicsDriver; int GraphicsDriver;
int TFB_DEBUG_HALT = 0; int TFB_DEBUG_HALT = 0;
static int gscale = GSCALE_IDENTITY;
static int gscale_mode = TFB_SCALE_NEAREST;
// Status: Ignored (only used in fmv.c) // Status: Ignored (only used in fmv.c)
void void
SetGraphicUseOtherExtra (int other) //Could this possibly be more cryptic?!? :) SetGraphicUseOtherExtra (int other) //Could this possibly be more cryptic?!? :)
@@ -62,3 +65,31 @@ LoadIntoExtraScreen (RECT *r)
TFB_DrawScreen_Copy(r, TFB_SCREEN_MAIN, TFB_SCREEN_EXTRA); 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;
}
+10 -3
View File
@@ -67,15 +67,22 @@ int TFB_InitGraphics (int driver, int flags, int width, int height);
void TFB_UninitGraphics (void); void TFB_UninitGraphics (void);
void TFB_ProcessEvents (void); void TFB_ProcessEvents (void);
// 3DO Graphics Stuff
#define GSCALE_IDENTITY 256 #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 LoadIntoExtraScreen (RECT *r);
void DrawFromExtraScreen (RECT *r); void DrawFromExtraScreen (RECT *r);
void SetGraphicGrabOther (int grab_other); void SetGraphicGrabOther (int grab_other);
void SetGraphicScale (int scale); int SetGraphicScale (int scale);
int GetGraphicScale (void); int GetGraphicScale (void);
int SetGraphicScaleMode (int mode /* enum SCALE */);
int GetGraphicScaleMode (void);
void SetGraphicUseOtherExtra (int other); void SetGraphicUseOtherExtra (int other);
void SetTransitionSource (RECT *pRect); void SetTransitionSource (RECT *pRect);
void ScreenTransition (int transition, RECT *pRect); void ScreenTransition (int transition, RECT *pRect);
@@ -22,20 +22,6 @@
#include "graphics/tfb_draw.h" #include "graphics/tfb_draw.h"
#include "libs/log.h" #include "libs/log.h"
static int gscale = GSCALE_IDENTITY;
void
SetGraphicScale (int scale)
{
gscale = (scale ? scale : GSCALE_IDENTITY);
}
int
GetGraphicScale ()
{
return gscale;
}
static void static void
read_screen (RECT *lpRect, FRAME DstFramePtr) read_screen (RECT *lpRect, FRAME DstFramePtr)
{ {
@@ -191,7 +191,6 @@ FRAME stretch_frame (FRAME FramePtr, int neww, int newh, int destroy)
CREATE_FLAGS flags; CREATE_FLAGS flags;
TFB_Image *tfbImg; TFB_Image *tfbImg;
TFB_Canvas src, dst; TFB_Canvas src, dst;
EXTENT ext;
flags = GetFrameParentDrawable (FramePtr)->Flags; flags = GetFrameParentDrawable (FramePtr)->Flags;
NewFrame = CaptureDrawable ( NewFrame = CaptureDrawable (
@@ -200,9 +199,7 @@ FRAME stretch_frame (FRAME FramePtr, int neww, int newh, int destroy)
LockMutex (tfbImg->mutex); LockMutex (tfbImg->mutex);
src = tfbImg->NormalImg; src = tfbImg->NormalImg;
dst = NewFrame->image->NormalImg; dst = NewFrame->image->NormalImg;
ext.width = neww; TFB_DrawCanvas_Rescale_Nearest (src, dst, -1, NULL, NULL, NULL);
ext.height = newh;
TFB_DrawCanvas_Rescale_Nearest (src, dst, ext);
UnlockMutex (tfbImg->mutex); UnlockMutex (tfbImg->mutex);
if (destroy) if (destroy)
DestroyDrawable (ReleaseDrawable (FramePtr)); DestroyDrawable (ReleaseDrawable (FramePtr));
+487 -213
View File
@@ -105,18 +105,18 @@ TFB_DrawCanvas_Image (TFB_Image *img, int x, int y, int scale,
if (scale != 0 && scale != GSCALE_IDENTITY) if (scale != 0 && scale != GSCALE_IDENTITY)
{ {
int type; int type = GetGraphicScaleMode ();
if (optMeleeScale == TFB_SCALE_TRILINEAR && img->MipmapImg)
if (type == TFB_SCALE_TRILINEAR && img->MipmapImg)
{ {
type = TFB_SCALE_TRILINEAR;
// only set the new palette if it changed // only set the new palette if it changed
if (((SDL_Surface *)img->MipmapImg)->format->palette if (((SDL_Surface *)img->MipmapImg)->format->palette
&& cmap && img->colormap_version != cmap->version) && cmap && img->colormap_version != cmap->version)
SDL_SetColors (img->MipmapImg, (SDL_Color*)palette, 0, 256); SDL_SetColors (img->MipmapImg, (SDL_Color*)palette, 0, 256);
} }
else else if (type == TFB_SCALE_TRILINEAR && !img->MipmapImg)
{ {
type = TFB_SCALE_NEAREST; type = TFB_SCALE_BILINEAR;
} }
TFB_DrawImage_FixScaling (img, scale, type); TFB_DrawImage_FixScaling (img, scale, type);
@@ -244,8 +244,7 @@ TFB_DrawCanvas_FilledImage (TFB_Image *img, int x, int y, int scale, int r, int
SDL_Rect srcRect, targetRect, *pSrcRect; SDL_Rect srcRect, targetRect, *pSrcRect;
SDL_Surface *surf; SDL_Surface *surf;
int i; int i;
SDL_Color pal[256]; bool force_fill = false;
EXTENT prevextent = img->extent;
if (img == 0) if (img == 0)
{ {
@@ -257,7 +256,16 @@ TFB_DrawCanvas_FilledImage (TFB_Image *img, int x, int y, int scale, int r, int
if (scale != 0 && scale != GSCALE_IDENTITY) if (scale != 0 && scale != GSCALE_IDENTITY)
{ {
TFB_DrawImage_FixScaling (img, scale, TFB_SCALE_NEAREST); int type = GetGraphicScaleMode ();
if (type == TFB_SCALE_TRILINEAR)
type = TFB_SCALE_BILINEAR;
// no point in trilinear for filled images
if (scale != img->last_scale || type != img->last_scale_type)
force_fill = true;
TFB_DrawImage_FixScaling (img, scale, type);
surf = img->ScaledImg; surf = img->ScaledImg;
srcRect.x = 0; srcRect.x = 0;
srcRect.y = 0; srcRect.y = 0;
@@ -270,6 +278,14 @@ TFB_DrawCanvas_FilledImage (TFB_Image *img, int x, int y, int scale, int r, int
} }
else 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; surf = img->NormalImg;
pSrcRect = NULL; pSrcRect = NULL;
@@ -279,6 +295,14 @@ TFB_DrawCanvas_FilledImage (TFB_Image *img, int x, int y, int scale, int r, int
if (surf->format->palette) if (surf->format->palette)
{ // set palette for fill-stamp { // set palette for fill-stamp
// Calling SDL_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 pal[256];
for (i = 0; i < 256; i++) for (i = 0; i < 256; i++)
{ {
pal[i].r = r; pal[i].r = r;
@@ -293,40 +317,43 @@ TFB_DrawCanvas_FilledImage (TFB_Image *img, int x, int y, int scale, int r, int
{ // fill the non-transparent parts of the image with fillcolor { // fill the non-transparent parts of the image with fillcolor
SDL_Surface *src = img->NormalImg; SDL_Surface *src = img->NormalImg;
SDL_Surface *newfill = img->FilledImg; SDL_Surface *newfill = img->FilledImg;
BOOLEAN force = FALSE;
if (newfill && (newfill->w < surf->w || newfill->h < surf->h))
{
TFB_DrawCanvas_Delete (newfill);
newfill = NULL;
}
// prepare the filled image // prepare the filled image
if (!newfill) if (!newfill)
{ {
newfill = SDL_CreateRGBSurface (SDL_SWSURFACE, newfill = SDL_CreateRGBSurface (SDL_SWSURFACE,
src->w, src->h, surf->w, surf->h,
surf->format->BitsPerPixel, surf->format->BitsPerPixel,
surf->format->Rmask, surf->format->Rmask,
surf->format->Gmask, surf->format->Gmask,
surf->format->Bmask, surf->format->Bmask,
surf->format->Amask); surf->format->Amask);
force = TRUE; force_fill = true;
} }
if (force || if (force_fill ||
prevextent.height != img->extent.height ||
prevextent.width != img->extent.width ||
img->last_fill.r != r || img->last_fill.r != r ||
img->last_fill.g != g || img->last_fill.g != g ||
img->last_fill.b != b) img->last_fill.b != b)
{ // image or fillcolor changed - regenerate { // image or fillcolor changed - regenerate
TFB_DrawCanvas_Fill (surf, img->extent.width, img->extent.height, TFB_DrawCanvas_Fill (surf, surf->w, surf->h,
SDL_MapRGBA (newfill->format, r, g, b, 0), newfill); SDL_MapRGBA (newfill->format, r, g, b, 0), newfill);
// important to keep alpha=0 in fillcolor // important to keep alpha=0 in fillcolor
// -- we process alpha ourselves // -- we process alpha ourselves
}
// cache filled image if possible // cache filled image if possible
img->last_fill.r = r; img->last_fill.r = r;
img->last_fill.g = g; img->last_fill.g = g;
img->last_fill.b = b; img->last_fill.b = b;
img->FilledImg = newfill; }
img->FilledImg = newfill;
surf = newfill; surf = newfill;
} }
@@ -486,16 +513,23 @@ TFB_DrawCanvas_New_ScaleTarget (TFB_Canvas canvas, TFB_Canvas oldcanvas, int typ
{ {
SDL_Surface *src = (SDL_Surface *)canvas; SDL_Surface *src = (SDL_Surface *)canvas;
SDL_Surface *old = (SDL_Surface *)oldcanvas; SDL_Surface *old = (SDL_Surface *)oldcanvas;
SDL_Surface *newsurf = old; 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 (type == TFB_SCALE_NEAREST)
{
if (old && type != last_type) if (old && type != last_type)
{ {
TFB_DrawCanvas_Delete (old); TFB_DrawCanvas_Delete (old);
old = NULL; old = NULL;
} }
if (!old) if (old)
return old; /* can just reuse the old one */
if (type == TFB_SCALE_NEAREST)
{ {
newsurf = SDL_CreateRGBSurface (SDL_SWSURFACE, src->w, newsurf = SDL_CreateRGBSurface (SDL_SWSURFACE, src->w,
src->h, src->h,
@@ -504,30 +538,16 @@ TFB_DrawCanvas_New_ScaleTarget (TFB_Canvas canvas, TFB_Canvas oldcanvas, int typ
src->format->Gmask, src->format->Gmask,
src->format->Bmask, src->format->Bmask,
src->format->Amask); src->format->Amask);
if (src->format->palette) TFB_DrawCanvas_CopyTransparencyInfo (src, newsurf);
TFB_DrawCanvas_SetTransparentIndex (newsurf, TFB_DrawCanvas_GetTransparentIndex (src), FALSE);
else
{
int r, g, b;
if (TFB_DrawCanvas_GetTransparentColor (src, &r, &g, &b))
TFB_DrawCanvas_SetTransparentColor (newsurf, r, g, b, FALSE);
}
}
} }
else else
{ {
if (old && type != last_type) // The scaled image may in fact be larger by 1 pixel than the source
{ // because of hotspot alignment and fractional edge pixels
TFB_DrawCanvas_Delete (old);
old = NULL;
}
if (!old)
{
if (SDL_Screen->format->BitsPerPixel == 32) if (SDL_Screen->format->BitsPerPixel == 32)
newsurf = TFB_DrawCanvas_New_ForScreen (src->w, src->h, TRUE); newsurf = TFB_DrawCanvas_New_ForScreen (src->w + 1, src->h + 1, TRUE);
else else
newsurf = TFB_DrawCanvas_New_TrueColor (src->w, src->h, TRUE); newsurf = TFB_DrawCanvas_New_TrueColor (src->w + 1, src->h + 1, TRUE);
}
} }
return newsurf; return newsurf;
@@ -556,14 +576,7 @@ TFB_DrawCanvas_New_RotationTarget (TFB_Canvas src_canvas, int angle)
SDL_GetError()); SDL_GetError());
exit (EXIT_FAILURE); exit (EXIT_FAILURE);
} }
if (src->format->palette) TFB_DrawCanvas_CopyTransparencyInfo (src, newsurf);
TFB_DrawCanvas_SetTransparentIndex (newsurf, TFB_DrawCanvas_GetTransparentIndex (src), FALSE);
else
{
int r, g, b;
if (TFB_DrawCanvas_GetTransparentColor (src, &r, &g, &b))
TFB_DrawCanvas_SetTransparentColor (newsurf, r, g, b, FALSE);
}
return newsurf; return newsurf;
} }
@@ -672,6 +685,25 @@ TFB_DrawCanvas_SetTransparentIndex (TFB_Canvas canvas, int index, BOOLEAN rleacc
} }
} }
void
TFB_DrawCanvas_CopyTransparencyInfo (TFB_Canvas src_canvas, TFB_Canvas dst_canvas)
{
SDL_Surface* src = (SDL_Surface*)src_canvas;
if (src->format->palette)
{
int index;
index = TFB_DrawCanvas_GetTransparentIndex (src_canvas);
TFB_DrawCanvas_SetTransparentIndex (dst_canvas, index, FALSE);
}
else
{
int r, g, b;
if (TFB_DrawCanvas_GetTransparentColor (src_canvas, &r, &g, &b))
TFB_DrawCanvas_SetTransparentColor (dst_canvas, r, g, b, FALSE);
}
}
BOOLEAN BOOLEAN
TFB_DrawCanvas_GetTransparentColor (TFB_Canvas canvas, int *r, int *g, int *b) TFB_DrawCanvas_GetTransparentColor (TFB_Canvas canvas, int *r, int *g, int *b)
{ {
@@ -707,39 +739,68 @@ TFB_DrawCanvas_SetTransparentColor (TFB_Canvas canvas, int r, int g, int b, BOOL
} }
void void
TFB_DrawCanvas_GetScaledExtent (TFB_Canvas src_canvas, HOT_SPOT src_hs, TFB_DrawCanvas_GetScaledExtent (TFB_Canvas src_canvas, HOT_SPOT* src_hs,
TFB_Canvas src_mipmap, HOT_SPOT mm_hs, TFB_Canvas src_mipmap, HOT_SPOT* mm_hs,
int scale, EXTENT *size, HOT_SPOT *hs) int scale, int type, EXTENT *size, HOT_SPOT *hs)
{ {
SDL_Surface *src = (SDL_Surface *)src_canvas; SDL_Surface *src = (SDL_Surface *)src_canvas;
sint32 x, y, w, h;
int frac;
if (!src_mipmap) if (!src_mipmap)
{ {
size->width = src->w * scale / GSCALE_IDENTITY; w = src->w * scale;
size->height = src->h * scale / GSCALE_IDENTITY; h = src->h * scale;
x = src_hs->x * scale;
hs->x = src_hs.x * scale / GSCALE_IDENTITY; y = src_hs->y * scale;
hs->y = src_hs.y * scale / GSCALE_IDENTITY;
} }
else else
{ {
// interpolates extents between src and mipmap to get smoother // interpolates extents between src and mipmap to get smoother
// transition when surface changes // transition when surface changes
SDL_Surface *mipmap = (SDL_Surface *)src_mipmap; SDL_Surface *mipmap = (SDL_Surface *)src_mipmap;
int ratio = scale * 2 - GSCALE_IDENTITY;
float ratio = (scale / (float)GSCALE_IDENTITY) * 2.0f - 1.0f; assert (scale >= GSCALE_IDENTITY / 2);
if (ratio < 0.0f)
ratio = 0.0f;
else if (ratio > 1.0f)
ratio = 1.0f;
size->width = (int)((src->w - mipmap->w) * ratio + mipmap->w + 0.5); w = mipmap->w * GSCALE_IDENTITY + (src->w - mipmap->w) * ratio;
size->height = (int)((src->h - mipmap->h) * ratio + mipmap->h + 0.5); h = mipmap->h * GSCALE_IDENTITY + (src->h - mipmap->h) * ratio;
hs->x = (int)((src_hs.x - mm_hs.x) * ratio + mm_hs.x + 0.5); // Seems it is better to use mipmap hotspot because some
hs->y = (int)((src_hs.y - mm_hs.y) * ratio + mm_hs.y + 0.5); // 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) if (!size->width && src->w)
size->width = 1; size->width = 1;
if (!size->height && src->h) if (!size->height && src->h)
@@ -756,57 +817,48 @@ TFB_DrawCanvas_GetExtent (TFB_Canvas canvas, EXTENT *size)
} }
void void
TFB_DrawCanvas_Rescale_Nearest (TFB_Canvas src_canvas, TFB_Canvas dest_canvas, EXTENT size) TFB_DrawCanvas_Rescale_Nearest (TFB_Canvas src_canvas, TFB_Canvas dst_canvas,
int scale, HOT_SPOT* src_hs, EXTENT* size, HOT_SPOT* dst_hs)
{ {
#define NNS_MAX_DIMS 600
SDL_Surface *src = (SDL_Surface *)src_canvas; SDL_Surface *src = (SDL_Surface *)src_canvas;
SDL_Surface *dst = (SDL_Surface *)dest_canvas; SDL_Surface *dst = (SDL_Surface *)dst_canvas;
int x, y, sx, sy, *csax, *csay, csx, csy; int x, y;
int saspace[NNS_MAX_DIMS]; int fsx = 0, fsy = 0; // source fractional dx and dy increments
int *sax, *say; int ssx = 0, ssy = 0; // source fractional x and y starting points
int w, h;
if (size.width + size.height > NNS_MAX_DIMS) if (scale > 0)
{ {
log_add (log_Warning, "TFB_DrawCanvas_Scale: Tried to zoom" TFB_DrawCanvas_GetScaledExtent (src, src_hs, NULL, NULL, scale,
" an image to unreasonable size! Failing."); TFB_SCALE_NEAREST, size, dst_hs);
return;
w = size->width;
h = size->height;
} }
if (size.width > dst->w || size.height > dst->h) else
{ {
log_add (log_Warning, "TFB_DrawCanvas_Scale: Tried to scale" // 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" " image to size %d %d when dest_canvas has only"
" dimensions of %d %d! Failing.", " dimensions of %d %d! Failing.",
size.width, size.height, dst->w, dst->h); w, h, dst->w, dst->h);
return; return;
} }
sx = sy = 0; if (w > 1)
if (size.width > 1) fsx = ((src->w - 1) << 16) / (w - 1);
sx = ((src->w - 1) << 16) / (size.width - 1); if (h > 1)
if (size.height > 1) fsy = ((src->h - 1) << 16) / (h - 1);
sy = ((src->h - 1) << 16) / (size.height - 1); // We start with a value in 0..0.5 range to shift the bigger
// jumps towards the center of the image
sax = saspace; ssx = 0x6000;
say = saspace + size.width; ssy = 0x6000;
/*
* Precalculate row increments
* We start with a value in 0..0.5 range to shift the bigger
* jumps towards the center of the image
*/
csax = sax;
for (x = 0, csx = 0x6000; x < size.width; x++) {
*csax = csx >> 16;
csax++;
csx &= 0xffff;
csx += sx;
}
csay = say;
for (y = 0, csy = 0x6000; y < size.height; y++) {
*csay = csy >> 16;
csay++;
csy &= 0xffff;
csy += sy;
}
SDL_LockSurface (src); SDL_LockSurface (src);
SDL_LockSurface (dst); SDL_LockSurface (dst);
@@ -814,29 +866,33 @@ TFB_DrawCanvas_Rescale_Nearest (TFB_Canvas src_canvas, TFB_Canvas dest_canvas, E
if (src->format->BytesPerPixel == 1 && dst->format->BytesPerPixel == 1) if (src->format->BytesPerPixel == 1 && dst->format->BytesPerPixel == 1)
{ {
Uint8 *sp, *csp, *dp, *cdp; Uint8 *sp, *csp, *dp, *cdp;
int sx, sy; // source fractional x and y positions
sp = csp = (Uint8 *) src->pixels; sp = csp = (Uint8 *) src->pixels;
dp = cdp = (Uint8 *) dst->pixels; dp = cdp = (Uint8 *) dst->pixels;
csay = say; for (y = 0, sy = ssy; y < h; ++y)
for (y = 0; y < size.height; ++y) { {
csp += (*csay) * src->pitch; csp += (sy >> 16) * src->pitch;
sp = csp; sp = csp;
dp = cdp; dp = cdp;
csax = sax; for (x = 0, sx = ssx; x < w; ++x)
for (x = 0; x < size.width; ++x) { {
sp += *csax; sp += (sx >> 16);
*dp = *sp; *dp = *sp;
++csax; sx &= 0xffff;
sx += fsx;
++dp; ++dp;
} }
++csay; sy &= 0xffff;
sy += fsy;
cdp += dst->pitch; cdp += dst->pitch;
} }
} }
else if (src->format->BytesPerPixel == 4 && dst->format->BytesPerPixel == 4) else if (src->format->BytesPerPixel == 4 && dst->format->BytesPerPixel == 4)
{ {
Uint32 *sp, *csp, *dp, *cdp; Uint32 *sp, *csp, *dp, *cdp;
int sx, sy; // source fractional x and y positions
int sgap, dgap; int sgap, dgap;
sgap = src->pitch >> 2; sgap = src->pitch >> 2;
@@ -845,19 +901,21 @@ TFB_DrawCanvas_Rescale_Nearest (TFB_Canvas src_canvas, TFB_Canvas dest_canvas, E
sp = csp = (Uint32 *) src->pixels; sp = csp = (Uint32 *) src->pixels;
dp = cdp = (Uint32 *) dst->pixels; dp = cdp = (Uint32 *) dst->pixels;
csay = say; for (y = 0, sy = ssy; y < h; ++y)
for (y = 0; y < size.height; ++y) { {
csp += (*csay) * sgap; csp += (sy >> 16) * sgap;
sp = csp; sp = csp;
dp = cdp; dp = cdp;
csax = sax; for (x = 0, sx = ssx; x < w; ++x)
for (x = 0; x < size.width; ++x) { {
sp += *csax; sp += (sx >> 16);
*dp = *sp; *dp = *sp;
++csax; sx &= 0xffff;
sx += fsx;
++dp; ++dp;
} }
++csay; sy &= 0xffff;
sy += fsy;
cdp += dgap; cdp += dgap;
} }
} }
@@ -913,7 +971,7 @@ blend_ratio_2 (Uint8 c1, Uint8 c2, int ratio)
} }
static inline Uint32 static inline Uint32
tri_get_pixel (void* ppix, SDL_PixelFormat *fmt, SDL_Color *pal, scale_read_pixel (void* ppix, SDL_PixelFormat *fmt, SDL_Color *pal,
Uint32 mask, Uint32 key) Uint32 mask, Uint32 key)
{ {
pixel_t p; pixel_t p;
@@ -956,29 +1014,41 @@ tri_get_pixel (void* ppix, SDL_PixelFormat *fmt, SDL_Color *pal,
return p.value; 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 void
TFB_DrawCanvas_Rescale_Trilinear (TFB_Canvas src_canvas, TFB_DrawCanvas_Rescale_Trilinear (TFB_Canvas src_canvas, TFB_Canvas src_mipmap,
TFB_Canvas dest_canvas, TFB_Canvas src_mipmap, EXTENT size) TFB_Canvas dst_canvas, int scale, HOT_SPOT* src_hs, HOT_SPOT* mm_hs,
EXTENT* size, HOT_SPOT* dst_hs)
{ {
SDL_Surface *src = (SDL_Surface *)src_canvas; SDL_Surface *src = (SDL_Surface *)src_canvas;
SDL_Surface *dst = (SDL_Surface *)dest_canvas; SDL_Surface *dst = (SDL_Surface *)dst_canvas;
SDL_Surface *mipmap = (SDL_Surface *)src_mipmap; SDL_Surface *mm = (SDL_Surface *)src_mipmap;
SDL_PixelFormat *srcfmt = src->format; SDL_PixelFormat *srcfmt = src->format;
SDL_PixelFormat *mmfmt = mipmap->format; SDL_PixelFormat *mmfmt = mm->format;
SDL_PixelFormat *dstfmt = dst->format; SDL_PixelFormat *dstfmt = dst->format;
SDL_Color *srcpal = srcfmt->palette? srcfmt->palette->colors : 0; SDL_Color *srcpal = srcfmt->palette? srcfmt->palette->colors : 0;
SDL_Color *mmpal = mmfmt->palette ? mmfmt->palette->colors : 0; SDL_Color *mmpal = mmfmt->palette ? mmfmt->palette->colors : 0;
const int sbpp = srcfmt->BytesPerPixel; const int sbpp = srcfmt->BytesPerPixel;
const int mmbpp = mmfmt->BytesPerPixel; const int mmbpp = mmfmt->BytesPerPixel;
const int slen = src->pitch; const int slen = src->pitch;
const int mmlen = mipmap->pitch; const int mmlen = mm->pitch;
const int dst_has_alpha = (dstfmt->Amask != 0); const int dst_has_alpha = (dstfmt->Amask != 0);
const int transparent = (dst->flags & SDL_SRCCOLORKEY) ? const int transparent = (dst->flags & SDL_SRCCOLORKEY) ?
dstfmt->colorkey : 0; dstfmt->colorkey : 0;
const int w = size.width, h = size.height;
const int alpha_threshold = dst_has_alpha ? 0 : 127; const int alpha_threshold = dst_has_alpha ? 0 : 127;
// src v. mipmap importance factor // src v. mipmap importance factor
int ratio; int ratio = scale * 2 - GSCALE_IDENTITY;
// source masks and keys // source masks and keys
Uint32 mk0 = 0, ck0 = ~0, mk1 = 0, ck1 = ~0; Uint32 mk0 = 0, ck0 = ~0, mk1 = 0, ck1 = ~0;
// source fractional x and y positions // source fractional x and y positions
@@ -987,36 +1057,67 @@ TFB_DrawCanvas_Rescale_Trilinear (TFB_Canvas src_canvas,
int fsx0 = 0, fsy0 = 0, fsx1 = 0, fsy1 = 0; int fsx0 = 0, fsy0 = 0, fsx1 = 0, fsy1 = 0;
// source fractional x and y starting points // source fractional x and y starting points
int ssx0 = 0, ssy0 = 0, ssx1 = 0, ssy1 = 0; int ssx0 = 0, ssy0 = 0, ssx1 = 0, ssy1 = 0;
int x, y; int x, y, w, h;
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; fsx0 = (src->w << 16) / w;
fsy0 = (src->h << 16) / h; fsy0 = (src->h << 16) / h;
if (w > 1) fsx1 = (mm->w << 16) / w;
fsx1 = ((mipmap->w - 1) << 16) / (w - 1); fsy1 = (mm->h << 16) / h;
if (h > 1)
fsy1 = ((mipmap->h - 1) << 16) / (h - 1);
// give equal importance to both edges // give equal importance to both edges
ssx0 = (((src->w - 1) << 16) - fsx0 * (w - 1)) >> 1; ssx0 = (((src->w - 1) << 16) - fsx0 * (w - 1)) >> 1;
ssy0 = (((src->h - 1) << 16) - fsy0 * (h - 1)) >> 1; ssy0 = (((src->h - 1) << 16) - fsy0 * (h - 1)) >> 1;
ssx1 = (((mipmap->w - 1) << 16) - fsx1 * (w - 1)) >> 1; ssx1 = (((mm->w - 1) << 16) - fsx1 * (w - 1)) >> 1;
ssy1 = (((mipmap->h - 1) << 16) - fsy1 * (h - 1)) >> 1; ssy1 = (((mm->h - 1) << 16) - fsy1 * (h - 1)) >> 1;
}
// src v. mipmap importance factor if (w > dst->w || h > dst->h)
ratio = (w << 9) / src->w - 256;
if (ratio < 0)
ratio = 0;
if (ratio > 256)
ratio = 256;
if (size.width > dst->w || size.height > dst->h)
{ {
log_add (log_Warning, "TFB_DrawCanvas_Rescale_Trilinear: " log_add (log_Warning, "TFB_DrawCanvas_Rescale_Trilinear: "
"Tried to scale image to size %d %d when dest_canvas" "Tried to scale image to size %d %d when dest_canvas"
" has only dimensions of %d %d! Failing.", " has only dimensions of %d %d! Failing.",
size.width, size.height, dst->w, dst->h); w, h, dst->w, dst->h);
return; return;
} }
@@ -1048,7 +1149,7 @@ TFB_DrawCanvas_Rescale_Trilinear (TFB_Canvas src_canvas,
mk1 = mmfmt->Amask; mk1 = mmfmt->Amask;
ck1 = 0; ck1 = 0;
} }
else if (mipmap->flags & SDL_SRCCOLORKEY) else if (mm->flags & SDL_SRCCOLORKEY)
{ // colorkey transparency { // colorkey transparency
mk1 = ~mmfmt->Amask; mk1 = ~mmfmt->Amask;
ck1 = mmfmt->colorkey & mk1; ck1 = mmfmt->colorkey & mk1;
@@ -1056,15 +1157,17 @@ TFB_DrawCanvas_Rescale_Trilinear (TFB_Canvas src_canvas,
SDL_LockSurface(src); SDL_LockSurface(src);
SDL_LockSurface(dst); SDL_LockSurface(dst);
SDL_LockSurface(mipmap); SDL_LockSurface(mm);
for (y = 0, sy0 = ssy0, sy1 = ssy1; for (y = 0, sy0 = ssy0, sy1 = ssy1;
y < h; y < h;
++y, sy0 += fsy0, sy1 += fsy1) ++y, sy0 += fsy0, sy1 += fsy1)
{ {
Uint32 *dst_p = (Uint32 *) ((Uint8*)dst->pixels + y * dst->pitch); Uint32 *dst_p = (Uint32 *) ((Uint8*)dst->pixels + y * dst->pitch);
Uint8 *src_a0 = (Uint8*)src->pixels + (sy0 >> 16) * slen; const int py0 = (sy0 >> 16);
Uint8 *src_a1 = (Uint8*)mipmap->pixels + (sy1 >> 16) * mmlen; 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 // retrieve the fractional portions of y
const Uint8 v0 = (sy0 >> 8) & 0xff; const Uint8 v0 = (sy0 >> 8) & 0xff;
const Uint8 v1 = (sy1 >> 8) & 0xff; const Uint8 v1 = (sy1 >> 8) & 0xff;
@@ -1074,8 +1177,10 @@ TFB_DrawCanvas_Rescale_Trilinear (TFB_Canvas src_canvas,
x < w; x < w;
++x, ++dst_p, sx0 += fsx0, sx1 += fsx1) ++x, ++dst_p, sx0 += fsx0, sx1 += fsx1)
{ {
Uint8 *src_p0 = src_a0 + (sx0 >> 16) * sbpp; const int px0 = (sx0 >> 16);
Uint8 *src_p1 = src_a1 + (sx1 >> 16) * mmbpp; const int px1 = (sx1 >> 16);
Uint8 *src_p0 = src_a0 + px0 * sbpp;
Uint8 *src_p1 = src_a1 + px1 * mmbpp;
// retrieve the fractional portions of x // retrieve the fractional portions of x
const Uint8 u0 = (sx0 >> 8) & 0xff; const Uint8 u0 = (sx0 >> 8) & 0xff;
const Uint8 u1 = (sx1 >> 8) & 0xff; const Uint8 u1 = (sx1 >> 8) & 0xff;
@@ -1092,81 +1197,58 @@ TFB_DrawCanvas_Rescale_Trilinear (TFB_Canvas src_canvas,
w0[1] = btable[u0][255 - v0]; w0[1] = btable[u0][255 - v0];
w0[2] = btable[255 - u0][v0]; w0[2] = btable[255 - u0][v0];
w0[3] = btable[u0][v0]; w0[3] = btable[u0][v0];
w1[0] = btable[255 - u1][255 - v1]; w1[0] = btable[255 - u1][255 - v1];
w1[1] = btable[u1][255 - v1]; w1[1] = btable[u1][255 - v1];
w1[2] = btable[255 - u1][v1]; w1[2] = btable[255 - u1][v1];
w1[3] = btable[u1][v1]; w1[3] = btable[u1][v1];
// collect interesting pixels from src image // Collect interesting pixels from src image
p0[0].value = tri_get_pixel (src_p0, srcfmt, srcpal, mk0, ck0); // 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;
if ((sx0 >> 16) <= src->w - 2) p0[0].value = scale_read_pixel (src_p, srcfmt,
{ srcpal, mk0, ck0);
p0[1].value = tri_get_pixel (src_p0 + sbpp, p0[1].value = scale_read_pixel (src_p + sbpp, srcfmt,
srcfmt, srcpal, mk0, ck0); srcpal, mk0, ck0);
p0[2].value = scale_read_pixel (src_p + slen, srcfmt,
if ((sy0 >> 16) <= src->h - 2) srcpal, mk0, ck0);
{ p0[3].value = scale_read_pixel (src_p + sbpp + slen, srcfmt,
p0[2].value = tri_get_pixel (src_p0 + slen, srcpal, mk0, ck0);
srcfmt, srcpal, mk0, ck0);
p0[3].value = tri_get_pixel (src_p0 + slen + sbpp,
srcfmt, srcpal, mk0, ck0);
} }
else else
{ {
p0[2].value = p0[0].value; p0[0].value = scale_get_pixel (src, mk0, ck0, px0, py0);
p0[3].value = p0[1].value; 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,
else px0 + 1, py0 + 1);
{
p0[1].value = p0[0].value;
if ((sy0 >> 16) <= src->h - 2)
{
p0[2].value = tri_get_pixel (src_p0 + slen,
srcfmt, srcpal, mk0, ck0);
}
else
{
p0[2].value = p0[0].value;
}
p0[3].value = p0[2].value;
} }
// collect interesting pixels from mipmap image // Collect interesting pixels from mipmap image
p1[0].value = tri_get_pixel (src_p1, mmfmt, mmpal, mk1, ck1); if (px1 >= 0 && px1 + 1 < mm->w && py1 >= 0 && py1 + 1 < mm->h)
{
Uint8 *mm_p = src_a1 + px1 * mmbpp;
if ((sx1 >> 16) <= mipmap->w - 2) p1[0].value = scale_read_pixel (mm_p, mmfmt,
{ mmpal, mk1, ck1);
p1[1].value = tri_get_pixel (src_p1 + mmbpp, p1[1].value = scale_read_pixel (mm_p + mmbpp, mmfmt,
mmfmt, mmpal, mk1, ck1); mmpal, mk1, ck1);
p1[2].value = scale_read_pixel (mm_p + mmlen, mmfmt,
if ((sy1 >> 16) <= mipmap->h - 2) mmpal, mk1, ck1);
{ p1[3].value = scale_read_pixel (mm_p + mmbpp + mmlen, mmfmt,
p1[2].value = tri_get_pixel (src_p1 + mmlen, mmpal, mk1, ck1);
mmfmt, mmpal, mk1, ck1);
p1[3].value = tri_get_pixel (src_p1 + mmlen + mmbpp,
mmfmt, mmpal, mk1, ck1);
} }
else else
{ {
p1[2].value = p1[0].value; p1[0].value = scale_get_pixel (mm, mk1, ck1, px1, py1);
p1[3].value = p1[1].value; 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,
else px1 + 1, py1 + 1);
{
p1[1].value = p1[0].value;
if ((sy1 >> 16) <= mipmap->h - 2)
{
p1[2].value = tri_get_pixel (src_p1 + mmlen,
mmfmt, mmpal, mk1, ck1);
}
else
{
p1[2].value = p1[0].value;
}
p1[3].value = p1[2].value;
} }
p0[4].c.a = dot_product_8_4 (p0, 3, w0); p0[4].c.a = dot_product_8_4 (p0, 3, w0);
@@ -1192,6 +1274,9 @@ TFB_DrawCanvas_Rescale_Trilinear (TFB_Canvas src_canvas,
p0[4].c.g = blend_ratio_2 (p0[4].c.g, p1[4].c.g, 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); 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 = *dst_p =
(p0[4].c.r << dstfmt->Rshift) | (p0[4].c.r << dstfmt->Rshift) |
(p0[4].c.g << dstfmt->Gshift) | (p0[4].c.g << dstfmt->Gshift) |
@@ -1251,7 +1336,196 @@ TFB_DrawCanvas_Rescale_Trilinear (TFB_Canvas src_canvas,
} }
} }
SDL_UnlockSurface(mipmap); 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 = (SDL_Surface *)src_canvas;
SDL_Surface *dst = (SDL_Surface *)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);
const int transparent = (dst->flags & SDL_SRCCOLORKEY) ?
dstfmt->colorkey : 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;
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 (src->flags & SDL_SRCCOLORKEY)
{ // colorkey transparency
mk = ~srcfmt->Amask;
ck = srcfmt->colorkey & 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(dst);
SDL_UnlockSurface(src); SDL_UnlockSurface(src);
} }
+17 -14
View File
@@ -292,6 +292,7 @@ TFB_DrawImage_New (TFB_Canvas canvas)
img->MipmapHs = NullHs; img->MipmapHs = NullHs;
img->last_scale_hs = NullHs; img->last_scale_hs = NullHs;
img->last_scale_type = -1; img->last_scale_type = -1;
img->last_scale = 0;
TFB_DrawCanvas_GetExtent (canvas, &img->extent); TFB_DrawCanvas_GetExtent (canvas, &img->extent);
img->Palette = TFB_DrawCanvas_ExtractPalette (canvas); img->Palette = TFB_DrawCanvas_ExtractPalette (canvas);
@@ -322,6 +323,7 @@ TFB_DrawImage_CreateForScreen (int w, int h, BOOLEAN withalpha)
img->MipmapHs = NullHs; img->MipmapHs = NullHs;
img->last_scale_hs = NullHs; img->last_scale_hs = NullHs;
img->last_scale_type = -1; img->last_scale_type = -1;
img->last_scale = 0;
img->Palette = NULL; img->Palette = NULL;
img->extent.width = w; img->extent.width = w;
img->extent.height = h; img->extent.height = h;
@@ -389,28 +391,29 @@ TFB_DrawImage_Delete (TFB_Image *image)
void void
TFB_DrawImage_FixScaling (TFB_Image *image, int target, int type) TFB_DrawImage_FixScaling (TFB_Image *image, int target, int type)
{ {
EXTENT old = image->extent; if (image->dirty || !image->ScaledImg ||
HOT_SPOT oldhs = image->last_scale_hs; target != image->last_scale ||
TFB_DrawCanvas_GetScaledExtent (image->NormalImg, image->NormalHs, type != image->last_scale_type)
image->MipmapImg, image->MipmapHs, target,
&image->extent, &image->last_scale_hs);
if ((old.width != image->extent.width) ||
(old.height != image->extent.height) || image->dirty ||
!image->ScaledImg || type != image->last_scale_type ||
(oldhs.x != image->last_scale_hs.x) ||
(oldhs.y != image->last_scale_hs.y))
{ {
image->dirty = FALSE; image->dirty = FALSE;
image->ScaledImg = TFB_DrawCanvas_New_ScaleTarget (image->NormalImg, image->ScaledImg = TFB_DrawCanvas_New_ScaleTarget (image->NormalImg,
image->ScaledImg, type, image->last_scale_type); image->ScaledImg, type, image->last_scale_type);
image->last_scale_type = type;
if (type == TFB_SCALE_NEAREST) if (type == TFB_SCALE_NEAREST)
TFB_DrawCanvas_Rescale_Nearest (image->NormalImg, TFB_DrawCanvas_Rescale_Nearest (image->NormalImg,
image->ScaledImg, image->extent); 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 else
TFB_DrawCanvas_Rescale_Trilinear (image->NormalImg, TFB_DrawCanvas_Rescale_Trilinear (image->NormalImg,
image->ScaledImg, image->MipmapImg, image->extent); image->MipmapImg, image->ScaledImg, target,
&image->NormalHs, &image->MipmapHs,
&image->extent, &image->last_scale_hs);
image->last_scale_type = type;
image->last_scale = target;
} }
} }
+12 -11
View File
@@ -32,12 +32,6 @@ typedef enum {
TFB_GFX_NUMSCREENS TFB_GFX_NUMSCREENS
} SCREEN; } SCREEN;
typedef enum {
TFB_SCALE_STEP, /* not really a scaler */
TFB_SCALE_NEAREST,
TFB_SCALE_TRILINEAR
} SCALE;
typedef struct tfb_palette typedef struct tfb_palette
{ {
UBYTE r; UBYTE r;
@@ -61,6 +55,7 @@ typedef struct tfb_image
HOT_SPOT NormalHs; HOT_SPOT NormalHs;
HOT_SPOT MipmapHs; HOT_SPOT MipmapHs;
HOT_SPOT last_scale_hs; HOT_SPOT last_scale_hs;
int last_scale;
int last_scale_type; int last_scale_type;
TFB_Palette last_fill; TFB_Palette last_fill;
EXTENT extent; EXTENT extent;
@@ -125,11 +120,16 @@ TFB_Canvas TFB_DrawCanvas_New_ScaleTarget (TFB_Canvas canvas, TFB_Canvas oldcanv
TFB_Canvas TFB_DrawCanvas_New_RotationTarget (TFB_Canvas src, int angle); TFB_Canvas TFB_DrawCanvas_New_RotationTarget (TFB_Canvas src, int angle);
TFB_Canvas TFB_DrawCanvas_ToScreenFormat (TFB_Canvas canvas); TFB_Canvas TFB_DrawCanvas_ToScreenFormat (TFB_Canvas canvas);
BOOLEAN TFB_DrawCanvas_IsPaletted (TFB_Canvas canvas); BOOLEAN TFB_DrawCanvas_IsPaletted (TFB_Canvas canvas);
void TFB_DrawCanvas_Rescale_Nearest (TFB_Canvas src, TFB_Canvas dst, EXTENT size); void TFB_DrawCanvas_Rescale_Nearest (TFB_Canvas src, TFB_Canvas dst,
void TFB_DrawCanvas_Rescale_Trilinear (TFB_Canvas src, TFB_Canvas dst, TFB_Canvas mipmap, EXTENT size); int scale, HOT_SPOT* src_hs, EXTENT* size, HOT_SPOT* dst_hs);
void TFB_DrawCanvas_GetScaledExtent (TFB_Canvas src_canvas, HOT_SPOT src_hs, void TFB_DrawCanvas_Rescale_Bilinear (TFB_Canvas src, TFB_Canvas dst,
TFB_Canvas src_mipmap, HOT_SPOT mm_hs, int scale, HOT_SPOT* src_hs, EXTENT* size, HOT_SPOT* dst_hs);
int scale, EXTENT *size, HOT_SPOT *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_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_GetRotatedExtent (TFB_Canvas src, int angle, EXTENT *size);
void TFB_DrawCanvas_GetExtent (TFB_Canvas canvas, EXTENT *size); void TFB_DrawCanvas_GetExtent (TFB_Canvas canvas, EXTENT *size);
@@ -148,6 +148,7 @@ int TFB_DrawCanvas_GetTransparentIndex (TFB_Canvas canvas);
void TFB_DrawCanvas_SetTransparentIndex (TFB_Canvas canvas, int i, BOOLEAN rleaccel); void TFB_DrawCanvas_SetTransparentIndex (TFB_Canvas canvas, int i, BOOLEAN rleaccel);
BOOLEAN TFB_DrawCanvas_GetTransparentColor (TFB_Canvas canvas, int *r, int *g, int *b); BOOLEAN TFB_DrawCanvas_GetTransparentColor (TFB_Canvas canvas, int *r, int *g, int *b);
void TFB_DrawCanvas_SetTransparentColor (TFB_Canvas canvas, int r, int g, int b, BOOLEAN rleaccel); void TFB_DrawCanvas_SetTransparentColor (TFB_Canvas canvas, int r, int g, int b, BOOLEAN rleaccel);
void TFB_DrawCanvas_CopyTransparencyInfo (TFB_Canvas src, TFB_Canvas dst);
void TFB_DrawCanvas_Initialize (void); void TFB_DrawCanvas_Initialize (void);
void TFB_DrawCanvas_GetScreenFormat (TFB_PixelFormat *fmt); void TFB_DrawCanvas_GetScreenFormat (TFB_PixelFormat *fmt);
void* TFB_DrawCanvas_GetLine (TFB_Canvas canvas, int line); void* TFB_DrawCanvas_GetLine (TFB_Canvas canvas, int line);
+5 -3
View File
@@ -54,7 +54,8 @@ RotatePlanet (int x, int dx, int dy, COUNT scale_amt, UBYTE zoom_from,
{ {
STAMP s; STAMP s;
FRAME pFrame[2]; FRAME pFrame[2];
COUNT i, num_frames, old_scale; COUNT i, num_frames;
int old_scale, old_mode;
RECT *rp = NULL; RECT *rp = NULL;
CONTEXT OldContext; CONTEXT OldContext;
PLANET_ORBIT *Orbit = &pSolarSysState->Orbit; PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
@@ -97,14 +98,15 @@ RotatePlanet (int x, int dx, int dy, COUNT scale_amt, UBYTE zoom_from,
RepairBackRect (rp); RepairBackRect (rp);
s.origin.x = dx; s.origin.x = dx;
s.origin.y = dy; s.origin.y = dy;
old_scale = GetGraphicScale (); old_mode = SetGraphicScaleMode (TFB_SCALE_BILINEAR);
SetGraphicScale (scale_amt); old_scale = SetGraphicScale (scale_amt);
for (i = 0; i < num_frames; i++) for (i = 0; i < num_frames; i++)
{ {
s.frame = pFrame[i]; s.frame = pFrame[i];
DrawStamp (&s); DrawStamp (&s);
} }
SetGraphicScale (old_scale); SetGraphicScale (old_scale);
SetGraphicScaleMode (old_mode);
UnbatchGraphics (); UnbatchGraphics ();
SetContext (OldContext); SetContext (OldContext);
} }
+2 -2
View File
@@ -1077,8 +1077,8 @@ GetGlobalOptions (GLOBALOPTS *opts)
opts->shield = (optWhichShield == OPT_3DO) ? OPTVAL_3DO : OPTVAL_PC; opts->shield = (optWhichShield == OPT_3DO) ? OPTVAL_3DO : OPTVAL_PC;
opts->fps = (GfxFlags & TFB_GFXFLAGS_SHOWFPS) ? opts->fps = (GfxFlags & TFB_GFXFLAGS_SHOWFPS) ?
OPTVAL_ENABLED : OPTVAL_DISABLED; OPTVAL_ENABLED : OPTVAL_DISABLED;
opts->meleezoom = (optMeleeScale == TFB_SCALE_TRILINEAR) ? opts->meleezoom = (optMeleeScale == TFB_SCALE_STEP) ?
OPTVAL_3DO : OPTVAL_PC; OPTVAL_PC : OPTVAL_3DO;
opts->stereo = optStereoSFX ? OPTVAL_ENABLED : OPTVAL_DISABLED; opts->stereo = optStereoSFX ? OPTVAL_ENABLED : OPTVAL_DISABLED;
/* These values are read in, but won't change during a run. */ /* These values are read in, but won't change during a run. */
opts->music = (optWhichMusic == OPT_3DO) ? OPTVAL_3DO : OPTVAL_PC; opts->music = (optWhichMusic == OPT_3DO) ? OPTVAL_3DO : OPTVAL_PC;
+2 -1
View File
@@ -396,7 +396,6 @@ main (int argc, char *argv[])
return optionsResult; return optionsResult;
} }
/* TODO: Once threading is gone, these become local variables /* TODO: Once threading is gone, these become local variables
again. In the meantime, they must be global so that again. In the meantime, they must be global so that
initAudio (in StarCon2Main) can see them. initAudio needed initAudio (in StarCon2Main) can see them. initAudio needed
@@ -660,6 +659,8 @@ parseOptions (int argc, char *argv[], struct options_struct *options)
options->meleeScale = TFB_SCALE_TRILINEAR; options->meleeScale = TFB_SCALE_TRILINEAR;
else if (!strcmp (optarg, "step") || !strcmp (optarg, "pc")) else if (!strcmp (optarg, "step") || !strcmp (optarg, "pc"))
options->meleeScale = TFB_SCALE_STEP; options->meleeScale = TFB_SCALE_STEP;
else if (!strcmp (optarg, "bilinear"))
options->meleeScale = TFB_SCALE_BILINEAR;
else else
{ {
InvalidArgument (optarg, "--meleezoom or -b"); InvalidArgument (optarg, "--meleezoom or -b");