kobodl/graphics/gfxengine.cpp
Ville Lindholm 762b095462
WIP: delete glSDL shim, replace with sdl_compat.h
Step 1+2 of the SDL 3 migration. The 2300-line graphics/glSDL.{c,h}
OpenGL-on-top-of-SDL-1.2 shim is obsolete under SDL 3 (SDL_Renderer
does what glSDL did natively), and its #define overrides of SDL
surface APIs collided directly with SDL 3's SDL_oldnames.h.

- Add graphics/sdl_compat.h: a one-line umbrella that just pulls in
  <SDL3/SDL.h>.
- Replace every #include "glSDL.h" with #include "sdl_compat.h" (15
  files across the tree).
- Delete graphics/glSDL.c, graphics/glSDL.h.
- Drop glSDL.c from graphics/CMakeLists.txt.
- Remove GFX_DRIVER_GLSDL from the gfx_drivers_t enum and from all
  switches/conditions in gfxengine.cpp.
- Drop the glSDL_VSync() call site (will be re-introduced as
  SDL_SetRenderVSync once gfxengine is ported to SDL_Renderer).
- Drop the two glSDL_Invalidate() calls in window.cpp (texture
  re-upload hints with no equivalent in a pure-surface compositor).
- options.cpp: remove the now-empty "OpenGL/glSDL" menu item; force
  videodriver to GFX_DRIVER_SDL2D.
- prefs.cpp: collapse legacy GLSDL setting onto SDL2D on load.

After this commit the macro-collision wall is gone (renamed_-token
errors drop from many to zero). The build still fails on the next
layer of work: SDL 1.2 surface APIs that no longer exist
(SDL_FreeSurface, SDL_SetAlpha, SDL_CreateRGBSurface, SDL_DisplayFormat,
SDL_PixelFormat struct member access, SDL audio callback API, etc.).

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

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

1380 lines
25 KiB
C++

