kobodl/graphics/filters.c
Ville Lindholm f11b6516da
WIP: port renderer + supporting layers to SDL 3 (builds clean)
Completes phases 3-6 of the migration plan. After this commit, the
codebase compiles and links against pure SDL 3 (no compat shim). The
binary is unverified at runtime but the build is clean.

Renderer (graphics/gfxengine.{cpp,h}):
- Replace SDL_SetVideoMode + SDL_Flip + page-flip machinery with
  SDL_CreateWindow + SDL_CreateRenderer + a streaming SDL_Texture.
  The CPU-side back buffer (softbuf) is now always ARGB8888; legacy
  HW/double-buffer/page flags are dead.
- flip() does a single full-frame SDL_UpdateTexture + SDL_RenderClear
  + SDL_RenderTexture + SDL_RenderPresent each frame. The dirtyrect
  iteration is retained only to drive widget phys_refresh() callbacks.
- SDL_SetRenderLogicalPresentation gives us letterbox scaling for free.
- SDL_SetRenderVSync replaces glSDL_VSync.
- title()/cursor() ported to SDL 3 window/cursor APIs.

Surface API (graphics/sprite.c, filters.c, window.cpp, sofont.cpp,
region.c):
- SDL_FreeSurface → SDL_DestroySurface
- SDL_FillRect → SDL_FillSurfaceRect
- SDL_SetClipRect → SDL_SetSurfaceClipRect
- SDL_SetColorKey(s, flags, key) → SDL_SetSurfaceColorKey(s, bool, key)
- SDL_SetAlpha(s, flags, a) → SDL_SetSurfaceAlphaMod + BlendMode
- SDL_SetColors → SDL_SetSurfacePalette
- SDL_CreateRGBSurface(...) → SDL_CreateSurface(w, h, format_enum)
- SDL_DisplayFormat[Alpha] → SDL_ConvertSurface(s, ARGB8888)
- SDL_MapRGB(surf->format, ...) → SDL_MapSurfaceRGB(surf, ...)
- surface->format->Field → SDL_GetPixelFormatDetails(surf->format)->field
- surface->format->Amask test → SDL_ISPIXELFORMAT_ALPHA
- surface->clip_rect → SDL_GetSurfaceClipRect

Events (kobo.cpp):
- SDL_{KEYDOWN,KEYUP,QUIT,VIDEOEXPOSE,MOUSE*,JOY*} → SDL_EVENT_*
- SDL_ACTIVEEVENT (with gain check) → SDL_EVENT_WINDOW_FOCUS_LOST
- ev.key.keysym.sym → ev.key.key (.unicode → text input, deferred)
- SDLK_PRINT/SYSREQ → SDLK_PRINTSCREEN; lowercase SDLK_p/y/n → uppercase
- ev.motion.x/y for button events → ev.button.x/y (struct layout differs)
- KMOD_META → SDL_KMOD_GUI
- SDL_INIT_NOPARACHUTE → SDL_Init(0) (no parachute by default in SDL 3)

Keymap (gamectl.{cpp,h}):
- SDLKey → SDL_Keycode
- SDLK_KP{1..9} → SDLK_KP_{1..9}

Stubs deferred for later phases (binary will run but with degraded
behavior):
- sound/audio.c: _start_SDL_output returns -1 immediately; SDL 1.2
  audio callback model needs full rewrite onto SDL_AudioStream.
- kobo.cpp init_js/close_js: SDL 3 joystick API uses instance IDs;
  re-port deferred.
- states.cpp SDL_WM_GrabInput/SDL_EnableUNICODE: need window pointer
  exposed from gfxengine; stubbed.
- gamectl.cpp SDL_EnableKeyRepeat: removed; relies on ev.key.repeat.

Other adjustments:
- options.cpp: GFX_DRIVER_GLSDL menu paths removed; show vsync option
  always (renderer handles it).
- sound/a_wave.c: AUDIO_S8/AUDIO_S16SYS → SDL_AUDIO_S8/S16, SDL_FreeWAV
  → SDL_free.
- gfxengine.cpp: SDL_putenv("SDL_VIDEO_CENTERED=1") obsolete (handled
  by SDL_CreateWindow defaults).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-28 21:54:55 +03:00

1141 lines
25 KiB
C

