Files
commandergenius/project/jni/application/enigma/src/video.cpp
2010-10-15 14:23:30 +03:00

833 lines
20 KiB
C++
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* Copyright (C) 2002,2004 Daniel Heck
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/*
* This file contains the code for initializing the video hardware and
* for various visual effects in the widest sense. This includes
*
* a) managing and rendering the mouse cursor,
* b) changing between windowed and fullscreen mode,
* c) fading between different screen using one of a number of
* predefined effects.
*
* The code in this file is independent of the game. For game-specific
* display code, refer to file display.cc
*/
#include "enigma.hh"
#include "video.hh"
#include "lua.hh"
#include "options.hh"
#include "main.hh"
#include "ecl.hh"
#include "SDL.h"
#include <cassert>
#include <cstdio>
#include <fstream>
#include "config.h"
#define SCREEN ecl::Screen::get_instance()
using namespace std;
using namespace ecl;
using namespace video;
using namespace enigma;
namespace
{
class Video_SDL {
SDL_Surface* sdlScreen;
string caption;
ecl::Screen* screen;
bool initialized;
public:
Video_SDL();
~Video_SDL();
bool init(int w, int h, int bpp, bool fullscreen);
void toggle_fullscreen();
void set_fullscreen(bool on_off);
bool is_fullscreen() const;
void set_caption(const char *str);
const string& get_caption() const { return caption; }
ecl::Screen *get_screen() { return screen; }
};
class MouseCursor {
public:
MouseCursor ();
~MouseCursor();
void set_image (ecl::Surface *s, int hotx_, int hoty_);
void move (int newx, int newy);
void redraw (); // Redraw if position/image changed
void draw(); // Draw cursor if visible
void show ();
void hide ();
Rect get_rect() const;
Rect get_oldrect() const;
bool has_changed() { return changedp; }
int get_x() const { return x; }
int get_y() const { return y; }
private:
// Private methods
void grab_bg ();
void init_bg();
void restore_bg();
// Variables
Surface *background; // Copy of screen contents behind cursor
Surface *cursor; // Pixmap of the cursor
int x, y;
int oldx, oldy;
int hotx, hoty; // Coordinates of hotspot inside cursor image
int visible;
bool changedp;
};
}
/* -------------------- Video Engine -------------------- */
Video_SDL::Video_SDL()
: sdlScreen(0),
screen(0),
initialized(false)
{}
Video_SDL::~Video_SDL()
{
SDL_WM_GrabInput(SDL_GRAB_OFF);
// if (sdlScreen != 0 && fullScreen)
// SDL_WM_ToggleFullScreen(sdlScreen);
delete screen;
}
void Video_SDL::set_caption(const char *str) {
caption = str;
if (initialized)
SDL_WM_SetCaption(str, 0);
}
bool Video_SDL::init(int w, int h, int bpp, bool fullscreen)
{
SDL_WM_SetCaption(caption.c_str(), 0);
Uint32 flags = SDL_SWSURFACE;
if (fullscreen)
flags |= SDL_FULLSCREEN;
// Try to initialize vide mode, return error code on failure
sdlScreen = 0;
bpp = SDL_VideoModeOK (w, h, bpp, flags);
if (bpp == 0)
return false;
sdlScreen = SDL_SetVideoMode(w, h, bpp, flags);
if (sdlScreen == 0)
return false;
// Video mode could be set
screen = new Screen(sdlScreen);
initialized = true;
#if 0
// the Mac SDL port seems to ignore the following ShowCursor,
// so we just set the Cursor to be invisible.
SDL_Cursor *hiddenCursor=SDL_CreateCursor(NULL, NULL, 0, 0, 0, 0);
SDL_SetCursor(hiddenCursor);
SDL_FreeCursor(hiddenCursor);
#endif
// Hack to hide the cursor after switching between
// window/fullscreen mode.
SDL_ShowCursor (SDL_ENABLE);
SDL_ShowCursor (SDL_DISABLE);
SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
SDL_DEFAULT_REPEAT_INTERVAL / 2);
return true;
}
bool Video_SDL::is_fullscreen() const
{
if (sdlScreen)
return (sdlScreen->flags & SDL_FULLSCREEN) != 0;
return false;
}
void Video_SDL::set_fullscreen(bool on_off) {
if (on_off != is_fullscreen())
toggle_fullscreen();
}
void Video_SDL::toggle_fullscreen()
{
SDL_WM_ToggleFullScreen (sdlScreen);
}
/* -------------------- MouseCursor -------------------- */
MouseCursor::MouseCursor ()
: background(0), cursor(0)
{
oldx = oldy = 0;
hotx = hoty = 0;
visible = 0;
changedp = true;
}
MouseCursor::~MouseCursor() {
delete background;
delete cursor;
}
void MouseCursor::set_image (ecl::Surface *s, int hx, int hy) {
delete cursor;
cursor = s;
hotx = hx;
hoty = hy;
if (visible > 0) {
init_bg();
}
}
void MouseCursor::draw () {
// we do not need a mouse cursor on a touchscreen device
#ifndef ANDROID
if (visible > 0) {
grab_bg();
GC gc(SCREEN->get_surface());
blit (gc, x-hotx, y-hoty, cursor);
SCREEN->update_rect (get_rect());
changedp = false;
}
#endif
}
void MouseCursor::redraw () {
if (visible > 0 && changedp) {
restore_bg ();
draw();
}
}
void MouseCursor::move(int newx, int newy) {
x = newx;
y = newy;
changedp = true;
}
void MouseCursor::show () {
if (++visible == 1) {
init_bg();
changedp=true;
}
}
void MouseCursor::hide () {
if (--visible == 0) {
changedp = true;
restore_bg();
delete background;
background=0;
}
}
Rect MouseCursor::get_rect() const {
int scrx=x-hotx;
int scry=y-hoty;
return Rect(scrx,scry,cursor->width(), cursor->height());
}
Rect MouseCursor::get_oldrect() const {
int scrx=oldx-hotx;
int scry=oldy-hoty;
return Rect(scrx,scry,cursor->width(), cursor->height());
}
void MouseCursor::init_bg() {
assert (visible > 0);
assert (cursor != 0);
if (background != 0) {
delete background;
}
background = ecl::MakeSurfaceLike (cursor->width(),
cursor->height(),
SCREEN->get_surface());
grab_bg();
}
void MouseCursor::grab_bg () {
assert (background != 0);
GC gc(background);
blit (gc, 0,0, SCREEN->get_surface(), get_rect());
oldx=x;
oldy=y;
}
void MouseCursor::restore_bg () {
if (background) {
GC gc(SCREEN->get_surface());
blit (gc, oldx-hotx, oldy-hoty, background);
SCREEN->update_rect (get_oldrect());
}
}
/* -------------------- Local Variables -------------------- */
namespace
{
Video_SDL *video_engine = 0;
MouseCursor *cursor = 0;
Surface *back_buffer = 0;
/*! List of available video modes. */
video::VMInfo video_modes[] = {
{
VM_640x480, 640, 480, 32, "640x480",
"models-32.lua", "gfx32/",
120, 78, "thumbs32", // thumbnail size/directory
Rect (0, 0, 640, 416), // game area
Rect (0, 416, 640, 64), // statusbar area
Rect (10, 425, 117, 43), // time area
Rect (100, 425, 30, 43), // moves area
Rect (152, 433, 490, 52),// inventory area
Rect (150, 434, 475, 35),// text area
VM_None, true,
},
{
VM_640x512, 640, 512, 32, "640x512",
"models-32.lua", "gfx32/",
120, 78, "thumbs32", // thumbnail size/directory
Rect (0, 0, 640, 416), // game area
Rect (0, 416, 640, 64), // statusbar area
Rect (15, 420, 110, 40), // time area
Rect (100, 420, 30, 40), // moves area
Rect (152, 433, 490, 52),// inventory area
Rect (150, 434, 475, 35), // text area
VM_640x480,
false, // 640x512 is deprecated!
},
#ifndef ANDROID
{
VM_800x600, 800, 600, 40, "800x600",
"models-40.lua", "gfx40/",
// 160, 104, "thumbs40", // thumbnail size/directory
120, 78, "thumbs32",
Rect (0, 0, 800, 520), // game area
Rect (0, 520, 800, 80), // statusbar area
Rect (15, 540, 140, 40), // time area
Rect (125, 540, 30, 40), // moves area
Rect (192, 539, 610, 46),// inventory area
Rect (185, 545, 600, 39), // text area
VM_640x480, true,
},
{
VM_1024x768, 1024, 768, 48, "1024x768",
"models-48.lua", "gfx48/",
// 160, 104, "thumbs40", // thumbnail size/directory
120, 78, "thumbs32",
Rect (32, 0, 960, 624), // game area
Rect (32, 624, 960, 96), // statusbar area
Rect (50, 640, 170, 60), // time area
Rect (185, 640, 30, 60), // moves area
Rect (260, 650, 710, 46),// inventory area
Rect (260, 655, 710, 40), // text area
VM_640x480, true,
},
#endif
};
VideoModes current_video_mode = VM_None;
}
/* -------------------- Auxiliary functions -------------------- */
namespace
{
bool vm_available (int w, int h, int &bpp, bool &fullscreen)
{
Uint32 flags = SDL_HWSURFACE;
if (fullscreen)
flags |= SDL_FULLSCREEN;
int newbpp = SDL_VideoModeOK (w, h, bpp, flags);
if (newbpp != 0) {
bpp = newbpp;
return true;
}
return false;
}
/*! This function is installed as an event filter by video::Init. It
intercepts mouse motions, which are used to update the position of
the mouse cursor (but passed on to the event queue) */
int event_filter(const SDL_Event *e)
{
if (e->type == SDL_MOUSEMOTION) {
cursor->move(e->motion.x, e->motion.y);
cursor->redraw();
}
return 1;
}
}
/* -------------------- Functions -------------------- */
void video::SetMouseCursor(ecl::Surface *s, int hotx, int hoty) {
cursor->set_image(s, hotx, hoty);
cursor->redraw();
}
void video::HideMouse() {
cursor->hide();
cursor->redraw();
}
void video::ShowMouse() {
cursor->show();
cursor->redraw();
}
int video::Mousex() {
return cursor->get_x();
}
int video::Mousey() {
return cursor->get_y();
}
/* -------------------- Input grabbing -------------------- */
TempInputGrab::TempInputGrab (bool onoff)
{
old_onoff = SetInputGrab (onoff);
}
TempInputGrab::~TempInputGrab()
{
SetInputGrab (old_onoff);
}
bool video::GetInputGrab()
{
return SDL_WM_GrabInput (SDL_GRAB_QUERY) == SDL_GRAB_ON;
}
bool video::SetInputGrab (bool onoff)
{
bool old_onoff = GetInputGrab ();
if (onoff) {
Screen *screen = GetScreen();
SDL_WarpMouse (screen->width()/2, screen->height()/2);
SDL_Event e;
while (SDL_PollEvent(&e)) {
; // swallow mouse motion event
}
SDL_WM_GrabInput (SDL_GRAB_ON);
}
else
SDL_WM_GrabInput (SDL_GRAB_OFF);
return old_onoff;
}
Surface* video::BackBuffer() {
if (back_buffer==0) {
back_buffer= Duplicate(SCREEN->get_surface());
}
return back_buffer;
}
const video::VMInfo *video::GetInfo (VideoModes vm)
{
return &video_modes[vm];
}
const video::VMInfo *video::GetInfo ()
{
return GetInfo (current_video_mode);
}
bool video::ModeAvailable (VideoModes vm)
{
const VMInfo *vminfo = GetInfo (vm);
string fname;
return (vminfo->available && app.systemFS->findFile (vminfo->initscript, fname));
}
void video::Init()
{
assert (NUMENTRIES(video_modes) == VM_COUNT);
int vidmode = Clamp (options::GetInt("VideoMode"), 0, VM_COUNT-1);
int oldvidmode = vidmode;
video_engine = new Video_SDL();
int bpp = 16; //options::BitsPerPixel;
assert(bpp==16 || bpp==32);
while (true) {
VMInfo *vminfo = &video_modes[vidmode];
int w = vminfo->width;
int h = vminfo->height;
bool fullscreen = options::GetBool("FullScreen");
if (ModeAvailable (static_cast<VideoModes> (vidmode))
&& vm_available (w, h, bpp, fullscreen)
&& video_engine->init (w, h, bpp, fullscreen))
{
// Success!
break;
}
// Video mode not available? Try the fallback video mode
vidmode = vminfo->fallback_videomode;
if (vidmode == VM_None) {
// Give up :-(
fprintf(stderr, "Couldn't open screen: %s\n", SDL_GetError());
exit(1);
}
}
current_video_mode = static_cast<VideoModes>(vidmode);
if (vidmode != oldvidmode) {
options::SetOption ("VideoMode", vidmode);
}
#ifndef MACOSX
Surface *icn = enigma::GetImage("enigma_marble");
if(icn)
SDL_WM_SetIcon(icn->get_surface(), NULL);
#endif
cursor = new MouseCursor;
int x, y;
SDL_GetMouseState(&x, &y);
cursor->move(x,y);
SDL_SetEventFilter(event_filter);
UpdateGamma();
}
void video::Shutdown()
{
SDL_SetEventFilter(0);
delete video_engine;
delete cursor;
delete back_buffer;
video_engine = 0;
cursor = 0;
back_buffer = 0;
}
void video::ChangeVideoMode()
{
MouseCursor *oldcursor = cursor;
cursor = 0;
Shutdown();
Init();
delete cursor;
cursor = oldcursor;
}
ecl::Screen * video::GetScreen() {
return SCREEN;
}
VideoModes video::GetVideoMode() {
return current_video_mode;
}
bool video::IsFullScreen()
{
return video_engine->is_fullscreen();
}
int video::GetColorDepth() {
return SCREEN->get_surface()->bipp();
}
bool video::SetFullscreen(bool onoff)
{
video_engine->set_fullscreen(onoff);
bool is_fullscreen = video_engine->is_fullscreen();
if (onoff == is_fullscreen) {
options::SetOption("FullScreen", is_fullscreen);
}
return is_fullscreen;
}
bool video::ToggleFullscreen()
{
return SetFullscreen (!video_engine->is_fullscreen());
}
void video::SetCaption(const char *str)
{
video_engine->set_caption(str);
}
const string& video::GetCaption()
{
return video_engine->get_caption();
}
void video::UpdateGamma()
{
float gamma = static_cast<float> (options::GetDouble ("Gamma"));
if (gamma < 0.25)
gamma = 0.25; // Windows does not set gamma for values < 0.2271
SDL_SetGamma (gamma, gamma, gamma);
}
void video::Screenshot (const std::string &fname)
{
// auto-create the directory if necessary
string directory;
if (ecl::split_path (fname, &directory, 0) && !ecl::FolderExists(directory)) {
ecl::FolderCreate (directory);
}
ecl::SavePNG (SCREEN->get_surface(), fname);
enigma::Log << "Wrote screenshot to '" << fname << "\n";
}
/* -------------------- Special Effects -------------------- */
void video::FX_Fade(FadeMode mode)
{
ecl::Screen *screen = ecl::Screen::get_instance();
Surface *d = screen->get_surface();
const double fadesec = 0.6;
double v = 255/fadesec;
ecl::Surface *buffer = Duplicate(d);
double dt;
double a = mode==FADEIN ? 0 : 255;
GC gc (d);
while (true) {
Uint32 otime = SDL_GetTicks();
box(gc, d->size());
buffer->set_alpha(int(a));
blit(gc, 0,0,buffer);
screen->update_all();
screen->flush_updates();
dt = (SDL_GetTicks()-otime)/1000.0;
if (mode==FADEIN && (a+=v*dt) > 255)
break;
else if (mode==FADEOUT && (a-=v*dt) < 0)
break;
}
if (mode==FADEIN) {
buffer->set_alpha(255);
blit(gc, 0,0,buffer);
} else
box (gc, d->size());
screen->update_all();
screen->flush_updates();
delete buffer;
}
void video::FX_Fly (Surface *newscr, int originx, int originy)
{
double rest_time = 0.5;
double velx = -originx / rest_time;
double vely = -originy / rest_time;
double origx = originx;
double origy = originy;
Screen *scr = SCREEN;
GC scrgc(scr->get_surface());
while (rest_time > 0)
{
Uint32 otime = SDL_GetTicks();
Rect r(static_cast<int>(origx),
static_cast<int>(origy),
scr->width(), scr->height());
blit (scrgc, r.x, r.y, newscr);
scr->update_rect(r);
scr->flush_updates();
double dt = (SDL_GetTicks()-otime)/1000.0;
if (dt > rest_time)
dt = rest_time;
rest_time -= dt;
origx += velx * dt;
origy += vely * dt;
}
}
namespace
{
class Effect_Push : public TransitionEffect {
public:
Effect_Push(ecl::Surface *newscr, int originx, int originy);
void tick (double dtime);
bool finished() const;
private:
double rest_time;
ecl::Surface *newscr;
std::auto_ptr<ecl::Surface > oldscr;
int originx, originy;
double velx, vely;
double accx, accy;
double x, y;
double t;
};
}
Effect_Push::Effect_Push(ecl::Surface *newscr_, int originx_, int originy_)
: rest_time (0.7),
newscr (newscr_),
oldscr (Duplicate(SCREEN->get_surface())),
originx (originx_),
originy (originy_),
velx (-2 * originx / rest_time),
vely (-2 * originy / rest_time),
accx (-0.5*velx/rest_time),
accy (-0.5*vely/rest_time),
x (originx),
y (originy),
t (0)
{
}
void Effect_Push::tick (double dtime)
{
Screen *scr = SCREEN;
GC scrgc(scr->get_surface());
if (rest_time > 0) {
if (dtime > rest_time)
dtime = rest_time;
rest_time -= dtime;
t+=dtime;
x = (accx*t + velx)*t + originx;
y = (accy*t + vely)*t + originy;
blit (scrgc, (int)x-originx, (int)y, oldscr.get());
blit (scrgc, (int)x, (int)y-originy, oldscr.get());
blit (scrgc, (int)x-originx, (int)y-originy, oldscr.get());
blit (scrgc, (int)x, (int) y, newscr);
scr->update_all();
scr->flush_updates();
}
else {
blit(scrgc, 0,0, newscr);
scr->update_all();
scr->flush_updates();
}
}
bool Effect_Push::finished() const
{
return rest_time <= 0;
}
TransitionEffect *
video::MakeEffect (TransitionModes tm, ecl::Surface *newscr)
{
int scrw = SCREEN->width();
int scrh = SCREEN->height();
switch (tm) {
case TM_PUSH_RANDOM: {
int xo=0, yo=0;
while (xo==0 && yo==0) {
xo = enigma::IntegerRand(-1,1)*scrw;
yo = enigma::IntegerRand(-1,1)*scrh;
}
return new Effect_Push(newscr, xo, yo);
}
case TM_PUSH_N: return new Effect_Push (newscr, 0, -scrh);
case TM_PUSH_S: return new Effect_Push (newscr, 0, +scrh);
case TM_PUSH_E: return new Effect_Push (newscr, +scrw, 0);
case TM_PUSH_W: return new Effect_Push (newscr, -scrw, 0);
default:
return 0;
};
}
void video::ShowScreen (TransitionModes tm, Surface *newscr) {
int scrw = SCREEN->width();
int scrh = SCREEN->height();
switch (tm) {
case TM_RANDOM:
break;
case TM_FADEOUTIN:
break;
case TM_SQUARES:
break;
case TM_FLY_N: FX_Fly (newscr, 0, -scrh); break;
case TM_FLY_S: FX_Fly (newscr, 0, +scrh); break;
case TM_FLY_E: FX_Fly (newscr, +scrw, 0); break;
case TM_FLY_W: FX_Fly (newscr, -scrw, 0); break;
default:
break;
}
}