/*(LGPL)
----------------------------------------------------------------------
gfxengine.cpp - Graphics Engine
----------------------------------------------------------------------
* 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.
*/
#define DBG(x) x
#include "logger.h"
#include <string.h>
#include <math.h>
#include "gfxengine.h"
#include "filters.h"
#include <SDL3_image/SDL_image.h>
#include "sdl_compat.h"
#include "sofont.h"
#include "window.h"
gfxengine_t *gfxengine;
gfxengine_t::gfxengine_t()
{
gfxengine = this; /* Uurgh! Kludge. */
screen_surface = NULL;
softbuf = NULL;
fullwin = NULL;
window = NULL;
windows = NULL;
wx = wy = 0;
xs = ys = 256; // 1.0
sxs = sys = 256; // 1.0
sf1 = df = dsf = acf = NULL;
gfx = NULL;
csengine = NULL;
_driver = GFX_DRIVER_SDL2D;
_shadow = 1;
_doublebuf = 1;
_pages = -1;
_vsync = 1;
_fullscreen = 0;
_centered = 0;
use_interpolation = 1;
_depth = 0;
_title = "GfxEngine v0.4";
_icontitle = "GfxEngine";
_cursor = 1;
_width = 320;
_height = 240;
_autoinvalidate = 1;
_scalemode = GFX_SCALE_NEAREST;
_clamping = 0;
xflags = 0;
_dither = 0;
_dither_type = 0;
broken_rgba8 = 0;
alpha_threshold = 0;
_brightness = 1.0;
_contrast = 1.0;
last_tick = -1000000;
ticks_per_frame = 1000.0/60.0;
is_running = 0;
is_showing = 0;
is_open = 0;
memset(fonts, 0, sizeof(fonts));
xscroll = yscroll = 0;
for(int i = 0; i < CS_LAYERS ; ++i)
xratio[i] = yratio[i] = 0.0;
dirtyrects[0] = 0;
dirtyrects[1] = 0;
frontpage = 0;
backpage = 1;
screenshot_count = 0;
}
gfxengine_t::~gfxengine_t()
{
stop();
hide();
close();
window_t *w = windows;
while(w)
{
w->engine = NULL;
w = w->next;
}
}
void gfxengine_t::output(window_t *outwin)
{
window = outwin;
}
/*----------------------------------------------------------
Initialization
----------------------------------------------------------*/
void gfxengine_t::size(int w, int h)
{
int was_showing = is_showing;
hide();
_width = w;
_height = h;
if(csengine)
cs_engine_set_size(csengine, w, h);
if(was_showing)
show();
}
void gfxengine_t::centered(int c)
{
int was_showing = is_showing;
hide();
_centered = c;
if(was_showing)
show();
}
void gfxengine_t::scale(float x, float y)
{
xs = (int)(x * 256.f);
ys = (int)(y * 256.f);
log_printf(DLOG, "gfxengine: Setting scale to %d:256 x %d:256.\n", xs, ys);
}
void gfxengine_t::mode(int bits, int fullscreen)
{
int was_showing = is_showing;
hide();
int olddepth = _depth;
_depth = bits;
if(_depth != olddepth)
reload();
_fullscreen = fullscreen;
if(was_showing)
show();
}
void gfxengine_t::driver(gfx_drivers_t drv)
{
int was_showing = is_showing;
hide();
_driver = drv;
if(was_showing)
show();
}
void gfxengine_t::doublebuffer(int use)
{
if(_doublebuf == use)
return;
int was_showing = is_showing;
hide();
_doublebuf = use;
if(was_showing)
show();
}
void gfxengine_t::pages(int np)
{
_pages = np;
}
void gfxengine_t::vsync(int use)
{
if(_vsync == use)
return;
int was_showing = is_showing;
hide();
_vsync = use;
if(was_showing)
show();
}
void gfxengine_t::shadow(int use)
{
if(_shadow == use)
return;
int was_showing = is_showing;
hide();
_shadow = use;
if(was_showing)
show();
}
void gfxengine_t::autoinvalidate(int use)
{
_autoinvalidate = use;
}
void gfxengine_t::period(float frameduration)
{
if(frameduration > 0)
{
ticks_per_frame = frameduration;
}
else
log_printf(DLOG, "gfxengine: Time line reset.\n");
if(csengine)
cs_engine_advance(csengine, 0);
}
void gfxengine_t::wrap(int x, int y)
{
wx = x;
wy = y;
if(csengine)
cs_engine_set_wrap(csengine, x, y);
}
/*----------------------------------------------------------
Data management
----------------------------------------------------------*/
void gfxengine_t::reset_filters()
{
/* Remove all filters */
s_remove_filter(NULL);
sf1 = df = dsf = acf = NULL;
/* Set up filter pipeline */
s_remove_filter(NULL);
s_add_filter(s_filter_rgba8);
#if 0
s_filter_t *fi;
fi = s_add_filter(s_filter_key2alpha);
fi->args.max = 1;
#endif
sf1 = s_add_filter(s_filter_scale);
sf2 = s_add_filter(s_filter_scale);
acf = s_add_filter(s_filter_cleanalpha);
bcf = s_add_filter(s_filter_brightness);
df = s_add_filter(s_filter_dither);
dsf = s_add_filter(s_filter_displayformat);
/* Set default parameters */
// colorkey(0, 0, 0);
clampcolor(0, 0, 0, 0);
scalemode(GFX_SCALE_NEAREST);
dither(0, 0);
noalpha(0);
brightness(1.0, 1.0);
filterflags(0);
}
void gfxengine_t::filterflags(int fgs)
{
s_filter_flags = fgs;
}
void gfxengine_t::scalemode(gfx_scalemodes_t sm, int clamping)
{
_scalemode = sm;
_clamping = clamping;
if(!sf1 || !df)
return;
int rxs = (xs * 256 + 128) / sxs;
int rys = (ys * 256 + 128) / sys;
#if 1
// Filter 2 is off in most cases
sf2->args.x = 0;
sf2->args.fx = 0.0f;
sf2->args.fy = 0.0f;
#endif
if((rxs == 256) && (rys == 256))
{
sf1->args.x = SF_SCALE_NEAREST;
sf1->args.fx = 1.0f;
sf1->args.fy = 1.0f;
return;
}
switch(clamping)
{
default:
case 0:
filterflags(0);
break;
case 1:
filterflags(SF_CLAMP_EXTEND);
break;
case 2:
filterflags(SF_CLAMP_SFONT);
break;
}
sf1->args.fx = rxs * (1.0f/256.0f);
sf1->args.fy = rys * (1.0f/256.0f);
switch(_scalemode)
{
case GFX_SCALE_NEAREST: //Nearest
sf1->args.x = SF_SCALE_NEAREST;
break;
case GFX_SCALE_BILINEAR: //Bilinear
sf1->args.x = SF_SCALE_BILINEAR;
break;
case GFX_SCALE_BILIN_OVER: //Bilinear + Oversampling
#if 0
sf1->args.x = SF_SCALE_BILINEAR;
sf2->args.x = SF_SCALE_BILINEAR;
sf1->args.fx = rxs * (1.5f/256.f);
sf1->args.fy = rys * (1.5f/256.f);
sf2->args.fx = 1.0f/1.5f;
sf2->args.fy = 1.0f/1.5f;
break;
/* Hack for better bilinear [+ oversampling] scaling... */
switch(clamping)
{
case 2:
sf1->args.x = SF_SCALE_NEAREST;
sf2->args.x = SF_SCALE_BILINEAR;
switch((rxs + 128) / 256)
{
case 2:
sf1->args.fx = 4.0f;
sf1->args.fy = 4.0f;
sf2->args.fx = 0.5f;
sf2->args.fy = 0.5f;
break;
case 3:
sf1->args.fx = 2.0f;
sf1->args.fy = 2.0f;
sf2->args.fx = 1.5f;
sf2->args.fy = 1.5f;
break;
case 4:
sf1->args.fx = 2.0f;
sf1->args.fy = 2.0f;
sf2->args.fx = 2.0f;
sf2->args.fy = 2.0f;
break;
default:
sf1->args.fx = rxs * (2.f/256.f);
sf1->args.fy = rys * (2.f/256.f);
sf2->args.fx = 0.5f;
sf2->args.fy = 0.5f;
break;
}
break;
default:
sf1->args.x = SF_SCALE_BILINEAR;
break;
}
#endif
sf1->args.x = SF_SCALE_BILINEAR;
break;
case GFX_SCALE_SCALE2X: //Scale2x
sf1->args.x = SF_SCALE_SCALE2X;
break;
case GFX_SCALE_DIAMOND: //Diamond2x
sf1->args.x = SF_SCALE_DIAMOND;
break;
}
}
void gfxengine_t::source_scale(float x, float y)
{
sxs = (int)(x * 256.f);
sys = (int)(y * 256.f);
scalemode(_scalemode, _clamping);
}
#if 0
void gfxengine_t::colorkey(Uint8 r, Uint8 g, Uint8 b)
{
s_colorkey.r = r;
s_colorkey.g = g;
s_colorkey.b = b;
}
#endif
void gfxengine_t::clampcolor(Uint8 r, Uint8 g, Uint8 b, Uint8 a)
{
s_clampcolor.r = r;
s_clampcolor.g = g;
s_clampcolor.b = b;
s_clampcolor.a = b;
}
void gfxengine_t::dither(int type, int _broken_rgba8)
{
_dither = type >= 0;
_dither_type = type;
broken_rgba8 = _broken_rgba8;
if(!df)
return;
if(!screen_surface)
return;
if(_dither)
{
df->args.x = 0;
df->args.r = 1<<(screen_surface->format->Rloss-1);
df->args.g = 1<<(screen_surface->format->Gloss-1);
df->args.b = 1<<(screen_surface->format->Bloss-1);
}
else
df->args.x = df->args.r = df->args.g = df->args.b = 0;
df->args.y = type;
}
void gfxengine_t::noalpha(int threshold)
{
alpha_threshold = threshold;
if(!dsf || !acf)
return;
if(threshold > 0)
{
acf->args.min = 128;
acf->args.max = 128;
acf->args.fx = 1.0;
acf->args.x = threshold;
dsf->args.x = 1;
}
else
{
acf->args.min = 16;
acf->args.max = 255-16;
acf->args.fx = 1.3;
acf->args.x = -16;
dsf->args.x = 0;
}
}
void gfxengine_t::brightness(float bright, float contr)
{
_brightness = bright;
_contrast = contr;
if(!bcf)
return;
bcf->args.fx = _brightness;
bcf->args.fy = _contrast;
bcf->args.min = 0;
bcf->args.max = 255;
}
int gfxengine_t::loadimage(int bank, const char *name)
{
if(!csengine)
{
log_printf(ELOG, "loadimage: Engine must be open!\n");
return -10;
}
s_blitmode = S_BLITMODE_AUTO;
log_printf(DLOG, "Loading image %s (bank %d)...\n", name, bank);
if(s_load_image(gfx, bank, name))
{
log_printf(ELOG, " Failed to load %s!\n", name);
return -1;
}
s_bank_t *b = s_get_bank(gfx, bank);
if(!b)
{
log_printf(ELOG, " gfxengine: Internal error 1!\n");
return -15;
}
cs_engine_set_image_size(csengine, bank, b->w, b->h);
log_printf(DLOG, " Ok.\n");
return 0;
}
int gfxengine_t::loadtiles(int bank, int w, int h, const char *name)
{
if(!csengine)
{
log_printf(ELOG, "loadtiles: Engine must be open!\n");
return -10;
}
s_blitmode = S_BLITMODE_AUTO;
log_printf(DLOG, "Loading tiles %s (bank %d; %dx%d)...\n",
name, bank, w, h);
if(s_load_bank(gfx, bank, w, h, name))
{
log_printf(ELOG, " Failed to load %s!\n", name);
return -2;
}
cs_engine_set_image_size(csengine, bank, w, h);
log_printf(DLOG, " Ok. (%d frames)\n", s_get_bank(gfx, bank)->max+1);
return 0;
}
int gfxengine_t::loadfont(int bank, const char *name)
{
if(!csengine)
{
log_printf(ELOG, "loadfont: Engine must be open!\n");
return -10;
}
s_blitmode = S_BLITMODE_AUTO;
scalemode(_scalemode, 2);
log_printf(DLOG, "Loading font %s (bank %d)...\n", name, bank);
if(s_load_image(gfx, bank, name))
{
log_printf(ELOG, " Failed to load %s!\n", name);
return -2;
}
if(bank >= GFX_BANKS)
{
log_printf(ELOG, " Too high bank #!\n");
return -3;
}
if(!fonts[bank])
fonts[bank] = new SoFont;
if(!fonts[bank])
{
log_printf(ELOG, " Failed to instantiate SoFont!\n");
return -4;
}
fonts[bank]->ExtraSpace((xs + 127) / 256);
if(fonts[bank]->load(s_get_sprite(gfx, bank, 0)->surface))
{
s_detach_sprite(gfx, bank, 0);
log_printf(DLOG, " Ok.\n");
return 0;
}
else
{
log_printf(ELOG, " SoFont::load() failed!\n");
return -5;
}
return 0;
}
int gfxengine_t::copyrect(int bank, int sbank, int sframe, SDL_Rect *r)
{
SDL_Rect sr = *r;
if(!csengine)
{
log_printf(ELOG, "copyrect: Engine must be open!\n");
return -10;
}
log_printf(DLOG, "Copying rect from %d:%d (bank %d)...\n",
sbank, sframe, bank);
int x2 = (int)((sr.x + sr.w) * xs + 128) >> 8;
int y2 = (int)((sr.y + sr.h) * ys + 128) >> 8;
sr.x = (int)(sr.x * xs + 128) >> 8;
sr.y = (int)(sr.y * ys + 128) >> 8;
sr.w = x2 - sr.x;
sr.h = y2 - sr.y;
if(s_copy_rect(gfx, bank, sbank, sframe, &sr) < 0)
{
log_printf(ELOG, " s_copy_rect() failed!\n");
return -1;
}
log_printf(DLOG, " Ok.\n");
return 0;
}
int gfxengine_t::is_loaded(int bank)
{
if(!csengine)
return 0;
if(!s_get_bank(gfx, (unsigned)bank))
return 0;
return 1;
}
void gfxengine_t::reload()
{
log_printf(DLOG, "Reloading all banks. (Not implemented!)\n");
// s_reload_all_banks(gfx);
}
void gfxengine_t::unload(int bank)
{
if(bank < 0)
{
log_printf(DLOG, "Unloading all banks.\n");
for(int i = 0; i < GFX_BANKS; ++i)
{
delete fonts[i];
fonts[i] = NULL;
}
if(gfx)
s_delete_all_banks(gfx);
}
else
{
log_printf(DLOG, "Unloading bank %d.\n", bank);
if(bank < GFX_BANKS)
{
delete fonts[bank];
fonts[bank] = NULL;
}
if(gfx)
s_delete_bank(gfx, bank);
}
}
/*----------------------------------------------------------
Engine open/close
----------------------------------------------------------*/
void gfxengine_t::on_frame(cs_engine_t *e)
{
gfxengine->__frame();
gfxengine->frame();
}
int gfxengine_t::open(int objects, int extraflags)
{
xflags = extraflags;
if(is_open)
return show();
log_printf(DLOG, "Opening engine...\n");
csengine = cs_engine_create(_width, _height, objects);
if(!csengine)
{
log_printf(ELOG, "Failed to set up control system engine!\n");
return -1;
}
csengine->on_frame = on_frame;
cs_engine_set_wrap(csengine, wx, wy);
gfx = s_new_container(GFX_BANKS);
if(!gfx)
{
log_printf(ELOG, "Failed to set up graphics container!\n");
cs_engine_delete(csengine);
return -2;
}
reset_filters();
is_open = 1;
return show();
}
void gfxengine_t::close()
{
if(!is_open)
return;
log_printf(DLOG ,"Closing engine...\n");
stop();
hide();
unload();
cs_engine_delete(csengine);
csengine = NULL;
s_remove_filter(NULL);
sf1 = df = dsf = acf = NULL;
s_delete_container(gfx);
gfx = NULL;
is_open = 0;
}
/*----------------------------------------------------------
Settings
----------------------------------------------------------*/
void gfxengine_t::title(const char *win, const char *icon)
{
_title = win;
_icontitle = icon;
if(screen_surface)
SDL_WM_SetCaption(_title, _icontitle);
}
/*----------------------------------------------------------
Display show/hide
----------------------------------------------------------*/
int gfxengine_t::show()
{
int flags = 0;
if(!is_open)
return -1;
if(is_showing)
return 0;
if(_centered && !_fullscreen)
SDL_putenv((char *)"SDL_VIDEO_CENTERED=1");
log_printf(DLOG, "Opening screen...\n");
if(!SDL_WasInit(SDL_INIT_VIDEO))
if(SDL_InitSubSystem(SDL_INIT_VIDEO) == -1)
{
log_printf(ELOG, "Failed to initialize SDL!\n");
return -2;
}
if(_doublebuf)
flags |= SDL_DOUBLEBUF | SDL_HWSURFACE;
else
{
if(!_shadow)
flags |= SDL_HWSURFACE;
}
if(_fullscreen)
flags |= SDL_FULLSCREEN;
flags |= xflags;
screen_surface = SDL_SetVideoMode(_width, _height, _depth, flags);
if(!screen_surface)
{
log_printf(ELOG, "Failed to open display!\n");
return -3;
}
if((screen_surface->flags & SDL_DOUBLEBUF) == SDL_DOUBLEBUF)
{
if(!_doublebuf)
{
log_printf(WLOG, "Could not get"
" single buffered display.\n");
doublebuffer(1);
}
}
else
{
if(_doublebuf)
{
log_printf(WLOG, "Could not get"
" double buffered display.\n");
doublebuffer(0);
}
}
if((screen_surface->flags & SDL_HWSURFACE) == SDL_HWSURFACE)
{
if(_shadow)
{
softbuf = SDL_CreateRGBSurface(SDL_SWSURFACE,
_width, _height,
screen_surface->format->BitsPerPixel,
screen_surface->format->Rmask,
screen_surface->format->Gmask,
screen_surface->format->Bmask,
screen_surface->format->Amask);
if(!softbuf)
{
log_printf(WLOG, "Failed to create shadow buffer! "
"Trying direct rendering.\n");
shadow(0);
}
}
}
else
{
if(_shadow)
log_printf(WLOG, "Shadow buffer requested; "
"relying on SDL's shadow buffer.\n");
else
{
log_printf(WLOG, "Could not get h/w display surface.\n");
shadow(0); //...which means we're using SDL's shadow.
}
}
SDL_WM_SetCaption(_title, _icontitle);
SDL_ShowCursor(_cursor);
cs_engine_set_size(csengine, _width, _height);
csengine->filter = use_interpolation;
dither(_dither);
noalpha(alpha_threshold);
is_showing = 1;
fullwin = new window_t;
fullwin->init(this);
fullwin->place(0, 0, (int)(_width / xscale()),
(int)(_height / yscale()));
clear();
return 0;
}
void gfxengine_t::clear(Uint32 _color)
{
if(!fullwin)
return;
fullwin->background(fullwin->map_rgb(_color));
fullwin->clear();
flip();
fullwin->clear();
flip();
fullwin->clear();
}
void gfxengine_t::hide(void)
{
if(!is_showing)
return;
log_printf(DLOG, "Closing screen...\n");
stop();
delete fullwin;
fullwin = NULL;
// Make sure no windows keep old surface pointers!
window_t *w = windows;
while(w)
{
if((w->surface == screen_surface) ||
(w->surface == softbuf))
w->surface = NULL;
w = w->next;
}
if(softbuf)
{
SDL_FreeSurface(softbuf);
softbuf = NULL;
}
screen_surface = NULL;
is_showing = 0;
}
void gfxengine_t::invalidate(SDL_Rect *rect, window_t *window)
{
switch(_pages)
{
case -1:
if(_doublebuf)
__invalidate(1, rect, window);
__invalidate(0, rect, window);
break;
case 0:
__invalidate(0, NULL, NULL);
break;
case 3:
__invalidate(2, rect, window);
// Fallthrough!
case 2:
__invalidate(1, rect, window);
// Fallthrough!
case 1:
__invalidate(0, rect, window);
break;
}
}
void gfxengine_t::__invalidate(int page, SDL_Rect *rect, window_t *window)
{
if(!screen_surface)
return;
if(!rect || (_pages == 0))
{
dirtyrects[page] = 1;
dirtytable[page][0].x = 0;
dirtytable[page][0].y = 0;
dirtytable[page][0].w = screen_surface->w;
dirtytable[page][0].h = screen_surface->h;
dirtywtable[page][0] = NULL;
return;
}
/* Clip to screen (stolen from SDL_surface.c) */
SDL_Rect dr = *rect;
int Amin, Amax, Bmin, Bmax;
/* Horizontal intersection */
Amin = dr.x;
Amax = Amin + dr.w;
Bmin = screen_surface->clip_rect.x;
Bmax = Bmin + screen_surface->clip_rect.w;
if(Bmin > Amin)
Amin = Bmin;
dr.x = Amin;
if(Bmax < Amax)
Amax = Bmax;
dr.w = Amax - Amin > 0 ? Amax - Amin : 0;
/* Vertical intersection */
Amin = dr.y;
Amax = Amin + dr.h;
Bmin = screen_surface->clip_rect.y;
Bmax = Bmin + screen_surface->clip_rect.h;
if(Bmin > Amin)
Amin = Bmin;
dr.y = Amin;
if(Bmax < Amax)
Amax = Bmax;
dr.h = Amax - Amin > 0 ? Amax - Amin : 0;
if(!dr.w || !dr.h)
return;
for(int i = 0; i < dirtyrects[page]; ++i)
if(memcmp(&dirtytable[page][i], &dr, sizeof(dr)) == 0)
return;
if(dirtyrects[page] < MAX_DIRTYRECTS - 1)
{
dirtytable[page][dirtyrects[page]] = dr;
dirtywtable[page][dirtyrects[page]] = window;
++dirtyrects[page];
}
else
log_printf(ELOG, "gfxengine: Page %d out of dirtyrects!\n",
page);
}
/*----------------------------------------------------------
Engine start/stop
----------------------------------------------------------*/
void gfxengine_t::start_engine()
{
last_tick = -1000000;
}
void gfxengine_t::stop_engine()
{
}
/*----------------------------------------------------------
Control
----------------------------------------------------------*/
/*
* Run engine until stop() is called.
*
* The virtual member frame() will be called once for
* each control system frame. pre_render() and
* post_render() will be called before/after the engine
* renders each video frame.
*/
void gfxengine_t::run()
{
open();
show();
start_engine();
is_running = 1;
double toframe = 0.0f;
double fdt = 1.0f;
cs_engine_advance(csengine, 0);
while(is_running)
{
int t = (int)SDL_GetTicks();
int dt = t - last_tick;
last_tick = t;
if(abs(dt) > 500)
last_tick = t;
if(dt > 250)
dt = (int)ticks_per_frame;
if(_timefilter)
fdt += (dt - fdt) * _timefilter;
else
fdt = ticks_per_frame;
toframe += fdt / ticks_per_frame;
cs_engine_advance(csengine, toframe);
pre_render();
window->select();
cs_engine_render(csengine);
post_render();
if(_autoinvalidate)
window->invalidate();
flip();
}
stop_engine();
}
void gfxengine_t::stop()
{
if(!is_running)
return;
log_printf(DLOG, "Stopping engine...\n");
is_running = 0;
}
void gfxengine_t::cursor(int csr)
{
_cursor = csr;
if(screen_surface)
SDL_ShowCursor(csr);
}
void gfxengine_t::interpolation(int inter)
{
use_interpolation = inter;
if(!csengine)
return;
csengine->filter = use_interpolation;
}
void gfxengine_t::timefilter(float coeff)
{
if(coeff < 0.001f)
_timefilter = 0.0f;
else if(coeff > 1.0f)
_timefilter = 1.0f;
else
_timefilter = coeff;
}
void gfxengine_t::scroll_ratio(int layer, float xr, float yr)
{
if(layer < 0)
return;
if(layer >= CS_LAYERS)
return;
xratio[layer] = xr;
yratio[layer] = yr;
}
void gfxengine_t::scroll(int xscr, int yscr)
{
xscroll = xscr;
yscroll = yscr;
/* Apply current scroll pos to layers */
for(int i = 0; i < CS_LAYERS ; ++i)
{
csengine->offsets[i].v.x = (int)floor(xscroll * xratio[i]);
csengine->offsets[i].v.y = (int)floor(yscroll * yratio[i]);
}
}
void gfxengine_t::force_scroll()
{
if(csengine)
for(int i = 0; i < CS_LAYERS ; ++i)
cs_point_force(&csengine->offsets[i]);
}
int gfxengine_t::xoffs(int layer)
{
if(layer < 0)
return 0;
if(layer >= CS_LAYERS)
return 0;
return csengine->offsets[layer].gx;
}
int gfxengine_t::yoffs(int layer)
{
if(layer < 0)
return 0;
if(layer >= CS_LAYERS)
return 0;
return csengine->offsets[layer].gy;
}
SDL_Surface *gfxengine_t::surface()
{
if(softbuf)
return softbuf;
else
return screen_surface;
}
void gfxengine_t::screenshot()
{
char filename[1024];
snprintf(filename, sizeof(filename), "screen%d.bmp", screenshot_count++);
SDL_SaveBMP(screen_surface, filename);
}
/*----------------------------------------------------------
Internal stuff
----------------------------------------------------------*/
/* Default frame handler */
void gfxengine_t::frame()
{
SDL_Event ev;
while(SDL_PollEvent(&ev))
{
if(ev.type == SDL_KEYDOWN)
if(ev.key.keysym.sym == SDLK_ESCAPE)
stop();
}
}
/* Internal frame handler */
void gfxengine_t::__frame()
{
}
void gfxengine_t::pre_render()
{
}
void gfxengine_t::post_render()
{
}
void gfxengine_t::refresh_rect(SDL_Rect *r)
{
window_t *w = windows;
for(w = windows; w; w = w->next)
{
if(!w->visible())
continue;
SDL_Rect dr = *r;
/* Clip to window */
int Amin, Amax, Bmin, Bmax;
/* Horizontal intersection */
Amin = dr.x;
Amax = Amin + dr.w;
Bmin = w->phys_rect.x;
Bmax = Bmin + w->phys_rect.w;
if(Bmin > Amin)
Amin = Bmin;
dr.x = Amin;
if(Bmax < Amax)
Amax = Bmax;
dr.w = Amax - Amin > 0 ? Amax - Amin : 0;
/* Vertical intersection */
Amin = dr.y;
Amax = Amin + dr.h;
Bmin = w->phys_rect.y;
Bmax = Bmin + w->phys_rect.h;
if(Bmin > Amin)
Amin = Bmin;
dr.y = Amin;
if(Bmax < Amax)
Amax = Bmax;
dr.h = Amax - Amin > 0 ? Amax - Amin : 0;
if(dr.w && dr.h)
w->phys_refresh(&dr);
}
}
void gfxengine_t::flip()
{
if(!screen_surface)
return;
// Init dirtyrect table flipping, if necessary.
switch(_pages)
{
case -1:
if(!_doublebuf)
frontpage = backpage = 0;
else if(frontpage == backpage)
{
frontpage = 0;
backpage = 1;
}
break;
case 0:
frontpage = backpage = 0;
invalidate();
break;
case 1:
frontpage = backpage = 0;
break;
case 2:
case 3:
if(frontpage == backpage)
{
frontpage = 0;
backpage = 1;
}
break;
}
// Process the dirtyrects.
int i;
for(i = 0; i < dirtyrects[backpage]; ++i)
{
if(dirtywtable[backpage][i])
{
if(!dirtywtable[backpage][i]->visible())
continue;
SDL_Rect dr = dirtytable[backpage][i];
dirtywtable[backpage][i]->phys_refresh(&dr);
}
else
refresh_rect(&dirtytable[backpage][i]);
}
// Perform the actual flip or update
if(_shadow)
{
for(i = 0; i < dirtyrects[backpage]; ++i)
SDL_BlitSurface(softbuf,
&dirtytable[backpage][i],
screen_surface,
&dirtytable[backpage][i]);
}
if(_doublebuf)
{
dirtyrects[backpage] = 0;
if(_pages == -1)
{
backpage = !backpage;
frontpage = !frontpage;
}
else if(_pages > 1)
{
backpage = (backpage + 1) % _pages;
frontpage = (frontpage + 1) % _pages;
}
SDL_Flip(screen_surface);
}
else
{
SDL_UpdateRects(screen_surface, dirtyrects[0], dirtytable[0]);
dirtyrects[0] = 0;
}
}
/*
* Generic render() callback for sprites and tiles.
*/
void gfxengine_t::render_sprite(cs_obj_t *o)
{
SDL_Rect dest_rect;
s_sprite_t *s = gfxengine->get_sprite(o->anim.bank, o->anim.frame);
if(!s || !s->surface)
return;
int x = o->point.gx - (s->x << 8);
int y = o->point.gy - (s->y << 8);
dest_rect.x = CS2PIXEL((x * gfxengine->xs + 128) >> 8);
dest_rect.y = CS2PIXEL((y * gfxengine->ys + 128) >> 8);
dest_rect.x += (gfxengine->window->x() * gfxengine->xs + 128) >> 8;
dest_rect.y += (gfxengine->window->y() * gfxengine->xs + 128) >> 8;
SDL_BlitSurface(s->surface, NULL, gfxengine->surface(), &dest_rect);
if(!gfxengine->_autoinvalidate)
{
dest_rect.w = s->surface->w;
dest_rect.h = s->surface->h;
gfxengine->invalidate(&dest_rect);
}
}
cs_obj_t *gfxengine_t::get_obj(int layer)
{
cs_obj_t *o = cs_engine_get_obj(csengine);
if(o)
{
o->render = render_sprite;
cs_obj_layer(o, layer);
cs_obj_activate(o);
}
return o;
}
void gfxengine_t::free_obj(cs_obj_t *obj)
{
cs_obj_free(obj);
}
int gfxengine_t::objects_in_use()
{
if(!csengine)
return 0;
return csengine->pool_total - csengine->pool_free;
}
SoFont *gfxengine_t::get_font(unsigned int f)
{
if(f < GFX_BANKS)
return fonts[f];
else
return NULL;
}