Skip to content

Commit

Permalink
ADDED scaling support to the screen class.
Browse files Browse the repository at this point in the history
The purpose is to keep using the desktop resolution and aspect ratio
when running in fullscreen mode, while also keeping a fairly equal
sized view of the game world. For that purpose, a minimum and maximum
view size has been defined, so that the actual view size can be
magnified to the desktop resolution by an integer scaling factor.
x# Please enter the commit message for your changes. Lines starting
  • Loading branch information
Kai Sterker committed Oct 31, 2010
1 parent e5b5d9b commit d6a2bb8
Show file tree
Hide file tree
Showing 12 changed files with 264 additions and 16 deletions.
17 changes: 14 additions & 3 deletions src/gfx/gfx.cc
Expand Up @@ -88,6 +88,16 @@ namespace gfx
goto bigerror;
}

screen::ShadowSurface = NULL;
screen::Scale = 1;

screen::get_video_mode_p = (void(*)(u_int16*, u_int16*, u_int8*)) lt_dlsym(dlhandle, "gfx_screen_get_video_mode");
if (!screen::get_video_mode_p)
{
LOG(ERROR) << logging::indent() << lt_dlerror();
goto bigerror;
}

screen::set_video_mode_p = (bool(*)(u_int16, u_int16, u_int8)) lt_dlsym(dlhandle, "gfx_screen_set_video_mode");
if (!screen::set_video_mode_p)
{
Expand Down Expand Up @@ -151,15 +161,14 @@ namespace gfx
// setup from configuration
void setup (base::configuration & cfg)
{
// option to toggle fullscreen on/off
screen::set_fullscreen (cfg.get_int ("Video", "Fullscreen", 1) == 1);
cfg.option ("Video", "Fullscreen", base::cfg_option::BOOL);
screen::setup(cfg);

if (!(surfaces = new surface_cacher()))
{
LOG(ERROR) << logging::indent() << "Unable to create a surface cacher";
return;
}

surfaces->set_max_mem (cfg.get_int("Video", "CacheSize", DEFAULT_CACHE_SIZE));
}

Expand All @@ -169,6 +178,8 @@ namespace gfx
delete surfaces;
surfaces = NULL;

delete screen::ShadowSurface;

if (gfxcleanup) gfxcleanup();
gfxcleanup = NULL;

Expand Down
1 change: 0 additions & 1 deletion src/gfx/gfx.h
Expand Up @@ -31,7 +31,6 @@
#ifndef GFX_H_
#define GFX_H_

#include "base/configuration.h"
#include "gfx/screen.h"

/**
Expand Down
88 changes: 88 additions & 0 deletions src/gfx/screen.cc
Expand Up @@ -30,6 +30,7 @@


#include <cstdio>
#include "gfx/gfx.h"
#include "gfx/screen.h"
#include "base/logging.h"

Expand All @@ -46,14 +47,101 @@ namespace gfx
u_int16 screen::length_, screen::height_;
u_int8 screen::bytes_per_pixel_;
bool screen::fullscreen_;
u_int8 screen::Scale;
surface *screen::ShadowSurface;

void (*screen::get_video_mode_p) (u_int16 *l, u_int16 *h, u_int8 *depth) = NULL;
bool (*screen::set_video_mode_p) (u_int16 nl, u_int16 nh, u_int8 depth) = NULL;
void (*screen::update_p)() = NULL;
u_int32 (*screen::trans_color_p)() = NULL;
void (*screen::clear_p)() = NULL;
surface * (*screen::get_surface_p)() = NULL;
std::string (*screen::info_p)() = NULL;

void screen::setup (base::configuration & cfg)
{
base::cfg_option *opt;

// option to toggle fullscreen on/off
screen::set_fullscreen (cfg.get_int ("Video", "Fullscreen", 1) == 1);
cfg.option ("Video", "Fullscreen", base::cfg_option::BOOL);

opt = cfg.option ("Video", "Scale", base::cfg_option::UNDEF);
if (opt != NULL)
{
Scale = cfg.get_int ("Video", "Scale", 1);
}

opt = cfg.option ("Video", "Width", base::cfg_option::UNDEF);
if (opt != NULL)
{
length_ = cfg.get_int ("Video", "Width", 0);
}

opt = cfg.option ("Video", "Height", base::cfg_option::UNDEF);
if (opt != NULL)
{
height_ = cfg.get_int ("Video", "Height", 0);
}
}

bool screen::set_native_mode (const u_int16 & min_x, const u_int16 & max_x, const u_int16 & min_y, const u_int16 & max_y)
{
u_int16 length, height;

if (length_ != 0 && height_ != 0)
{
// configuration override
length = length_ * Scale;
height = height_ * Scale;
}
else if (is_fullscreen())
{
// scale to desktop size
get_video_mode_p (&length, &height, &bytes_per_pixel_);
for (int i = 1; true; i++)
{
length_ = length/i;
height_ = height/i;

if (length_ < min_x && height_ < min_y)
{
LOG(ERROR) << "*** error: Failed setting a valid video mode. Please configure your own!";
return false;
}

if (length_ <= max_x && height_ <= max_y)
{
Scale = i;
break;
}
}
}
else
{
// use maximum allowed view size for windowed mode
length_ = length = max_x;
height_ = height = max_y;
}

if (!set_video_mode_p (length, height, bytes_per_pixel_*8))
{
LOG(ERROR) << "*** error: Failed setting video mode to " << length << " x " << height << " @ " << (int) bytes_per_pixel_*8 << " bpp!";
return false;
}

if (Scale > 1)
{
ShadowSurface = gfx::create_surface();
ShadowSurface->set_alpha(255, 0);
ShadowSurface->resize (length_, height_);
}

LOG(ERROR) << "*** info: Set internal view to " << length_ << " x " << height_ << ".";
LOG(ERROR) << "*** info: Set display size to " << length << " x " << height << ".";
return true;
}

bool screen::set_video_mode(u_int16 nl, u_int16 nh, u_int8 depth)
{
bool res = set_video_mode_p(nl, nh, depth);
Expand Down
80 changes: 71 additions & 9 deletions src/gfx/screen.h
Expand Up @@ -32,10 +32,10 @@
#define SCREEN_H_

#include "gfx/surface.h"
#include "base/configuration.h"

namespace gfx
{

/** Screen access is made through this class.
* This static class sets video modes, flush the frame buffer to the physical
* screen and make abstraction of the real screen depth to ease the
Expand All @@ -44,10 +44,36 @@ namespace gfx
class screen
{
public:
/** Sets the video mode.
* @param nl X screen resolution.
* @param nh Y screen resolution.
* @param depth desired screen depth.
/**
* Setup the screen from settings given in the configuration
* file.
* @param cfg the configuration.
*/
static void setup (base::configuration & cfg);

/**
* Sets the video mode to the native resolution and depth,
* scaling the view if necessary. Scaling becomes necessary
* if the native resolution is bigger than the maximum
* resolution allowed for the internal view. Will fail if
* the resolution is lower than the minimum resolution
* allowed for the internal view.
*
* @param min_x minimum X game resolution.
* @param max_x maximum X game resolution.
* @param min_y minimum Y game resolution.
* @param max_y maximum Y game resolution.
*
* @return true on success, false otherwise.
*/
static bool set_native_mode (const u_int16 & min_x = 512, const u_int16 & max_x = 640, const u_int16 & min_y = 360, const u_int16 & max_y = 512);

/**
* Sets the video mode to the given resolution and depth.
* @param nl X screen resolution.
* @param nh Y screen resolution.
* @param depth desired screen depth.
* @return true on success, false otherwise.
*/
static bool set_video_mode(u_int16 nl, u_int16 nh, u_int8 depth = 0);

Expand Down Expand Up @@ -77,12 +103,29 @@ namespace gfx
{
return bytes_per_pixel_;
}


/**
* Return the factor by which the view is scaled
* and displayed on screen.
* @return scale factor >= 1.
*/
static u_int8 scale ()
{
return Scale;
}

/**
* Ensures the framebuffer is copied to the physical screen.
*
*/
static void update () { update_p(); }
static void update ()
{
if (Scale > 1)
{
ShadowSurface->scale (get_surface_p(), Scale);
}
update_p();
}

/**
* Returns the display's transparent color. (i.e. the color
Expand All @@ -100,7 +143,11 @@ namespace gfx
* New content will not appear before a call to screen::update().
* @return display surface.
*/
static surface * get_surface() { return get_surface_p(); }
static surface * get_surface()
{
if (Scale > 1) return ShadowSurface;
return get_surface_p();
}

/** Returns whether the current mode is fullscreen or windowed.
* @return
Expand All @@ -116,7 +163,11 @@ namespace gfx
* Totally clears the screen with black.
*
*/
static void clear() { clear_p(); }
static void clear()
{
if (Scale > 1) ShadowSurface->fillrect(0, 0, length_, height_, 0);
clear_p();
}

/**
* Toggles between fullscreen/windowed mode. Needs to be called
Expand Down Expand Up @@ -154,12 +205,22 @@ namespace gfx
///@}

protected:
/// whether fullscreen mode is enabled
static bool fullscreen_;

/// internal screen width
static u_int16 length_;
/// internal screen height
static u_int16 height_;
/// color depth
static u_int8 bytes_per_pixel_;

/// scale mode if scaling is active
static u_int8 Scale;
/// surface to draw on when scaling is active
static surface *ShadowSurface;

static void (*get_video_mode_p) (u_int16 *l, u_int16 *h, u_int8 *depth);
static bool (*set_video_mode_p) (u_int16 nl, u_int16 nh, u_int8 depth);
static void (*update_p)();
static u_int32 (*trans_color_p)();
Expand All @@ -168,6 +229,7 @@ namespace gfx
static std::string (*info_p)();

friend bool gfx::init(const std::string &);
friend void gfx::cleanup();
};
}

Expand Down
10 changes: 10 additions & 0 deletions src/gfx/sdl/screen_sdl.cc
Expand Up @@ -19,6 +19,7 @@

#ifdef USE_LIBTOOL
/* exported names for libltdl */
#define gfx_screen_get_video_mode _sdl_LTX_gfx_screen_get_video_mode
#define gfx_screen_set_video_mode _sdl_LTX_gfx_screen_set_video_mode
#define gfx_screen_update _sdl_LTX_gfx_screen_update
#define gfx_screen_trans_color _sdl_LTX_gfx_screen_trans_color
Expand All @@ -34,6 +35,7 @@ u_int32 trans_color = 0;

extern "C"
{
void gfx_screen_get_video_mode(u_int16 *l, u_int16 *h, u_int8 *depth);
bool gfx_screen_set_video_mode(u_int16 nl, u_int16 nh, u_int8 depth);
void gfx_screen_update();
u_int32 gfx_screen_trans_color();
Expand All @@ -42,6 +44,14 @@ extern "C"
std::string gfx_screen_info();
}

void gfx_screen_get_video_mode(u_int16 *l, u_int16 *h, u_int8 *depth)
{
const SDL_VideoInfo* vi = SDL_GetVideoInfo();
*l = vi->current_w;
*h = vi->current_h;
*depth = vi->vfmt->BytesPerPixel;
}

bool gfx_screen_set_video_mode(u_int16 nl, u_int16 nh, u_int8 depth)
{
u_int32 SDL_flags = SDL_HWSURFACE | SDL_DOUBLEBUF;
Expand Down
45 changes: 44 additions & 1 deletion src/gfx/sdl/surface_sdl.cc
Expand Up @@ -73,7 +73,7 @@ namespace gfx
{
SDL_Surface * display_target;

if (target == NULL) display_target = display->vis;
if (target == NULL) display_target = ((surface_sdl *)screen::get_surface())->vis;
else display_target = ((surface_sdl *)target)->vis;

setup_rects (x, y, sx, sy, sl, sh, da_opt);
Expand Down Expand Up @@ -219,6 +219,49 @@ namespace gfx
return col;
}

void surface_sdl::scale(surface *target, const u_int32 & factor) const
{
if (length() * factor > target->length() ||
height() * factor > target->height())
return;

lock();
target->lock();

u_int8 *target_data = (u_int8*) ((surface_sdl *)target)->vis->pixels;
s_int32 target_line_length = ((surface_sdl *)target)->vis->format->BytesPerPixel * target->length();

for (s_int32 src_y = 0; src_y < height(); ++src_y)
{
s_int32 target_x = 0;
s_int32 target_x_end = 0;

// we scale one line horizontally
for (s_int32 src_x = 0; src_x < length(); ++src_x)
{
u_int32 px = get_pix (src_x, src_y);
for (target_x_end += factor; target_x < target_x_end; ++target_x)
{
target->put_pix (target_x, src_y * factor, px);
}
}

// the next lines will be the same, so we just copy them
for (u_int32 i = 1; i < factor; i++)
{
u_int8 *target_next_line = target_data + ((surface_sdl *)target)->vis->pitch;
memcpy (target_next_line, target_data, target_line_length);
target_data = target_next_line;
}

// goto next line
target_data += ((surface_sdl *)target)->vis->pitch;
}

target->unlock();
unlock();
}

surface & surface_sdl::operator = (const surface& src)
{
const surface_sdl & src_sdl = (const surface_sdl &) src;
Expand Down

0 comments on commit d6a2bb8

Please sign in to comment.