/*(LGPL)
----------------------------------------------------------------------
filters.c - Filters for the sprite manager
----------------------------------------------------------------------
* Copyright (C) 2001, 2003, 2006, 2007 David Olofson
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <string.h>
#include <math.h>
#include "logger.h"
#include "sdl_compat.h"
#include <SDL3_image/SDL_image.h>
#include "sprite.h"
#include "filters.h"
/*--------------------------------------------------
RNG
--------------------------------------------------*/
static unsigned int rnd = 16576;
//Resets the noise generator
static void noise_reset(int seed)
{
rnd = 16576 + seed;
}
//Returns a pseudo random number in the range [0, 65535]
static inline int noise(void)
{
rnd *= 1566083941UL;
rnd++;
rnd &= 0xffffffffUL; /* NOP on 32 bit machines */
return (int)(rnd * (rnd >> 16) >> 16);
}
/*--------------------------------------------------
Pixel access ops for 32 bit RGBA
--------------------------------------------------*/
static pix_t getpix32_empty = {0x7f, 0x7f, 0x7f, 0x7f};
static inline pix_t getpix32(SDL_Surface *s, int x, int y)
{
pix_t *p;
if((x < 0) || (x >= s->w) || (y < 0) || (y >= s->h))
return getpix32_empty;
p = (pix_t *)((char *)s->pixels + y * s->pitch);
return p[x];
}
/* Clamping version */
static inline pix_t getpix32c(SDL_Surface *s, int x, int y)
{
pix_t *p;
if(x < 0)
x = 0;
else if(x >= s->w-1)
x = s->w-1;
if(y < 0)
y = 0;
else if(y >= s->h-1)
y = s->h-1;
p = (pix_t *)((char *)s->pixels + y * s->pitch);
return p[x];
}
#define GETPIXI(gp) \
{ \
int c0x, c0y, c1x, c1y; \
int c[4]; \
pix_t e = {0, 0, 0, 0}; \
pix_t p[4], r; \
getpix32_empty = e; \
\
/* Calculate filter core */ \
x -= 8; \
y -= 8; \
c1x = x & 0xf; \
c1y = y & 0xf; \
c0x = 16 - c1x; \
c0y = 16 - c1y; \
c[0] = c0x * c0y; \
c[1] = c1x * c0y; \
c[2] = c0x * c1y; \
c[3] = c1x * c1y; \
\
/* Grab input pixels */ \
x >>= 4; \
y >>= 4; \
p[0] = gp(s, x, y); \
p[1] = gp(s, x+1, y); \
p[2] = gp(s, x, y+1); \
p[3] = gp(s, x+1, y+1); \
\
/* Interpolate... */ \
r.r = (p[0].r*c[0] + p[1].r*c[1] + p[2].r*c[2] + p[3].r*c[3])>>8; \
r.g = (p[0].g*c[0] + p[1].g*c[1] + p[2].g*c[2] + p[3].g*c[3])>>8; \
r.b = (p[0].b*c[0] + p[1].b*c[1] + p[2].b*c[2] + p[3].b*c[3])>>8; \
r.a = (p[0].a*c[0] + p[1].a*c[1] + p[2].a*c[2] + p[3].a*c[3])>>8; \
return r; \
}
/* Interpolated; 28:4 fixed point coords. */
static inline pix_t getpix32i(SDL_Surface *s, int x, int y)
{
GETPIXI(getpix32)
}
/* Clamping version */
static inline pix_t getpix32ic(SDL_Surface *s, int x, int y)
{
GETPIXI(getpix32c)
}
static inline pix_t *pix32(SDL_Surface *s, int x, int y)
{
static pix_t dummy = {0x7f, 0x7f, 0x7f, 0x7f};
pix_t *p;
if((x < 0) || (x >= s->w) || (y < 0) || (y >= s->h))
return &dummy;
p = (pix_t *)((char *)s->pixels + y * s->pitch);
return p + x;
}
static inline void setpix32(SDL_Surface *s, int x, int y, pix_t pix)
{
pix_t *p;
if((x < 0) || (x >= s->w) || (y < 0) || (y >= s->h))
return;
p = (pix_t *)((char *)s->pixels + y * s->pitch);
p[x] = pix;
}
/*--------------------------------------------------
Non-clipping versions of everything
(No clamping here, obviously...!)
--------------------------------------------------*/
static inline pix_t getpix32_nc(SDL_Surface *s, int x, int y)
{
pix_t *p = (pix_t *)((char *)s->pixels + y * s->pitch);
#ifdef DEBUG
if((x < 0) || (x >= s->w) || (y < 0) || (y >= s->h))
{
log_printf(ELOG, "Clip in getpix32_nc()!"
" x = %d, y = %d\n", x, y);
*((char *)NULL) = 0;
return getpix32_empty;
}
#endif
return p[x];
}
/* Interpolated; 28:4 fixed point coords. */
static inline pix_t getpix32i_nc(SDL_Surface *s, int x, int y)
{
#ifdef DEBUG
if((x < 0) || (x >= s->w << 4) || (y < 0) || (y >= s->h << 4))
{
log_printf(ELOG, "Clip in getpix32i_nc()!"
" x = %d, y = %d\n", x>>4, y>>4);
*((char *)NULL) = 0;
// return getpix32_empty;
}
GETPIXI(getpix32)
#else
GETPIXI(getpix32_nc)
#endif
}
#undef GETPIXI
static inline void setpix32_nc(SDL_Surface *s, int x, int y, pix_t pix)
{
pix_t *p = (pix_t *)((char *)s->pixels + y * s->pitch);
#ifdef DEBUG
if((x < 0) || (x >= s->w) || (y < 0) || (y >= s->h))
{
log_printf(ELOG, "Clip in setpix32_nc()!"
" x = %d, y = %d\n", x, y);
*((char *)NULL) = 0;
// return;
}
#endif
p[x] = pix;
}
static inline pix_t *pix32_nc(SDL_Surface *s, int x, int y)
{
pix_t *p = (pix_t *)((char *)s->pixels + y * s->pitch);
#ifdef DEBUG
// static pix_t trash;
if((x < 0) || (x >= s->w) || (y < 0) || (y >= s->h))
{
log_printf(ELOG, "Clip in pix32_nc()!"
" x = %d, y = %d\n", x, y);
*((char *)NULL) = 0;
// return &trash;
}
#endif
return p + x;
}
/*--------------------------------------------------
The filters
--------------------------------------------------*/
int s_filter_rgba8(s_bank_t *b, unsigned first, unsigned frames,
s_filter_args_t *args)
{
unsigned i;
for(i = 0; i < frames; ++i)
{
SDL_Surface *tmp;
s_sprite_t *s = s_get_sprite_b(b, first+i);
if(!s)
continue;
tmp = SDL_ConvertSurface(s->surface, SDL_PIXELFORMAT_RGBA32);
if(!tmp)
return -1;
SDL_DestroySurface(s->surface);
s->surface = tmp;
}
return 0;
}
int s_filter_dither(s_bank_t *b, unsigned first, unsigned frames,
s_filter_args_t *args)
{
int x, y;
unsigned i;
int ar, ag, ab;
if(!args->x && !args->r && !args->g && !args->b)
return 0;
noise_reset(first);
for(i = 0; i < frames; ++i)
{
s_sprite_t *s = s_get_sprite_b(b, first+i);
if(!s)
continue;
ar = args->r;
ag = args->g;
ab = args->b;
if(args->x)
switch (s_blitmode)
{
case S_BLITMODE_AUTO:
if(SDL_ISPIXELFORMAT_ALPHA(s->surface->format))
ar = ag = ab = 8;
break;
case S_BLITMODE_OPAQUE:
break;
case S_BLITMODE_COLORKEY:
case S_BLITMODE_ALPHA:
ar = ag = ab = 8;
break;
}
switch(args->y)
{
default:
case 0:
case 1:
ar >>= 1;
ag >>= 1;
ab >>= 1;
case 2:
break;
}
for(y = (args->flags & SF_CLAMP_SFONT) ? 1 : 0; y < b->h; ++y)
{
pix_t *p = (pix_t *)((char *)s->surface->pixels +
y * s->surface->pitch);
switch(args->y)
{
default:
case 0:
/* 2x2 filter */
for(x = 0; x < b->w; ++x)
{
int r, g, b;
pix_t *pix = p + x;
if((x^y)&1)
{
r = pix->r + ar;
g = pix->g + ag;
b = pix->b + ab;
if(r>255)
r = 255;
if(g>255)
g = 255;
if(b>255)
b = 255;
}
else
{
r = pix->r - ar;
g = pix->g - ag;
b = pix->b - ab;
if(r<0)
r = 0;
if(g<0)
g = 0;
if(b<0)
b = 0;
}
pix->r = r;
pix->g = g;
pix->b = b;
}
break;
case 1:
/* 4x4 filter */
for(x = 0; x < b->w; ++x)
{
int dr, dg, db;
int r, g, b;
pix_t *pix = p + x;
r = pix->r;
g = pix->g;
b = pix->b;
if((x^y) & 1)
{
dr = ar;
dg = ag;
db = ab;
}
else
{
dr = -ar;
dg = -ag;
db = -ab;
}
if(y & 1)
{
dr += ar >> 1;
dg += ag >> 1;
db += ab >> 1;
}
else
{
dr -= ar >> 1;
dg -= ag >> 1;
db -= ab >> 1;
}
r += dr;
g += dg;
b += db;
if(r>255)
r = 255;
else if(r<0)
r = 0;
if(g>255)
g = 255;
else if(g<0)
g = 0;
if(b>255)
b = 255;
else if(b<0)
b = 0;
pix->r = r;
pix->g = g;
pix->b = b;
}
break;
case 2:
/* Random */
for(x = 0; x < b->w; ++x)
{
int r, g, b, z;
pix_t *pix = p + x;
z = noise();
r = pix->r - ar + (((ar << 1) - 1) & z);
g = pix->g - ag + (((ag << 1) - 1) & z);
b = pix->b - ab + (((ab << 1) - 1) & z);
if(r>255)
r = 255;
else if(r<0)
r = 0;
if(g>255)
g = 255;
else if(g<0)
g = 0;
if(b>255)
b = 255;
else if(b<0)
b = 0;
pix->r = r;
pix->g = g;
pix->b = b;
}
break;
}
}
}
return 0;
}
/*
* Ugly hack to get around the problems caused by
* SDL_SetColorKey() ignoring the alpha channel.
* Basically just sets the blue LSB of any pixels
* that match the colorkey, but have non-transparent
* alpha.
*/
static void tweak_ck(SDL_Surface *s)
{
int x, y;
const SDL_PixelFormatDetails *fmt = SDL_GetPixelFormatDetails(s->format);
if(!fmt || fmt->bits_per_pixel != 32)
return;
for(y = 0; y < s->h; ++y)
{
pix_t *p = (pix_t *)((char *)s->pixels + y * s->pitch);
for(x = 0; x < s->w; ++x)
{
pix_t *pix = p + x;
if(!pix->r && !pix->g && !pix->b)
if(pix->a)
pix->b |= 1;
}
}
}
int s_filter_displayformat(s_bank_t * b, unsigned first, unsigned frames,
s_filter_args_t * args)
{
unsigned i;
for(i = 0; i < frames; ++i)
{
SDL_Surface *tmp;
s_sprite_t *s = s_get_sprite_b(b, first + i);
if(!s)
continue;
switch(s_blitmode)
{
case S_BLITMODE_AUTO:
if(SDL_ISPIXELFORMAT_ALPHA(s->surface->format))
{
SDL_SetSurfaceAlphaMod(s->surface,
SDL_ALPHA_OPAQUE);
SDL_SetSurfaceBlendMode(s->surface,
SDL_BLENDMODE_BLEND);
}
else
SDL_SetSurfaceColorKey(s->surface, false, 0);
break;
case S_BLITMODE_OPAQUE:
SDL_SetSurfaceColorKey(s->surface, false, 0);
break;
case S_BLITMODE_COLORKEY:
SDL_SetSurfaceColorKey(s->surface, true,
SDL_MapSurfaceRGB(s->surface,
s_colorkey.r,
s_colorkey.g,
s_colorkey.b));
break;
case S_BLITMODE_ALPHA:
SDL_SetSurfaceAlphaMod(s->surface, s_alpha);
SDL_SetSurfaceBlendMode(s->surface,
SDL_BLENDMODE_BLEND);
break;
}
// SDL 3 dropped SDL_DisplayFormat. We always convert sprites
// to the back-buffer format (ARGB8888); see gfxengine.cpp.
if(args->x)
{
if(SDL_ISPIXELFORMAT_ALPHA(s->surface->format))
tweak_ck(s->surface);
tmp = SDL_ConvertSurface(s->surface,
SDL_PIXELFORMAT_ARGB8888);
if(tmp && SDL_ISPIXELFORMAT_ALPHA(s->surface->format))
SDL_SetSurfaceColorKey(tmp, true,
SDL_MapSurfaceRGB(tmp, 0,0,0));
}
else
{
tmp = SDL_ConvertSurface(s->surface,
SDL_PIXELFORMAT_ARGB8888);
}
if(!tmp)
return -1;
SDL_DestroySurface(s->surface);
s->surface = tmp;
}
return 0;
}
typedef struct
{
SDL_Surface *src; /* Source surface */
SDL_Surface *dst; /* Destination surface */
int scx, scy; /* Scale (fixp 16:16) */
int start_sy, start_sx; /* Src start pos (fixp 16:16) */
int start_x, start_y; /* Dst start pos (pixels) */
int max_x, max_y; /* Dst end pos (pixels) */
} scale_params_t;
static inline void select_rect(scale_params_t *p, scale_params_t *orig,
int x, int y, int w, int h)
{
p->start_sx = orig->start_sx + x * orig->scx;
p->start_sy = orig->start_sy + y * orig->scy;
p->start_x = orig->start_x + x;
p->start_y = orig->start_y + y;
p->max_x = p->start_x + w;
p->max_y = p->start_y + h;
}
/* Optimization tool; use the slow clipping funcs only around the edges! */
static inline void do_scale(scale_params_t *p,
void (*border)(scale_params_t *p),
void (*body)(scale_params_t *p))
{
/* Copy the parameters */
scale_params_t lp = *p;
int w = p->max_x - p->start_x;
int h = p->max_y - p->start_y;
int bx = (65535 + p->scx) / p->scx;
int by = (65535 + p->scy) / p->scy;
if(bx * 2 >= w || by * 2 >= h)
{
/* Area too small - body size would be zero or negative! */
select_rect(&lp, p, 0, 0, w, h);
border(&lp);
return;
}
/* Top border */
select_rect(&lp, p, 0, 0, w, by);
border(&lp);
/* Bottom border */
select_rect(&lp, p, 0, h - by, w, by);
border(&lp);
/* Left border */
select_rect(&lp, p, 0, by, bx, h - 2 * by);
border(&lp);
/* Right border */
select_rect(&lp, p, w - bx, by, bx, h - 2 * by);
border(&lp);
/* Body */
select_rect(&lp, p, bx, by, w - 2 * bx, h - 2 * by);
body(&lp);
}
/*
* Ugly but fast "nearest pixel" scaling
*/
static void scale_nearest(scale_params_t *p)
{
int x, y, sx, sy;
for(y = p->start_y, sy = p->start_sy; y < p->max_y; ++y, sy += p->scy)
{
pix_t *pix = (pix_t *)((char *)p->dst->pixels +
y * p->dst->pitch);
for(x = p->start_x, sx = p->start_sx; x < p->max_x;
++x, sx += p->scx)
pix[x] = getpix32_nc(p->src, sx >> 16, sy >> 16);
}
}
/*
* Bilinear scaling
*/
static void do_scale_bilinear_noclip(scale_params_t *p)
{
int x, y, sx, sy;
for(y = p->start_y, sy = p->start_sy; y < p->max_y; ++y, sy += p->scy)
{
pix_t *pix = (pix_t *)((char *)p->dst->pixels +
y * p->dst->pitch);
for(x = p->start_x, sx = p->start_sx; x < p->max_x;
++x, sx += p->scx)
pix[x] = getpix32i_nc(p->src, sx >> 12, sy >> 12);
}
}
static void do_scale_bilinear_clip(scale_params_t *p)
{
int x, y, sx, sy;
for(y = p->start_y, sy = p->start_sy; y < p->max_y; ++y, sy += p->scy)
{
pix_t *pix = (pix_t *)((char *)p->dst->pixels +
y * p->dst->pitch);
for(x = p->start_x, sx = p->start_sx; x < p->max_x;
++x, sx += p->scx)
pix[x] = getpix32i(p->src, sx >> 12, sy >> 12);
}
}
static void do_scale_bilinear_clamp(scale_params_t *p)
{
int x, y, sx, sy;
for(y = p->start_y, sy = p->start_sy; y < p->max_y; ++y, sy += p->scy)
{
pix_t *pix = (pix_t *)((char *)p->dst->pixels +
y * p->dst->pitch);
for(x = p->start_x, sx = p->start_sx; x < p->max_x;
++x, sx += p->scx)
pix[x] = getpix32ic(p->src, sx >> 12, sy >> 12);
}
}
static void scale_bilinear(scale_params_t *p)
{
do_scale(p, do_scale_bilinear_clip, do_scale_bilinear_noclip);
}
static void scale_bilinear_clamp(scale_params_t *p)
{
do_scale(p, do_scale_bilinear_clamp, do_scale_bilinear_noclip);
}
/*
* Scale2x scaling
*
* This one's cool for 16 color graphics and the like, but
* even with the fuzziness factor, it doesn't work well
* with 24 bit data, or even 256 color graphics.
*/
#define CDIFF(x,y) (((x).r^(y).r) | ((x).g^(y).g) | ((x).b^(y).b) | ((x).a^(y).a))
#define SCALE2X(gp, sp) \
{ \
int DB, BF, DH, FH; \
pix_t B ; \
pix_t D, E, F; \
pix_t H ; \
pix_t E0, E1; \
pix_t E2, E3; \
B = gp(p->src, sx, sy-1); \
D = gp(p->src, sx-1, sy); \
E = gp(p->src, sx, sy); \
F = gp(p->src, sx+1, sy); \
H = gp(p->src, sx, sy+1); \
DB = CDIFF(D, B) & 0xc0; \
BF = CDIFF(B, F) & 0xc0; \
DH = CDIFF(D, H) & 0xc0; \
FH = CDIFF(F, H) & 0xc0; \
E0 = (!DB && BF && DH) ? D : E; \
E1 = (!BF && DB && FH) ? F : E; \
E2 = (!DH && DB && FH) ? D : E; \
E3 = (!FH && DH && BF) ? F : E; \
sp(p->dst, x, y, E0); \
sp(p->dst, x+1, y, E1); \
sp(p->dst, x, y+1, E2); \
sp(p->dst, x+1, y+1, E3); \
}
static void do_scale_2x_noclip(scale_params_t *p)
{
int x, y, sx, sy;
for(y = p->start_y, sy = p->start_sy >> 16; y < p->max_y; y += 2, ++sy)
for(x = p->start_x, sx = p->start_sx >> 16; x < p->max_x;
x += 2, ++sx)
SCALE2X(getpix32_nc, setpix32_nc);
}
static void do_scale_2x_clip(scale_params_t *p)
{
int x, y, sx, sy;
for(y = p->start_y, sy = p->start_sy >> 16; y < p->max_y; y += 2, ++sy)
for(x = p->start_x, sx = p->start_sx >> 16; x < p->max_x;
x += 2, ++sx)
SCALE2X(getpix32, setpix32);
}
static void do_scale_2x_clamp(scale_params_t *p)
{
int x, y, sx, sy;
for(y = p->start_y, sy = p->start_sy >> 16; y < p->max_y; y += 2, ++sy)
for(x = p->start_x, sx = p->start_sx >> 16; x < p->max_x;
x += 2, ++sx)
SCALE2X(getpix32c, setpix32);
}
#undef CDIFF
#undef SCALE2X
static void scale_2x(scale_params_t *p)
{
do_scale(p, do_scale_2x_clip, do_scale_2x_noclip);
}
static void scale_2x_clamp(scale_params_t *p)
{
do_scale(p, do_scale_2x_clamp, do_scale_2x_noclip);
}
/*
* Diamond2x scaling
*
* This works *great* though; kicks the butt of
* Linear/Oversampled real hard - and at an order
* of magnitude higher speed! :-)
*/
#define MIX(t,x,y,z) (t).r = ((x).r+(x).r+(y).r+(z).r)>>2; \
(t).g = ((x).g+(x).g+(y).g+(z).g)>>2; \
(t).b = ((x).b+(x).b+(y).b+(z).b)>>2; \
(t).a = ((x).a+(x).a+(y).a+(z).a)>>2;
#define DIAMOND(gp, sp) \
{ \
pix_t B; \
pix_t D, E, F; \
pix_t H; \
pix_t E0, E1; \
pix_t E2, E3; \
B = gp(p->src, sx, sy-1); \
D = gp(p->src, sx-1, sy); \
E = gp(p->src, sx, sy); \
F = gp(p->src, sx+1, sy); \
H = gp(p->src, sx, sy+1); \
MIX(E0, E, B, D); \
MIX(E1, E, B, F); \
MIX(E2, E, H, D); \
MIX(E3, E, H, F); \
sp(p->dst, x, y, E0); \
sp(p->dst, x+1, y, E1); \
sp(p->dst, x, y+1, E2); \
sp(p->dst, x+1, y+1, E3); \
}
static void do_scale_diamond2x_noclip(scale_params_t *p)
{
int x, y, sx, sy;
for(y = p->start_y, sy = p->start_sy >> 16; y < p->max_y; y += 2, ++sy)
for(x = p->start_x, sx = p->start_sx >> 16; x < p->max_x;
x += 2, ++sx)
DIAMOND(getpix32_nc, setpix32_nc);
}
static void do_scale_diamond2x_clip(scale_params_t *p)
{
int x, y, sx, sy;
for(y = p->start_y, sy = p->start_sy >> 16; y < p->max_y; y += 2, ++sy)
for(x = p->start_x, sx = p->start_sx >> 16; x < p->max_x;
x += 2, ++sx)
DIAMOND(getpix32, setpix32);
}
static void do_scale_diamond2x_clamp(scale_params_t *p)
{
int x, y, sx, sy;
for(y = p->start_y, sy = p->start_sy >> 16; y < p->max_y; y += 2, ++sy)
for(x = p->start_x, sx = p->start_sx >> 16; x < p->max_x;
x += 2, ++sx)
DIAMOND(getpix32c, setpix32);
}
#undef MIX
#undef DIAMOND
static void scale_diamond2x(scale_params_t *p)
{
do_scale(p, do_scale_diamond2x_clip, do_scale_diamond2x_noclip);
}
static void scale_diamond2x_clamp(scale_params_t *p)
{
do_scale(p, do_scale_diamond2x_clamp, do_scale_diamond2x_noclip);
}
int s_filter_scale(s_bank_t *b, unsigned first, unsigned frames,
s_filter_args_t *args)
{
scale_params_t params;
int fmode, smode = args->x;
unsigned i;
if((args->fx <= 0.0f) && (args->fy <= 0.0f))
return 0;
if((args->fx == 1.0f) && (args->fy <= 1.0f))
return 0;
switch(smode)
{
case SF_SCALE_NEAREST:
case SF_SCALE_BILINEAR:
break;
case SF_SCALE_SCALE2X:
case SF_SCALE_DIAMOND:
if((args->fx != 2.0) || (args->fy != 2.0))
{
log_printf(WLOG, "Scale2x and Diamond2x scaling "
"works only with a scaling factor "
"of exactly 2.0!\n");
/* smode = SF_SCALE_NEAREST;*/
smode = SF_SCALE_BILINEAR;
}
break;
}
getpix32_empty = s_clampcolor;
params.max_x = (int)ceil(b->w * args->fx);
params.scx = (int)(65536.0 / args->fx);
params.scy = (int)(65536.0 / args->fy);
params.start_x = 0;
params.start_sx = 0;
/* Check for special SFont mode */
if(args->flags & SF_CLAMP_SFONT)
{
params.start_y = 1;
params.start_sy = 65536;
params.max_y = (int)((b->h - 1) * args->fy + 1 + 0.5f);
}
else
{
params.start_y = 0;
params.start_sy = 0;
params.max_y = (int)(b->h * args->fy + 0.5f);
}
/* Decode filter type + SF_CLAMP_EXTEND flag */
fmode = (args->flags & SF_CLAMP_EXTEND) ? 1 : 0;
switch(smode)
{
case SF_SCALE_NEAREST:
fmode += 0;
break;
case SF_SCALE_BILINEAR:
fmode += 2;
/* Offset by half a pixel */
params.start_sx += params.scx / 2;
params.start_sy += params.scy / 2;
break;
case SF_SCALE_SCALE2X:
fmode += 4;
break;
case SF_SCALE_DIAMOND:
fmode += 6;
break;
}
for(i = 0; i < frames; ++i)
{
s_sprite_t *s = s_get_sprite_b(b, first+i);
if(!s)
continue;
params.src = s->surface;
params.dst = SDL_CreateSurface(params.max_x, params.max_y,
SDL_PIXELFORMAT_RGBA32);
if(!params.dst)
return -1;
#ifdef DEBUG
SDL_FillSurfaceRect(params.dst, NULL,
SDL_MapSurfaceRGB(params.dst, 255, 128, 0));
#endif
/* Deal with SFont marker row */
if(args->flags & SF_CLAMP_SFONT)
{
int x, sx;
pix_t clear = {0, 0, 0, 0};
/* Scale marker row with "nearest" filter */
for(x = 0, sx = params.start_sx; x < params.max_x;
++x, sx += params.scx)
setpix32_nc(params.dst, x, 0,
getpix32(params.src,
sx >> 16, 0));
/* Remove marker row to avoid garbage */
for(x = 0; x < b->w; ++x)
setpix32_nc(params.src, x, 0, clear);
}
/* Do the scaling! */
switch(fmode)
{
case 0: /*Nearest*/
case 1: /*Nearest + extend clamping*/
scale_nearest(&params);
break;
case 2: /*Bilinear*/
scale_bilinear(&params);
break;
case 3: /*Bilinear + extend clamping*/
scale_bilinear_clamp(&params);
break;
case 4: /*Scale2x*/
scale_2x(&params);
break;
case 5: /*Scale2x + extend clamping*/
scale_2x_clamp(&params);
break;
case 6: /*Diamond2x*/
scale_diamond2x(&params);
break;
case 7: /*Diamond2x + extend clamping*/
scale_diamond2x_clamp(&params);
break;
}
SDL_DestroySurface(s->surface);
s->surface = params.dst;
}
b->w = params.max_x;
b->h = params.max_y;
return 0;
}
int s_filter_cleanalpha(s_bank_t *b, unsigned first, unsigned frames,
s_filter_args_t *args)
{
int min = args->min;
int max = args->max;
int x, y;
unsigned i;
int contrast = (int)(args->fx * 256);
int offset = args->x + 128; /* 128 for the bias! */
if(!contrast)
contrast = 256; /*Default = 1.0*/
for(i = 0; i < frames; ++i)
{
s_sprite_t *s = s_get_sprite_b(b, first+i);
if(!s)
continue;
for(y = (args->flags & SF_CLAMP_SFONT) ? 1 : 0; y < b->h; ++y)
{
pix_t *p = (pix_t *)((char *)s->surface->pixels +
y * s->surface->pitch);
for(x = 0; x < b->w; ++x)
{
pix_t *pix = p + x;
int alpha = pix->a;
alpha = (alpha-128) * contrast >> 8;
alpha += offset;
if(alpha <= min)
pix->a = pix->r = pix->g = pix->b = 0;
else if(alpha > max)
pix->a = 255;
else
pix->a = alpha;
}
}
}
return 0;
}
int s_filter_brightness(s_bank_t *b, unsigned first, unsigned frames,
s_filter_args_t *args)
{
int min = args->min;
int max = args->max;
int x, y;
unsigned i;
int contrast = (int)(args->fy * 256);
int offset = (int)(args->fx * 256 - 256) + 128; /* bias! */
if((256 == contrast) && (128 == offset) && (min <= 0) && (max >= 255))
return 0; /* No effect! --> */
for(i = 0; i < frames; ++i)
{
s_sprite_t *s = s_get_sprite_b(b, first + i);
if(!s)
continue;
for(y = (args->flags & SF_CLAMP_SFONT) ? 1 : 0; y < b->h; ++y)
{
pix_t *p = (pix_t *)((char *)s->surface->pixels +
y * s->surface->pitch);
for(x = 0; x < b->w; ++x)
{
int v;
pix_t *pix = p + x;
v = ((pix->r - 128) * contrast >> 8) + offset;
if(v < min)
v = min;
else if(v > max)
v = max;
pix->r = v;
v = ((pix->g - 128) * contrast >> 8) + offset;
if(v < min)
v = min;
else if(v > max)
v = max;
pix->g = v;
v = ((pix->b - 128) * contrast >> 8) + offset;
if(v < min)
v = min;
else if(v > max)
v = max;
pix->b = v;
}
}
}
return 0;
}
static void construct_limits(int *min, int *max, int maxdev)
{
*min -= maxdev;
if(*min < 0)
*min = 0;
*max += maxdev;
if(*max > 255)
*max = 255;
}
static inline int check_limits(int min, int max, int value)
{
if(value < min)
return 0;
if(value > max)
return 0;
return 1;
}
int s_filter_key2alpha(s_bank_t *b, unsigned first, unsigned frames,
s_filter_args_t *args)
{
int x, y, krmin, krmax, kgmin, kgmax, kbmin, kbmax;
unsigned i;
krmin = krmax = s_colorkey.r;
kgmin = kgmax = s_colorkey.g;
kbmin = kbmax = s_colorkey.b;
construct_limits(&krmin, &krmax, args->max);
construct_limits(&kgmin, &kgmax, args->max);
construct_limits(&kbmin, &kbmax, args->max);
for(i = 0; i < frames; ++i)
{
s_sprite_t *s = s_get_sprite_b(b, first+i);
if(!s)
continue;
for(y = (args->flags & SF_CLAMP_SFONT) ? 1 : 0; y < b->h; ++y)
{
pix_t *p = (pix_t *)((char *)s->surface->pixels +
y * s->surface->pitch);
for(x = 0; x < b->h; ++x)
{
pix_t *pix = p + x;
if(check_limits(krmin, krmax, pix->r)
&& check_limits(kgmin, kgmax, pix->g)
&& check_limits(kbmin, kbmax, pix->b))
pix->r = pix->g = pix->b = pix->a = 0;
}
}
}
return 0;
}
int s_filter_noise(s_bank_t *b, unsigned first, unsigned frames,
s_filter_args_t *args)
{
int x, y;
unsigned i;
int br, bg, bb; /* bias */
int ar, ag, ab; /* amplitude */
i = args->min;
br = args->r * i >> 8;
bg = args->g * i >> 8;
bb = args->b * i >> 8;
i = args->max - args->min;
ar = args->r * i >> 8;
ag = args->g * i >> 8;
ab = args->b * i >> 8;
noise_reset(args->x);
for(i = 0; i < frames; ++i)
{
s_sprite_t *s = s_get_sprite_b(b, first+i);
if(!s)
continue;
for(y = (args->flags & SF_CLAMP_SFONT) ? 1 : 0; y < b->h; ++y)
{
pix_t *p = (pix_t *)((char *)s->surface->pixels +
y * s->surface->pitch);
for(x = 0; x < b->w; ++x)
{
int r, g, b;
pix_t *pix = p + x;
int rnd = noise();
r = rnd * ar + br + pix->r;
g = rnd * ag + bg + pix->g;
b = rnd * ab + bb + pix->b;
if(r>255)
r = 255;
if(g>255)
g = 255;
if(b>255)
b = 255;
pix->r = r;
pix->g = g;
pix->b = b;
}
}
}
return 0;
}