From 8669bbaf99b2fe17de8fba6586077e6370b4cb22 Mon Sep 17 00:00:00 2001 From: avolkov Date: Wed, 1 Aug 2007 03:25:08 +0000 Subject: [PATCH] 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 --- sc2/ChangeLog | 3 + sc2/src/sc2code/battle.c | 4 + sc2/src/sc2code/intro.c | 12 +- sc2/src/sc2code/libs/graphics/gfx_common.c | 31 + sc2/src/sc2code/libs/graphics/gfx_common.h | 15 +- sc2/src/sc2code/libs/graphics/sdl/3do_blt.c | 14 - .../sc2code/libs/graphics/sdl/3do_getbody.c | 5 +- sc2/src/sc2code/libs/graphics/sdl/canvas.c | 742 ++++++++++++------ sc2/src/sc2code/libs/graphics/tfb_draw.c | 31 +- sc2/src/sc2code/libs/graphics/tfb_draw.h | 23 +- sc2/src/sc2code/planets/pl_stuff.c | 8 +- sc2/src/sc2code/setupmenu.c | 4 +- sc2/src/starcon2.c | 3 +- 13 files changed, 604 insertions(+), 291 deletions(-) diff --git a/sc2/ChangeLog b/sc2/ChangeLog index 23be6eaaf..db7711c6f 100644 --- a/sc2/ChangeLog +++ b/sc2/ChangeLog @@ -1,4 +1,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 custom resolutions in OpenGL mode - Mika - Add /var/tmp as possible location for temporary files. Don't try diff --git a/sc2/src/sc2code/battle.c b/sc2/src/sc2code/battle.c index bad04608c..d8cc1ac2e 100644 --- a/sc2/src/sc2code/battle.c +++ b/sc2/src/sc2code/battle.c @@ -19,6 +19,7 @@ #include "battle.h" #include "controls.h" +#include "options.h" #include "init.h" #include "intel.h" #ifdef NETPLAY @@ -422,6 +423,9 @@ Battle (void) battle_counter[0] = CountLinks (&race_q[0]); battle_counter[1] = CountLinks (&race_q[1]); + if (optMeleeScale != TFB_SCALE_STEP) + SetGraphicScaleMode (optMeleeScale); + setupBattleInputOrder (); #ifdef NETPLAY initBattleInputBuffers (); diff --git a/sc2/src/sc2code/intro.c b/sc2/src/sc2code/intro.c index 4a0e88476..590521acb 100644 --- a/sc2/src/sc2code/intro.c +++ b/sc2/src/sc2code/intro.c @@ -585,20 +585,23 @@ DoPresentation (void *pIS) int cargs; int draw_what; int index, x, y, scale, angle; + int scale_mode; char ImgName[16]; - int OldScale; + int old_scale, old_mode; STAMP s; if (1 == sscanf (pStr, "%15s", ImgName) && strcmp (strupr (ImgName), "SIS") == 0) { draw_what = PRES_DRAW_SIS; + scale_mode = TFB_SCALE_NEAREST; cargs = sscanf (pStr, "%*s %d %d %d %d", &x, &y, &scale, &angle) + 1; } else { draw_what = PRES_DRAW_INDEX; + scale_mode = TFB_SCALE_BILINEAR; cargs = sscanf (pStr, "%d %d %d %d %d", &index, &x, &y, &scale, &angle); } @@ -646,11 +649,12 @@ DoPresentation (void *pIS) s.origin.y = y; if (!pPIS->Batched) LockMutex (GraphicsLock); - OldScale = GetGraphicScale (); - SetGraphicScale (scale); + old_mode = SetGraphicScaleMode (scale_mode); + old_scale = SetGraphicScale (scale); SetContextClipping (TRUE); DrawStamp (&s); - SetGraphicScale (OldScale); + SetGraphicScale (old_scale); + SetGraphicScaleMode (old_mode); if (!pPIS->Batched) UnlockMutex (GraphicsLock); } diff --git a/sc2/src/sc2code/libs/graphics/gfx_common.c b/sc2/src/sc2code/libs/graphics/gfx_common.c index 702043c2e..a4a0a678b 100644 --- a/sc2/src/sc2code/libs/graphics/gfx_common.c +++ b/sc2/src/sc2code/libs/graphics/gfx_common.c @@ -34,6 +34,9 @@ int ScreenColorDepth; int GraphicsDriver; int TFB_DEBUG_HALT = 0; +static int gscale = GSCALE_IDENTITY; +static int gscale_mode = TFB_SCALE_NEAREST; + // Status: Ignored (only used in fmv.c) void 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); } +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; +} diff --git a/sc2/src/sc2code/libs/graphics/gfx_common.h b/sc2/src/sc2code/libs/graphics/gfx_common.h index f2d14b89f..519f71496 100644 --- a/sc2/src/sc2code/libs/graphics/gfx_common.h +++ b/sc2/src/sc2code/libs/graphics/gfx_common.h @@ -67,15 +67,22 @@ int TFB_InitGraphics (int driver, int flags, int width, int height); void TFB_UninitGraphics (void); void TFB_ProcessEvents (void); -// 3DO Graphics Stuff - #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); void SetGraphicGrabOther (int grab_other); -void SetGraphicScale (int scale); -int GetGraphicScale (void); +int SetGraphicScale (int scale); +int GetGraphicScale (void); +int SetGraphicScaleMode (int mode /* enum SCALE */); +int GetGraphicScaleMode (void); void SetGraphicUseOtherExtra (int other); void SetTransitionSource (RECT *pRect); void ScreenTransition (int transition, RECT *pRect); diff --git a/sc2/src/sc2code/libs/graphics/sdl/3do_blt.c b/sc2/src/sc2code/libs/graphics/sdl/3do_blt.c index 0557151b5..6f7c09634 100644 --- a/sc2/src/sc2code/libs/graphics/sdl/3do_blt.c +++ b/sc2/src/sc2code/libs/graphics/sdl/3do_blt.c @@ -22,20 +22,6 @@ #include "graphics/tfb_draw.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 read_screen (RECT *lpRect, FRAME DstFramePtr) { diff --git a/sc2/src/sc2code/libs/graphics/sdl/3do_getbody.c b/sc2/src/sc2code/libs/graphics/sdl/3do_getbody.c index 3c7fba4c2..b8d33f1ac 100644 --- a/sc2/src/sc2code/libs/graphics/sdl/3do_getbody.c +++ b/sc2/src/sc2code/libs/graphics/sdl/3do_getbody.c @@ -191,7 +191,6 @@ FRAME stretch_frame (FRAME FramePtr, int neww, int newh, int destroy) CREATE_FLAGS flags; TFB_Image *tfbImg; TFB_Canvas src, dst; - EXTENT ext; flags = GetFrameParentDrawable (FramePtr)->Flags; NewFrame = CaptureDrawable ( @@ -200,9 +199,7 @@ FRAME stretch_frame (FRAME FramePtr, int neww, int newh, int destroy) LockMutex (tfbImg->mutex); src = tfbImg->NormalImg; dst = NewFrame->image->NormalImg; - ext.width = neww; - ext.height = newh; - TFB_DrawCanvas_Rescale_Nearest (src, dst, ext); + TFB_DrawCanvas_Rescale_Nearest (src, dst, -1, NULL, NULL, NULL); UnlockMutex (tfbImg->mutex); if (destroy) DestroyDrawable (ReleaseDrawable (FramePtr)); diff --git a/sc2/src/sc2code/libs/graphics/sdl/canvas.c b/sc2/src/sc2code/libs/graphics/sdl/canvas.c index 16994ccab..c78182d33 100644 --- a/sc2/src/sc2code/libs/graphics/sdl/canvas.c +++ b/sc2/src/sc2code/libs/graphics/sdl/canvas.c @@ -105,18 +105,18 @@ TFB_DrawCanvas_Image (TFB_Image *img, int x, int y, int scale, if (scale != 0 && scale != GSCALE_IDENTITY) { - int type; - if (optMeleeScale == TFB_SCALE_TRILINEAR && img->MipmapImg) + int type = GetGraphicScaleMode (); + + if (type == TFB_SCALE_TRILINEAR && img->MipmapImg) { - type = TFB_SCALE_TRILINEAR; // only set the new palette if it changed if (((SDL_Surface *)img->MipmapImg)->format->palette && cmap && img->colormap_version != cmap->version) 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); @@ -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_Surface *surf; int i; - SDL_Color pal[256]; - EXTENT prevextent = img->extent; + bool force_fill = false; 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) { - 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; srcRect.x = 0; srcRect.y = 0; @@ -270,6 +278,14 @@ TFB_DrawCanvas_FilledImage (TFB_Image *img, int x, int y, int scale, int r, int } 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; @@ -279,6 +295,14 @@ TFB_DrawCanvas_FilledImage (TFB_Image *img, int x, int y, int scale, int r, int if (surf->format->palette) { // 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++) { 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 SDL_Surface *src = img->NormalImg; 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 if (!newfill) { newfill = SDL_CreateRGBSurface (SDL_SWSURFACE, - src->w, src->h, + surf->w, surf->h, surf->format->BitsPerPixel, surf->format->Rmask, surf->format->Gmask, surf->format->Bmask, surf->format->Amask); - force = TRUE; + force_fill = true; } - if (force || - prevextent.height != img->extent.height || - prevextent.width != img->extent.width || + if (force_fill || img->last_fill.r != r || img->last_fill.g != g || img->last_fill.b != b) { // 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); // important to keep alpha=0 in fillcolor // -- we process alpha ourselves + + // cache filled image if possible + img->last_fill.r = r; + img->last_fill.g = g; + img->last_fill.b = b; } - // cache filled image if possible - img->last_fill.r = r; - img->last_fill.g = g; - img->last_fill.b = b; img->FilledImg = newfill; - surf = newfill; } @@ -486,48 +513,41 @@ TFB_DrawCanvas_New_ScaleTarget (TFB_Canvas canvas, TFB_Canvas oldcanvas, int typ { SDL_Surface *src = (SDL_Surface *)canvas; 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 (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) { - if (old && type != last_type) - { - TFB_DrawCanvas_Delete (old); - old = NULL; - } - if (!old) - { - newsurf = SDL_CreateRGBSurface (SDL_SWSURFACE, src->w, - src->h, - src->format->BitsPerPixel, - src->format->Rmask, - src->format->Gmask, - src->format->Bmask, - src->format->Amask); - if (src->format->palette) - 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); - } - } + 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 { - if (old && type != last_type) - { - TFB_DrawCanvas_Delete (old); - old = NULL; - } - if (!old) - { - if (SDL_Screen->format->BitsPerPixel == 32) - newsurf = TFB_DrawCanvas_New_ForScreen (src->w, src->h, TRUE); - else - newsurf = TFB_DrawCanvas_New_TrueColor (src->w, src->h, TRUE); - } + // 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; @@ -556,14 +576,7 @@ TFB_DrawCanvas_New_RotationTarget (TFB_Canvas src_canvas, int angle) SDL_GetError()); exit (EXIT_FAILURE); } - if (src->format->palette) - 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); - } + TFB_DrawCanvas_CopyTransparencyInfo (src, 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 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 -TFB_DrawCanvas_GetScaledExtent (TFB_Canvas src_canvas, HOT_SPOT src_hs, - TFB_Canvas src_mipmap, HOT_SPOT mm_hs, - int scale, EXTENT *size, HOT_SPOT *hs) +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 = (SDL_Surface *)src_canvas; + sint32 x, y, w, h; + int frac; if (!src_mipmap) { - size->width = src->w * scale / GSCALE_IDENTITY; - size->height = src->h * scale / GSCALE_IDENTITY; - - hs->x = src_hs.x * scale / GSCALE_IDENTITY; - hs->y = src_hs.y * scale / GSCALE_IDENTITY; + 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 = (SDL_Surface *)src_mipmap; + int ratio = scale * 2 - GSCALE_IDENTITY; - float ratio = (scale / (float)GSCALE_IDENTITY) * 2.0f - 1.0f; - if (ratio < 0.0f) - ratio = 0.0f; - else if (ratio > 1.0f) - ratio = 1.0f; + assert (scale >= GSCALE_IDENTITY / 2); - size->width = (int)((src->w - mipmap->w) * ratio + mipmap->w + 0.5); - size->height = (int)((src->h - mipmap->h) * ratio + mipmap->h + 0.5); + w = mipmap->w * GSCALE_IDENTITY + (src->w - mipmap->w) * ratio; + 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); - hs->y = (int)((src_hs.y - mm_hs.y) * ratio + mm_hs.y + 0.5); + // 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) @@ -756,57 +817,48 @@ TFB_DrawCanvas_GetExtent (TFB_Canvas canvas, EXTENT *size) } 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 *dst = (SDL_Surface *)dest_canvas; - int x, y, sx, sy, *csax, *csay, csx, csy; - int saspace[NNS_MAX_DIMS]; - int *sax, *say; + SDL_Surface *dst = (SDL_Surface *)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 (size.width + size.height > NNS_MAX_DIMS) + if (scale > 0) { - log_add (log_Warning, "TFB_DrawCanvas_Scale: Tried to zoom" - " an image to unreasonable size! Failing."); - return; + TFB_DrawCanvas_GetScaledExtent (src, src_hs, NULL, NULL, scale, + TFB_SCALE_NEAREST, size, dst_hs); + + 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" " dimensions of %d %d! Failing.", - size.width, size.height, dst->w, dst->h); + w, h, dst->w, dst->h); return; } - sx = sy = 0; - if (size.width > 1) - sx = ((src->w - 1) << 16) / (size.width - 1); - if (size.height > 1) - sy = ((src->h - 1) << 16) / (size.height - 1); - - sax = saspace; - say = saspace + size.width; - /* - * 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; - } + 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); @@ -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) { Uint8 *sp, *csp, *dp, *cdp; + int sx, sy; // source fractional x and y positions sp = csp = (Uint8 *) src->pixels; dp = cdp = (Uint8 *) dst->pixels; - csay = say; - for (y = 0; y < size.height; ++y) { - csp += (*csay) * src->pitch; + for (y = 0, sy = ssy; y < h; ++y) + { + csp += (sy >> 16) * src->pitch; sp = csp; dp = cdp; - csax = sax; - for (x = 0; x < size.width; ++x) { - sp += *csax; + for (x = 0, sx = ssx; x < w; ++x) + { + sp += (sx >> 16); *dp = *sp; - ++csax; + sx &= 0xffff; + sx += fsx; ++dp; } - ++csay; + 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; @@ -845,19 +901,21 @@ TFB_DrawCanvas_Rescale_Nearest (TFB_Canvas src_canvas, TFB_Canvas dest_canvas, E sp = csp = (Uint32 *) src->pixels; dp = cdp = (Uint32 *) dst->pixels; - csay = say; - for (y = 0; y < size.height; ++y) { - csp += (*csay) * sgap; + for (y = 0, sy = ssy; y < h; ++y) + { + csp += (sy >> 16) * sgap; sp = csp; dp = cdp; - csax = sax; - for (x = 0; x < size.width; ++x) { - sp += *csax; + for (x = 0, sx = ssx; x < w; ++x) + { + sp += (sx >> 16); *dp = *sp; - ++csax; + sx &= 0xffff; + sx += fsx; ++dp; } - ++csay; + sy &= 0xffff; + sy += fsy; cdp += dgap; } } @@ -913,7 +971,7 @@ blend_ratio_2 (Uint8 c1, Uint8 c2, int ratio) } 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) { pixel_t p; @@ -956,29 +1014,41 @@ tri_get_pixel (void* ppix, SDL_PixelFormat *fmt, SDL_Color *pal, 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 dest_canvas, TFB_Canvas src_mipmap, EXTENT size) +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 = (SDL_Surface *)src_canvas; - SDL_Surface *dst = (SDL_Surface *)dest_canvas; - SDL_Surface *mipmap = (SDL_Surface *)src_mipmap; + SDL_Surface *dst = (SDL_Surface *)dst_canvas; + SDL_Surface *mm = (SDL_Surface *)src_mipmap; SDL_PixelFormat *srcfmt = src->format; - SDL_PixelFormat *mmfmt = mipmap->format; + SDL_PixelFormat *mmfmt = mm->format; SDL_PixelFormat *dstfmt = dst->format; SDL_Color *srcpal = srcfmt->palette? srcfmt->palette->colors : 0; SDL_Color *mmpal = mmfmt->palette ? mmfmt->palette->colors : 0; const int sbpp = srcfmt->BytesPerPixel; const int mmbpp = mmfmt->BytesPerPixel; 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 transparent = (dst->flags & SDL_SRCCOLORKEY) ? dstfmt->colorkey : 0; - const int w = size.width, h = size.height; const int alpha_threshold = dst_has_alpha ? 0 : 127; // src v. mipmap importance factor - int ratio; + 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 @@ -987,36 +1057,67 @@ TFB_DrawCanvas_Rescale_Trilinear (TFB_Canvas src_canvas, 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; + int x, y, w, h; - fsx0 = (src->w << 16) / w; - fsy0 = (src->h << 16) / h; + if (scale > 0) + { + int fw, fh; - if (w > 1) - fsx1 = ((mipmap->w - 1) << 16) / (w - 1); - if (h > 1) - fsy1 = ((mipmap->h - 1) << 16) / (h - 1); + // Use (scale / GSCALE_IDENTITY) sizing factor + TFB_DrawCanvas_GetScaledExtent (src, src_hs, mm, mm_hs, scale, + TFB_SCALE_TRILINEAR, size, dst_hs); - // give equal importance to both edges - ssx0 = (((src->w - 1) << 16) - fsx0 * (w - 1)) >> 1; - ssy0 = (((src->h - 1) << 16) - fsy0 * (h - 1)) >> 1; + w = size->width; + h = size->height; - ssx1 = (((mipmap->w - 1) << 16) - fsx1 * (w - 1)) >> 1; - ssy1 = (((mipmap->h - 1) << 16) - fsy1 * (h - 1)) >> 1; + fw = mm->w * GSCALE_IDENTITY + (src->w - mm->w) * ratio; + fh = mm->h * GSCALE_IDENTITY + (src->h - mm->h) * ratio; - // src v. mipmap importance factor - ratio = (w << 9) / src->w - 256; - if (ratio < 0) - ratio = 0; - if (ratio > 256) - ratio = 256; + // 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; - if (size.width > dst->w || size.height > dst->h) + 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.", - size.width, size.height, dst->w, dst->h); + w, h, dst->w, dst->h); return; } @@ -1048,7 +1149,7 @@ TFB_DrawCanvas_Rescale_Trilinear (TFB_Canvas src_canvas, mk1 = mmfmt->Amask; ck1 = 0; } - else if (mipmap->flags & SDL_SRCCOLORKEY) + else if (mm->flags & SDL_SRCCOLORKEY) { // colorkey transparency mk1 = ~mmfmt->Amask; ck1 = mmfmt->colorkey & mk1; @@ -1056,15 +1157,17 @@ TFB_DrawCanvas_Rescale_Trilinear (TFB_Canvas src_canvas, SDL_LockSurface(src); SDL_LockSurface(dst); - SDL_LockSurface(mipmap); + 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); - Uint8 *src_a0 = (Uint8*)src->pixels + (sy0 >> 16) * slen; - Uint8 *src_a1 = (Uint8*)mipmap->pixels + (sy1 >> 16) * mmlen; + 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; @@ -1074,8 +1177,10 @@ TFB_DrawCanvas_Rescale_Trilinear (TFB_Canvas src_canvas, x < w; ++x, ++dst_p, sx0 += fsx0, sx1 += fsx1) { - Uint8 *src_p0 = src_a0 + (sx0 >> 16) * sbpp; - Uint8 *src_p1 = src_a1 + (sx1 >> 16) * mmbpp; + const int px0 = (sx0 >> 16); + 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 const Uint8 u0 = (sx0 >> 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[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 - p0[0].value = tri_get_pixel (src_p0, srcfmt, srcpal, mk0, ck0); + // 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; - if ((sx0 >> 16) <= src->w - 2) - { - p0[1].value = tri_get_pixel (src_p0 + sbpp, - srcfmt, srcpal, mk0, ck0); - - if ((sy0 >> 16) <= src->h - 2) - { - p0[2].value = tri_get_pixel (src_p0 + slen, - srcfmt, srcpal, mk0, ck0); - p0[3].value = tri_get_pixel (src_p0 + slen + sbpp, - srcfmt, srcpal, mk0, ck0); - } - else - { - p0[2].value = p0[0].value; - p0[3].value = p0[1].value; - } + 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[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; + 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 - p1[0].value = tri_get_pixel (src_p1, mmfmt, mmpal, mk1, ck1); + // 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; - if ((sx1 >> 16) <= mipmap->w - 2) - { - p1[1].value = tri_get_pixel (src_p1 + mmbpp, - mmfmt, mmpal, mk1, ck1); - - if ((sy1 >> 16) <= mipmap->h - 2) - { - p1[2].value = tri_get_pixel (src_p1 + mmlen, - mmfmt, mmpal, mk1, ck1); - p1[3].value = tri_get_pixel (src_p1 + mmlen + mmbpp, - mmfmt, mmpal, mk1, ck1); - } - else - { - p1[2].value = p1[0].value; - p1[3].value = p1[1].value; - } + p1[0].value = scale_read_pixel (mm_p, mmfmt, + mmpal, mk1, ck1); + p1[1].value = scale_read_pixel (mm_p + mmbpp, mmfmt, + mmpal, mk1, ck1); + p1[2].value = scale_read_pixel (mm_p + mmlen, mmfmt, + mmpal, mk1, ck1); + p1[3].value = scale_read_pixel (mm_p + mmbpp + mmlen, mmfmt, + mmpal, mk1, ck1); } else { - 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; + 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); @@ -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.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) | @@ -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(src); } diff --git a/sc2/src/sc2code/libs/graphics/tfb_draw.c b/sc2/src/sc2code/libs/graphics/tfb_draw.c index 2fe4f63fb..09e8842eb 100644 --- a/sc2/src/sc2code/libs/graphics/tfb_draw.c +++ b/sc2/src/sc2code/libs/graphics/tfb_draw.c @@ -292,6 +292,7 @@ TFB_DrawImage_New (TFB_Canvas canvas) img->MipmapHs = NullHs; img->last_scale_hs = NullHs; img->last_scale_type = -1; + img->last_scale = 0; TFB_DrawCanvas_GetExtent (canvas, &img->extent); img->Palette = TFB_DrawCanvas_ExtractPalette (canvas); @@ -322,6 +323,7 @@ TFB_DrawImage_CreateForScreen (int w, int h, BOOLEAN withalpha) img->MipmapHs = NullHs; img->last_scale_hs = NullHs; img->last_scale_type = -1; + img->last_scale = 0; img->Palette = NULL; img->extent.width = w; img->extent.height = h; @@ -389,28 +391,29 @@ TFB_DrawImage_Delete (TFB_Image *image) void TFB_DrawImage_FixScaling (TFB_Image *image, int target, int type) { - EXTENT old = image->extent; - HOT_SPOT oldhs = image->last_scale_hs; - TFB_DrawCanvas_GetScaledExtent (image->NormalImg, image->NormalHs, - 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)) + 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); - image->last_scale_type = type; if (type == TFB_SCALE_NEAREST) 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 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; } } diff --git a/sc2/src/sc2code/libs/graphics/tfb_draw.h b/sc2/src/sc2code/libs/graphics/tfb_draw.h index 5978be0b0..55ccd65e6 100644 --- a/sc2/src/sc2code/libs/graphics/tfb_draw.h +++ b/sc2/src/sc2code/libs/graphics/tfb_draw.h @@ -32,12 +32,6 @@ typedef enum { TFB_GFX_NUMSCREENS } SCREEN; -typedef enum { - TFB_SCALE_STEP, /* not really a scaler */ - TFB_SCALE_NEAREST, - TFB_SCALE_TRILINEAR -} SCALE; - typedef struct tfb_palette { UBYTE r; @@ -61,6 +55,7 @@ typedef struct tfb_image HOT_SPOT NormalHs; HOT_SPOT MipmapHs; HOT_SPOT last_scale_hs; + int last_scale; int last_scale_type; TFB_Palette last_fill; 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_ToScreenFormat (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_Trilinear (TFB_Canvas src, TFB_Canvas dst, TFB_Canvas mipmap, EXTENT size); -void TFB_DrawCanvas_GetScaledExtent (TFB_Canvas src_canvas, HOT_SPOT src_hs, - TFB_Canvas src_mipmap, HOT_SPOT mm_hs, - int scale, EXTENT *size, HOT_SPOT *hs); +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); @@ -148,6 +148,7 @@ 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, int *r, int *g, int *b); 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_GetScreenFormat (TFB_PixelFormat *fmt); void* TFB_DrawCanvas_GetLine (TFB_Canvas canvas, int line); diff --git a/sc2/src/sc2code/planets/pl_stuff.c b/sc2/src/sc2code/planets/pl_stuff.c index 53910e842..b1996f82b 100644 --- a/sc2/src/sc2code/planets/pl_stuff.c +++ b/sc2/src/sc2code/planets/pl_stuff.c @@ -54,7 +54,8 @@ RotatePlanet (int x, int dx, int dy, COUNT scale_amt, UBYTE zoom_from, { STAMP s; FRAME pFrame[2]; - COUNT i, num_frames, old_scale; + COUNT i, num_frames; + int old_scale, old_mode; RECT *rp = NULL; CONTEXT OldContext; PLANET_ORBIT *Orbit = &pSolarSysState->Orbit; @@ -97,14 +98,15 @@ RotatePlanet (int x, int dx, int dy, COUNT scale_amt, UBYTE zoom_from, RepairBackRect (rp); s.origin.x = dx; s.origin.y = dy; - old_scale = GetGraphicScale (); - SetGraphicScale (scale_amt); + old_mode = SetGraphicScaleMode (TFB_SCALE_BILINEAR); + old_scale = SetGraphicScale (scale_amt); for (i = 0; i < num_frames; i++) { s.frame = pFrame[i]; DrawStamp (&s); } SetGraphicScale (old_scale); + SetGraphicScaleMode (old_mode); UnbatchGraphics (); SetContext (OldContext); } diff --git a/sc2/src/sc2code/setupmenu.c b/sc2/src/sc2code/setupmenu.c index 8ead9625e..cb2054a4d 100644 --- a/sc2/src/sc2code/setupmenu.c +++ b/sc2/src/sc2code/setupmenu.c @@ -1077,8 +1077,8 @@ GetGlobalOptions (GLOBALOPTS *opts) opts->shield = (optWhichShield == OPT_3DO) ? OPTVAL_3DO : OPTVAL_PC; opts->fps = (GfxFlags & TFB_GFXFLAGS_SHOWFPS) ? OPTVAL_ENABLED : OPTVAL_DISABLED; - opts->meleezoom = (optMeleeScale == TFB_SCALE_TRILINEAR) ? - OPTVAL_3DO : OPTVAL_PC; + opts->meleezoom = (optMeleeScale == TFB_SCALE_STEP) ? + OPTVAL_PC : OPTVAL_3DO; opts->stereo = optStereoSFX ? OPTVAL_ENABLED : OPTVAL_DISABLED; /* These values are read in, but won't change during a run. */ opts->music = (optWhichMusic == OPT_3DO) ? OPTVAL_3DO : OPTVAL_PC; diff --git a/sc2/src/starcon2.c b/sc2/src/starcon2.c index 54be30334..0299f39f3 100644 --- a/sc2/src/starcon2.c +++ b/sc2/src/starcon2.c @@ -396,7 +396,6 @@ main (int argc, char *argv[]) return optionsResult; } - /* TODO: Once threading is gone, these become local variables again. In the meantime, they must be global so that 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; else if (!strcmp (optarg, "step") || !strcmp (optarg, "pc")) options->meleeScale = TFB_SCALE_STEP; + else if (!strcmp (optarg, "bilinear")) + options->meleeScale = TFB_SCALE_BILINEAR; else { InvalidArgument (optarg, "--meleezoom or -b");