kobodl/graphics/region.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

223 lines
4.4 KiB
C

/*(LGPL)
----------------------------------------------------------------------
region.c - Graphics Engine
----------------------------------------------------------------------
* Copyright (C) 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 "region.h"
#include <stdlib.h>
static struct
{
RGN_region *region;
int x, y;
SDL_Surface *target;
} s;
static Uint32 gp1(SDL_Surface *surface, int x, int y)
{
return *((Uint8 *)surface->pixels + y * surface->pitch + x);
}
static Uint32 gp2(SDL_Surface *surface, int x, int y)
{
return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
}
static Uint32 gp3(SDL_Surface *surface, int x, int y)
{
const SDL_PixelFormatDetails *fmt =
SDL_GetPixelFormatDetails(surface->format);
int bpp = fmt ? fmt->bytes_per_pixel : 3;
Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
#if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
return p[0] << 16 | p[1] << 8 | p[2];
#else
return p[0] | p[1] << 8 | p[2] << 16;
#endif
}
static Uint32 gp4(SDL_Surface *surface, int x, int y)
{
return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
}
RGN_region *RGN_ScanMask(SDL_Surface *src, Uint32 key)
{
RGN_region *rgn = (RGN_region *)calloc(1, sizeof(RGN_region));
Uint32 (*gp)(SDL_Surface *surface, int x, int y);
int x, y;
if(!rgn)
return NULL;
rgn->rows = src->h;
rgn->spans = (Uint16 **)calloc(1, sizeof(Uint16 *) * src->h);
if(!rgn->spans)
{
RGN_FreeRegion(rgn);
return NULL;
}
const SDL_PixelFormatDetails *src_fmt =
SDL_GetPixelFormatDetails(src->format);
if(!src_fmt)
{
RGN_FreeRegion(rgn);
return NULL;
}
switch(src_fmt->bytes_per_pixel)
{
case 1:
gp = gp1;
break;
case 2:
gp = gp2;
break;
case 3:
gp = gp3;
break;
case 4:
gp = gp4;
break;
default:
RGN_FreeRegion(rgn);
return NULL;
}
SDL_LockSurface(src);
for(y = 0; y < src->h; ++y)
{
int i = 0;
int drawing = 0;
Uint16 *sp = (Uint16 *)malloc(sizeof(Uint16) * 2 * (src->w + 1));
if(!sp)
{
SDL_UnlockSurface(src);
RGN_FreeRegion(rgn);
return NULL;
}
for(x = 0; x < src->w; ++x)
{
int nd = (gp(src, x, y) == key);
if(nd != drawing)
{
sp[i++] = x;
drawing = nd;
}
}
if(drawing)
sp[i++] = src->w;
sp[i++] = 0;
sp[i++] = 0;
rgn->spans[y] = (Uint16 *)realloc(sp, sizeof(Uint16) * i);
if(!rgn->spans[y])
rgn->spans[y] = sp;
}
SDL_UnlockSurface(src);
return rgn;
}
void RGN_FreeRegion(RGN_region *rgn)
{
if(!rgn)
return;
if(rgn->spans)
{
int i;
for(i = rgn->rows - 1; i >= 0; --i)
free(rgn->spans[i]);
free(rgn->spans);
}
free(rgn);
}
void RGN_Target(SDL_Surface *tgt)
{
s.target = tgt;
}
void RGN_SetRegion(RGN_region *rgn, int xpos, int ypos)
{
s.region = rgn;
s.x = xpos;
s.y = ypos;
}
int RGN_Blit(SDL_Surface *src, SDL_Rect *sr, int x, int y)
{
// FIXME: Clipping is only half done here...
int yy0, yy, yy1;
int sxmin, sxmax, symin, symax;
SDL_Rect sr2;
if(!s.region)
{
SDL_Rect dr;
dr.x = x;
dr.y = y;
return SDL_BlitSurface(src, sr, s.target, &dr);
}
if(sr)
{
sxmin = sr->x;
symin = sr->y;
sxmax = sxmin + sr->w;
symax = symin + sr->h;
}
else
{
sxmin = 0;
symin = 0;
sxmax = src->w;
symax = src->h;
}
yy0 = s.y;
if(yy0 >= y + (symax - symin))
return 0;
if(yy0 < y)
yy0 = y;
yy1 = s.y + s.region->rows;
if(y >= yy1)
return 0;
if(yy1 > y + (symax - symin))
yy1 = y + (symax - symin);
sr2.h = 1;
for(yy = yy0; yy < yy1; ++yy)
{
SDL_Rect dr;
Uint16 *spans = s.region->spans[yy - s.y];
sr2.y = symin + y - yy0;
dr.y = yy;
for( ; spans[0] && spans[1] ; spans += 2)
{
int x0 = s.x + spans[0];
int x1 = s.x + spans[1];
dr.x = x0;
sr2.x = sxmin + x0 - x;
sr2.w = x1 - x0;
SDL_BlitSurface(src, &sr2, s.target, &dr);
}
}
return 0;
}