[Scummvm-devel] SDL scaler refactoring

LionsPhil lionsphil at gmail.com
Mon Aug 2 23:40:03 CEST 2010


  Hi,

The current way scalers are implemented in SDL seems to be rather deeply 
entangled with the backend as a whole; both rendering and co-ordinate 
transforms for things like mouse events are calculated at the points of 
use, with the scaling assumed to be a simple, integer multiplier with no 
offset. This is problematic for the end goal of patch 2784961, which 
eventually wants to centre the upscaled surface within an oversized 
physical one, and for a related patch I've yet to submit (since it is a 
largely useless addition on its own) which forces ScummVM to use the 
desktop resolution if possible to avoid cheap and nasty TFT upscaling.

I propose that it might be better to refactor the scaling operations out 
to an appropriate Scaler class which has virtual member functions for 
performing game/hardware co-ordinate transforms, as well as the actual 
graphical manipulation. In addition, this gives scalers a well-defined 
context to stash any cacheable or precomputed resources (such as 
centering offsets, or the lookup table used by the HQ scalers, which is 
currently marked as FIXME for hanging around the whole time).

I'm hoping this'll stimulate some discussion with the people more deeply 
familiar with the SDL backend, but as a starting point my plan of attack 
would be (the backend should be left in a working state inbetween each 
complete step):

  - Factor out co-ordinate transforms to member functions of 
OSystem_SDL; say mapHardwareToGame(xh, hy, &gx, &gy), 
mapGameToHardware(gx, gy, &hx, &hy), still in terms of scaleFactor. (Not 
allowing for 200->240 aspect correction, since that's currently a 
separate pass?) I believe we will also need a mapHardwareToOverlay and 
converse, since although the overlay forces the Normal1x scaler (in 
internUpdateScreen), it may eventually require an offset to centre it 
within an oversized physical surface. I believe the mouse layer should 
always be on the same co-ordinate system as one or the other?
  - Migrate the scaler functions into classes. Instead of setting 
[new]scalerProc and [new]scaleFactor, the switch blocks of 
setGraphicsMode and setGraphicsModeIntern would construct an instance of 
an appropriate Scaler subclass. scalerProc is converted from a function 
pointer to a virtual method call, and scaleFactor is set to a value 
returned by scalerobject->scaleFactor().
  - Migrate mapFooToBar methods from OSystem_SDL to the Scaler class, 
and change calls appropriately. Determine the desired screen resolution 
in loadGFXMode() by querying the Scaler object. Remove scaleFactor from 
OSystem_SDL and the Scaler class.

By this point, everything about scaling co-ordinates (and graphics) 
should be encapsulated within the Scaler subclass. This also allows us 
to implement scalers with non-integer and variable scale factors, 
without forcing floating point upon everything (as keeping scaleFactor 
public would). In particular, given a Scaler base class along the lines 
of (pseudocode):

class Scaler {
     virtual void targetResolution(&w, &h);
     virtual void mapFooToBar(...);
     virtual void scale(...);
};

We can express current scalers, such as Normal2x:

class ScalerNormal2x : public Scaler {
     ScalerNormal2x(int gameWidth, int gameHeight) { ... }
     virtual void targetResolution { (return double the game dimensions) }
     ...
};

And new variable scalers, such as a best-fit aspect-maintaining stretch, 
suitable for use with fixed resolution fullscreen (e.g. construct it 
using the desktop resolution):

class ScalerStretchToFit : public Scaler {
     ScalerStretchToFit(int gameWidth, int gameHeight, int targetWidth, 
int targetHeight) { ... }
     virtual void targetResolution { (return targetWidth and targetHeight) }
     ...
};

A future development to this may be allowing Scalers to handle their own 
200->240 correction, to avoid a second pass. For example, if 
StretchToFit were a simple nearest-neighbour sampling aiming for sharp 
pixel boundaries, it could avoid the resampling performed by 
stretch200To240().

So, if you've read through all this, thoughts? :)

-- 
LionsPhil




More information about the Scummvm-devel mailing list