Page MenuHomePhabricator (Chris)

No OneTemporary

Authored By
Unknown
Size
304 KB
Referenced Files
None
Subscribers
None
This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/util/events.cpp b/util/events.cpp
index 96970953..7cdbdaeb 100644
--- a/util/events.cpp
+++ b/util/events.cpp
@@ -1,645 +1,628 @@
#ifdef USE_SDL
#include <SDL.h>
#endif
#ifdef USE_ALLEGRO5
#include <allegro5/allegro.h>
#endif
#include <vector>
#include <map>
#include "graphics/bitmap.h"
#include "events.h"
#include "exceptions/shutdown_exception.h"
#include "configuration.h"
#include "debug.h"
#include "funcs.h"
#include "thread.h"
#include "init.h"
#include "parameter.h"
#include "input/keyboard.h"
#include "input/joystick.h"
#include "input/input-manager.h"
#include "input/input-source.h"
#include "system.h"
using std::map;
namespace Util{
int do_shutdown = 0;
bool shutdown(){
return do_shutdown > 0;
}
EventManager::EventManager():
bufferKeys(false),
deferResize(false){
resize.enable = false;
#ifdef USE_ALLEGRO5
queue = al_create_event_queue();
if (al_is_keyboard_installed()){
al_register_event_source(queue, al_get_keyboard_event_source());
}
if (al_is_joystick_installed()){
al_register_event_source(queue, al_get_joystick_event_source());
}
if (Graphics::the_display != NULL){
al_register_event_source(queue, al_get_display_event_source(Graphics::the_display));
}
#endif
}
#ifdef USE_SDL
static void handleKeyDown(Keyboard & keyboard, const SDL_Event & event){
keyboard.press(event.key.keysym.sym, event.key.keysym.unicode);
}
static void handleKeyUp(Keyboard & keyboard, const SDL_Event & event){
keyboard.release(event.key.keysym.sym);
}
static void handleJoystickButtonUp(map<int, ReferenceCount<Joystick> > joysticks, const SDL_Event & event){
int device = event.jbutton.which;
int button = event.jbutton.button;
ReferenceCount<Joystick> joystick = joysticks[device];
if (joystick != NULL){
joystick->releaseButton(button);
}
}
static void handleJoystickHat(map<int, ReferenceCount<Joystick> > joysticks, const SDL_Event & event){
int device = event.jhat.which;
int motion = event.jhat.value;
ReferenceCount<Joystick> joystick = joysticks[device];
if (joystick != NULL){
joystick->hatMotion(motion);
}
#if 0
/* should up/down control left/right -- flip these values? */
#if WII
const int axis_up_down = 0;
const int axis_left_right = 1;
const int up = 1;
const int down = -1;
const int left = -1;
const int right = 1;
#else
const int axis_up_down = 1;
const int axis_left_right = 0;
const int up = -1;
const int down = 1;
const int left = -1;
const int right = 1;
#endif
switch (motion){
case SDL_HAT_CENTERED: break;
case SDL_HAT_UP: joystick->axisMotion(axis_up_down, up); break;
case SDL_HAT_DOWN: joystick->axisMotion(axis_up_down, down); break;
case SDL_HAT_RIGHT: joystick->axisMotion(axis_left_right, right); break;
case SDL_HAT_LEFT: joystick->axisMotion(axis_left_right, left); break;
default: break;
}
#endif
}
static void handleJoystickButtonDown(map<int, ReferenceCount<Joystick> > joysticks, const SDL_Event & event){
int device = event.jbutton.which;
int button = event.jbutton.button;
ReferenceCount<Joystick> joystick = joysticks[device];
if (joystick != NULL){
joystick->pressButton(button);
}
}
static void handleJoystickAxis(map<int, ReferenceCount<Joystick> > joysticks, const SDL_Event & event){
int device = event.jaxis.which;
int axis = event.jaxis.axis;
int value = event.jaxis.value;
ReferenceCount<Joystick> joystick = joysticks[device];
if (joystick != NULL){
joystick->axisMotion(axis, value);
}
}
void EventManager::runSDL(Keyboard & keyboard, map<int, ReferenceCount<Joystick> > joysticks){
keyboard.poll();
for (map<int, ReferenceCount<Joystick> >::iterator it = joysticks.begin(); it != joysticks.end(); it++){
ReferenceCount<Joystick> joystick = it->second;
if (joystick != NULL){
joystick->poll();
}
}
SDL_Event event;
/* FIXME: android gets into an infinite loop while reading events */
#ifdef ANDROID
// int good = SDL_PollEvent(&event);
// for (int check = 0; check < 10 && good; check++, good = SDL_PollEvent(&event)){
// if (SDL_PollEvent(&event) == 1){
while (SDL_PollEvent(&event) == 1){
#else
while (SDL_PollEvent(&event) == 1){
#endif
switch (event.type){
case SDL_QUIT : {
dispatch(CloseWindow);
break;
}
case SDL_KEYDOWN : {
handleKeyDown(keyboard, event);
// dispatch(Key, event.key.keysym.sym);
break;
}
case SDL_KEYUP : {
handleKeyUp(keyboard, event);
break;
}
case SDL_JOYBUTTONDOWN: {
handleJoystickButtonDown(joysticks, event);
break;
}
case SDL_JOYHATMOTION : {
handleJoystickHat(joysticks, event);
break;
}
case SDL_JOYBUTTONUP: {
handleJoystickButtonUp(joysticks, event);
break;
}
case SDL_JOYAXISMOTION: {
handleJoystickAxis(joysticks, event);
break;
}
case SDL_VIDEORESIZE : {
int width = event.resize.w;
int height = event.resize.h;
/* to keep the perspective correct
* 640/480 = 1.33333
*/
/*
double ratio = (double) 640 / (double) 480;
if (width > height){
height = (int)((double) width / ratio);
} else {
width = (int)((double) height * ratio);
}
*/
dispatch(ResizeScreen, width, height);
break;
}
default : {
break;
}
}
}
}
#endif
#ifdef USE_ALLEGRO
void EventManager::runAllegro(Keyboard & keyboard, map<int, ReferenceCount<Joystick> > joystick){
keyboard.poll();
}
#endif
#ifdef USE_ALLEGRO5
static void handleKeyDown(Keyboard & keyboard, const ALLEGRO_EVENT & event){
keyboard.press(event.keyboard.keycode, event.keyboard.unichar);
}
static void handleKeyUp(Keyboard & keyboard, const ALLEGRO_EVENT & event){
keyboard.release(event.keyboard.keycode);
}
static void handleResize(const ALLEGRO_EVENT & event){
double width = event.display.width;
double height = event.display.height;
/*
if (width < 640){
width = 640;
}
if (height < 480){
height = 480;
}
*/
/* to keep the perspective correct
* 640/480 = 1.33333
*/
/*
double ratio = (double) 640 / (double) 480;
if (width > height){
height = width / ratio;
} else {
width = height * ratio;
}
*/
ALLEGRO_DISPLAY * display = event.display.source;
al_acknowledge_resize(display);
Configuration::setScreenWidth((int) width);
Configuration::setScreenHeight((int) height);
al_resize_display(display, (int) width, (int) height);
Graphics::getScreenBuffer()->clear();
/*
ALLEGRO_TRANSFORM transformation;
al_identity_transform(&transformation);
// al_scale_transform(&transformation, (double) al_get_display_width(display) / (double) GFX_X, (double) al_get_display_height(display) / (double) GFX_Y);
al_scale_transform(&transformation, (double) width / (double) GFX_X, (double) height / (double) GFX_Y);
al_set_target_bitmap(Graphics::getScreenBuffer()->getData()->getBitmap());
al_use_transform(&transformation);
*/
}
void EventManager::runAllegro5(Keyboard & keyboard, const map<int, ReferenceCount<Joystick> > & joysticks){
keyboard.poll();
for (map<int, ReferenceCount<Joystick> >::const_iterator it = joysticks.begin(); it != joysticks.end(); it++){
ReferenceCount<Joystick> joystick = it->second;
if (joystick != NULL){
joystick->poll();
}
}
ALLEGRO_EVENT event;
while (al_get_next_event(queue, &event)){
switch (event.type){
/*
case ALLEGRO_EVENT_KEY_DOWN: {
Global::debug(0) << "Key down " << event.keyboard.keycode << std::endl;
handleKeyDown(keyboard, event);
break;
}
*/
case ALLEGRO_EVENT_DISPLAY_RESIZE: {
handleResize(event);
break;
}
case ALLEGRO_EVENT_DISPLAY_CLOSE: {
throw ShutdownException();
break;
}
case ALLEGRO_EVENT_KEY_UP: {
handleKeyUp(keyboard, event);
break;
}
case ALLEGRO_EVENT_KEY_CHAR : {
// Global::debug(0) << "Key char " << event.keyboard.keycode << " unicode " << event.keyboard.unichar << std::endl;
handleKeyDown(keyboard, event);
break;
}
case ALLEGRO_EVENT_JOYSTICK_CONFIGURATION: {
/* FIXME: tell input manager to reconfigure joysticks */
// al_reconfigure_joysticks();
break;
}
}
}
/*
if (joystick){
joystick->poll();
}
SDL_Event event;
while (SDL_PollEvent(&event) == 1){
switch (event.type){
case SDL_QUIT : {
dispatch(CloseWindow);
break;
}
case SDL_KEYDOWN : {
handleKeyDown(keyboard, event);
// dispatch(Key, event.key.keysym.sym);
break;
}
case SDL_KEYUP : {
handleKeyUp(keyboard, event);
break;
}
case SDL_JOYBUTTONDOWN: {
if (joystick != NULL){
handleJoystickButtonDown(joystick, event);
}
break;
}
case SDL_JOYHATMOTION : {
if (joystick != NULL){
handleJoystickHat(joystick, event);
}
break;
}
case SDL_JOYBUTTONUP: {
if (joystick != NULL){
handleJoystickButtonUp(joystick, event);
}
break;
}
case SDL_JOYAXISMOTION: {
if (joystick != NULL){
handleJoystickAxis(joystick, event);
}
break;
}
case SDL_VIDEORESIZE : {
int width = event.resize.w;
int height = event.resize.h;
/ * to keep the perspective correct
* 640/480 = 1.33333
* /
if (width > height){
height = (int)((double) width / 1.3333333333);
} else {
width = (int)((double) height * 1.3333333333);
}
dispatch(ResizeScreen, width, height);
break;
}
default : {
break;
}
}
}
*/
}
#endif
void EventManager::run(Keyboard & keyboard, std::map<int, ReferenceCount<Joystick> > joysticks){
#ifdef USE_SDL
runSDL(keyboard, joysticks);
#elif USE_ALLEGRO
runAllegro(keyboard, joysticks);
#elif USE_ALLEGRO5
runAllegro5(keyboard, joysticks);
#endif
}
/* kill the program if the user requests */
void EventManager::waitForThread(WaitThread & thread){
// Keyboard dummy;
while (!thread.isRunning()){
try{
/* input manager will run the event manager */
InputManager::poll();
// run(dummy);
} catch (const ShutdownException & death){
thread.kill();
throw death;
}
Util::rest(10);
}
}
EventManager::~EventManager(){
#ifdef USE_ALLEGRO5
al_destroy_event_queue(queue);
#endif
}
void EventManager::enableKeyBuffer(){
bufferKeys = true;
}
void EventManager::disableKeyBuffer(){
bufferKeys = false;
}
void EventManager::dispatch(Event type, int arg1){
switch (type){
case Key : {
if (bufferKeys){
keys.push_back(KeyType(arg1));
}
break;
}
default : {
break;
}
}
}
void EventManager::dispatch(Event type, int arg1, int arg2){
switch (type){
case ResizeScreen : {
if (deferResize){
resize.type = ResizeScreen;
resize.width = arg1;
resize.height = arg2;
resize.enable = true;
} else {
Global::debug(1) << "Resizing screen to " << arg1 << ", " << arg2 << std::endl;
if (Graphics::setGraphicsMode(0, arg1, arg2) == 0){
Configuration::setScreenWidth(arg1);
Configuration::setScreenHeight(arg2);
}
}
break;
}
default: break;
}
}
void EventManager::dispatch(Event type){
switch (type){
case CloseWindow : {
throw ShutdownException();
}
default : break;
}
}
void EventManager::deferResizeEvents(bool defer){
deferResize = defer;
if (!deferResize && resize.enable){
dispatch(resize.type, resize.width, resize.height);
resize.enable = false;
}
}
class LoopDone: public std::exception {
public:
LoopDone(){
}
~LoopDone() throw () {
}
};
Logic::~Logic(){
}
Draw::Draw():
frames(0),
second_counter(Global::second_counter),
fps(0){
}
void Draw::drawFirst(const Graphics::Bitmap & screen){
draw(screen);
}
Draw::~Draw(){
}
double Draw::getFps() const {
return fps;
}
void Draw::updateFrames(){
if (second_counter != Global::second_counter){
int difference = Global::second_counter - second_counter;
double alpha = 0.2;
/* unlikely, but just in case */
if (difference == 0){
difference = 1;
}
fps = (alpha * fps) + ((1 - alpha) * (double) frames / difference);
// fps[fps_index] = (double) frames / (double) difference;
// fps_index = (fps_index+1) % max_fps_index;
second_counter = Global::second_counter;
frames = 0;
}
frames += 1;
}
static void changeScreenMode(){
Configuration::setFullscreen(!Configuration::getFullscreen());
int gfx = (Configuration::getFullscreen() ? Global::FULLSCREEN : Global::WINDOWED);
Graphics::changeGraphicsMode(gfx, Graphics::Bitmap::getScreenWidth(), Graphics::Bitmap::getScreenHeight());
}
static void checkFullscreen(){
InputMap<int> input;
input.set(Keyboard::Key_F11, 0, true, 5);
std::vector<InputMap<int>::InputEvent> events = InputManager::getEvents(input, InputSource(true));
for (std::vector<InputMap<int>::InputEvent>::iterator it = events.begin(); it != events.end(); it++){
InputMap<int>::InputEvent event = *it;
if (!event.enabled){
continue;
}
if (event.out == 5){
changeScreenMode();
}
}
}
-static Graphics::Bitmap blackBars(const Graphics::Bitmap & screen){
- double width = screen.getWidth();
- double height = screen.getHeight();
-
- double ratio = (double) 640 / (double) 480;
-
- width = (double) height * ratio;
- if (width > screen.getWidth()){
- width = screen.getWidth();
- height = width / ratio;
- }
-
- int x = (screen.getWidth() - width) / 2;
- int y = (screen.getHeight() - height) / 2;
- return Graphics::Bitmap(screen, x, y, (int) width, (int) height);
-}
-
static void doStandardLoop(Logic & logic, Draw & draw){
if (Graphics::screenParameter.current() == NULL){
throw Exception::Base(__FILE__, __LINE__);
}
const Graphics::Bitmap & screen = *Graphics::screenParameter.current();
screen.clear();
- draw.drawFirst(blackBars(screen));
+ draw.drawFirst(screen.aspectRatio(640, 480));
screen.BlitToScreen();
Global::speed_counter4 = 0;
double runCounter = 0;
try{
const int maxCount = 20;
int frameCount = 0;
uint64_t frameTime = 0;
int logicCount = 0;
uint64_t logicTime = 0;
while (!logic.done()){
if (Global::speed_counter4 > 0){
// Global::debug(0) << "Speed counter " << Global::speed_counter4 << std::endl;
runCounter += logic.ticks(Global::speed_counter4);
Global::speed_counter4 = 0;
bool need_draw = false;
while (runCounter >= 1.0){
need_draw = true;
InputManager::poll();
checkFullscreen();
runCounter -= 1;
logicCount += 1;
uint64_t now = System::currentMilliseconds();
logic.run();
uint64_t later = System::currentMilliseconds();
logicTime += (later - now);
if (logicCount >= maxCount){
// Global::debug(0) << "Logic average " << (logicTime / logicCount) << "ms" << std::endl;
logicCount = 0;
logicTime = 0;
}
if (shutdown()){
throw ShutdownException();
}
if (logic.done()){
/* quit the loop immediately */
throw LoopDone();
}
}
if (need_draw){
frameCount += 1;
draw.updateFrames();
uint64_t now = System::currentMilliseconds();
screen.clear();
- draw.draw(blackBars(screen));
+ draw.draw(screen.aspectRatio(640, 480));
screen.BlitToScreen();
uint64_t later = System::currentMilliseconds();
frameTime += (later - now);
if (frameCount >= maxCount){
// Global::debug(0) << "Draw average " << (frameTime / frameCount) << "ms" << std::endl;
frameCount = 0;
frameTime = 0;
}
}
}
while (Global::speed_counter4 == 0){
/* if the fps is limited then don't keep redrawing */
if (Global::rateLimit){
rest(1);
} else {
draw.updateFrames();
screen.clear();
- draw.draw(blackBars(screen));
+ draw.draw(screen.aspectRatio(640, 480));
screen.BlitToScreen();
}
}
}
} catch (const LoopDone & done){
}
}
void standardLoop(Logic & logic, Draw & draw){
/* if a screen already exists (because we have nested standardLoops) then
* leave this parameter alone, otherwise set a new parameter.
*/
/*
if (Parameter<Graphics::Bitmap*>::current() == NULL){
doStandardLoop(logic, draw);
} else {
doStandardLoop(logic, draw);
}
*/
doStandardLoop(logic, draw);
}
}
diff --git a/util/funcs.h b/util/funcs.h
index 17b6f3eb..33c6f822 100644
--- a/util/funcs.h
+++ b/util/funcs.h
@@ -1,116 +1,118 @@
#ifndef _paintown_funcs_h
#define _paintown_funcs_h
#include <stdlib.h>
#include <vector>
#include <string>
#include <stdarg.h>
#include "regex.h"
#include "file-system.h"
namespace Exception{
class Base;
}
namespace Graphics{
class Bitmap;
}
namespace Util{
extern const double pi;
/* returns a number between 0 and q-1. you will never get `q'. if
* you wanted to get `q' then pass in q+1
*/
inline int rnd( int q ){
if (q <= 0){
return 0;
}
return (int)(rand() % q);
}
// std::vector< std::string > getFiles(const Filesystem::AbsolutePath & dataPath, const std::string & find );
double radians(double degree);
double degrees(double radians);
Filesystem::AbsolutePath getDataPath2();
void setDataPath( const std::string & str );
bool exists( const std::string & file );
/* check that `version' is compatible with this program, mostly used
* for network clients.
*/
// bool checkVersion(int version);
/* return a random number + some range between min/max */
int rnd( int q, int min, int max );
int max(int a, int b);
inline int min(int a, int b){
if (a<b){
return a;
}
return b;
}
double min(double a, double b);
double max(double a, double b);
double distance(double x1, double y1, double x2, double y2);
int clamp(int value, int min, int max);
double clamp(double value, double min, double max);
/* return a number between min/max */
int rnd( int min, int max );
/* rest in milliseconds */
void rest( int x );
/* rest in seconds */
void restSeconds(double x);
std::string trim(const std::string & str);
/* only upper cases the first letter of a string */
std::string upcase(std::string str);
/* lower cases the entire string */
std::string lowerCaseAll(std::string str);
/* upper cases the entire string */
std::string upperCaseAll(std::string str);
// Split string
std::vector<std::string> splitString(std::string str, char splitter);
// Join strings
std::string joinStrings(const std::vector< std::string > & message, unsigned int start = 0);
/* convert a size in bytes into human readable form.
* 234823592 = 223.94m
*/
std::string niceSize(unsigned long size);
int levenshtein(const std::string & str1, const std::string & str2);
// int getPipe(int files[2]);
void limitPrintf(char * buffer, int size, const char * format, va_list args);
void showError(const Graphics::Bitmap & screen, const Exception::Base & exception, const std::string & info);
/* will create a screen of size GFX_X, GFX_Y */
void showError(const Exception::Base & exception, const std::string & info);
std::string join(const std::vector<std::string> & list, const std::string & middle);
+template <class T, size_t N> size_t array_size(T (&)[N]){ return N; }
+
}
#endif
diff --git a/util/graphics/allegro5/bitmap.cpp b/util/graphics/allegro5/bitmap.cpp
index 8edd18eb..a5ff8a94 100644
--- a/util/graphics/allegro5/bitmap.cpp
+++ b/util/graphics/allegro5/bitmap.cpp
@@ -1,1593 +1,1600 @@
#include <sstream>
#include <allegro5/allegro5.h>
#include <allegro5/allegro_memfile.h>
#include <allegro5/allegro_primitives.h>
#include "util/debug.h"
#include "util/thread.h"
#include <vector>
namespace Graphics{
ALLEGRO_DISPLAY * the_display = NULL;
static std::vector<ALLEGRO_SHADER*> shaders;
// static ALLEGRO_SHADER * shader_default;
static ALLEGRO_SHADER * shader_shadow;
static ALLEGRO_SHADER * shader_lit_sprite;
enum BlendingType{
Translucent,
Add,
Difference,
Multiply
};
struct BlendingData{
BlendingData():
red(0), green(0), blue(0), alpha(0), type(Translucent){
}
int red, green, blue, alpha;
BlendingType type;
};
static BlendingData globalBlend;
Color makeColorAlpha(unsigned char red, unsigned char green, unsigned char blue, unsigned char alpha){
return Color(al_map_rgba(red, green, blue, alpha));
}
Color MaskColor(){
static Color mask = makeColorAlpha(0, 0, 0, 0);
return mask;
}
Color getBlendColor(){
/* sort of a hack */
if (globalBlend.type == Multiply){
return makeColorAlpha(255, 255, 255, 255);
}
return makeColorAlpha(255, 255, 255, globalBlend.alpha);
}
Color doTransBlend(const Color & color, int alpha){
unsigned char red, green, blue;
al_unmap_rgb(color.color, &red, &green, &blue);
return makeColorAlpha(red, green, blue, alpha);
/*
red *= alpha_f;
green *= alpha_f;
blue *= alpha_f;
return al_map_rgb_f(red, green, blue);
*/
}
Color transBlendColor(const Color & color){
return doTransBlend(color, globalBlend.alpha);
}
int getRealWidth(const Bitmap & what){
return al_get_bitmap_width(what.getData()->getBitmap());
}
int getRealHeight(const Bitmap & what){
return al_get_bitmap_height(what.getData()->getBitmap());
}
class Blender{
public:
Blender(){
}
virtual ~Blender(){
/* default is to draw the source and ignore the destination */
al_set_blender(ALLEGRO_ADD, ALLEGRO_ONE, ALLEGRO_ZERO);
}
};
class MaskedBlender: public Blender {
public:
MaskedBlender(){
al_set_blender(ALLEGRO_ADD, ALLEGRO_ALPHA, ALLEGRO_INVERSE_ALPHA);
}
};
class LitBlender: public Blender {
public:
LitBlender(ALLEGRO_COLOR lit){
// al_set_blend_color(lit);
// al_set_blender(ALLEGRO_ADD, ALLEGRO_ONE_MINUS_DST_COLOR, ALLEGRO_ZERO);
}
};
class TransBlender: public Blender {
public:
TransBlender(){
switch (globalBlend.type){
case Translucent: al_set_blender(ALLEGRO_ADD, ALLEGRO_ALPHA, ALLEGRO_INVERSE_ALPHA); break;
case Add: al_set_blender(ALLEGRO_ADD, ALLEGRO_ONE, ALLEGRO_ONE); break;
case Multiply: al_set_blender(ALLEGRO_ADD, ALLEGRO_DEST_COLOR, ALLEGRO_INVERSE_ALPHA); break;
case Difference: al_set_blender(ALLEGRO_DEST_MINUS_SRC, ALLEGRO_ONE, ALLEGRO_ONE); break;
}
}
};
static const int WINDOWED = 0;
static const int FULLSCREEN = 1;
// static Bitmap * Scaler = NULL;
Bitmap::Bitmap():
mustResize(false),
bit8MaskColor(makeColor(0, 0, 0)),
width(0),
height(0){
/* TODO */
}
Bitmap::Bitmap( const char * load_file ):
mustResize(false),
bit8MaskColor(makeColor(0, 0, 0)){
internalLoadFile(load_file);
width = al_get_bitmap_width(getData()->getBitmap());
height = al_get_bitmap_height(getData()->getBitmap());
}
Bitmap::Bitmap( const std::string & load_file ):
mustResize(false),
bit8MaskColor(makeColor(0, 0, 0)){
internalLoadFile(load_file.c_str());
width = al_get_bitmap_width(getData()->getBitmap());
height = al_get_bitmap_height(getData()->getBitmap());
}
Bitmap::Bitmap(ALLEGRO_BITMAP * who, bool deep_copy):
mustResize(false),
bit8MaskColor(makeColor(0, 0, 0)){
if (deep_copy){
ALLEGRO_BITMAP * clone = al_clone_bitmap(who);
setData(Util::ReferenceCount<BitmapData>(new BitmapData(clone)));
} else {
setData(Util::ReferenceCount<BitmapData>(new BitmapData(who)));
}
this->width = al_get_bitmap_width(getData()->getBitmap());
this->height = al_get_bitmap_height(getData()->getBitmap());
}
Bitmap::Bitmap(int width, int height):
mustResize(false),
bit8MaskColor(makeColor(0, 0, 0)){
ALLEGRO_BITMAP * bitmap = al_create_bitmap(width, height);
if (bitmap == NULL){
std::ostringstream out;
out << "Could not create bitmap with dimensions " << width << ", " << height;
throw BitmapException(__FILE__, __LINE__, out.str());
}
/*
if (al_get_bitmap_flags(bitmap) & ALLEGRO_VIDEO_BITMAP){
ALLEGRO_BITMAP * old = al_get_target_bitmap();
al_set_target_bitmap(bitmap);
al_use_shader(shader_default);
al_set_target_bitmap(old);
}
*/
setData(Util::ReferenceCount<BitmapData>(new BitmapData(bitmap)));
this->width = al_get_bitmap_width(getData()->getBitmap());
this->height = al_get_bitmap_height(getData()->getBitmap());
}
Bitmap::Bitmap( const Bitmap & copy, bool deep_copy):
mustResize(false),
bit8MaskColor(copy.bit8MaskColor),
width(copy.width),
height(copy.height){
if (deep_copy){
ALLEGRO_BITMAP * clone = al_clone_bitmap(copy.getData()->getBitmap());
setData(Util::ReferenceCount<BitmapData>(new BitmapData(clone)));
} else {
setData(copy.getData());
}
}
void Bitmap::convertToVideo(){
ALLEGRO_BITMAP * original = getData()->getBitmap();
ALLEGRO_BITMAP * copy = al_clone_bitmap(original);
if (copy == NULL){
throw BitmapException(__FILE__, __LINE__, "Could not create video bitmap");
}
al_destroy_bitmap(getData()->getBitmap());
getData()->setBitmap(copy);
/*
if (al_get_bitmap_flags(copy) & ALLEGRO_VIDEO_BITMAP){
ALLEGRO_BITMAP * old = al_get_target_bitmap();
al_set_target_bitmap(copy);
al_use_shader(shader_default);
al_set_target_bitmap(old);
}
*/
}
void changeTarget(const Bitmap & from, const Bitmap & who){
/* pray that if drawing is held then who is already the current target */
if (!al_is_bitmap_drawing_held()){
if (al_get_target_bitmap() != who.getData()->getBitmap()){
al_set_target_bitmap(who.getData()->getBitmap());
}
if ((al_get_bitmap_flags(who.getData()->getBitmap()) & ALLEGRO_VIDEO_BITMAP) &&
(al_get_bitmap_flags(from.getData()->getBitmap()) & ALLEGRO_MEMORY_BITMAP)){
((Bitmap&) from).convertToVideo();
/* How can from == who? If they were the same then the bitmap flags above
* would not have been different.
*/
if (&from == &who){
al_set_target_bitmap(who.getData()->getBitmap());
}
}
}
}
void changeTarget(const Bitmap * from, const Bitmap & who){
changeTarget(*from, who);
}
void changeTarget(const Bitmap & from, const Bitmap * who){
changeTarget(from, *who);
}
void changeTarget(const Bitmap * from, const Bitmap * who){
changeTarget(*from, *who);
}
void dumpColor(const Color & color){
unsigned char red, green, blue, alpha;
al_unmap_rgba(color.color, &red, &green, &blue, &alpha);
Global::debug(0) << "red " << (int) red << " green " << (int) green << " blue " << (int) blue << " alpha " << (int) alpha << std::endl;
}
Color pcxMaskColor(unsigned char * data, const int length){
if (length >= 769){
if (data[length - 768 - 1] == 12){
unsigned char * palette = &data[length - 768];
unsigned char red = palette[0];
unsigned char green = palette[1];
unsigned char blue = palette[2];
return makeColorAlpha(red, green, blue, 255);
}
}
return makeColorAlpha(255, 255, 255, 255);
}
Bitmap memoryPCX(unsigned char * const data, const int length, const bool mask){
ALLEGRO_FILE * memory = al_open_memfile((void *) data, length, "r");
ALLEGRO_BITMAP * pcx = al_load_bitmap_f(memory, ".pcx");
al_fclose(memory);
if (pcx == NULL){
throw BitmapException(__FILE__, __LINE__, "Could not load pcx");
}
// dumpColor(al_get_pixel(pcx, 0, 0));
Bitmap out(pcx);
out.set8BitMaskColor(pcxMaskColor(data, length));
return out;
}
static bool isVideoBitmap(ALLEGRO_BITMAP * bitmap){
return (al_get_bitmap_flags(bitmap) & ALLEGRO_VIDEO_BITMAP) &&
!(al_get_bitmap_flags(bitmap) & ALLEGRO_MEMORY_BITMAP);
}
void Bitmap::replaceColor(const Color & original, const Color & replaced){
changeTarget(this, this);
if (isVideoBitmap(getData()->getBitmap())){
al_lock_bitmap(getData()->getBitmap(), ALLEGRO_PIXEL_FORMAT_ANY, ALLEGRO_LOCK_READWRITE);
}
int width = getRealWidth(*this);
int height = getRealHeight(*this);
for (int x = 0; x < width; x++){
for (int y = 0; y < height; y++){
Color pixel = getPixel(x, y);
if (pixel == original){
al_put_pixel(x, y, replaced.color);
}
}
}
if (isVideoBitmap(getData()->getBitmap())){
al_unlock_bitmap(getData()->getBitmap());
}
}
static ALLEGRO_BITMAP * memoryGIF(const char * data, int length){
ALLEGRO_FILE * memory = al_open_memfile((void *) data, length, "r");
al_fclose(memory);
/* FIXME: get gif addon for a5 */
#if 0
RGB * palette = NULL;
/* algif will close the packfile for us in both error and success cases */
BITMAP * gif = load_gif_packfile(pack, palette);
if (!gif){
al_fclose(memory);
// pack_fclose(pack);
ostringstream out;
out <<"Could not load gif from memory: " << (void*) data << " length " << length;
throw LoadException(__FILE__, __LINE__, out.str());
}
BITMAP * out = create_bitmap(gif->w, gif->h);
blit(gif, out, 0, 0, 0, 0, gif->w, gif->h);
destroy_bitmap(gif);
// pack_fclose(pack);
#endif
ALLEGRO_BITMAP * out = NULL;
return out;
}
void Bitmap::internalLoadFile(const char * path){
this->path = path;
ALLEGRO_BITMAP * loaded = al_load_bitmap(path);
if (loaded == NULL){
std::ostringstream out;
out << "Could not load file '" << path << "'";
throw BitmapException(__FILE__, __LINE__, out.str());
}
al_convert_mask_to_alpha(loaded, al_map_rgb(255, 0, 255));
setData(Util::ReferenceCount<BitmapData>(new BitmapData(loaded)));
}
static ALLEGRO_BITMAP * do_load_from_memory(const char * data, int length, const char * type){
ALLEGRO_FILE * memory = al_open_memfile((void*) data, length, "r");
ALLEGRO_BITMAP * bitmap = al_load_bitmap_f(memory, type);
al_fclose(memory);
al_convert_mask_to_alpha(bitmap, al_map_rgb(255, 0, 255));
return bitmap;
}
static ALLEGRO_BITMAP * load_bitmap_from_memory(const char * data, int length, ImageFormat type){
switch (type){
case FormatBMP: return do_load_from_memory(data, length, ".bmp");
case FormatPNG: return do_load_from_memory(data, length, ".png");
case FormatJPG: return do_load_from_memory(data, length, ".jpg");
case FormatPCX: return do_load_from_memory(data, length, ".pcx");
case FormatTGA: return do_load_from_memory(data, length, ".tga");
case FormatTIF: return do_load_from_memory(data, length, ".tif");
case FormatXPM: return do_load_from_memory(data, length, ".xpm");
case FormatUnknown: break;
case FormatGIF : {
return memoryGIF(data, length);
break;
}
}
std::ostringstream out;
out << "Could not load the bitmap because its format was not known";
throw BitmapException(__FILE__, __LINE__, out.str());
}
Bitmap::Bitmap(const char * data, int length):
mustResize(false),
bit8MaskColor(makeColor(0, 0, 0)){
loadFromMemory(data, length);
}
void Bitmap::loadFromMemory(const char * data, int length){
setData(Util::ReferenceCount<BitmapData>(new BitmapData(load_bitmap_from_memory(data, length, identifyImage((const unsigned char *) data, length)))));
if (getData()->getBitmap() == NULL){
std::ostringstream out;
out << "Could not create bitmap from memory";
throw BitmapException(__FILE__, __LINE__, out.str());
}
width = al_get_bitmap_width(getData()->getBitmap());
height = al_get_bitmap_height(getData()->getBitmap());
}
Bitmap::Bitmap( const Bitmap & copy, int x, int y, int width, int height ):
mustResize(false),
bit8MaskColor(copy.bit8MaskColor),
width(width),
height(height){
path = copy.getPath();
ALLEGRO_BITMAP * his = copy.getData()->getBitmap();
if (x < 0)
x = 0;
if (y < 0)
y = 0;
if (width < 1 || height < 1){
std::ostringstream out;
out << "Attempting to create a sub-bitmap.";
if (width < 1){
out << " Width was less than 1: " << width;
}
if (height < 1){
out << " Height was less than 1: " << height;
}
throw BitmapException(__FILE__, __LINE__, out.str());
}
/*
if (width + x > al_get_bitmap_width(his)){
width = al_get_bitmap_width(his) - x;
}
if (height + y > al_get_bitmap_height(his)){
height = al_get_bitmap_height(his) - y;
}
*/
ALLEGRO_BITMAP * old_target = al_get_target_bitmap();
ALLEGRO_TRANSFORM transform;
al_identity_transform(&transform);
if (al_get_target_bitmap() != copy.getData()->getBitmap()){
al_set_target_bitmap(copy.getData()->getBitmap());
}
if (al_get_current_transform() != NULL){
if (old_target != copy.getData()->getBitmap()){
al_set_target_bitmap(copy.getData()->getBitmap());
}
al_copy_transform(&transform, al_get_current_transform());
}
float x_scaled = x;
float y_scaled = y;
float width_scaled = width;
float height_scaled = height;
al_transform_coordinates(&transform, &x_scaled, &y_scaled);
al_transform_coordinates(&transform, &width_scaled, &height_scaled);
// ALLEGRO_BITMAP * sub = al_create_sub_bitmap(his, x, y, width, height);
ALLEGRO_BITMAP * sub = al_create_sub_bitmap(his, (int) x_scaled, (int) y_scaled, (int) width_scaled, (int) height_scaled);
// ALLEGRO_BITMAP * sub = al_create_sub_bitmap(his, (int) x_scaled, (int) y_scaled, width, height);
setData(Util::ReferenceCount<BitmapData>(new BitmapData(sub)));
al_set_target_bitmap(sub);
al_use_transform(&transform);
al_set_target_bitmap(old_target);
}
static bool isBackBuffer(ALLEGRO_BITMAP * bitmap){
return bitmap == al_get_backbuffer(the_display);
}
int Bitmap::getWidth() const {
/* Always return the true dimensions of the backbuffer */
if (getData() != NULL && isBackBuffer(getData()->getBitmap())){
return al_get_bitmap_width(getData()->getBitmap());
}
return width;
/*
if (getData()->getBitmap() != NULL){
return al_get_bitmap_width(getData()->getBitmap());
}
return 0;
*/
}
int getRed(Color color){
unsigned char red, green, blue;
al_unmap_rgb(color.color, &red, &green, &blue);
return red;
}
int getAlpha(Color color){
unsigned char red, green, blue, alpha;
al_unmap_rgba(color.color, &red, &green, &blue, &alpha);
return alpha;
}
int getGreen(Color color){
unsigned char red, green, blue;
al_unmap_rgb(color.color, &red, &green, &blue);
return green;
}
int getBlue(Color color){
unsigned char red, green, blue;
al_unmap_rgb(color.color, &red, &green, &blue);
return blue;
}
Color makeColor(int red, int blue, int green){
return Color(al_map_rgb(red, blue, green));
}
int Bitmap::getHeight() const {
if (getData() != NULL && isBackBuffer(getData()->getBitmap())){
return al_get_bitmap_height(getData()->getBitmap());
}
return height;
/*
if (getData()->getBitmap() != NULL){
return al_get_bitmap_height(getData()->getBitmap());
}
return 0;
*/
}
void initializeExtraStuff(){
// al_set_new_bitmap_format(ALLEGRO_PIXEL_FORMAT_RGB_565);
}
std::string defaultVertexShader(){
return std::string(al_get_default_shader_source(ALLEGRO_SHADER_GLSL, ALLEGRO_VERTEX_SHADER));
}
std::string defaultPixelShader(){
return std::string(al_get_default_shader_source(ALLEGRO_SHADER_GLSL, ALLEGRO_PIXEL_SHADER));
}
void setShaderSampler(ALLEGRO_SHADER * shader, const std::string & name, const Bitmap & texture, int unit){
al_set_shader_sampler(name.c_str(), texture.getData()->getBitmap(), unit);
}
void setShaderBool(ALLEGRO_SHADER * shader, const std::string & name, bool value){
setShaderInt(shader, name, value);
}
void setShaderInt(ALLEGRO_SHADER * shader, const std::string & name, int value){
al_set_shader_int(name.c_str(), value);
}
void setShaderFloat(ALLEGRO_SHADER * shader, const std::string & name, float value){
al_set_shader_float(name.c_str(), value);
}
void setShaderVec4(ALLEGRO_SHADER * shader, const std::string & name, float v1, float v2, float v3, float v4){
float vector[4];
vector[0] = v1;
vector[1] = v2;
vector[2] = v3;
vector[3] = v4;
al_set_shader_float_vector(name.c_str(), 4, &vector[0], 1);
}
ALLEGRO_SHADER * create_shader(const std::string & vertex, const std::string & pixel){
ALLEGRO_SHADER * shader = al_create_shader(ALLEGRO_SHADER_GLSL);
if (shader == NULL){
return NULL;
}
if (!al_attach_shader_source(shader, ALLEGRO_VERTEX_SHADER, vertex.c_str())){
Global::debug(0) << "attach vertex shader source failed: " << al_get_shader_log(shader) << std::endl << vertex << std::endl;
return NULL;
}
if (!al_attach_shader_source(shader, ALLEGRO_PIXEL_SHADER, pixel.c_str())){
Global::debug(0) << "attach pixel shader source failed: " << al_get_shader_log(shader) << std::endl << pixel << std::endl;
return NULL;
}
if (!al_build_shader(shader)){
Global::debug(0) << "shader al_link_shader failed: " << al_get_shader_log(shader) << std::endl;
return NULL;
}
return shader;
}
int changeGraphicsMode(int mode, int width, int height){
switch (mode){
case FULLSCREEN: {
al_set_display_flag(the_display, ALLEGRO_FULLSCREEN_WINDOW, true);
break;
}
case WINDOWED: {
al_set_display_flag(the_display, ALLEGRO_FULLSCREEN_WINDOW, false);
break;
}
}
return !al_resize_display(the_display, width, height);
}
static int createShaders(){
try{
shader_shadow = create_shader(defaultVertexShader(), Storage::readFile(Storage::instance().find(Filesystem::RelativePath("shaders/shadow.fragment.glsl"))));
if (shader_shadow == NULL){
return 1;
}
shaders.push_back(shader_shadow);
Global::debug(1) << "Created shadow shader" << std::endl;
} catch (const Filesystem::NotFound & fail){
Global::debug(0) << "Could not load shadow shader: " << fail.getTrace() << std::endl;
return 1;
}
try{
shader_lit_sprite = create_shader(defaultVertexShader(), Storage::readFile(Storage::instance().find(Filesystem::RelativePath("shaders/lit-sprite.fragment.glsl"))));
if (shader_lit_sprite == NULL){
return 1;
}
shaders.push_back(shader_lit_sprite);
Global::debug(1) << "Created lit sprite shader" << std::endl;
} catch (const Filesystem::NotFound & fail){
Global::debug(0) << "Could not load lit sprite shader: " << fail.getTrace() << std::endl;
return 1;
}
return 0;
}
int setGraphicsMode(int mode, int width, int height){
initializeExtraStuff();
/* FIXME: the configuration should pass in fullscreen mode here */
#ifdef IPHONE
mode = FULLSCREEN;
#endif
switch (mode){
case FULLSCREEN: {
#ifdef IPHONE
al_set_new_display_option(ALLEGRO_SUPPORTED_ORIENTATIONS, ALLEGRO_DISPLAY_ORIENTATION_LANDSCAPE, ALLEGRO_SUGGEST);
al_set_new_display_flags(ALLEGRO_FULLSCREEN_WINDOW | ALLEGRO_PROGRAMMABLE_PIPELINE);
#else
#ifdef ANDROID
al_set_new_display_flags(ALLEGRO_FULLSCREEN | ALLEGRO_PROGRAMMABLE_PIPELINE);
#else
al_set_new_display_flags(ALLEGRO_FULLSCREEN_WINDOW | ALLEGRO_RESIZABLE |
ALLEGRO_OPENGL | ALLEGRO_PROGRAMMABLE_PIPELINE);
#endif
#endif
break;
}
case WINDOWED: {
al_set_new_display_flags(ALLEGRO_WINDOWED | ALLEGRO_RESIZABLE |
ALLEGRO_PROGRAMMABLE_PIPELINE |
ALLEGRO_OPENGL);
break;
}
default: break;
}
the_display = al_create_display(width, height);
if (the_display == NULL){
std::ostringstream out;
out << "Could not create display with dimensions " << width << ", " << height;
throw BitmapException(__FILE__, __LINE__, out.str());
}
// Global::debug(0) << "Set width " << al_get_display_width(the_display) << " height " << al_get_display_height(the_display) << std::endl;
// Global::debug(0) << "Backbuffer width " << al_get_bitmap_width(al_get_backbuffer(the_display)) << " height " << al_get_bitmap_height(al_get_backbuffer(the_display)) << std::endl;
try{
/* TODO: maybe find a more general way to get the icon */
ALLEGRO_BITMAP * icon = al_load_bitmap(Storage::instance().find(Filesystem::RelativePath("menu/icon.bmp")).path().c_str());
if (icon != NULL){
al_set_display_icon(the_display, icon);
}
} catch (const Filesystem::NotFound & fail){
Global::debug(0) << "Could not set window icon: " << fail.getTrace() << std::endl;
}
Screen = new Bitmap(al_get_backbuffer(the_display));
/* dont destroy the backbuffer */
Screen->getData()->setDestroy(false);
/*
ALLEGRO_TRANSFORM transformation;
al_identity_transform(&transformation);
al_scale_transform(&transformation, (double) Screen->getWidth() / (double) width, (double) Screen->getHeight() / (double) height);
al_set_target_bitmap(Screen->getData()->getBitmap());
al_use_transform(&transformation);
*/
// Scaler = new Bitmap(width, height);
/* default drawing mode */
// al_set_blender(ALLEGRO_ADD, ALLEGRO_ONE, ALLEGRO_ZERO);
al_set_blender(ALLEGRO_ADD, ALLEGRO_ALPHA, ALLEGRO_INVERSE_ALPHA);
// shader_default = create_shader(defaultVertexShader(), defaultPixelShader());
// al_use_shader(shader_default);
/* Default shader */
/*
shader_default = al_create_shader(ALLEGRO_SHADER_GLSL);
al_attach_shader_source(shader_default, ALLEGRO_VERTEX_SHADER, al_get_default_glsl_vertex_shader());
al_attach_shader_source(shader_default, ALLEGRO_PIXEL_SHADER, al_get_default_glsl_pixel_shader());
if (!al_link_shader(shader_default)){
Global::debug(0) << "default shader al_link_shader failed: " << al_get_shader_log(shader_default) << std::endl;
return 1;
}
*/
// al_set_shader(the_display, shader_default);
// shaders.push_back(shader_default);
// Global::debug(1) << "Created default shader" << std::endl;
if (createShaders()){
return 1;
}
return 0;
}
void Bitmap::lock() const {
al_lock_bitmap(getData()->getBitmap(), ALLEGRO_PIXEL_FORMAT_ANY, ALLEGRO_LOCK_READWRITE);
}
void Bitmap::lock(int x, int y, int width, int height) const {
al_lock_bitmap_region(getData()->getBitmap(), x, y, width, height, ALLEGRO_PIXEL_FORMAT_ANY, ALLEGRO_LOCK_READWRITE);
}
void Bitmap::unlock() const {
al_unlock_bitmap(getData()->getBitmap());
}
Color Bitmap::getPixel(const int x, const int y) const {
// changeTarget(this, this);
return Color(al_get_pixel(getData()->getBitmap(), x, y));
}
void Bitmap::putPixel(int x, int y, Color pixel) const {
changeTarget(this, this);
// al_put_pixel(x, y, pixel);
al_draw_pixel(x, y, pixel.color);
}
void Bitmap::putPixelNormal(int x, int y, Color col) const {
putPixel(x, y, col);
/*
changeTarget(this, this);
al_put_pixel(x, y, col.color);
*/
}
void Bitmap::fill(Color color) const {
changeTarget(this, this);
al_clear_to_color(color.color);
}
void Bitmap::startDrawing() const {
/* we are about to draw on this bitmap so make sure we are the target */
changeTarget(this, this);
al_hold_bitmap_drawing(true);
}
void Bitmap::endDrawing() const {
al_hold_bitmap_drawing(false);
}
void TranslucentBitmap::startDrawing() const {
al_set_blender(ALLEGRO_ADD, ALLEGRO_ALPHA, ALLEGRO_INVERSE_ALPHA);
}
void TranslucentBitmap::endDrawing() const {
al_set_blender(ALLEGRO_ADD, ALLEGRO_ONE, ALLEGRO_ZERO);
}
Color Bitmap::blendColor(const Color & input) const {
return input;
}
Color TranslucentBitmap::blendColor(const Color & color) const {
unsigned char red, green, blue;
unsigned char alpha = globalBlend.alpha;
al_unmap_rgb(color.color, &red, &green, &blue);
return makeColorAlpha(red, green, blue, alpha);
}
void Bitmap::StretchHqx(const Bitmap & where, const int sourceX, const int sourceY, const int sourceWidth, const int sourceHeight, const int destX, const int destY, const int destWidth, const int destHeight) const {
/* TODO */
}
void Bitmap::StretchXbr(const Bitmap & where, const int sourceX, const int sourceY, const int sourceWidth, const int sourceHeight, const int destX, const int destY, const int destWidth, const int destHeight) const {
/* TODO */
}
void Bitmap::Stretch(const Bitmap & where, const int sourceX, const int sourceY, const int sourceWidth, const int sourceHeight, const int destX, const int destY, const int destWidth, const int destHeight) const {
changeTarget(this, where);
al_draw_scaled_bitmap(getData()->getBitmap(),
sourceX, sourceY, sourceWidth, sourceHeight,
destX, destY, destWidth, destHeight,
0);
}
void Bitmap::StretchBy2(const Bitmap & where){
Stretch(where, 0, 0, getWidth(), getHeight(), 0, 0, getWidth() * 2, getHeight() * 2);
}
void Bitmap::StretchBy4(const Bitmap & where){
Stretch(where, 0, 0, getWidth(), getHeight(), 0, 0, getWidth() * 4, getHeight() * 4);
}
void Bitmap::drawRotate(const int x, const int y, const int angle, const Bitmap & where ){
changeTarget(this, where);
MaskedBlender blender;
al_draw_rotated_bitmap(getData()->getBitmap(), getWidth() / 2, getHeight() / 2, x, y, Util::radians(angle), ALLEGRO_FLIP_HORIZONTAL);
}
void Bitmap::drawPivot( const int centerX, const int centerY, const int x, const int y, const int angle, const Bitmap & where ){
changeTarget(this, where);
MaskedBlender blender;
al_draw_rotated_bitmap(getData()->getBitmap(), getWidth() / 2, getHeight() / 2, x, y, Util::radians(-angle), 0);
}
void Bitmap::drawPivot( const int centerX, const int centerY, const int x, const int y, const int angle, const double scale, const Bitmap & where ){
/* TODO */
}
void Bitmap::drawStretched( const int x, const int y, const int new_width, const int new_height, const Bitmap & who ) const {
/* FIXME */
changeTarget(this, who);
MaskedBlender blender;
al_draw_scaled_bitmap(getData()->getBitmap(), 0, 0, al_get_bitmap_width(getData()->getBitmap()), al_get_bitmap_height(getData()->getBitmap()), x, y, new_width, new_height, 0);
#if 0
ALLEGRO_TRANSFORM save;
al_copy_transform(&save, al_get_current_transform());
ALLEGRO_TRANSFORM stretch;
al_identity_transform(&stretch);
// al_translate_transform(&stretch, x / ((double) new_width / getWidth()), y / ((double) new_height / getHeight()));
al_scale_transform(&stretch, (double) new_width / getWidth(), (double) new_height / getHeight());
al_translate_transform(&stretch, x, y);
// al_translate_transform(&stretch, -x / ((double) new_width / getWidth()), -y / ((double) (new_height / getHeight())));
al_use_transform(&stretch);
/* any source pixels with an alpha value of 0 will be masked */
// al_draw_bitmap(getData().getBitmap(), x, y, 0);
al_draw_bitmap(getData().getBitmap(), 0, 0, 0);
al_use_transform(&save);
#endif
}
Bitmap Bitmap::scaleTo(const int width, const int height) const {
if (width == getRealWidth(*this) && height == getRealHeight(*this)){
return *this;
}
Bitmap scaled(width, height);
changeTarget(*this, scaled);
al_draw_scaled_bitmap(getData()->getBitmap(), 0, 0, getRealWidth(*this), getRealHeight(*this),
0, 0, width, height, 0);
return scaled;
}
void Bitmap::Blit(const int mx, const int my, const int width, const int height, const int wx, const int wy, const Bitmap & where) const {
// double start = al_get_time();
// changeTarget(this, where);
// al_set_blender(ALLEGRO_ADD, ALLEGRO_ONE, ALLEGRO_ZERO);
/*
if (&where != Screen){
al_draw_bitmap(getData().getBitmap(), wx, wy, 0);
}
*/
changeTarget(this, where);
Bitmap part(*this, mx, my, width, height);
// al_use_shader(shader_default);
// al_set_blender(ALLEGRO_ADD, ALLEGRO_ONE, ALLEGRO_ZERO);
al_draw_bitmap(part.getData()->getBitmap(), wx, wy, 0);
/*
double end = al_get_time();
Global::debug(0) << "Draw in " << (end - start) << " seconds" << std::endl;
*/
}
void Bitmap::drawHFlip(const int x, const int y, const Bitmap & where) const {
draw(x, y, NULL, where, ALLEGRO_FLIP_HORIZONTAL);
}
void Bitmap::drawHFlip(const int x, const int y, Filter * filter, const Bitmap & where) const {
draw(x, y, filter, where, ALLEGRO_FLIP_HORIZONTAL);
}
void Bitmap::drawVFlip( const int x, const int y, const Bitmap & where ) const {
draw(x, y, NULL, where, ALLEGRO_FLIP_VERTICAL);
}
void Bitmap::drawVFlip( const int x, const int y, Filter * filter, const Bitmap & where ) const {
draw(x, y, filter, where, ALLEGRO_FLIP_VERTICAL);
}
void Bitmap::drawHVFlip( const int x, const int y, const Bitmap & where ) const {
draw(x, y, NULL, where, ALLEGRO_FLIP_HORIZONTAL | ALLEGRO_FLIP_VERTICAL);
}
void Bitmap::drawHVFlip( const int x, const int y, Filter * filter, const Bitmap & where ) const {
draw(x, y, filter, where, ALLEGRO_FLIP_HORIZONTAL | ALLEGRO_FLIP_VERTICAL);
}
void Bitmap::BlitMasked(const int mx, const int my, const int width, const int height, const int wx, const int wy, const Bitmap & where) const {
/* TODO */
}
void Bitmap::BlitToScreen(const int upper_left_x, const int upper_left_y) const {
#if 0
if (getWidth() != Screen->getWidth() || getHeight() != Screen->getHeight()){
/*
this->Blit( upper_left_x, upper_left_y, *Buffer );
Buffer->Stretch(*Scaler);
Scaler->Blit(0, 0, 0, 0, *Screen);
*/
this->Stretch(*Scaler, 0, 0, getWidth(), getHeight(), upper_left_x, upper_left_y, Scaler->getWidth(), Scaler->getHeight());
Scaler->Blit(0, 0, 0, 0, *Screen);
} else {
this->Blit(upper_left_x, upper_left_y, *Screen);
}
#endif
/*
if (&where == Screen){
al_flip_display();
}
*/
changeTarget(this, Screen);
if (getData()->getBitmap() != Screen->getData()->getBitmap()){
Blit(*Screen);
}
al_flip_display();
}
void Bitmap::BlitAreaToScreen(const int upper_left_x, const int upper_left_y) const {
changeTarget(this, Screen);
/*
if (getData()->getBitmap() != Screen->getData()->getBitmap()){
Blit(upper_left_y, upper_left_y, *Screen);
}
*/
al_flip_display();
}
void Bitmap::draw(const int x, const int y, Filter * filter, const Bitmap & where, int flags) const {
changeTarget(this, where);
MaskedBlender blender;
Util::ReferenceCount<Shader> shader;
if (filter != NULL){
shader = filter->getShader();
}
ALLEGRO_SHADER * a5shader = NULL;
if (shader != NULL){
a5shader = shader->getShader();
}
if (a5shader != NULL){
al_use_shader(a5shader);
filter->setupShader(shader);
}
/* any source pixels with an alpha value of 0 will be masked */
al_draw_bitmap(getData()->getBitmap(), x, y, flags);
if (a5shader != NULL){
al_use_shader(NULL);
}
}
void Bitmap::draw(const int x, const int y, const Bitmap & where) const {
draw(x, y, NULL, where, 0);
/*
// TransBlender blender;
changeTarget(this, where);
MaskedBlender blender;
/ * any source pixels with an alpha value of 0 will be masked * /
al_draw_bitmap(getData()->getBitmap(), x, y, 0);
*/
}
void Bitmap::draw(const int x, const int y, Filter * filter, const Bitmap & where) const {
draw(x, y, filter, where, 0);
}
void Bitmap::drawShadow(Bitmap & where, int x, int y, int intensity, Color color, double scale, bool facingRight) const {
changeTarget(this, where);
MaskedBlender blender;
int newHeight = fabs(scale) * getHeight();
int flags = 0;
if (!facingRight){
flags |= ALLEGRO_FLIP_HORIZONTAL;
}
float shadowColor[4];
al_unmap_rgb_f(color.color, &shadowColor[0], &shadowColor[1], &shadowColor[2]);
shadowColor[3] = (float) intensity / 255.0;
al_use_shader(shader_shadow);
if (!al_set_shader_float_vector("shadow", 4, shadowColor, 1)){
/* Well.. thats not good. Did the shader source get messed up? */
}
al_draw_scaled_bitmap(getData()->getBitmap(), 0, 0, getWidth(), getHeight(), x, y - newHeight, getWidth(), newHeight, flags);
al_use_shader(NULL);
}
void Bitmap::hLine(const int x1, const int y, const int x2, const Color color) const {
line(x1, y, x2, y, color);
}
void Bitmap::vLine(const int y1, const int x, const int y2, const Color color) const {
line(x, y1, x, y2, color);
}
void Bitmap::arc(const int x, const int y, const double ang1, const double ang2, const int radius, const Color color ) const {
changeTarget(this, this);
al_draw_arc(x, y, radius, ang1 - Util::pi/2, ang2 - ang1, color.color, 1);
}
void TranslucentBitmap::arc(const int x, const int y, const double ang1, const double ang2, const int radius, const Color color ) const {
TransBlender blender;
Bitmap::arc(x, y, ang1, ang2, radius, transBlendColor(color));
/*
changeTarget(this);
al_draw_arc(x, y, radius, ang1 + S_PI/2, ang2 - ang1, doTransBlend(color, globalBlend.alpha), 0);
*/
}
/* from http://www.allegro.cc/forums/thread/605684/892721#target */
#if 0
void al_draw_filled_pieslice(float cx, float cy, float r, float start_theta,
float delta_theta, ALLEGRO_COLOR color){
ALLEGRO_VERTEX vertex_cache[ALLEGRO_VERTEX_CACHE_SIZE];
int num_segments, ii;
num_segments = fabs(delta_theta / (2 * ALLEGRO_PI) * ALLEGRO_PRIM_QUALITY * sqrtf(r));
if (num_segments < 2)
return;
if (num_segments >= ALLEGRO_VERTEX_CACHE_SIZE) {
num_segments = ALLEGRO_VERTEX_CACHE_SIZE - 1;
}
al_calculate_arc(&(vertex_cache[1].x), sizeof(ALLEGRO_VERTEX), cx, cy, r, r, start_theta, delta_theta, 0, num_segments);
vertex_cache[0].x = cx; vertex_cache[0].y = cy;
for (ii = 0; ii < num_segments + 1; ii++) {
vertex_cache[ii].color = color;
vertex_cache[ii].z = 0;
}
al_draw_prim(vertex_cache, NULL, NULL, 0, num_segments + 1, ALLEGRO_PRIM_TRIANGLE_FAN);
// al_draw_prim(vertex_cache, NULL, NULL, 0, 3, ALLEGRO_PRIM_TRIANGLE_FAN);
}
#endif
static float min(float a, float b){
return a < b ? a : b;
}
void Bitmap::roundRect(int radius, int x1, int y1, int x2, int y2, Color color) const {
changeTarget(this, this);
double mx = x2 - x1;
double my = y2 - y1;
double radius_use = min(min(radius, mx / 2), my / 2);
al_draw_rounded_rectangle(x1, y1, x2, y2, radius_use, radius_use, color.color, 1);
}
void TranslucentBitmap::roundRect(int radius, int x1, int y1, int x2, int y2, Color color) const {
TransBlender blender;
Bitmap::roundRect(radius, x1, y1, x2, y2, transBlendColor(color));
}
void Bitmap::roundRectFill(int radius, int x1, int y1, int x2, int y2, Graphics::Color color) const {
changeTarget(this, this);
double mx = x2 - x1;
double my = y2 - y1;
double radius_use = min(min(radius, mx / 2), my / 2);
al_draw_filled_rounded_rectangle(x1, y1, x2, y2, radius_use, radius_use, color.color);
}
void TranslucentBitmap::roundRectFill(int radius, int x1, int y1, int x2, int y2, Graphics::Color color) const {
TransBlender blender;
Bitmap::roundRectFill(radius, x1, y1, x2, y2, transBlendColor(color));
}
void Bitmap::arcFilled(const int x, const int y, const double ang1, const double ang2, const int radius, const Color color ) const {
changeTarget(this, this);
al_draw_filled_pieslice(x, y, radius, ang1 - Util::pi/2, ang2 - ang1, color.color);
}
void TranslucentBitmap::arcFilled(const int x, const int y, const double ang1, const double ang2, const int radius, const Color color ) const {
changeTarget(this, this);
TransBlender blender;
Bitmap::arcFilled(x, y, ang1, ang2, radius, transBlendColor(color));
}
void Bitmap::floodfill( const int x, const int y, const Color color ) const {
/* TODO */
}
void Bitmap::line(const int x1, const int y1, const int x2, const int y2, const Color color) const {
changeTarget(this, this);
al_draw_line(x1, y1, x2, y2, color.color, 1);
}
void TranslucentBitmap::line(const int x1, const int y1, const int x2, const int y2, const Color color) const {
TransBlender blender;
Bitmap::line(x1, y1, x2, y2, transBlendColor(color));
}
void Bitmap::circleFill(int x, int y, int radius, Color color) const {
changeTarget(this, this);
al_draw_filled_circle(x, y, radius, color.color);
}
void Bitmap::circle(int x, int y, int radius, Color color) const {
changeTarget(this, this);
al_draw_circle(x, y, radius, color.color, 1);
}
void Bitmap::circle(int x, int y, int radius, int thickness, Color color) const {
changeTarget(this, this);
al_draw_circle(x, y, radius, color.color, thickness);
}
void Bitmap::rectangle(int x1, int y1, int x2, int y2, Color color ) const {
changeTarget(this, this);
// al_draw_rectangle(x1, y1, x2, y2, color.color, 0);
al_draw_rectangle(x1 + 0.5, y1 + 0.5, x2 - 0.5, y2 - 0.5, color.color, 1);
}
void Bitmap::rectangleFill( int x1, int y1, int x2, int y2, Color color ) const {
changeTarget(this, this);
// al_draw_filled_rectangle(x1 - 0.5, y1 - 0.5, x2 + 0.5, y2 + 0.5, color.color);
al_draw_filled_rectangle(x1, y1, x2 + 1, y2 + 1, color.color);
}
void Bitmap::triangle( int x1, int y1, int x2, int y2, int x3, int y3, Color color ) const {
changeTarget(this, this);
al_draw_filled_triangle(x1, y1, x2, y2, x3, y3, color.color);
}
void Bitmap::polygon( const int * verts, const int nverts, const Color color ) const {
/* TODO */
}
void Bitmap::ellipse( int x, int y, int rx, int ry, Color color ) const {
changeTarget(this, this);
al_draw_ellipse(x, y, rx, ry, color.color, 0);
}
void Bitmap::ellipseFill( int x, int y, int rx, int ry, Color color ) const {
changeTarget(this, this);
al_draw_filled_ellipse(x, y, rx, ry, color.color);
}
void Bitmap::applyTrans(const Color color) const {
TransBlender blender;
changeTarget(this, this);
al_draw_filled_rectangle(0, 0, getWidth(), getHeight(), transBlendColor(color).color);
}
void Bitmap::light(int x, int y, int width, int height, int start_y, int focus_alpha, int edge_alpha, Color focus_color, Color edge_color) const {
/* TODO */
}
void Bitmap::drawCharacter( const int x, const int y, const Color color, const int background, const Bitmap & where ) const {
/* TODO */
}
void Bitmap::save( const std::string & str ) const {
/* TODO */
}
void Bitmap::readLine(std::vector<Color> & line, int y){
/* TODO */
}
void TranslucentBitmap::draw(const int x, const int y, Filter * filter, const Bitmap & where, int flags) const {
changeTarget(this, where);
TransBlender blender;
Util::ReferenceCount<Shader> shader;
if (filter != NULL){
shader = filter->getShader();
}
ALLEGRO_SHADER * a5shader = NULL;
if (shader != NULL){
a5shader = shader->getShader();
}
if (a5shader != NULL){
al_use_shader(a5shader);
filter->setupShader(shader);
}
al_draw_tinted_bitmap(getData()->getBitmap(), getBlendColor().color, x, y, flags);
if (a5shader != NULL){
al_use_shader(NULL);
}
}
void TranslucentBitmap::draw(const int x, const int y, const Bitmap & where) const {
draw(x, y, NULL, where, 0);
}
void LitBitmap::draw(const int x, const int y, const Bitmap & where) const {
draw(x, y, NULL, where, 0);
}
void LitBitmap::draw(const int x, const int y, Filter * filter, const Bitmap & where, int flags) const {
if (filter == NULL){
changeTarget(this, where);
/*
TransBlender blender;
Util::ReferenceCount<Shader> shader;
if (filter != NULL){
shader = filter->getShader();
}
ALLEGRO_SHADER * a5shader = NULL;
if (shader != NULL){
a5shader = shader->getShader();
}
if (a5shader != NULL){
al_set_shader(the_display, a5shader);
al_use_shader(a5shader, true);
}
al_draw_tinted_bitmap(getData()->getBitmap(), getBlendColor().color, x, y, flags);
if (a5shader != NULL){
al_set_shader(the_display, shader_default);
al_use_shader(shader_default, true);
}
*/
MaskedBlender blender;
ALLEGRO_SHADER * shader = shader_lit_sprite;
float light[4];
Color color = makeColor(globalBlend.red, globalBlend.green, globalBlend.blue);
al_unmap_rgb_f(color.color, &light[0], &light[1], &light[2]);
light[3] = 1;
float intensity = (float) globalBlend.alpha / 255.0;
al_use_shader(shader);
if (!al_set_shader_float_vector("light_color", 4, light, 1)){
/* Well.. thats not good. Did the shader source get messed up? */
}
if (!al_set_shader_float("light_intensity", intensity)){
}
al_draw_bitmap(getData()->getBitmap(), x, y, flags);
al_use_shader(NULL);
} else {
Bitmap temp(getWidth(), getHeight());
temp.fill(MaskColor());
Bitmap::draw(0, 0, filter, temp, 0);
LitBitmap(temp).draw(x, y, NULL, where, flags);
}
}
void LitBitmap::draw( const int x, const int y, Filter * filter, const Bitmap & where ) const {
draw(x, y, filter, where, 0);
// LitBlender blender(makeColorAlpha(globalBlend.red, globalBlend.green, globalBlend.blue, globalBlend.alpha));
// TransBlender blender;
// al_draw_bitmap(getData()->getBitmap(), x, y, 0);
// al_draw_tinted_bitmap(getData()->getBitmap(), al_map_rgba_f(1, 0, 0, 1), x, y, 0);
}
void LitBitmap::drawHFlip( const int x, const int y, const Bitmap & where ) const {
draw(x, y, NULL, where, ALLEGRO_FLIP_HORIZONTAL);
}
void LitBitmap::drawHFlip( const int x, const int y, Filter * filter, const Bitmap & where ) const {
draw(x, y, filter, where, ALLEGRO_FLIP_HORIZONTAL);
}
void LitBitmap::drawVFlip( const int x, const int y, const Bitmap & where ) const {
draw(x, y, NULL, where, ALLEGRO_FLIP_VERTICAL);
}
void LitBitmap::drawVFlip( const int x, const int y, Filter * filter, const Bitmap & where ) const {
draw(x, y, filter, where, ALLEGRO_FLIP_VERTICAL);
}
void LitBitmap::drawHVFlip( const int x, const int y, const Bitmap & where ) const {
draw(x, y, NULL, where, ALLEGRO_FLIP_HORIZONTAL | ALLEGRO_FLIP_VERTICAL);
}
void LitBitmap::drawHVFlip( const int x, const int y, Filter * filter, const Bitmap & where ) const {
draw(x, y, filter, where, ALLEGRO_FLIP_HORIZONTAL | ALLEGRO_FLIP_VERTICAL);
}
void TranslucentBitmap::draw( const int x, const int y, Filter * filter, const Bitmap & where ) const {
draw(x, y, filter, where, 0);
}
void TranslucentBitmap::drawHFlip( const int x, const int y, const Bitmap & where ) const {
draw(x, y, NULL, where, ALLEGRO_FLIP_HORIZONTAL);
}
void TranslucentBitmap::drawHFlip( const int x, const int y, Filter * filter, const Bitmap & where ) const {
draw(x, y, filter, where, ALLEGRO_FLIP_HORIZONTAL);
}
void TranslucentBitmap::drawVFlip( const int x, const int y, const Bitmap & where ) const {
draw(x, y, NULL, where, ALLEGRO_FLIP_VERTICAL);
}
void TranslucentBitmap::drawVFlip( const int x, const int y, Filter * filter, const Bitmap & where ) const {
draw(x, y, filter, where, ALLEGRO_FLIP_VERTICAL);
}
void TranslucentBitmap::drawHVFlip( const int x, const int y, const Bitmap & where ) const {
draw(x, y, NULL, where, ALLEGRO_FLIP_HORIZONTAL | ALLEGRO_FLIP_VERTICAL);
}
void TranslucentBitmap::drawHVFlip( const int x, const int y, Filter * filter,const Bitmap & where ) const {
draw(x, y, filter, where, ALLEGRO_FLIP_HORIZONTAL | ALLEGRO_FLIP_VERTICAL);
}
void TranslucentBitmap::hLine( const int x1, const int y, const int x2, const Color color ) const {
TransBlender blender;
Bitmap::hLine(x1, y, x2, doTransBlend(color, globalBlend.alpha));
}
void TranslucentBitmap::circleFill(int x, int y, int radius, Color color) const {
TransBlender blender;
Bitmap::circleFill(x, y, radius, doTransBlend(color, globalBlend.alpha));
}
void TranslucentBitmap::putPixelNormal(int x, int y, Color color) const {
TransBlender blender;
Bitmap::putPixelNormal(x, y, doTransBlend(color, globalBlend.alpha));
}
void TranslucentBitmap::rectangle( int x1, int y1, int x2, int y2, Color color ) const {
TransBlender blender;
Bitmap::rectangle(x1, y1, x2, y2, doTransBlend(color, globalBlend.alpha));
}
void TranslucentBitmap::rectangleFill(int x1, int y1, int x2, int y2, Color color) const {
TransBlender blender;
Bitmap::rectangleFill(x1, y1, x2, y2, doTransBlend(color, globalBlend.alpha));
}
void TranslucentBitmap::ellipse( int x, int y, int rx, int ry, Color color ) const {
TransBlender blender;
Bitmap::ellipse(x, y, rx, ry, doTransBlend(color, globalBlend.alpha));
}
void TranslucentBitmap::ellipseFill( int x, int y, int rx, int ry, Color color ) const {
TransBlender blender;
Bitmap::ellipseFill(x, y, rx, ry, doTransBlend(color, globalBlend.alpha));
}
void Bitmap::setClipRect( int x1, int y1, int x2, int y2 ) const {
/* TODO */
}
void Bitmap::getClipRect(int & x1, int & y1, int & x2, int & y2) const {
/* TODO */
}
int setGfxModeFullscreen(int x, int y){
return setGraphicsMode(FULLSCREEN, x, y);
}
int setGfxModeWindowed( int x, int y ){
return setGraphicsMode(WINDOWED, x, y);
}
int setGfxModeText(){
/* TODO */
return 0;
}
bool Bitmap::getError(){
/* TODO */
return false;
}
void Bitmap::alphaBlender(int source, int dest){
/* TODO */
}
void Bitmap::transBlender(int r, int g, int b, int a){
globalBlend.red = r;
globalBlend.green = g;
globalBlend.blue = b;
globalBlend.alpha = a;
globalBlend.type = Translucent;
}
void Bitmap::addBlender(int r, int g, int b, int a){
globalBlend.red = r;
globalBlend.green = g;
globalBlend.blue = b;
globalBlend.alpha = a;
globalBlend.type = Add;
}
void Bitmap::differenceBlender( int r, int g, int b, int a ){
globalBlend.red = r;
globalBlend.green = g;
globalBlend.blue = b;
globalBlend.alpha = a;
globalBlend.type = Difference;
}
void Bitmap::multiplyBlender( int r, int g, int b, int a ){
globalBlend.red = r;
globalBlend.green = g;
globalBlend.blue = b;
globalBlend.alpha = a;
globalBlend.type = Multiply;
}
/*
void Bitmap::drawingMode(int type){
}
*/
void Bitmap::shutdown(){
/* Make sure the display is set */
if (Screen != NULL){
al_set_target_bitmap(Screen->getData()->getBitmap());
}
al_use_shader(NULL);
for (std::vector<ALLEGRO_SHADER*>::iterator it = shaders.begin(); it != shaders.end(); it++){
ALLEGRO_SHADER * shader = *it;
al_destroy_shader(shader);
}
delete Screen;
Screen = NULL;
al_destroy_display(the_display);
the_display = NULL;
/*
delete Scaler;
Scaler = NULL;
delete Buffer;
Buffer = NULL;
*/
}
-StretchedBitmap::StretchedBitmap(int width, int height, const Bitmap & parent, QualityFilter filter):
+StretchedBitmap::StretchedBitmap(int width, int height, const Bitmap & parent, Clear clear, QualityFilter filter):
Bitmap(parent, 0, 0, parent.getWidth(), parent.getHeight()),
width(width),
height(height),
where(parent),
-filter(filter){
+filter(filter),
+clearKind(clear){
scale_x = (double) parent.getWidth() / width;
scale_y = (double) parent.getHeight() / height;
ALLEGRO_BITMAP * old_target = al_get_target_bitmap();
if (al_get_target_bitmap() != parent.getData()->getBitmap()){
al_set_target_bitmap(parent.getData()->getBitmap());
}
ALLEGRO_TRANSFORM transform;
al_identity_transform(&transform);
if (al_get_current_transform() != NULL){
al_copy_transform(&transform, al_get_current_transform());
}
al_scale_transform(&transform, scale_x, scale_y);
if (al_get_target_bitmap() != getData()->getBitmap()){
al_set_target_bitmap(getData()->getBitmap());
}
al_use_transform(&transform);
if (old_target != al_get_target_bitmap()){
al_set_target_bitmap(old_target);
}
+ switch (clear){
+ case NoClear: break;
+ case FullClear: this->clear();
+ case Mask: break;
+ }
+
/* TODO: handle filter */
}
void StretchedBitmap::start(){
#if 0
ALLEGRO_TRANSFORM transform;
changeTarget(this, this);
al_copy_transform(&transform, al_get_current_transform());
// al_identity_transform(&transform);
// al_scale_transform(&transform, Bitmap::getWidth() / width, Bitmap::getHeight() / height);
al_scale_transform(&transform, scale_x, scale_y);
al_use_transform(&transform);
#endif
}
void StretchedBitmap::finish(){
#if 0
ALLEGRO_TRANSFORM transform;
changeTarget(this, this);
al_copy_transform(&transform, al_get_current_transform());
/* apply the inverse transform */
al_scale_transform(&transform, 1.0/scale_x, 1.0/scale_y);
// al_identity_transform(&transform);
al_use_transform(&transform);
#endif
}
TranslatedBitmap::TranslatedBitmap(int x, int y, const Bitmap & where):
Bitmap(where),
x(x),
y(y){
ALLEGRO_TRANSFORM transform;
changeTarget(this, where);
al_identity_transform(&transform);
if (al_get_current_transform() != NULL){
al_copy_transform(&transform, al_get_current_transform());
}
al_translate_transform(&transform, x, y);
al_use_transform(&transform);
}
void TranslatedBitmap::BlitToScreen() const {
Bitmap::BlitToScreen();
}
TranslatedBitmap::~TranslatedBitmap(){
ALLEGRO_TRANSFORM transform;
al_copy_transform(&transform, al_get_current_transform());
al_translate_transform(&transform, -x, -y);
al_use_transform(&transform);
}
Bitmap * getScreenBuffer(){
return Screen;
}
RestoreState::RestoreState(){
al_store_state(&state, ALLEGRO_STATE_ALL);
}
RestoreState::~RestoreState(){
al_restore_state(&state);
}
Shader::Shader():
shader(NULL){
}
Shader::~Shader(){
if (shader != NULL){
al_destroy_shader(shader);
}
}
Shader::Shader(ALLEGRO_SHADER * shader):
shader(shader){
}
ALLEGRO_SHADER * Shader::getShader(){
return shader;
}
}
static inline bool close(float x, float y){
static float epsilon = 0.001;
return fabs(x - y) < epsilon;
}
static inline bool sameColor(const ALLEGRO_COLOR & color1, const ALLEGRO_COLOR & color2){
// return memcmp(&color1, &color2, sizeof(Graphics::Color)) == 0;
float r1, g1, b1, a1;
float r2, g2, b2, a2;
al_unmap_rgba_f(color1, &r1, &g1, &b1, &a1);
al_unmap_rgba_f(color2, &r2, &g2, &b2, &a2);
return close(r1, r2) &&
close(g1, g2) &&
close(b1, b2) &&
close(a1, a2);
/*
unsigned char r1, g1, b1, a1;
unsigned char r2, g2, b2, a2;
al_unmap_rgba(color1, &r1, &g1, &b1, &a1);
al_unmap_rgba(color2, &r2, &g2, &b2, &a2);
return r1 == r2 &&
g1 == g2 &&
b1 == b2 &&
a1 == a2;
*/
}
static uint32_t quantify(const ALLEGRO_COLOR & color){
unsigned char red, green, blue, alpha;
al_unmap_rgba(color, &red, &green, &blue, &alpha);
return (red << 24) |
(green << 16) |
(blue << 8) |
alpha;
}
bool operator<(const ALLEGRO_COLOR & color1, const ALLEGRO_COLOR & color2){
return quantify(color1) < quantify(color2);
}
bool operator!=(const ALLEGRO_COLOR & color1, const ALLEGRO_COLOR & color2){
return !(color1 == color2);
}
bool operator==(const ALLEGRO_COLOR & color1, const ALLEGRO_COLOR & color2){
return sameColor(color1, color2);
}
diff --git a/util/graphics/bitmap.cpp b/util/graphics/bitmap.cpp
index 04cae31c..436e68f8 100644
--- a/util/graphics/bitmap.cpp
+++ b/util/graphics/bitmap.cpp
@@ -1,547 +1,564 @@
#include "../funcs.h"
#include "bitmap.h"
#include "../file-system.h"
#include <string>
#include <stdio.h>
#include <math.h>
namespace Graphics{
static Bitmap * Screen = NULL;
/* bitmaps that should always be resized to the dimensions of the screen */
static std::vector<Bitmap*> needResize;
Util::Parameter<Bitmap*> screenParameter;
Util::Parameter<Util::ReferenceCount<ShaderManager> > shaderManager;
ShaderManager::ShaderManager(){
}
ShaderManager::~ShaderManager(){
}
Util::ReferenceCount<Shader> ShaderManager::getShader(const std::string & name, Util::ReferenceCount<Shader> (*create)()){
std::map<std::string, Util::ReferenceCount<Shader> >::iterator it = shaders.find(name);
if (it == shaders.end()){
shaders[name] = create();
it = shaders.find(name);
}
return it->second;
}
/* implementation independant definitions can go here */
Bitmap * Bitmap::temporary_bitmap = NULL;
Bitmap * Bitmap::temporary_bitmap2 = NULL;
/*
int SCALE_X = 0;
int SCALE_Y = 0;
*/
/*
const int Bitmap::MODE_TRANS = 0;
const int Bitmap::MODE_SOLID = 1;
*/
const int SPRITE_NO_FLIP = 0;
const int SPRITE_V_FLIP = 1;
const int SPRITE_H_FLIP = 2;
const int SPRITE_NORMAL = 1;
const int SPRITE_LIT = 2;
const int SPRITE_TRANS = 3;
static inline int max(int a, int b){
return a > b ? a : b;
}
INTERNAL_COLOR Color::defaultColor(){
return makeColor(0, 0, 0).color;
}
void initializeExtraStuff();
Bitmap::Bitmap(Storage::File & file):
mustResize(false),
error(false),
bit8MaskColor(makeColor(0, 0, 0)){
int length = file.getSize();
if (length == -1){
throw BitmapException(__FILE__, __LINE__, std::string("Could not read from file"));
}
char * data = new char[length];
try{
file.readLine(data, length);
loadFromMemory(data, length);
delete[] data;
} catch (const BitmapException & fail){
delete[] data;
throw;
} catch (...){
delete[] data;
throw;
}
}
QualityFilter qualityFilterName(const std::string & type){
if (type == "xbr"){
return XbrFilter;
}
if (type == "hqx"){
return HqxFilter;
}
return NoFilter;
}
Bitmap::~Bitmap(){
if (mustResize){
for (std::vector<Bitmap*>::iterator it = needResize.begin(); it != needResize.end(); it++){
Bitmap * who = *it;
if (who == this){
needResize.erase(it);
break;
}
}
}
}
static Bitmap makeTemporaryBitmap(Bitmap *& temporary, int w, int h){
if (temporary == NULL){
temporary = new Bitmap(w, h);
} else if (temporary->getWidth() < w || temporary->getHeight() < h){
int mw = max(temporary->getWidth(), w);
int mh = max(temporary->getHeight(), h);
// printf("Create temporary bitmap %d %d\n", mw, mh);
delete temporary;
temporary = new Bitmap(mw, mh);
}
if (temporary == NULL){
printf("*bug* temporary bitmap is null\n");
}
return Bitmap(*temporary, 0, 0, w, h);
}
/*
Bitmap Bitmap::temporaryBitmap(int w, int h){
return makeTemporaryBitmap(temporary_bitmap, w, h);
}
Bitmap Bitmap::temporaryBitmap2(int w, int h){
return makeTemporaryBitmap(temporary_bitmap2, w, h);
}
*/
void Bitmap::cleanupTemporaryBitmaps(){
if (temporary_bitmap != NULL){
delete temporary_bitmap;
temporary_bitmap = NULL;
}
if (temporary_bitmap2 != NULL){
delete temporary_bitmap2;
temporary_bitmap2 = NULL;
}
}
Bitmap & Bitmap::operator=(const Bitmap & copy){
path = copy.getPath();
this->width = copy.getWidth();
this->height = copy.getHeight();
setData(copy.getData());
return *this;
}
double Bitmap::getScale(){
/* the game is pretty much hard coded to run at 320 scaled upto 640
* and then scaled to whatever the user wants, but as long as
* 320 and 640 remain this number will be 2.
* maybe calculate this at some point
*/
return 2;
/*
if (Scaler != NULL && Buffer != NULL){
double x1 = Scaler->getWidth();
double x2 = Buffer->getWidth();
return x2 / x1;
}
return 1;
*/
}
bool Bitmap::isEmpty() const {
return getWidth() == 0 || getHeight() == 0;
}
/* taken from the color addon from allegro 4.9 */
static void al_color_cmyk_to_rgb(float cyan, float magenta, float yellow, float key, float *red, float *green, float *blue){
float max = 1 - key;
*red = max - cyan * max;
*green = max - magenta * max;
*blue = max - yellow * max;
}
void Bitmap::cymkToRGB(int c, int y, int m, int k, int * r, int * g, int * b){
float fc = (float)c / 255.0;
float fy = (float)y / 255.0;
float fm = (float)m / 255.0;
float fk = (float)k / 255.0;
float fr, fg, fb;
al_color_cmyk_to_rgb(fc, fm, fy, fk, &fr, &fg, &fb);
*r = (int)(fr * 255.0);
*g = (int)(fg * 255.0);
*b = (int)(fb * 255.0);
}
void Bitmap::updateOnResize(){
if (!mustResize){
mustResize = true;
needResize.push_back(this);
}
}
void Bitmap::updateSize(const int width, const int height){
if (getWidth() == width && getHeight() == height){
return;
}
Bitmap created(width, height);
*this = created;
}
/* resize the internal bitmap. not guaranteed to destroy the internal bitmap */
void Bitmap::resize(const int width, const int height){
/* if internal bitmap is already the proper size, do nothing */
if (getWidth() == width && getHeight() == height){
return;
}
Bitmap created(width, height);
Stretch(created);
*this = created;
}
/* decrement bitmap reference counter and free memory if counter hits 0 */
#if 0
void Bitmap::releaseInternalBitmap(){
const int MAGIC_DEBUG = 0xa5a5a5;
if (own != NULL){
if (*own == MAGIC_DEBUG){
printf("[bitmap] Trying to delete an already deleted reference counter %p\n", own);
}
(*own) -= 1;
if ( *own == 0 ){
*own = MAGIC_DEBUG;
delete own;
destroyPrivateData();
own = NULL;
}
}
}
#endif
void Bitmap::BlitToScreen() const {
// this->Blit( *Bitmap::Screen );
this->BlitToScreen(0, 0);
}
void Bitmap::load( const std::string & str ){
// releaseInternalBitmap();
internalLoadFile( str.c_str() );
}
Bitmap Bitmap::scaleBy(const double widthRatio, const double heightRatio) const {
return scaleTo(getWidth() * widthRatio, getHeight() * heightRatio);
}
void Bitmap::border( int min, int max, Color color ) const {
int w = getWidth();
int h = getHeight();
for (int i = min; i < max; i++){
rectangle(i, i, w - 1 - i, h - 1 - i, color);
}
}
void Bitmap::drawHFlip(const int x, const int y, const int startWidth, const int startHeight, const int width, const int height, const Bitmap & where) const {
drawHFlip(x, y, startWidth, startHeight, width, height, NULL, where);
}
void Bitmap::drawHFlip(const int x, const int y, const int startWidth, const int startHeight, const int width, const int height, Filter * filter, const Bitmap & where) const {
Bitmap sub(*this, getWidth() - width, getHeight() - height, getWidth() - startWidth, getHeight() - startHeight);
sub.drawHFlip(x + startWidth, y + startHeight, filter, where);
}
void Bitmap::drawRotateCenter(const int x, const int y, const int angle, const Bitmap & where){
drawRotate(x - getWidth() / 2, y - getHeight() / 2, angle, where);
}
void Bitmap::drawCenter(const int x, const int y, const Bitmap & where) const {
draw(x - getWidth() / 2, y - getHeight() / 2, where);
}
void Bitmap::drawStretched(const Bitmap & who) const {
drawStretched(0, 0, who.getWidth(), who.getHeight(), who);
}
void Bitmap::draw(const int x, const int y, const int startWidth, const int startHeight, const int width, const int height, const Bitmap & where) const {
draw(x, y, startWidth, startHeight, width, height, NULL, where);
/*
Bitmap sub(*this, startWidth, startHeight, width, height);
sub.draw(x + startWidth, y + startHeight, where);
*/
}
void Bitmap::draw(const int x, const int y, const int startWidth, const int startHeight, const int width, const int height, Filter * filter, const Bitmap & where) const {
Bitmap sub(*this, startWidth, startHeight, width, height);
sub.draw(x + startWidth, y + startHeight, filter, where);
}
void Bitmap::horizontalLine( const int x1, const int y, const int x2, const Graphics::Color color ) const{
this->hLine(x1, y, x2, color);
}
void Bitmap::equilateralTriangle(int x, int y, int angle, int size, Color color) const {
double radians = Util::radians(angle);
int x1 = x + size / 2 * cos(radians + 2 * Util::pi / 3);
int y1 = y + size / 2 * sin(radians + 2 * Util::pi / 3);
int x2 = x + size / 2 * cos(radians - 2 * Util::pi / 3);
int y2 = y + size / 2 * sin(radians - 2 * Util::pi / 3);
int x3 = x + size / 2 * cos(radians);
int y3 = y + size / 2 * sin(radians);
triangle(x1, y1, x2, y2, x3, y3, color);
}
Bitmap Bitmap::greyScale(){
Bitmap grey(getWidth(), getHeight());
for (int x = 0; x < getWidth(); x++){
for (int y = 0; y < getHeight(); y++){
Color pixel = getPixel(x, y);
int val = (int)((0.299*getRed(pixel) + 0.587*getGreen(pixel) + 0.114*getBlue(pixel) + 0.5) + 16);
if (val > 255){
val = 255;
}
grey.putPixel(x, y, makeColor(val, val, val));
}
}
return grey;
}
bool Bitmap::inRange(int x, int y) const {
int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
getClipRect(x1, y1, x2, y2);
return (x >= x1 && x <= x2 &&
y >= y1 && y <= y2);
}
void Bitmap::drawMask( const int _x, const int _y, const Bitmap & where ){
Color mask = MaskColor();
for (int x = 0; x < getWidth(); x++){
for (int y = 0; y < getHeight(); y++){
if (getPixel(x,y) == mask){
where.putPixel(x+_x, y+_y, mask);
}
}
}
}
void Bitmap::set8BitMaskColor(const Color & color){
bit8MaskColor = color;
}
Color Bitmap::get8BitMaskColor(){
return bit8MaskColor;
}
void Bitmap::setFakeGraphicsMode(int width, int height){
initializeExtraStuff();
Screen = new Bitmap(width, height);
}
void Bitmap::Blit( const std::string & xpath ) const {
Bitmap duh(xpath);
duh.Blit(*this);
}
void Bitmap::Blit(const Bitmap & where) const {
this->Blit(0, 0, where);
}
void Bitmap::Blit(const int x, const int y, const Bitmap & where) const {
Blit(0, 0, x, y, where);
}
void Bitmap::Blit(const int mx, const int my, const int wx, const int wy, const Bitmap & where) const {
Blit(mx, my, getWidth(), getHeight(), wx, wy, where);
}
void Bitmap::BlitFromScreen(const int x, const int y) const {
Screen->Blit(x, y, getWidth(), getHeight(), 0, 0, *this);
}
//! min (borrowed from allegro)
static inline int Min(int x, int y){ return (((x) < (y)) ? (x) : (y)); }
//! max (borrowed from allegro)
static inline int Max(int x, int y){ return (((x) > (y)) ? (x) : (y)); }
//! mid (borrowed from allegro)
static inline int Mid(int x,int y,int z){ return (Max((x), Min((y), (z)))); }
int Bitmap::getScreenWidth(){
if (Screen != 0){
return Screen->getWidth();
}
return 0;
}
int Bitmap::getScreenHeight(){
if (Screen != 0){
return Screen->getHeight();
}
return 0;
}
void Bitmap::clear() const {
fill(makeColor(0, 0, 0));
}
void Bitmap::copy(const Bitmap & him){
resize(him.getWidth(), him.getHeight());
him.Blit(*this);
}
void Bitmap::Stretch( const Bitmap & where ) const {
if (getWidth() == where.getWidth() && getHeight() == where.getHeight()){
Blit(where);
} else {
Stretch(where, 0, 0, getWidth(), getHeight(), 0, 0, where.getWidth(), where.getHeight());
}
}
void Bitmap::StretchHqx(const Bitmap & where) const {
if (getWidth() == where.getWidth() && getHeight() == where.getHeight()){
Blit(where);
} else {
StretchHqx(where, 0, 0, getWidth(), getHeight(), 0, 0, where.getWidth(), where.getHeight());
}
}
void Bitmap::StretchXbr(const Bitmap & where) const {
if (getWidth() == where.getWidth() && getHeight() == where.getHeight()){
Blit(where);
} else {
StretchXbr(where, 0, 0, getWidth(), getHeight(), 0, 0, where.getWidth(), where.getHeight());
}
}
+Bitmap Bitmap::aspectRatio(int aspectWidth, int aspectHeight) const {
+ double width = getWidth();
+ double height = getHeight();
+
+ double ratio = (double) aspectWidth / (double) aspectHeight;
+
+ width = (double) height * ratio;
+ if (width > getWidth()){
+ width = getWidth();
+ height = width / ratio;
+ }
+
+ int x = (getWidth() - width) / 2;
+ int y = (getHeight() - height) / 2;
+ return Bitmap(*this, x, y, (int) width, (int) height);
+}
+
Color darken(Color color, double factor ){
int r = (int)((double)getRed(color) / factor);
int g = (int)((double)getGreen(color) / factor);
int b = (int)((double)getBlue(color) / factor);
return makeColor(r, g, b);
}
LitBitmap::LitBitmap(const Bitmap & b):
Bitmap(b){
int x1, y1, x2, y2;
b.getClipRect(x1, y1, x2, y2);
setClipRect(x1, y1, x2, y2);
}
LitBitmap::LitBitmap():
Bitmap(){
}
LitBitmap::~LitBitmap(){
}
LitBitmap Bitmap::lit() const {
return LitBitmap(*this);
}
TranslucentBitmap Bitmap::translucent() const {
return TranslucentBitmap(*this);
}
TranslucentBitmap Bitmap::translucent(int red, int green, int blue, int alpha) const {
transBlender(red, green, blue, alpha);
return TranslucentBitmap(*this);
}
TranslucentBitmap::TranslucentBitmap(const Bitmap & b):
Bitmap(b){
int x1, y1, x2, y2;
b.getClipRect(x1, y1, x2, y2);
setClipRect(x1, y1, x2, y2);
}
TranslucentBitmap::TranslucentBitmap():
Bitmap(){
}
TranslucentBitmap::~TranslucentBitmap(){
}
void TranslucentBitmap::fill(Color color) const {
Bitmap::applyTrans(color);
}
int StretchedBitmap::getWidth() const {
return width;
}
int StretchedBitmap::getHeight() const {
return height;
}
double StretchedBitmap::getScaleWidth() const {
return (double) where.getWidth() / (double) getWidth();
}
double StretchedBitmap::getScaleHeight() const {
return (double) where.getHeight() / (double) getHeight();
}
void blend_palette(Color * pal, int mp, Color startColor, Color endColor){
/*
ASSERT(pal);
ASSERT(mp != 0);
*/
int sc_r = Graphics::getRed(startColor);
int sc_g = Graphics::getGreen(startColor);
int sc_b = Graphics::getBlue(startColor);
int ec_r = Graphics::getRed(endColor);
int ec_g = Graphics::getGreen(endColor);
int ec_b = Graphics::getBlue(endColor);
for ( int q = 0; q < mp; q++ ) {
float j = (float)( q + 1 ) / (float)( mp );
int f_r = (int)( 0.5 + (float)( sc_r ) + (float)( ec_r-sc_r ) * j );
int f_g = (int)( 0.5 + (float)( sc_g ) + (float)( ec_g-sc_g ) * j );
int f_b = (int)( 0.5 + (float)( sc_b ) + (float)( ec_b-sc_b ) * j );
pal[q] = Graphics::makeColor( f_r, f_g, f_b );
}
}
}
#ifdef USE_ALLEGRO
#include "allegro/bitmap.cpp"
#endif
#ifdef USE_SDL
#include "sdl/bitmap.cpp"
#endif
#ifdef USE_ALLEGRO5
#include "allegro5/bitmap.cpp"
#endif
diff --git a/util/graphics/bitmap.h b/util/graphics/bitmap.h
index 3cddab90..3584c11c 100644
--- a/util/graphics/bitmap.h
+++ b/util/graphics/bitmap.h
@@ -1,728 +1,744 @@
#ifndef _paintown_bitmap_h_
#define _paintown_bitmap_h_
#include "../exceptions/exception.h"
#include "../pointer.h"
#include "../parameter.h"
#include <string>
#include <vector>
#include <map>
#include <iostream>
#ifdef USE_ALLEGRO
#include "allegro/bitmap.h"
#endif
#ifdef USE_SDL
#include "sdl/bitmap.h"
#endif
#ifdef USE_ALLEGRO5
#include "allegro5/bitmap.h"
struct ALLEGRO_SHADER;
#endif
#include "color.h"
#if !defined(USE_ALLEGRO) && !defined(USE_SDL) && !defined(USE_ALLEGRO5)
#error No backend specified. Define one of USE_ALLEGRO, USE_SDL, or USE_ALLEGRO5
#endif
namespace Storage{
class File;
}
namespace Graphics{
class Bitmap;
class Color{
public:
explicit Color(const INTERNAL_COLOR & color):
color(color){
}
static INTERNAL_COLOR defaultColor();
Color():
color(defaultColor()){
}
Color & operator=(const Color & him){
this->color = him.color;
return *this;
}
bool operator==(const Color & him) const {
return color == him.color;
}
bool operator!=(const Color & him) const {
return !(*this == him);
}
bool operator<(const Color & him) const {
return this->color < him.color;
}
INTERNAL_COLOR color;
};
enum ImageFormat{
FormatPNG,
FormatBMP,
FormatJPG,
FormatPCX,
FormatTGA,
FormatTIF,
FormatXPM,
FormatGIF,
FormatUnknown
};
ImageFormat identifyImage(const unsigned char * data, int length);
class TranslucentBitmap;
class LitBitmap;
class Bitmap;
extern Util::Parameter<Bitmap*> screenParameter;
extern const int SPRITE_NORMAL;
extern const int SPRITE_LIT;
extern const int SPRITE_TRANS;
extern const int SPRITE_NO_FLIP;
extern const int SPRITE_V_FLIP;
extern const int SPRITE_H_FLIP;
/*
extern int SCALE_X;
extern int SCALE_Y;
*/
class BitmapException: public Exception::Base {
public:
BitmapException(const std::string & file, int line, const std::string & reason):
Base(file, line),
reason(reason){
}
BitmapException(const BitmapException & copy):
Base(copy),
reason(copy.reason){
}
virtual void throwSelf() const {
throw *this;
}
virtual ~BitmapException() throw () {
}
protected:
virtual const std::string getReason() const {
return reason;
}
std::string reason;
};
/* create a color from components */
Color makeColor(int r, int g, int b);
Color darken(Color color, double factor);
void hsvToRGB( float h, float s, float v, int * r, int * g, int * b );
int setGfxModeText();
int setGfxModeFullscreen( int x, int y );
int setGfxModeWindowed( int x, int y );
/* Only call this method once, from init. After that call changeGraphicsMode */
int setGraphicsMode(int mode, int width, int height);
int changeGraphicsMode(int mode, int width, int height);
/* get color components */
int getRed(Color x);
int getBlue(Color x);
int getGreen(Color x);
int getAlpha(Color x);
Color MaskColor();
enum QualityFilter{
NoFilter,
HqxFilter,
XbrFilter
};
QualityFilter qualityFilterName(const std::string & type);
/* FIXME: try to move the shader stuff to shader.h */
class Shader{
public:
Shader();
virtual ~Shader();
#ifdef USE_ALLEGRO5
Shader(ALLEGRO_SHADER * shader);
ALLEGRO_SHADER * getShader();
ALLEGRO_SHADER * shader;
#endif
};
class ShaderManager{
public:
ShaderManager();
Util::ReferenceCount<Shader> getShader(const std::string & name, Util::ReferenceCount<Shader> (*create)());
virtual ~ShaderManager();
protected:
std::map<std::string, Util::ReferenceCount<Shader> > shaders;
};
extern Util::Parameter<Util::ReferenceCount<ShaderManager> > shaderManager;
#ifdef USE_ALLEGRO5
ALLEGRO_SHADER * create_shader(const std::string & vertex, const std::string & pixel);
void setShaderSampler(ALLEGRO_SHADER * shader, const std::string & name, const Bitmap & texture, int unit);
void setShaderBool(ALLEGRO_SHADER * shader, const std::string & name, bool value);
void setShaderInt(ALLEGRO_SHADER * shader, const std::string & name, int value);
void setShaderFloat(ALLEGRO_SHADER * shader, const std::string & name, float value);
void setShaderVec4(ALLEGRO_SHADER * shader, const std::string & name, float v1, float v2, float v3, float v4);
std::string defaultVertexShader();
std::string defaultPixelShader();
#endif
class Bitmap{
private:
/* these constructors don't really matter, get rid of them at some point */
Bitmap( const Bitmap & copy, int sx, int sy, double accuracy );
Bitmap( const char * load_file, int sx, int sy, double accuracy );
public:
/* equivalent to a GPU shader */
class Filter{
public:
virtual Color filter(Color pixel) const = 0;
/* getShader should only return the Shader object and not set any uniforms/attributes */
virtual Util::ReferenceCount<Shader> getShader() = 0;
/* set the uniforms/attributes */
virtual void setupShader(const Util::ReferenceCount<Shader> &) = 0;
virtual ~Filter(){
}
};
/* default constructor makes 10x10 bitmap */
Bitmap();
Bitmap( int x, int y );
Bitmap( const char * load_file );
Bitmap(const char * data, int length);
Bitmap( const std::string & load_file );
Bitmap( const char * load_file, int sx, int sy );
/* Load a bitmap from an abstract file */
Bitmap(Storage::File & file);
/* 4/24/2010: remove this at some point */
#ifdef USE_ALLEGRO
explicit Bitmap( BITMAP * who, bool deep_copy = false );
#endif
#ifdef USE_SDL
explicit Bitmap(SDL_Surface * who, bool deep_copy = false );
#endif
#ifdef USE_ALLEGRO5
explicit Bitmap(ALLEGRO_BITMAP * who, bool deep_copy = false );
#endif
Bitmap( const Bitmap & copy, bool deep_copy = false );
Bitmap( const Bitmap & copy, int sx, int sy );
Bitmap( const Bitmap & copy, int x, int y, int width, int height );
virtual ~Bitmap();
virtual TranslucentBitmap translucent() const;
/* will call transBlender() with the supplied values for you */
virtual TranslucentBitmap translucent(int red, int green, int blue, int alpha) const;
virtual LitBitmap lit() const;
virtual void save( const std::string & str ) const;
virtual Bitmap & operator=( const Bitmap & );
virtual void load( const std::string & str );
virtual int getWidth() const;
virtual int getHeight() const;
/* true if this bitmap doesn't contain an image of any sort. if so, don't
* try to use this object!!
*/
virtual bool isEmpty() const;
/*
inline const int getWidth() const{
return getBitmap()->w;
}
inline const int getHeight() const{
return getBitmap()->h;
}
*/
/*
inline const int getWidth() const{
return my_bitmap->w;
}
inline const int getHeight() const{
return my_bitmap->h;
}
*/
void detach();
/* replace all pixels that have value `original' with `replaced' */
void replaceColor(const Color & original, const Color & replaced);
void set8BitMaskColor(const Color & color);
Color get8BitMaskColor();
/* Blend between source pixel and destination pixel.
* Source and dest should be 0-255.
* source = 64, dest = 128
* pixel = source_pixel * source / 255 + dest_pixel * dest / 255
*/
static void alphaBlender(int source, int dest);
static void transBlender( int r, int g, int b, int a );
static void multiplyBlender( int r, int g, int b, int a );
static void dissolveBlender( int r, int g, int b, int a );
static void addBlender( int r, int g, int b, int a );
static void burnBlender( int r, int g, int b, int a );
static void colorBlender( int r, int g, int b, int a );
static void differenceBlender( int r, int g, int b, int a );
static void dodgeBlender( int r, int g, int b, int a );
static void hueBlender( int r, int g, int b, int a );
static void luminanceBlender( int r, int g, int b, int a );
static void invertBlender( int r, int g, int b, int a );
static void screenBlender( int r, int g, int b, int a );
/* for testing */
static void setFakeGraphicsMode(int width, int height);
/* clean up any remaining state */
static void shutdown();
// static void drawingMode( int type );
virtual Color blendColor(const Color & input) const;
/* sets the blend state. try not to use these function unless
* absolutely necessary.
*/
virtual void startDrawing() const;
virtual void endDrawing() const;
void acquire();
void release();
void updateOnResize();
/* Resizes the bitmap and stretches the old pixels to the new bitmap */
void resize(const int width, const int height);
/* Just resizes the bitmap without copying pixels */
void updateSize(const int width, const int height);
/* Makes this bitmap the same as the parameter.
* Width/height is copied and then a Blit is called.
*/
void copy(const Bitmap & him);
void debugSelf() const;
/* convert to a grey scale version */
virtual Bitmap greyScale();
virtual void triangle( int x1, int y1, int x2, int y2, int x3, int y3, Color color ) const;
/* draws an equilateral triangle centered at (x,y) pointing at `angle'
* where each side has `size' pixels using the color.
*/
virtual void equilateralTriangle(int x, int y, int angle, int size, Color color) const;
virtual void ellipse( int x, int y, int rx, int ry, Color color ) const;
virtual void ellipseFill( int x, int y, int rx, int ry, Color color ) const;
virtual void light(int x, int y, int width, int height, int start_y, int focus_alpha, int edge_alpha, Color focus_color, Color edge_color) const;
virtual void applyTrans(const Color color) const;
virtual void border( int min, int max, Color color ) const;
virtual void rectangle( int x1, int y1, int x2, int y2, Color color ) const;
virtual void rectangleFill( int x1, int y1, int x2, int y2, Color color ) const;
virtual void circleFill( int x, int y, int radius, Color color ) const;
virtual void circle( int x, int y, int radius, Color color ) const;
virtual void circle(int x, int y, int radius, int thickness, Color color) const;
virtual void line( const int x1, const int y1, const int x2, const int y2, const Color color ) const;
virtual void floodfill( const int x, const int y, const Color color ) const;
virtual void horizontalLine( const int x1, const int y, const int x2, const Color color ) const;
virtual void hLine( const int x1, const int y, const int x2, const Color color ) const;
virtual void vLine( const int y1, const int x, const int y2, const Color color ) const;
virtual void polygon( const int * verts, const int nverts, const Color color ) const;
virtual void arc(const int x, const int y, const double ang1, const double ang2, const int radius, const Color color ) const;
virtual void arcFilled(const int x, const int y, const double ang1, const double ang2, const int radius, const Color color ) const;
/* x/y is in the center of the bitmap */
virtual void drawCenter(const int x, const int y, const Bitmap & where) const;
virtual void draw(const int x, const int y, const Bitmap & where) const;
virtual void draw(const int x, const int y, Filter * filter, const Bitmap & where) const;
virtual void draw(const int x, const int y, const int startWidth, const int startHeight, const int width, const int height, const Bitmap & where) const;
virtual void draw(const int x, const int y, const int startWidth, const int startHeight, const int width, const int height, Filter * filter, const Bitmap & where) const;
virtual void drawCharacter( const int x, const int y, const Color color, const int background, const Bitmap & where ) const;
/* flip horizontally */
virtual void drawHFlip(const int x, const int y, const Bitmap & where) const;
virtual void drawHFlip(const int x, const int y, Filter * filter, const Bitmap & where) const;
virtual void drawHFlip(const int x, const int y, const int startWidth, const int startHeight, const int width, const int height, const Bitmap & where) const;
virtual void drawHFlip(const int x, const int y, const int startWidth, const int startHeight, const int width, const int height, Filter * filter, const Bitmap & where) const;
/* flip vertically */
virtual void drawVFlip( const int x, const int y, const Bitmap & where ) const;
virtual void drawVFlip( const int x, const int y, Filter * filter, const Bitmap & where ) const;
/* flip horizontal and vertical */
virtual void drawHVFlip( const int x, const int y, const Bitmap & where ) const;
virtual void drawHVFlip( const int x, const int y, Filter * filter, const Bitmap & where ) const;
// virtual void drawTrans( const int x, const int y, const Bitmap & where ) const;
// virtual void drawTrans( const int x, const int y, Filter * filter, const Bitmap & where ) const;
// virtual void drawTransHFlip( const int x, const int y, const Bitmap & where ) const;
// virtual void drawTransHFlip( const int x, const int y, Filter * filter, const Bitmap & where ) const;
// virtual void drawTransVFlip( const int x, const int y, const Bitmap & where ) const;
// virtual void drawTransVFlip( const int x, const int y, Filter * filter, const Bitmap & where ) const;
// virtual void drawTransHVFlip( const int x, const int y, const Bitmap & where ) const;
// virtual void drawTransHVFlip( const int x, const int y, Filter * filter, const Bitmap & where ) const;
virtual void drawMask( const int x, const int y, const Bitmap & where );
virtual void drawStretched( const int x, const int y, const int new_width, const int new_height, const Bitmap & who ) const;
virtual void drawStretched(const Bitmap & who) const;
/* middle of the bitmap is at x, y */
virtual void drawRotateCenter(const int x, const int y, const int angle, const Bitmap & where);
/* upper left hand corner is at x, y*/
virtual void drawRotate(const int x, const int y, const int angle, const Bitmap & where);
virtual void drawPivot( const int centerX, const int centerY, const int x, const int y, const int angle, const Bitmap & where );
virtual void drawPivot( const int centerX, const int centerY, const int x, const int y, const int angle, const double scale, const Bitmap & where );
/* Stretch using the hqx algorithm */
virtual void StretchHqx(const Bitmap & where) const;
virtual void StretchHqx(const Bitmap & where, const int sourceX, const int sourceY, const int sourceWidth, const int sourceHeight, const int destX, const int destY, const int destWidth, const int destHeight) const;
/* Xbr is defined here:
* http://board.byuu.org/viewtopic.php?f=10&t=2248
*/
virtual void StretchXbr(const Bitmap & where) const;
virtual void StretchXbr(const Bitmap & where, const int sourceX, const int sourceY, const int sourceWidth, const int sourceHeight, const int destX, const int destY, const int destWidth, const int destHeight) const;
/* Just copies pixels (fast) */
virtual void Stretch(const Bitmap & where) const;
virtual void Stretch(const Bitmap & where, const int sourceX, const int sourceY, const int sourceWidth, const int sourceHeight, const int destX, const int destY, const int destWidth, const int destHeight ) const;
virtual void StretchBy2( const Bitmap & where );
virtual void StretchBy4( const Bitmap & where );
virtual void Blit( const std::string & xpath ) const;
virtual void Blit( const Bitmap & where ) const;
virtual void Blit( const int x, const int y, const Bitmap & where ) const;
virtual void Blit( const int mx, const int my, const int wx, const int wy, const Bitmap & where ) const;
virtual void Blit( const int mx, const int my, const int width, const int height, const int wx, const int wy, const Bitmap & where ) const;
virtual void BlitMasked( const int mx, const int my, const int width, const int height, const int wx, const int wy, const Bitmap & where ) const;
virtual void BlitToScreen() const;
virtual void BlitAreaToScreen(const int upper_left_x, const int upper_left_y) const;
virtual void BlitToScreen(const int upper_left_x, const int upper_left_y) const;
virtual void BlitFromScreen(const int x, const int y) const;
virtual void roundRect(int radius, int x1, int y1, int x2, int y2, Color color) const;
virtual void roundRectFill(int radius, int x1, int y1, int x2, int y2, Graphics::Color color) const;
virtual void drawShadow(Bitmap & where, int x, int y, int intensity, Color color, double scale, bool facingRight) const;
+ /* Return a bitmap that has an aspect ratio the same as the given numbers */
+ virtual Bitmap aspectRatio(int aspectWidth, int aspectHeight) const;
+
/* returned a scaled version of this bitmap of the given dimensions */
virtual Bitmap scaleTo(const int width, const int height) const;
/* scale the image by the given ratios, i.e: a ratio of 1 means do nothing.
* less than 1 will make it smaller and larger than 1 will make it larger.
*/
virtual Bitmap scaleBy(const double widthRatio, const double heightRatio) const;
/* try to call Global::getScreenWidth/Height() instead of these directly */
static int getScreenWidth();
static int getScreenHeight();
virtual void fill(Color color) const;
virtual void clear() const;
inline void clearToMask() const{
this->fill(MaskColor());
}
bool getError();
inline const Util::ReferenceCount<BitmapData> & getData() const {
return data;
}
inline Util::ReferenceCount<BitmapData> getData(){
return data;
}
void setData(Util::ReferenceCount<BitmapData> data){
this->data = data;
}
virtual void readLine( std::vector<Color> & vec, int y );
Color getPixel( const int x, const int y ) const;
/* lock video bitmap to memory */
void lock() const;
void lock(int x, int y, int width, int height) const;
void unlock() const;
/* true if the point is within the bounds of the bitmap */
bool inRange(int x, int y) const;
/* uses _putpixel16 underneath which ignores translucent behavior */
void putPixel( int x, int y, Color col ) const;
/* respects the current trans mode */
virtual void putPixelNormal(int x, int y, Color col) const;
/*
inline int getPixel( int x, int y ) const{
if ( x >= 0 && x < my_bitmap->w && y >= 0 && y <= my_bitmap->h )
return _getpixel16( my_bitmap, x, y );
return -1;
}
inline void putPixel( int x, int y, int col ) const{
if ( x >= 0 && x < my_bitmap->w && y >= 0 && y <= my_bitmap->h )
_putpixel16( my_bitmap, x, y, col );
}
*/
void setClipRect( int x1, int y1, int x2, int y2 ) const;
void getClipRect( int & x1, int & y1, int & x2, int & y2 ) const;
inline const std::string & getPath() const{
return path;
}
/* produce a temporary bitmap that is not guaranteed to be preserved
* after your function returns. do *not* hold references to this bitmap
* and if you make a temporary bitmap, do *not* call functions that
* might also make temporary bitmaps.
*/
/*
static Bitmap temporaryBitmap(int w, int h);
static Bitmap temporaryBitmap2(int w, int h);
*/
/* call this method to delete all temporary bitmaps.
* don't call this unless you know what you are doing
*/
static void cleanupTemporaryBitmaps();
static double getScale();
/*
* Convert color values between the HSV and RGB color spaces. The RGB values
* range from 0 to 255, hue is from 0 to 360, and saturation and value are
* from 0 to 1.
*/
static void rgbToHSV(int r, int g, int b, float * h, float * s, float * v);
/* convert cymk to rgb. values should be in the range 0-255 */
static void cymkToRGB(int c, int y, int m, int k, int * r, int * g, int * b);
/* Add two RGB16 colors together
* r = c1.r + c2.r
* g = c1.g + c2.g
* b = c1.b + c2.b
*/
static Color addColor( Color color1, Color color2 );
/*
inline static int makeColor( int r, int g, int b ){
return makecol16( r, g, b );
}
*/
// static const int MaskColor = MASK_COLOR_16;
/*
static const int MODE_TRANS;
static const int MODE_SOLID;
*/
#ifdef USE_ALLEGRO5
void convertToVideo();
#endif
protected:
#ifdef USE_ALLEGRO5
virtual void draw(const int x, const int y, Filter * filter, const Bitmap & where, int flags) const;
#endif
/* release a reference count, and possibly destroy data */
// void releaseInternalBitmap();
/* really destroy private data */
// void destroyPrivateData();
/*
inline void setBitmap( BITMAP * bitmap ){
if ( bitmap == NULL ){
std::cout << "*FATAL* Setting null bitmap" << std::endl;
}
_my_bitmap = bitmap;
}
*/
void loadFromMemory(const char * data, int length);
void internalLoadFile( const char * load_file );
/* implementation specific data */
Util::ReferenceCount<BitmapData> data;
// int * own;
bool mustResize;
// bool own;
bool error;
std::string path;
static Bitmap * temporary_bitmap;
static Bitmap * temporary_bitmap2;
Color bit8MaskColor;
/* only used by allegro5 for now */
int width, height;
};
/* 8-bit bitmap that supports a palette */
class PaletteBitmap: public Bitmap {
public:
PaletteBitmap();
virtual ~PaletteBitmap();
using Bitmap::operator=;
protected:
Color palette[256];
};
Bitmap memoryPCX(unsigned char * const data, const int length, const bool mask = true);
/* creates a bitmap that can be used as a buffer for the screen.
* on opengl/allegro5 systems this will return the current backbuffer
*/
Bitmap * getScreenBuffer();
void blend_palette(Color * pal, int mp, Color sc, Color ec);
// bool sameColors(Color color1, Color color2);
class LitBitmap: public Bitmap {
public:
LitBitmap( const Bitmap & b );
LitBitmap();
virtual ~LitBitmap();
using Bitmap::draw;
virtual void draw(const int x, const int y, const Bitmap & where) const;
virtual void draw(const int x, const int y, Filter * filter, const Bitmap & where ) const;
using Bitmap::drawHFlip;
virtual void drawHFlip( const int x, const int y, const Bitmap & where ) const;
virtual void drawHFlip( const int x, const int y, Filter * filter, const Bitmap & where ) const;
using Bitmap::drawVFlip;
virtual void drawVFlip( const int x, const int y, const Bitmap & where ) const;
virtual void drawVFlip( const int x, const int y, Filter * filter, const Bitmap & where ) const;
using Bitmap::drawHVFlip;
virtual void drawHVFlip( const int x, const int y, const Bitmap & where ) const;
virtual void drawHVFlip( const int x, const int y, Filter * filter, const Bitmap & where ) const;
using Bitmap::operator=;
protected:
#ifdef USE_ALLEGRO5
virtual void draw(const int x, const int y, Filter * filter, const Bitmap & where, int flags) const;
#endif
};
class StretchedBitmap: public Bitmap {
public:
- StretchedBitmap(int width, int height, const Bitmap & where, QualityFilter filter = NoFilter);
+ /* How to initialize the bitmap */
+ enum Clear{
+ /* Don't do anything */
+ NoClear,
+ /* Clear (to black) */
+ FullClear,
+ /* Fill with the masking color for software rendering and
+ * do nothing for opengl
+ */
+ Mask
+ };
+
+ StretchedBitmap(int width, int height, const Bitmap & where, Clear = NoClear, QualityFilter filter = NoFilter);
void finish();
void start();
virtual int getWidth() const;
virtual int getHeight() const;
using Bitmap::operator=;
virtual double getScaleWidth() const;
virtual double getScaleHeight() const;
protected:
double width;
double height;
double scale_x, scale_y;
const Bitmap & where;
const QualityFilter filter;
+ const Clear clearKind;
Bitmap scaleToFilter;
};
class TranslatedBitmap: public Bitmap {
public:
TranslatedBitmap(int x, int y, const Bitmap & where);
using Bitmap::operator=;
using Bitmap::BlitToScreen;
virtual void BlitToScreen() const;
virtual ~TranslatedBitmap();
public:
int x, y;
};
class TranslucentBitmap: public Bitmap {
public:
TranslucentBitmap(const Bitmap & b);
TranslucentBitmap();
virtual ~TranslucentBitmap();
using Bitmap::operator=;
virtual Color blendColor(const Color & color) const;
virtual void startDrawing() const;
virtual void endDrawing() const;
virtual void putPixelNormal(int x, int y, Color col) const;
virtual void rectangleFill(int x1, int y1, int x2, int y2, Color color) const;
virtual void rectangle(int x1, int y1, int x2, int y2, Color color) const;
virtual void fill(Color color) const;
virtual void line( const int x1, const int y1, const int x2, const int y2, const Color color ) const;
virtual void hLine( const int x1, const int y, const int x2, const Color color ) const;
virtual void arc(const int x, const int y, const double ang1, const double ang2, const int radius, const Color color) const;
virtual void arcFilled(const int x, const int y, const double ang1, const double ang2, const int radius, const Color color ) const;
virtual void roundRect(int radius, int x1, int y1, int x2, int y2, Color color) const;
virtual void roundRectFill(int radius, int x1, int y1, int x2, int y2, Graphics::Color color) const;
virtual void circleFill( int x, int y, int radius, Color color ) const;
virtual void ellipse( int x, int y, int rx, int ry, Color color ) const;
virtual void ellipseFill( int x, int y, int rx, int ry, Color color ) const;
using Bitmap::draw;
virtual void draw(const int x, const int y, const Bitmap & where) const;
virtual void draw(const int x, const int y, Filter * filter, const Bitmap & where) const;
// virtual void draw(const int x, const int y, const int startWidth, const int startHeight, const int width, const int height, const Bitmap & where) const;
// virtual void draw(const int x, const int y, const int startWidth, const int startHeight, const int width, const int height, Filter * filter, const Bitmap & where) const;
using Bitmap::drawHFlip;
virtual void drawHFlip(const int x, const int y, const Bitmap & where) const;
virtual void drawHFlip(const int x, const int y, Filter * filter, const Bitmap & where) const;
// virtual void drawHFlip(const int x, const int y, const int startWidth, const int startHeight, const int width, const int height, const Bitmap & where) const;
// virtual void drawHFlip(const int x, const int y, const int startWidth, const int startHeight, const int width, const int height, Filter * filter, const Bitmap & where) const;
virtual void drawVFlip( const int x, const int y, const Bitmap & where ) const;
virtual void drawVFlip( const int x, const int y, Filter * filter, const Bitmap & where ) const;
virtual void drawHVFlip( const int x, const int y, const Bitmap & where ) const;
virtual void drawHVFlip( const int x, const int y, Filter * filter, const Bitmap & where ) const;
protected:
#ifdef USE_ALLEGRO5
virtual void draw(const int x, const int y, Filter * filter, const Bitmap & where, int flags) const;
#endif
};
}
#endif
diff --git a/util/graphics/software-renderer/bitmap.cpp b/util/graphics/software-renderer/bitmap.cpp
index 8848ca1f..99d0c518 100644
--- a/util/graphics/software-renderer/bitmap.cpp
+++ b/util/graphics/software-renderer/bitmap.cpp
@@ -1,247 +1,259 @@
/* generic software implementation of various bitmap operations */
namespace Graphics{
Color Bitmap::blendColor(const Color & input) const {
return input;
}
Color TranslucentBitmap::blendColor(const Color & input) const {
return input;
}
void Bitmap::startDrawing() const {
}
void Bitmap::endDrawing() const {
}
int getAlpha(Color input){
return 255;
}
void Bitmap::drawShadow(Bitmap & where, int x, int y, int intensity, Color color, double scale, bool facingRight) const {
const double newheight = getHeight() * scale;
Bitmap shade(getWidth(), (int) fabs(newheight));
Stretch(shade);
/* Could be slow, but meh, lets do it for now to make it look like a real shadow */
for (int h = 0; h < shade.getHeight(); ++h){
for (int w = 0; w < shade.getWidth(); ++w){
Color pix = shade.getPixel(w, h);
if (pix != MaskColor()){
shade.putPixel(w, h, color);
}
}
}
transBlender(0, 0, 0, intensity);
if (scale > 0){
if (facingRight){
shade.translucent().drawVFlip(x, y, where);
} else {
shade.translucent().drawHVFlip(x, y, where);
}
} else if (scale < 0){
y -= fabs(newheight);
if (facingRight){
shade.translucent().draw(x + 3, y, where);
} else {
shade.translucent().drawHFlip(x - 3, y, where);
}
}
}
void TranslucentBitmap::roundRect(int radius, int x1, int y1, int x2, int y2, Color color) const {
Bitmap::roundRect(radius, x1, y1, x2, y2, color);
}
void Bitmap::roundRect(int radius, int x1, int y1, int x2, int y2, Color color) const {
const int width = x2 - x1;
const int height = y2 - y1;
radius = Mid(0, radius, Min((x1+width - x1)/2, (y1+height - y1)/2));
line(x1+radius, y1, x1+width-radius, y1, color);
line(x1+radius, y1+height, x1+width-radius,y1+height, color);
line(x1, y1+radius,x1, y1+height-radius, color);
line(x1+width, y1+radius,x1+width, y1+height-radius, color);
double quarterTurn = Util::pi / 2;
double quadrant1 = 0;
/* signs are flipped because the coordinate system is reflected over the y-axis */
double quadrant2 = -Util::pi / 2;
double quadrant3 = Util::pi;
double quadrant4 = -3 * Util::pi / 2;
/* upper right. draw from 90 to 0 */
arc(x1+radius + (width - radius *2), y1 + radius, quadrant1, quadrant1 + quarterTurn, radius, color);
/* upper left. draw from 180 to 270 */
arc(x1 + radius, y1 + radius, quadrant2, quadrant2 + quarterTurn, radius, color);
/* lower left. draw from 180 to 270 */
arc(x1 + radius, y1 + height - radius, quadrant3, quadrant3 + quarterTurn, radius, color);
/* lower right. draw from 0 to 270 */
arc(x1+width-radius, y1+height-radius, quadrant4, quadrant4 + quarterTurn, radius, color);
}
void TranslucentBitmap::roundRectFill(int radius, int x1, int y1, int x2, int y2, Graphics::Color color) const {
Bitmap::roundRectFill(radius, x1, y1, x2, y2, color);
}
void Bitmap::roundRectFill(int radius, int x1, int y1, int x2, int y2, Graphics::Color color) const {
const int width = x2 - x1;
const int height = y2 - y1;
radius = Mid(0, radius, Min((x1+width - x1)/2, (y1+height - y1)/2));
double quarterTurn = Util::pi / 2;
double quadrant1 = 0;
/* signs are flipped because the coordinate system is reflected over the y-axis */
double quadrant2 = -Util::pi / 2;
double quadrant3 = Util::pi;
double quadrant4 = -3 * Util::pi / 2;
/* upper right. draw from 90 to 0 */
arcFilled(x2 - radius, y1 + radius, quadrant1, quadrant1 + quarterTurn, radius, color);
/* upper left. draw from 180 to 90 */
arcFilled(x1 + radius, y1 + radius, quadrant2, quadrant2 + quarterTurn, radius, color);
/* lower left. draw from 270 to 180 */
arcFilled(x1 + radius, y2 - radius, quadrant3, quadrant3 + quarterTurn, radius, color);
/* lower right. draw from 360 to 270 */
arcFilled(x2 - radius, y2 - radius, quadrant4, quadrant4 + quarterTurn, radius, color);
rectangleFill(x1+radius + 1, y1, x2-radius - 1, y1+radius - 1, color);
rectangleFill(x1, y1+radius, x2, y2-radius, color);
rectangleFill(x1+radius + 1, y2-radius + 1, x2-radius - 1, y2, color);
}
void TranslucentBitmap::startDrawing() const {
}
void TranslucentBitmap::endDrawing() const {
}
void Bitmap::lock() const {
}
void Bitmap::unlock() const {
}
Bitmap Bitmap::scaleTo(const int width, const int height) const {
if (width == getWidth() && height == getHeight()){
return *this;
}
Bitmap scaled(width, height);
this->Stretch(scaled);
return scaled;
}
/* Filters only work for multiples of 320x240 up to 4x */
static bool filterMultiple(int width, int height, int toWidth, int toHeight){
return (toWidth == width * 2 && toHeight == height * 2) ||
(toWidth == width * 3 && toHeight == height * 3) ||
(toWidth == width * 4 && toHeight == height * 4);
}
static void closestMultipleSize(int width, int height, int toWidth, int toHeight, int * outWidth, int * outHeight){
/* If we are down-scaling then we don't need any filters */
if (toWidth < width || toHeight < height){
*outWidth = 1;
*outHeight = 1;
} else if (toWidth < width * 2 && toHeight < height * 2){
*outWidth = 1;
*outHeight = 1;
} else if (toWidth < width * 3 && toHeight < height * 3){
*outWidth = width * 2;
*outHeight = height * 2;
} else {
*outWidth = width * 3;
*outHeight = height * 3;
}
}
-StretchedBitmap::StretchedBitmap(int width, int height, const Bitmap & parent, QualityFilter filter):
+StretchedBitmap::StretchedBitmap(int width, int height, const Bitmap & parent, Clear clear, QualityFilter filter):
Bitmap(1, 1),
width(1),
height(1),
where(parent),
filter(filter),
+clearKind(clear),
scaleToFilter(1, 1){
if (width == parent.getWidth() && height == parent.getHeight()){
setData(parent.getData());
} else {
updateSize(width, height);
}
if (!filterMultiple(width, height, parent.getWidth(), parent.getHeight()) &&
filter != NoFilter){
int multipleWidth, multipleHeight;
closestMultipleSize(width, height, parent.getWidth(), parent.getHeight(), &multipleWidth, &multipleHeight);
scaleToFilter.updateSize(multipleWidth, multipleHeight);
}
this->width = width;
this->height = height;
+
+ switch (clear){
+ case NoClear: break;
+ case FullClear: this->clear(); break;
+ case Mask: this->clearToMask(); break;
+ }
}
void StretchedBitmap::start(){
}
void StretchedBitmap::finish(){
if (getData() != where.getData()){
- switch (filter){
- case NoFilter: Stretch(where); break;
- case HqxFilter: {
- if (scaleToFilter.getWidth() > 1 &&
- scaleToFilter.getHeight() > 1){
- StretchHqx(scaleToFilter);
- scaleToFilter.Stretch(where);
- } else {
- StretchHqx(where);
+ /* FIXME: make scalers understand the masking color. I kinf of doubt this is possible.. */
+ if (clearKind == Mask){
+ drawStretched(where);
+ } else {
+ switch (filter){
+ case NoFilter: Stretch(where); break;
+ case HqxFilter: {
+ if (scaleToFilter.getWidth() > 1 &&
+ scaleToFilter.getHeight() > 1){
+ StretchHqx(scaleToFilter);
+ scaleToFilter.Stretch(where);
+ } else {
+ StretchHqx(where);
+ }
+ break;
}
- break;
- }
- case XbrFilter: {
- if (scaleToFilter.getWidth() > 1 &&
- scaleToFilter.getHeight() > 1){
- StretchXbr(scaleToFilter);
- scaleToFilter.Stretch(where);
- } else {
- StretchXbr(where);
+ case XbrFilter: {
+ if (scaleToFilter.getWidth() > 1 &&
+ scaleToFilter.getHeight() > 1){
+ StretchXbr(scaleToFilter);
+ scaleToFilter.Stretch(where);
+ } else {
+ StretchXbr(where);
+ }
+ break;
}
- break;
}
}
}
}
/*
Bitmap getScreenBuffer(){
return Bitmap(GFX_X, GFX_Y);
}
*/
RestoreState::RestoreState(){
}
RestoreState::~RestoreState(){
}
TranslatedBitmap::TranslatedBitmap(int x, int y, const Bitmap & where):
Bitmap(where),
x(x),
y(y){
}
void TranslatedBitmap::BlitToScreen() const {
Bitmap::BlitToScreen(x, y);
}
TranslatedBitmap::~TranslatedBitmap(){
}
Shader::Shader(){
}
Shader::~Shader(){
}
}
diff --git a/util/loading.cpp b/util/loading.cpp
index e3114c32..1ce54272 100644
--- a/util/loading.cpp
+++ b/util/loading.cpp
@@ -1,553 +1,556 @@
#include "graphics/bitmap.h"
#include <math.h>
#include <iostream>
#include "messages.h"
#include "loading.h"
#include "file-system.h"
#include "font.h"
#include "funcs.h"
#include "version.h"
#include "graphics/gradient.h"
#include "parameter.h"
#include "thread.h"
#include <vector>
#include "thread.h"
#include "message-queue.h"
#include "init.h"
#include "events.h"
#include "input/input-map.h"
#include "input/input-manager.h"
using namespace std;
namespace Loader{
volatile bool done_loading = true;
typedef struct pair{
int x, y;
} ppair;
class MessageInfo{
public:
MessageInfo(){
MessageQueue::registerInfo(&messages);
}
bool transferMessages(Messages & box){
bool did = false;
while (messages.hasAny()){
const string & str = messages.get();
box.addMessage(str);
did = true;
}
return did;
}
~MessageInfo(){
MessageQueue::unregisterInfo(&messages);
}
private:
MessageQueue messages;
};
Info::Info(const string & message, const Filesystem::AbsolutePath & background):
x(-1),
y(-1),
_loadingMessage(message),
background(NULL),
_loadingBackground(background){
}
Info::Info(const Info & info){
this->x = info.x;
this->y = info.y;
this->_loadingMessage = info._loadingMessage;
this->background = info.background;
this->_loadingBackground = info._loadingBackground;
}
Info::~Info(){
}
void Info::setBackground(const Graphics::Bitmap * background){
this->background = background;
}
void Info::setLoadingMessage(const std::string & str){
this->_loadingMessage = str;
}
void Info::setPosition(int x, int y){
this->x = x;
this->y = y;
}
const Graphics::Bitmap * Info::getBackground() const {
return background;
}
const string & Info::loadingMessage() const {
return _loadingMessage;
}
const Filesystem::AbsolutePath & Info::loadingBackground() const {
return _loadingBackground;
}
int Info::getPositionX() const {
return x;
}
int Info::getPositionY() const {
return y;
}
void * loadingScreenSimple1(void * arg);
static void setupBackground(const Graphics::Bitmap & background, int load_x, int load_y, int load_width, int load_height, int infobox_x, int infobox_y, int infoWidth, int infoHeight, const Graphics::Bitmap & infoBackground, const Graphics::Bitmap & screen){
int startX = background.getWidth() - Font::getDefaultFont().textLength("Paintown version 9.9.9.9");
int startY = background.getHeight() - Font::getDefaultFont().getHeight() * 4;
int height = Font::getDefaultFont().getHeight();
Font::getDefaultFont().printf(startX, startY + height * 0, Graphics::makeColor(192, 192, 192), background, "Paintown version %s", 0, Version::getVersionString().c_str());
Font::getDefaultFont().printf(startX, startY + height * 1, Graphics::makeColor(192, 192, 192), background, "Made by Jon Rafkind", 0);
Font::getDefaultFont().printf(startX, startY + height * 2, Graphics::makeColor(192, 192, 192), background, "http://paintown.org", 0);
/* we have to blit to the screen object passed in because that is the bitmap
* that will be operated on in the draw() method of loadingScreen1.
* we also have to blit to the real screen because the screen object
* is not drawn in its entirety to the real screen, only the part
* that shows the 'Loading ...' message and the info box.
* drawing twice in Allegro5 is redundant because the screen object is the real
* screen but for Allegro4 and SDL we need to do this because the screen object
* is a buffer.
*/
background.Blit(screen);
background.BlitToScreen();
background.Blit(infobox_x, infobox_y, infoWidth, infoHeight, 0, 0, infoBackground);
}
/* converts a bitmap with some text on it into a sequence of points */
static vector<ppair> generateFontPixels(const Font & myFont, const string & message, int width, int height){
Graphics::Bitmap letters(width, height);
letters.fill(Graphics::MaskColor());
myFont.printf(0, 0, Graphics::makeColor(255, 255, 255), letters, message.c_str(), 0);
vector<ppair> pairs;
/* store every pixel we need to draw */
letters.lock();
for (int x = 0; x < letters.getWidth(); x++){
for (int y = 0; y < letters.getHeight(); y++){
Graphics::Color pixel = letters.getPixel(x, y);
if (pixel != Graphics::MaskColor()){
ppair p;
p.x = x;
p.y = y;
pairs.push_back(p);
}
}
}
letters.unlock();
// Graphics::resetDisplay();
return pairs;
}
/* shows time elapsed */
class TimeCounter{
public:
TimeCounter():
work(200, 40){
start = Global::second_counter;
last = 0;
}
void draw(int x, int y){
const Font & font = Font::getDefaultFont(24, 24);
if (Global::second_counter != last){
work.clear();
last = Global::second_counter;
font.printf(0, 0, Graphics::makeColor(192, 192, 192), work, "Waiting.. %d", 0, last - start);
work.BlitAreaToScreen(x, y);
}
}
Graphics::Bitmap work;
unsigned int start;
unsigned int last;
};
enum LoadingKeys{
Activate
};
static void loadingScreen1(LoadingContext & context, const Info & levelInfo){
int load_x = 80;
int load_y = 220;
const int infobox_width = 300;
const int infobox_height = 150;
const Font & myFont = Font::getDefaultFont(24, 24);
if (levelInfo.getPositionX() != -1){
load_x = levelInfo.getPositionX();
}
if (levelInfo.getPositionY() != -1){
load_y = levelInfo.getPositionY();
}
// const char * the_string = (arg != NULL) ? (const char *) arg : "Loading...";
int load_width = myFont.textLength(levelInfo.loadingMessage().c_str());
int load_height = myFont.getHeight(levelInfo.loadingMessage().c_str());
Global::debug(2) << "loading screen" << endl;
Messages infobox(infobox_width, infobox_height);
const int MAX_COLOR = 200;
/* blend from dark grey to light red */
Effects::Gradient gradient(MAX_COLOR, Graphics::makeColor(16, 16, 16), Graphics::makeColor(192, 8, 8));
TimeCounter counter;
struct State{
bool drawInfo;
};
class Logic: public Util::Logic {
public:
Logic(LoadingContext & context, State & state, Effects::Gradient & gradient, Messages & infoBox):
context(context),
state(state),
gradient(gradient),
infobox(infoBox),
active(false){
input.set(Keyboard::Key_SPACE, 0, true, Activate);
input.set(Keyboard::Key_ENTER, 0, true, Activate);
input.set(Joystick::Button1, 0, true, Activate);
}
LoadingContext & context;
State & state;
Effects::Gradient & gradient;
MessageInfo info;
Messages & infobox;
bool active;
InputMap<LoadingKeys> input;
void doInput(){
class Handler: public InputHandler<LoadingKeys> {
public:
Handler(bool & active):
active(active){
}
bool & active;
void press(const LoadingKeys & out, Keyboard::unicode_t unicode){
if (out == Activate){
/* the info box can't really be turned off because once
* its drawn it will remain there. the background would
* have to be drawn on top of it to remove the old
* info box. maybe do this in the future, if so use
* active = ! active
* to toggle it.
*/
active = ! active;
}
}
void release(const LoadingKeys & out, Keyboard::unicode_t unicode){
}
};
Handler handler(active);
InputManager::handleEvents(input, InputSource(true), handler);
}
void run(){
for (int i = 0; i < 3; i++){
gradient.backward();
}
doInput();
info.transferMessages(infobox);
// state.drawInfo = active && (info.transferMessages(infobox) || state.drawInfo);
state.drawInfo = active;
}
double ticks(double system){
return system * Global::ticksPerSecond(8);
}
bool done(){
return context.done();
}
};
class Draw: public Util::Draw {
public:
Draw(const Info & levelInfo, State & state, Messages & infobox, Effects::Gradient & gradient, int load_width, int load_height, int infobox_width, int infobox_height, int load_x, int load_y):
levelInfo(levelInfo),
gradient(gradient),
state(state),
infobox(infobox),
infobox_x(load_x),
infobox_y(load_y + load_height * 2),
infobox_width(infobox_width),
infobox_height(infobox_height),
load_x(load_x),
load_y(load_y),
load_width(load_width),
load_height(load_height){
if (levelInfo.loadingBackground() != Filesystem::AbsolutePath("")){
background = Graphics::Bitmap(levelInfo.loadingBackground().path());
}
const Font & myFont = Font::getDefaultFont(24, 24);
pairs = generateFontPixels(myFont, levelInfo.loadingMessage(), load_width, load_height);
}
const Info & levelInfo;
Effects::Gradient & gradient;
State & state;
Messages & infobox;
Graphics::Bitmap background;
// Graphics::Bitmap infoWork;
// Graphics::Bitmap infoBackground;
vector<ppair> pairs;
const int infobox_x;
const int infobox_y;
const int infobox_width;
const int infobox_height;
const int load_x;
const int load_y;
const int load_width;
const int load_height;
/*
void drawFirst(const Graphics::Bitmap & screen){
if (levelInfo.getBackground() != NULL){
setupBackground(*levelInfo.getBackground(), load_x, load_y, load_width, load_height, infobox_x, infobox_y, infoBackground.getWidth(), infoBackground.getHeight(), infoBackground, screen);
} else {
Graphics::Bitmap background;
if (levelInfo.loadingBackground() != Filesystem::AbsolutePath("")){
background = Graphics::Bitmap(levelInfo.loadingBackground().path());
} else {
background = Graphics::Bitmap(*Graphics::screenParameter.current(), true);
}
setupBackground(background, load_x, load_y, load_width, load_height, infobox_x, infobox_y, infoBackground.getWidth(), infoBackground.getHeight(), infoBackground, screen);
}
}
*/
void drawAuthorInfo(const Graphics::Bitmap & screen){
int startX = screen.getWidth() - Font::getDefaultFont().textLength("Paintown version 9.9.9.9");
int startY = screen.getHeight() - Font::getDefaultFont().getHeight() * 4;
int height = Font::getDefaultFont().getHeight();
Font::getDefaultFont().printf(startX, startY + height * 0, Graphics::makeColor(192, 192, 192), screen, "Paintown version %s", 0, Version::getVersionString().c_str());
Font::getDefaultFont().printf(startX, startY + height * 1, Graphics::makeColor(192, 192, 192), screen, "Made by Jon Rafkind", 0);
Font::getDefaultFont().printf(startX, startY + height * 2, Graphics::makeColor(192, 192, 192), screen, "http://paintown.org", 0);
}
void draw(const Graphics::Bitmap & screen){
if (!background.isEmpty()){
background.drawStretched(screen);
}
Graphics::Bitmap work(screen, load_x, load_y, load_width, load_height);
// work.lock();
for (vector< ppair >::iterator it = pairs.begin(); it != pairs.end(); it++){
Graphics::Color color = gradient.current(it->x);
work.putPixel(it->x, it->y, color);
}
// work.unlock();
if (state.drawInfo){
// infoBackground.Blit(infoWork);
const Font & infoFont = Font::getDefaultFont(24, 24);
/* cheesy hack to change the font size. the font
* should store the size and change it on its own
*/
Graphics::Bitmap infoWork(screen, infobox_x, infobox_y, infobox_width, infobox_height);
Font::getDefaultFont(13, 13);
infobox.draw(0, 0, infoWork, infoFont);
Font::getDefaultFont(24, 24);
infoWork.BlitAreaToScreen(infobox_x, infobox_y);
// infoWork.BlitToScreen();
// state.drawInfo = false;
}
drawAuthorInfo(screen);
/* work already contains the correct background */
// work.Blit( load_x, load_y, *Bitmap::Screen );
// work.BlitToScreen();
// work.BlitAreaToScreen(load_x, load_y);
}
};
State state;
// state.drawInfo = true;
Logic logic(context, state, gradient, infobox);
Draw draw(levelInfo, state, infobox, gradient, load_width, load_height, infobox_width, infobox_height, load_x, load_y);
Util::standardLoop(logic, draw);
}
static void loadingScreenSimpleX1(LoadingContext & context, const Info & levelInfo){
class Logic: public Util::Logic {
public:
Logic(LoadingContext & context, int & angle, int speed):
context(context),
speed(speed),
angle(angle){
}
LoadingContext & context;
/* speed of rotation */
const int speed;
int & angle;
double ticks(double system){
- return system / 2;
+ return system * Global::ticksPerSecond(10);
}
bool done(){
return context.done();
}
void run(){
angle += speed * 2;
+ angle = angle % 360;
}
};
class Draw: public Util::Draw {
public:
Draw(int & angle, const int speed):
- original(40, 40),
angle(angle),
speed(speed){
- original.BlitFromScreen(0, 0);
-
- color1 = Graphics::makeColor(0, 0, 0);
- color2 = Graphics::makeColor(0x00, 0x99, 0xff);
- color3 = Graphics::makeColor(0xff, 0x22, 0x33);
- color4 = Graphics::makeColor(0x44, 0x77, 0x33);
- colors[0] = color1;
- colors[1] = color2;
- colors[2] = color3;
- colors[3] = color4;
+ /*
+ if (levelInfo.loadingBackground() != Filesystem::AbsolutePath("")){
+ background = Graphics::Bitmap(levelInfo.loadingBackground().path());
+ }
+ */
+
+ colors[0] = Graphics::makeColor(0x18, 0x52, 0xa0);
+ colors[1] = Graphics::makeColor(0x00, 0x99, 0xff);
+ colors[2] = Graphics::makeColor(0xff, 0x22, 0x33);
+ colors[3] = Graphics::makeColor(0x44, 0x77, 0x33);
Graphics::Bitmap::transBlender(0, 0, 0, 64);
}
- Graphics::Bitmap original;
+ Graphics::Bitmap background;
int & angle;
const int speed;
- Graphics::Color color1;
- Graphics::Color color2;
- Graphics::Color color3;
- Graphics::Color color4;
/* the length of this array is the number of circles to show */
Graphics::Color colors[4];
- ~Draw(){
+ virtual ~Draw(){
}
void draw(const Graphics::Bitmap & screen){
+ if (!background.isEmpty()){
+ background.drawStretched(screen);
+ }
+
Graphics::Bitmap work(screen, 0, 0, 40, 40);
- int max = sizeof(colors) / sizeof(int);
+ int max = Util::array_size(colors);
double middleX = work.getWidth() / 2;
double middleY = work.getHeight() / 2;
- original.Blit(work);
+ int size = 15;
+ int radius = 3;
for (int i = 0; i < max; i++){
- double x = cos(Util::radians(angle + 360 / max * i)) * 15;
- double y = sin(Util::radians(angle + 360 / max * i)) * 15;
+ double new_angle = angle + 360.0 / (double) max * i;
+ int lag = 17;
+ double x = cos(Util::radians(new_angle - lag)) * size;
+ double y = sin(Util::radians(new_angle - lag)) * size;
/* ghost circle */
- work.translucent().circleFill(middleX + x, middleY + y, 2, colors[i]);
- x = cos(Util::radians(angle + speed + 360 / max * i)) * 15;
- y = sin(Util::radians(angle + speed + 360 / max * i)) * 15;
+ work.translucent(0, 0, 0, 128).circleFill(middleX + x, middleY + y, radius, colors[i]);
+ x = cos(Util::radians(new_angle)) * size;
+ y = sin(Util::radians(new_angle)) * size;
/* real circle */
- work.circleFill(middleX + x, middleY + y, 2, colors[i]);
+ work.circleFill(middleX + x, middleY + y, radius, colors[i]);
}
// work.BlitAreaToScreen(0, 0);
}
};
int angle = 0;
- int speed = 7;
+ int speed = 10;
Logic logic(context, angle, speed);
Draw draw(angle, speed);
Util::standardLoop(logic, draw);
}
LoadingContext::LoadingContext():
finished(false){
Util::Thread::initializeLock(&lock);
}
LoadingContext::~LoadingContext(){
}
void LoadingContext::doLoad(){
this->load();
Util::Thread::acquireLock(&lock);
finished = true;
Util::Thread::releaseLock(&lock);
}
bool LoadingContext::done(){
bool ok = false;
Util::Thread::acquireLock(&lock);
ok = this->finished;
Util::Thread::releaseLock(&lock);
return ok;
}
void * LoadingContext::load_it(void * arg){
LoadingContext * context = (LoadingContext*) arg;
context->doLoad();
return NULL;
}
static void showLoadMessage(){
int screenX = 80;
int screenY = 50;
Graphics::Bitmap work(110, 50);
work.BlitFromScreen(screenX, screenY);
Graphics::Bitmap top(110, 50);
top.fill(Graphics::makeColor(0, 0, 0));
Font::getDefaultFont(25, 25).printf(10, 5, Graphics::makeColor(192, 192, 192), top, "Loading", 0);
Graphics::Bitmap::transBlender(0, 0, 0, 200);
top.translucent().draw(0, 0, work);
work.BlitAreaToScreen(screenX, screenY);
}
void loadScreen(LoadingContext & context, const Info & info, Kind kind){
Util::Thread::Id loadingThread;
bool created = Util::Thread::createThread(&loadingThread, NULL, (Util::Thread::ThreadFunction) LoadingContext::load_it, &context);
if (!created){
Global::debug(0) << "Could not create loading thread. Loading will occur in the main thread" << endl;
showLoadMessage();
LoadingContext::load_it(&context);
// throw LoadException(__FILE__, __LINE__, "Could not create loader thread");
} else {
InputManager::deferResizeEvents(true);
switch (kind){
case Default: loadingScreen1(context, info); break;
case SimpleCircle: loadingScreenSimpleX1(context, info); break;
default: loadingScreen1(context, info); break;
}
Util::Thread::joinThread(loadingThread);
InputManager::deferResizeEvents(false);
}
}
}
diff --git a/util/menu/menu.cpp b/util/menu/menu.cpp
index 7a77bd96..792c58d6 100644
--- a/util/menu/menu.cpp
+++ b/util/menu/menu.cpp
@@ -1,1849 +1,1849 @@
#include "util/graphics/bitmap.h"
#include "menu.h"
#include "menu_option.h"
#include "util/version.h"
#include "util/funcs.h"
#include "util/sound/sound.h"
#include "util/font.h"
#include "util/token.h"
#include "util/events.h"
#include "util/tokenreader.h"
#include "util/file-system.h"
#include "util/resource.h"
#include "util/debug.h"
#include "util/init.h"
#include "util/configuration.h"
#include "util/sound/music.h"
#include "util/graphics/gradient.h"
#include "util/exceptions/shutdown_exception.h"
#include "util/exceptions/exception.h"
#include "optionfactory.h"
#include "actionfactory.h"
#include "util/input/input-manager.h"
#include "util/input/input-map.h"
#include "util/input/input-source.h"
#include "util/parameter.h"
#include <queue>
#include <map>
#include <ostream>
#include <sstream>
#include "util/gui/context-box.h"
using namespace std;
using namespace Gui;
/* the current font is a property of the dynamic execution. so it will
* be modified by various functions that call Parameter::push
*/
static const Font & currentFont(){
return Menu::menuFontParameter.current()->get();
}
Util::Parameter<Util::ReferenceCount<Menu::FontInfo> > Menu::menuFontParameter;
/*
static std::string sharedFont = "fonts/arial.ttf";
static int sharedFontWidth = 24;
static int sharedFontHeight = 24;
*/
Effects::Gradient Menu::standardGradient(){
return Gui::standardGradient(50);
}
Menu::Point::Point():
x(0),
y(0){
}
Menu::Point::Point(int x, int y):
x(x),
y(y){
}
Menu::Point::~Point(){
}
Menu::InfoBox::InfoBox():
state(NotActive),
fadeAlpha(0){
popup.setFadeSpeed(20);
}
Menu::InfoBox::~InfoBox(){
}
void Menu::InfoBox::act(const Font & font){
popup.act(font);
int speed = 9;
switch (state){
case Opening: {
if (fadeAlpha < 255){
fadeAlpha += speed;
}
if (fadeAlpha >= 255){
fadeAlpha = 255;
if (popup.isActive()){
state = Active;
}
}
break;
}
case Closing: {
if (fadeAlpha > 0){
fadeAlpha -= speed;
}
if (fadeAlpha <= 0){
fadeAlpha = 0;
if (!popup.isActive()){
state = NotActive;
}
}
break;
}
case Active:
case NotActive:
default:
break;
}
}
void Menu::InfoBox::render(const Graphics::Bitmap &){
throw MenuException(__FILE__, __LINE__, "Don't call the render(Bitmap) function");
}
void Menu::InfoBox::render(const Graphics::Bitmap & bmp, const Font & vFont){
popup.render(bmp);
// const Font & vFont = Configuration::getMenuFont()->get(*font);
const int x1 = popup.getArea().getX()+(int)(popup.getTransforms().getRadius()/2);
const int y1 = popup.getArea().getY()+2;
const int x2 = popup.getArea().getX2()-(int)(popup.getTransforms().getRadius()/2);
const int y2 = popup.getArea().getY2()-2;
bmp.setClipRect(x1, y1, x2, y2);
// FIXME height is proportionally wrong in a majority of the cases, this is perhaps due to ftalleg.
int sy = location.getY() - vFont.getHeight()/6;// - location.getHeight()/2 - vFont.getHeight()/2;
static Graphics::Color white = Graphics::makeColor(255,255,255);
unsigned int padding_index = 0;
for (vector<string>::iterator it = text.begin(); it != text.end(); it++){
string & str = *it;
if (fadeAlpha < 255){
Graphics::Bitmap::transBlender(0, 0, 0, fadeAlpha);
vFont.printf(location.getX() + padding[padding_index]/2, sy, white, bmp.translucent(), str, 0 );
} else {
vFont.printf(location.getX() + padding[padding_index]/2, sy, white, bmp, str, 0 );
}
sy += vFont.getHeight();
padding_index++;
}
bmp.setClipRect(0, 0, bmp.getWidth(), bmp.getHeight());
}
void Menu::InfoBox::open(){
state = Opening;
popup.location = location;
popup.transforms = transforms;
popup.colors = colors;
popup.open();
fadeAlpha = 0;
}
void Menu::InfoBox::close(){
state = Closing;
popup.close();
}
/* dimensions are computed lazily when we get a font, but only compute once per font */
void Menu::InfoBox::initialize(const Font & font){
int maxWidth = 0;
int height = 0;
for (vector<string>::iterator it = text.begin(); it != text.end(); it++){
// Add the padding
ostringstream pad;
pad << (*it)[0] << (*it)[(*it).size()-1];
padding.push_back(font.textLength(pad.str().c_str()));
int w = font.textLength((*it).c_str()) + padding.back();
if (w > maxWidth){
maxWidth = w;
}
height += font.getHeight();
}
location.setDimensions(maxWidth, height);
}
void Menu::InfoBox::setText(const std::string & info){
if (info.empty()){
return;
}
text.clear();
// const Font & vFont = Configuration::getMenuFont()->get(*font);
size_t start = 0;
size_t last = 0;
start = info.find("\n");
while (start != string::npos){
text.push_back(info.substr(last, start - last));
last = start + 1;
start = info.find("\n", last);
}
text.push_back(info.substr(last));
}
static std::vector<Util::ReferenceCount<ContextItem> > toContextList(const ContextBox & context, const std::vector<Util::ReferenceCount<MenuOption> > & list){
std::vector<Util::ReferenceCount<ContextItem> > contextItems;
for (std::vector<Util::ReferenceCount<MenuOption> >::const_iterator i = list.begin(); i != list.end(); ++i){
const Util::ReferenceCount<MenuOption> & option = *i;
contextItems.push_back(option.convert<ContextItem>());
}
return contextItems;
}
static void tryPlaySound(const Filesystem::RelativePath & path){
Util::ReferenceCount<Sound> ok = Resource::getSound(path);
if (ok != NULL){
ok->play();
}
}
/*
* FIXME Exception handling for ValueHolder*/
Menu::MenuException::MenuException(const std::string & file, int line, const std::string reason ):
Exception::Base(file, line),
reason(reason){
}
Menu::MenuException::MenuException(const MenuException & copy):
Exception::Base(copy),
reason(copy.reason){
}
Menu::MenuException::MenuException(const Exception::Base & copy):
Exception::Base(copy),
reason("unknown"){
}
Menu::MenuException::~MenuException() throw(){
}
Exception::Base * Menu::MenuException::copy() const {
return new MenuException(*this);
}
Menu::Reload::Reload(const std::string & file, int line, const std::string reason):
MenuException(file, line, reason){
}
Menu::Reload::~Reload() throw() {
}
Exception::Base * Menu::Reload::copy() const {
return new Reload(*this);
}
Menu::ValueHolder::ValueHolder(const std::string & name):
name(name),
location(0){
}
Menu::ValueHolder::~ValueHolder(){
}
Menu::ValueHolder::ValueHolder(const ValueHolder & copy){
// reset position
this->location = 0;
this->name = copy.name;
this->values = copy.values;
}
Menu::ValueHolder & Menu::ValueHolder::operator=(const ValueHolder & copy){
// reset position
this->location = 0;
this->name = copy.name;
this->values = copy.values;
return *this;
}
Menu::ValueHolder & Menu::ValueHolder::operator<<(const std::string & val){
values.push_back(val);
return *this;
}
Menu::ValueHolder & Menu::ValueHolder::operator<<(bool val){
std::ostringstream o;
o << val;
return *this << o.str();
}
Menu::ValueHolder & Menu::ValueHolder::operator<<(int val){
std::ostringstream o;
o << val;
return *this << o.str();
}
Menu::ValueHolder & Menu::ValueHolder::operator<<(double val){
std::ostringstream o;
o << val;
return *this << o.str();
}
Menu::ValueHolder & Menu::ValueHolder::operator<<(TokenView & view){
std::string temp;
view >> temp;
return *this << temp;
}
Menu::ValueHolder & Menu::ValueHolder::operator>>(std::string & val){
if (values[location].empty()){
throw MenuException(__FILE__, __LINE__, "Empty value.");
}
val = values[location];
next();
return *this;
}
Menu::ValueHolder & Menu::ValueHolder::operator>>(bool & val){
if (values[location].empty()){
throw MenuException(__FILE__, __LINE__, "Empty value.");
}
std::istringstream i(values[location]);
i >> val;
next();
return *this;
}
Menu::ValueHolder & Menu::ValueHolder::operator>>(int & val){
if (values[location].empty()){
throw MenuException(__FILE__, __LINE__, "Empty value.");
}
std::istringstream i(values[location]);
i >> val;
next();
return *this;
}
Menu::ValueHolder & Menu::ValueHolder::operator>>(double & val){
if (values[location].empty()){
throw MenuException(__FILE__, __LINE__, "Empty value.");
}
std::istringstream i(values[location]);
i >> val;
next();
return *this;
}
void Menu::ValueHolder::next(){
location++;
if (location >= values.size()){
location = 0;
}
}
const std::string Menu::ValueHolder::getValues() {
std::string temp;
for (std::vector<std::string>::iterator i = values.begin(); i != values.end(); ++i){
temp += *i + "; ";
}
return temp;
}
static bool parseDisplayList(const Token * token, ContextBox & menu){
if (*token == "display-list"){
TokenView view = token->view();
while (view.hasMore()){
const Token * tok;
view >> tok;
std::string type;
bool wrap = true;
if (tok->match("type", type)){
if (type == "normal"){
menu.setListType(ContextBox::Normal);
} else if (type == "scroll"){
menu.setListType(ContextBox::Scroll);
}
} else if (tok->match("wrap", wrap)){
menu.setListWrap(wrap);
} else if ( *tok == "items"){
Gui::ListValues values;
values.getValues(tok);
menu.setListValues(values);
}
}
return true;
}
return false;
}
Menu::Renderer::Renderer(){
}
Menu::Renderer::~Renderer(){
// Kill info boxes
for (std::vector< ::Menu::InfoBox *>::iterator i = info.begin(); i != info.end();++i){
if (*i){
delete *i;
}
}
}
/*
void Menu::Renderer::setFont(const Util::ReferenceCount<FontInfo> & font){
}
*/
void Menu::Renderer::addInfo(const std::string & text, const Gui::Widget & defaults, Context & context, const Font & font){
if (!info.empty()){
info.back()->close();
}
if (text.empty()){
return;
}
::Menu::InfoBox * temp = new ::Menu::InfoBox();
// temp->setFont(context.getFont());
temp->setText(text);
temp->initialize(font);
const int width = temp->location.getWidth();
const int height = temp->location.getHeight();
temp->location.setPosition(Gui::AbsolutePoint(context.getInfoLocation().getX() - width/2, context.getInfoLocation().getY() - height/2));
// have to pass the dimensions back in to correct proper placement
temp->location.setPosition2(Gui::AbsolutePoint(temp->location.getX() + width,temp->location.getY() + height));
temp->transforms.setRadius(defaults.transforms.getRadius());
temp->colors = defaults.colors;
temp->open();
info.push_back(temp);
}
void Menu::Renderer::actInfo(const Font & font){
for (std::vector< ::Menu::InfoBox *>::iterator i = info.begin(); i != info.end();){
::Menu::InfoBox *box = *i;
box->act(font);
if (!box->isActive()){
delete box;
i = info.erase(i);
} else {
i++;
}
}
}
void Menu::Renderer::renderInfo(const Graphics::Bitmap & work, const Font & font){
for (std::vector< ::Menu::InfoBox *>::iterator i = info.begin(); i != info.end(); ++i){
::Menu::InfoBox *box = *i;
box->render(work, font);
}
}
Menu::DefaultRenderer::DefaultRenderer():
hasOverride(false),
overrideIndex(0){
// Default the menu to a certain size and details
menu.transforms.setRadius(15);
menu.location.set(-.6, -.3, .6, .8);
menu.colors.body = Graphics::makeColor(0,0,0);
menu.colors.bodyAlpha = 128;
menu.colors.border = Graphics::makeColor(200,200,200);
menu.colors.borderAlpha = 255;
}
vector<Util::ReferenceCount<MenuOption> > Menu::DefaultRenderer::getOptions() const {
return options;
}
void Menu::DefaultRenderer::setPosition(const Gui::Coordinate & coordinate){
menu.location = coordinate;
}
void Menu::DefaultRenderer::invokeOverride(const Context & context){
if (hasOverride){
options[overrideIndex]->run(context);
throw Exception::Return(__FILE__, __LINE__);
}
}
Menu::DefaultRenderer::~DefaultRenderer(){
}
Menu::Renderer::Type Menu::DefaultRenderer::getType() const {
return Default;
}
bool Menu::DefaultRenderer::readToken(const Token * token, const OptionFactory & factory){
if( *token == "option" ) {
try{
MenuOption * temp = factory.getOption(menu, token);
if (temp){
// Check for info/name
{
TokenView view = token->view();
while (view.hasMore()){
const Token * tok;
view >> tok;
try{
if (*tok == "info"){
temp->addInfo(tok);
} else if (*tok == "name"){
temp->addName(tok);
}
} catch (const TokenException & ex){
// Output something
}
}
}
options.push_back(Util::ReferenceCount<MenuOption>(temp));
if (!hasOverride){
const Token * tok;
token->view() >> tok;
if (tok->findToken("_/override") != NULL){
overrideIndex = options.size()-1;
hasOverride = true;
}
}
}
} catch (const LoadException & le){
Global::debug(0) << "Could not read option: " << le.getTrace() << endl;
token->print(" ");
}
} else if ( *token == "position" ) {
// This handles the placement of the menu list and surrounding box
menu.setCoordinates(token);
} else if ( *token == "relative-position"){
menu.setCoordinates(token);
} else if ( *token == "coordinate"){
menu.setCoordinates(token);
} else if ( *token == "position-body" ) {
// This handles the body color of the menu box
menu.setColors(token);
} else if ( *token == "position-border" ) {
// This handles the border color of the menu box
menu.setColors(token);
} else if ( *token == "transforms" ) {
// This handles the border color of the menu box
menu.setTransforms(token);
} else if ( *token == "fade-speed" ) {
// Menu fade in speed
int speed;
token->view() >> speed;
menu.setFadeSpeed(speed);
} else if ( parseDisplayList(token, menu) ){
} else {
return false;
}
return true;
}
void Menu::DefaultRenderer::initialize(Context & context){
menu.setList(toContextList(menu, options));
menu.open();
// const Font & font = Configuration::getMenuFont()->get(context.getFont()->get());
const Font & font = currentFont();
// Menu info
if (!context.getMenuInfoText().empty()){
menuInfo.setText(context.getMenuInfoText());
menuInfo.initialize(font);
const int width = menuInfo.location.getWidth();
const int height = menuInfo.location.getHeight();
menuInfo.location.setPosition(Gui::AbsolutePoint(context.getMenuInfoLocation().getX() - width/2, context.getMenuInfoLocation().getY() - height/2));
// have to pass the dimensions back in to correct proper placement
menuInfo.location.setPosition2(Gui::AbsolutePoint(menuInfo.location.getX() + width, menuInfo.location.getY() + height));
menuInfo.transforms.setRadius(menu.transforms.getRadius());
menuInfo.colors = menu.colors;
}
menuInfo.open();
// Add first info option
addInfo(options[menu.getCurrentIndex()]->getInfoText(), menu, context, font);
}
void Menu::DefaultRenderer::finish(){
menu.close();
menuInfo.close();
}
bool Menu::DefaultRenderer::active(){
return menu.isActive();
}
void Menu::DefaultRenderer::act(const Context & context){
// const Font & font = Configuration::getMenuFont()->get(context.getFont()->get());
const Font & font = currentFont();
// FIXME find a better way to get options to update this is a waste
for (std::vector<Util::ReferenceCount<MenuOption> >::iterator i = options.begin(); i != options.end(); ++i){
Util::ReferenceCount<MenuOption> & option = *i;
option->logic();
}
menu.act(font);
menuInfo.act(font);
actInfo(font);
}
void Menu::DefaultRenderer::render(const Graphics::Bitmap & bmp, const Font & font){
menu.render(bmp, font);
menuInfo.render(bmp, font);
renderInfo(bmp, font);
}
void Menu::DefaultRenderer::addOption(MenuOption * opt){
this->options.push_back(Util::ReferenceCount<MenuOption>(opt));
}
void Menu::DefaultRenderer::doAction(const Actions & action, Context & context){
const Font & font = currentFont();
switch(action){
case Up:
if (menu.previous(font)){
context.playSound(Up);
addInfo(options[menu.getCurrentIndex()]->getInfoText(), menu, context, font);
}
break;
case Down:
if (menu.next(font)){
context.playSound(Down);
addInfo(options[menu.getCurrentIndex()]->getInfoText(), menu, context, font);
}
break;
case Left:
if (options[menu.getCurrentIndex()]->leftKey()){
// setFont(context.getFont());
context.playSound(Left);
}
break;
case Right:
if (options[menu.getCurrentIndex()]->rightKey()){
// setFont(context.getFont());
context.playSound(Right);
}
break;
case Select: {
if (options[menu.getCurrentIndex()]->isRunnable()){
try{
context.playSound(Select);
options[menu.getCurrentIndex()]->run(context);
} catch (const Reload & reload){
menu.open();
menuInfo.open();
}
// setFont(context.getFont());
context.playMusic();
/* font might have been recreated */
const Font & font = currentFont();
addInfo(options[menu.getCurrentIndex()]->getInfoText(), menu, context, font);
menuInfo.initialize(font);
}
break;
}
case Cancel:
context.playSound(Cancel);
throw Exception::Return(__FILE__, __LINE__);
break;
default:
break;
}
}
Menu::TabInfo::TabInfo(){
}
Menu::TabInfo::~TabInfo(){
}
void Menu::TabInfo::act(){
// Miguel: FIXME find a better way to get options to update this is a waste
// Jon: Whats wrong with it?
for (std::vector<Util::ReferenceCount<MenuOption> >::iterator i = options.begin(); i != options.end(); ++i){
Util::ReferenceCount<MenuOption> & option = *i;
option->logic();
}
}
Menu::TabRenderer::TabRenderer():
hasOverride(false),
overrideIndex(0){
// Default the menu to a certain size and details
//menu.transforms.setRadius(15);
menu.location.set(-.6, -.3, .6, .8);
menu.colors.body = Graphics::makeColor(0,0,0);
menu.colors.bodyAlpha = 128;
menu.colors.border = Graphics::makeColor(200,200,200);
menu.colors.borderAlpha = 255;
}
Menu::Renderer::Type Menu::TabRenderer::getType() const {
return Tabbed;
}
void Menu::TabRenderer::setPosition(const Gui::Coordinate & coordinate){
menu.location = coordinate;
}
vector<Util::ReferenceCount<MenuOption> > Menu::TabRenderer::getOptions() const {
vector<Util::ReferenceCount<MenuOption> > options;
for (vector<TabInfo *>::const_iterator it = tabs.begin(); it != tabs.end(); ++it){
options.insert(options.end(), (*it)->options.begin(), (*it)->options.end());
}
return options;
}
/* FIXME need to implement override for tabs */
void Menu::TabRenderer::invokeOverride(const Context & context){
if (hasOverride){
//options[overrideIndex]->run(context);
throw Exception::Return(__FILE__, __LINE__);
}
}
Menu::TabRenderer::~TabRenderer(){
// Kill tabs
for (std::vector<TabInfo *>::iterator i = tabs.begin(); i != tabs.end(); ++i){
if (*i){
delete *i;
}
}
}
bool Menu::TabRenderer::readToken(const Token * token, const OptionFactory & factory){
if (*token == "menu"){
TabInfo * tabInfo = new TabInfo();
Gui::Tab * tab = new Gui::Tab();
TokenView view = token->view();
while (view.hasMore()){
const Token * tok;
view >> tok;
try{
if (*tok == "name"){
tok->view() >> tabInfo->name;
tok->view() >> tab->name;
} else if (*tok == "info"){
tok->view() >> tabInfo->info;
} else if (*tok == "menuinfo"){
tok->view() >> tabInfo->menuInfo;
} else if (parseDisplayList(tok, tab->getContext())){
} else if (*tok == "option"){
try {
MenuOption *temp = factory.getOption(tab->getContext(), tok);
if (temp){
// Check for info/name
{
TokenView info = tok->view();
while (info.hasMore()){
const Token * check;
info >> check;
try{
if (*check == "info"){
temp->addInfo(check);
} else if (*check == "name"){
temp->addName(check);
}
} catch (const TokenException & ex){
// Output something
}
}
}
Util::ReferenceCount<MenuOption> ref(temp);
tabInfo->options.push_back(ref);
tab->addOption(ref.convert<Gui::ContextItem>());
// tab->addOption(ref->getAsScrollItem<ContextItem>(tab->getContext()));
}
} catch (const LoadException & le){
tok->print(" ");
}
}
} catch (const TokenException & ex){
// Output something
}
}
menu.addTab(tab);
tabs.push_back(tabInfo);
} else if ( *token == "position" ) {
// This handles the placement of the menu list and surrounding box
menu.setCoordinates(token);
} else if ( *token == "relative-position"){
menu.setCoordinates(token);
} else if ( *token == "coordinate"){
menu.setCoordinates(token);
} else if ( *token == "position-body" ) {
// This handles the body color of the menu box
menu.setColors(token);
} else if ( *token == "position-border" ) {
// This handles the border color of the menu box
menu.setColors(token);
} else if ( *token == "transforms" ) {
// This handles the border color of the menu box
menu.setTransforms(token);
} else if ( *token == "tab-body" ) {
int r,g,b;
token->view() >> r >> g >> b >> menu.tabColors.bodyAlpha;
menu.tabColors.body = Graphics::makeColor(r,g,b);
} else if ( *token == "tab-border" ) {
int r,g,b;
token->view() >> r >> g >> b >> menu.tabColors.borderAlpha;
menu.tabColors.border = Graphics::makeColor(r,g,b);
} else if ( *token == "selectedtab-body" ) {
int r,g,b;
token->view() >> r >> g >> b >> menu.selectedTabColors.bodyAlpha;
menu.selectedTabColors.body = Graphics::makeColor(r,g,b);
} else if ( *token == "selectedtab-border" ) {
int r,g,b;
token->view() >> r >> g >> b >> menu.selectedTabColors.borderAlpha;
menu.selectedTabColors.border = Graphics::makeColor(r,g,b);
} else if ( *token == "runningtab-body" ) {
int r,g,b;
token->view() >> r >> g >> b >> menu.runningTabColors.bodyAlpha;
menu.runningTabColors.body = Graphics::makeColor(r,g,b);
} else if ( *token == "runningtab-border" ) {
int r,g,b;
token->view() >> r >> g >> b >> menu.runningTabColors.borderAlpha;
menu.runningTabColors.border = Graphics::makeColor(r,g,b);
} else if ( *token == "font-color" ) {
int r,g,b;
token->view() >> r >> g >> b;
menu.setTabFontColor(Graphics::makeColor(r,g,b));
} else if ( *token == "selectedfont-color" ) {
int r,g,b;
token->view() >> r >> g >> b;
menu.setSelectedTabFontColor(Graphics::makeColor(r,g,b));
} else if ( *token == "runningfont-color" ) {
} else if ( *token == "fade-speed" ) {
// Menu fade in speed
int speed;
token->view() >> speed;
//menu.setFadeSpeed(speed);
} else {
return false;
}
return true;
}
void Menu::TabRenderer::initialize(Context & context){
const Font & font = currentFont();
// Menu info
if (!context.getMenuInfoText().empty()){
menuInfo.setText(context.getMenuInfoText());
menuInfo.initialize(font);
const int width = menuInfo.location.getWidth();
const int height = menuInfo.location.getHeight();
menuInfo.location.setPosition(Gui::AbsolutePoint(context.getMenuInfoLocation().getX() - width/2, context.getMenuInfoLocation().getY() - height/2));
// have to pass the dimensions back in to correct proper placement
menuInfo.location.setPosition2(Gui::AbsolutePoint(menuInfo.location.getX() + width,menuInfo.location.getY() + height));
menuInfo.transforms.setRadius(menu.transforms.getRadius());
menuInfo.colors = menu.colors;
}
menuInfo.open();
if (tabs.size() > 0 && tabs[menu.getCurrentTab()]->options.size() > menu.getCurrentIndex()){
// Add first info option
addInfo(tabs[menu.getCurrentTab()]->options[menu.getCurrentIndex()]->getInfoText(), menu, context, font);
}
}
void Menu::TabRenderer::finish(){
//menu.close();
menuInfo.close();
}
bool Menu::TabRenderer::active(){
return true;//menu.isActive();
}
void Menu::TabRenderer::act(const Context & context){
const Font & font = currentFont();
// FIXME find a better way to get options to update this is a waste
for (std::vector<TabInfo *>::iterator i = tabs.begin(); i != tabs.end(); ++i){
TabInfo * tab = *i;
tab->act();
}
menu.act(font);
menuInfo.act(font);
actInfo(font);
}
void Menu::TabRenderer::render(const Graphics::Bitmap & bmp, const Font & font){
menu.render(bmp, font);
menuInfo.render(bmp, font);
renderInfo(bmp, font);
}
void Menu::TabRenderer::addOption(MenuOption * opt){
//this->options.push_back(opt);
}
void Menu::TabRenderer::doAction(const Actions & action, Context & context){
const Font & font = currentFont();
bool updateInfo = false;
switch (action){
case Up:
menu.up(font);
context.playSound(Up);
updateInfo = true;
break;
case Down:
menu.down(font);
context.playSound(Down);
updateInfo = true;
break;
case Left:
menu.left(font);
context.playSound(Up);
updateInfo = true;
break;
case Right:
menu.right(font);
context.playSound(Down);
updateInfo = true;
break;
case Select:
try{
if (!menu.isInTab()){
menu.toggleTabSelect();
} else {
if (menu.getCurrentTab() < tabs.size() &&
menu.getCurrentIndex() < tabs[menu.getCurrentTab()]->options.size() &&
tabs[menu.getCurrentTab()]->options[menu.getCurrentIndex()]->isRunnable()){
tabs[menu.getCurrentTab()]->options[menu.getCurrentIndex()]->run(context);
}
// tabs[menu.getCurrentTab()]->run(menu.getCurrentIndex(), context);
}
context.playSound(Select);
} catch (const Exception::Return & ex){
menuInfo.open();
}
context.playMusic();
updateInfo = true;
break;
case Cancel:
context.playSound(Cancel);
if (menu.isInTab()){
menu.toggleTabSelect();
} else {
throw Exception::Return(__FILE__, __LINE__);
}
break;
default:
break;
}
if (updateInfo){
if (tabs.size() > menu.getCurrentTab() &&
tabs[menu.getCurrentTab()]->options.size() > menu.getCurrentIndex()){
addInfo(tabs[menu.getCurrentTab()]->options[menu.getCurrentIndex()]->getInfoText(), menu, context, font);
}
}
}
Menu::Context::Context():
cleanup(true),
state(NotStarted),
fades(0),
font(NULL),
infoLocation(0, -.5),
menuInfoLocation(0,.95){
}
Menu::Context::Context(const Context & parent, const Context & child):
cleanup(false),
state(NotStarted),
fades(NULL),
font(NULL),
infoLocation(0,-.5),
menuInfoLocation(0,.95),
/* include child's languages?? */
languages(parent.getLanguages()){
// Update with parents info
fades = parent.fades;
background = parent.background;
sounds = parent.sounds;
music = parent.music;
// font = parent.font;
infoLocation = parent.infoLocation;
menuInfoLocation = parent.menuInfoLocation;
// Then overwrite with childs
if (child.fades != NULL){
fades = child.fades;
}
if (!child.background.empty()){
background = child.background;
}
if (!child.sounds.empty()){
sounds = child.sounds;
}
if (Storage::instance().exists(child.music)){
music = child.music;
}
if (child.hasFont()){
font = child.getFontInfo();
} else if (parent.hasFont()){
font = parent.getFontInfo();
}
/* what are these magic numbers -.5 and .95? */
if (child.infoLocation.getRelativeX() != 0 || child.infoLocation.getRelativeY() != -.5){
infoLocation = child.infoLocation;
}
if (child.menuInfoLocation.getRelativeX() != 0 || child.menuInfoLocation.getRelativeY() != .95){
menuInfoLocation = child.menuInfoLocation;
}
if (!child.menuInfo.empty()){
menuInfo = child.menuInfo;
}
}
Menu::Context::~Context(){
// Only delete if required
if (cleanup){
if (fades != NULL){
delete fades;
}
}
}
std::vector<std::string> Menu::Context::getLanguages() const {
return languages;
}
void Menu::Context::setLanguages(const std::vector<std::string> & languages){
this->languages = languages;
}
bool Menu::Context::hasFont() const {
return font != NULL;
}
void Menu::Context::parseToken(const Token * token){
if ( *token != "context" ){
throw LoadException(__FILE__, __LINE__, "Not a menu context");
} else if (!token->hasTokens()){
return;
}
// Token
const Token * tok;
token->view() >> tok;
TokenView view = tok->view();
while (view.hasMore()){
const Token * context;
view >> context;
if (*context == "fade"){
// Fade info
if (!fades){
fades = new Gui::FadeTool();
}
// Set fader default to white
fades->setFadeInColor(Graphics::makeColor(255,255,255));
fades->setFadeOutColor(Graphics::makeColor(255,255,255));
fades->setFadeInTime(25);
fades->setFadeOutTime(12);
// Load defaults
fades->parseDefaults(context);
} else if (*context == "animation" || *context == "background"){
// Backgrounds
addBackground(context);
} else if (*context == ""){
}
}
}
void Menu::Context::addBackground(const Token * token){
// Backgrounds
/*if (background == NULL){
background = new Gui::AnimationManager();
}*/
background.add(Util::ReferenceCount<Gui::Animation>(new Gui::Animation(token)));
}
void Menu::Context::addBackground(const Graphics::Bitmap & image){
/*if (background == NULL){
background = new Gui::AnimationManager();
}*/
background.add(Util::ReferenceCount<Gui::Animation>(new Gui::Animation(Util::ReferenceCount<Graphics::Bitmap>(new Graphics::Bitmap(image)))));
}
void Menu::Context::addBackground(const std::string & image){
// Backgrounds
/*if (background == NULL){
background = new Gui::AnimationManager();
}*/
background.add(Util::ReferenceCount<Gui::Animation>(new Gui::Animation(image)));
}
void Menu::Context::initialize(){
if (fades){
// state
state = Initializing;
// set fader state
fades->setState(FadeTool::FadeIn);
} else {
// Running
state = Running;
}
}
void Menu::Context::finish(){
if (fades){
// state
state = Finishing;
// set fader state
fades->setState(FadeTool::FadeOut);
} else {
// Completed
state = Completed;
}
}
void Menu::Context::playSound(const Actions & sound) const {
map<Actions, Filesystem::RelativePath>::const_iterator find = sounds.find(sound);
if (find != sounds.end() && Storage::instance().exists(find->second)){
tryPlaySound(find->second);
}
}
void Menu::Context::addSound(const Actions & sound, const Filesystem::RelativePath & path){
sounds[sound] = path;
}
void Menu::Context::playMusic(){
if (Storage::instance().exists(music)){
if (Music::loadSong(Storage::instance().find(music).path())){
Music::pause();
Music::play();
}
}
}
void Menu::Context::act(){
// fader
if (fades){
fades->act();
if (state == Initializing){
if(fades->getState() == Gui::FadeTool::NoFade){
state = Running;
}
} else if (state == Finishing){
if(fades->getState() == Gui::FadeTool::EndFade){
state = Completed;
}
}
}
// Backgrounds
background.act();
}
void Menu::Context::renderBackground(const Graphics::Bitmap & bmp) const {
if (!background.empty()){
// background
background.render(Gui::Animation::BackgroundBottom, bmp);
background.render(Gui::Animation::BackgroundMiddle, bmp);
background.render(Gui::Animation::BackgroundTop, bmp);
} else {
bmp.fill(Graphics::makeColor(0,0,0));
}
}
void Menu::Context::renderForeground(const Graphics::Bitmap & bmp) const {
if (!background.empty()){
// foreground
background.render(Gui::Animation::ForegroundBottom, bmp);
background.render(Gui::Animation::ForegroundMiddle, bmp);
background.render(Gui::Animation::ForegroundTop, bmp);
}
}
void Menu::Context::render(const Util::ReferenceCount<Renderer> & renderer, const Graphics::Bitmap & bmp) const {
renderBackground(bmp);
// Menu
if (renderer != NULL){
renderer->render(bmp, currentFont());
}
renderForeground(bmp);
// Fades
if (fades){
fades->draw(bmp);
}
}
void Menu::Context::setFadeTool(Gui::FadeTool *fade){
fades = fade;
}
/*
void Menu::Context::setBackground(Background *bg){
background = bg;
}
*/
/* New Menu */
// Utilizes default renderer
Menu::Menu::Menu(const Renderer::Type & type):
type(type){
setRenderer(type);
}
Menu::Menu::Menu(const Util::ReferenceCount<Renderer> & renderer):
renderer(renderer),
type(renderer->getType()){
}
Menu::Menu::Menu(const Filesystem::AbsolutePath & filename, const Renderer::Type & type):
type(type){
// Load up tokenizer
try{
Global::debug(1,"menu") << "Loading menu " << filename.path() << endl;
TokenReader tr;
Token * token = tr.readTokenFromFile(*Storage::instance().open(filename));
OptionFactory defaultFactory;
load(token, defaultFactory);
} catch (const TokenException & e){
throw LoadException(__FILE__, __LINE__, e, "Error loading menu");
}
}
Menu::Menu::Menu(const Filesystem::AbsolutePath & filename, const OptionFactory & factory, const Renderer::Type & type):
renderer(0),
type(type){
// Load up tokenizer
try{
Global::debug(1,"menu") << "Loading menu " << filename.path() << endl;
TokenReader tr;
Token * token = tr.readTokenFromFile(*Storage::instance().open(filename));
load(token, factory);
} catch (const TokenException & e){
throw LoadException(__FILE__, __LINE__, e, "Error loading menu");
}
}
Menu::Menu::Menu(const Token * token, const Renderer::Type & type):
renderer(0),
type(type){
OptionFactory defaultFactory;
load(token, defaultFactory);
}
Menu::Menu::Menu(const Token * token, const OptionFactory & factory, const Renderer::Type & type):
renderer(0),
type(type){
load(token, factory);
}
Menu::Menu::~Menu(){
// Kill values
for (std::map<string,ValueHolder *>::iterator i = data.begin(); i != data.end(); ++i){
if (i->second){
delete i->second;
}
}
}
void Menu::Menu::setPosition(const Gui::Coordinate & coordinate){
if (renderer != NULL){
renderer->setPosition(coordinate);
}
}
void Menu::Menu::setFont(const Util::ReferenceCount<FontInfo> & font){
context.setFont(font);
/*
if (renderer){
renderer->setFont(font);
}
*/
}
void Menu::Menu::load(const Token * token, const OptionFactory & factory){
// version info;
int major=0, minor=0, micro=0;
if (!token->hasTokens()){
throw LoadException(__FILE__, __LINE__, "Empty Menu");
} else {
const Token *ourToken = token->findToken("_/type");
if (ourToken != NULL){
try {
std::string menuType;
ourToken->view() >> menuType;
if (menuType == "default"){
type = Renderer::Default;
} else if (menuType == "tabbed"){
type = Renderer::Tabbed;
}
} catch (const TokenException & ex){
}
}
ourToken = token->findToken("_/version");
if (ourToken != NULL){
try {
ourToken->view() >> major >> minor >> micro;
} catch (const TokenException & ex){
}
} else {
Global::debug(0, "menu") << "No version indicated, assuming 3.3.1 or below." << endl;
major = 3;
minor = 3;
micro = 1;
}
}
setRenderer(type);
if (Version::getVersion(major, minor, micro) != Version::getVersion()){
// Do compatible translations if necessary
handleCompatibility(token, Version::getVersion(major, minor, micro), factory);
} else {
handleCurrentVersion(token);
}
}
typedef Menu::Menu MenuClass;
class LanguageMenu: public Menu::Menu {
public:
class LanguageOption: public MenuOption {
public:
LanguageOption(const Gui::ContextBox & box, const string & language):
MenuOption(box, NULL){
setText(language);
setInfoText(language);
}
virtual void logic(){
}
virtual void run(const ::Menu::Context & context){
Configuration::setLanguage(getText());
Configuration::saveConfiguration();
throw ::Menu::MenuException(__FILE__, __LINE__);
}
};
static vector<string> findLanguages(const MenuClass & original){
return original.getLanguages();
}
static vector<string> putEnglishFirst(vector<string> languages){
vector<string> out;
bool haveEnglish = false;
for (vector<string>::iterator it = languages.begin(); it != languages.end(); it++){
const string & name = *it;
if (name == "English"){
out.insert(out.begin(), name);
haveEnglish = true;
} else {
out.push_back(name);
}
}
/* We should always have at least english available */
if (!haveEnglish){
out.insert(out.begin(), "English");
}
return out;
}
LanguageMenu(const MenuClass & original){
Util::ReferenceCount< ::Menu::DefaultRenderer > renderer = getRenderer().convert< ::Menu::DefaultRenderer>();
vector<string> languages = putEnglishFirst(findLanguages(original));
for (vector<string>::iterator it = languages.begin(); it != languages.end(); it++){
addOption(new LanguageOption(renderer->getBox(), *it));
}
}
};
void Menu::Menu::setupDefaultLanguage(const Context & context, const MenuClass & parent){
LanguageMenu menu(parent);
menu.setFont(Util::ReferenceCount<FontInfo>(new RelativeFontInfo(Font::getDefaultFontPath(), 24, 24)));
Configuration::setLanguage("English");
try{
menu.run(context);
} catch (const ::Menu::MenuException & ignore){
}
}
void Menu::Menu::openOptions(){
vector<Util::ReferenceCount<MenuOption> > options = getRenderer()->getOptions();
for (vector<Util::ReferenceCount<MenuOption> >::iterator it = options.begin(); it != options.end(); it++){
Util::ReferenceCount<MenuOption> & option = *it;
option->open();
}
}
void Menu::Menu::closeOptions(){
vector<Util::ReferenceCount<MenuOption> > options = getRenderer()->getOptions();
for (vector<Util::ReferenceCount<MenuOption> >::iterator it = options.begin(); it != options.end(); it++){
Util::ReferenceCount<MenuOption> & option = *it;
option->close();
}
}
vector<string> Menu::Menu::getLanguages() const {
return languages;
}
void Menu::Menu::run(const Context & parentContext){
/* TODO: replace with Parameter */
Keyboard::pushRepeatState(true);
try{
// Setup context from parent and this menu and initialize
Context localContext(parentContext, context);
Util::Parameter<Util::ReferenceCount<FontInfo> > currentFont(menuFontParameter);
if (context.hasFont()){
currentFont.push(context.getFontInfo());
}
localContext.initialize();
/* Not sure if this is the right place to set the languages.
* For now the semantics is that if a sub-menu specifies a set
* of languages then we will use those, otherwise the
* languages will come from the parentContext.
*
* getLanguages() is supposed to hold at least one language
* which might be English, the default.
*
* This logic is sort of lame.. fix it.
*/
if (getLanguages().size() > 1 || (getLanguages().size() == 1 && getLanguages()[0] != "English")){
localContext.setLanguages(getLanguages());
}
// Setup menu fonts etc
if (renderer != NULL){
renderer->initialize(localContext);
// Invoke Override if available
renderer->invokeOverride(localContext);
}
//Play music
localContext.playMusic();
if (Configuration::getLanguage() == ""){
setupDefaultLanguage(localContext, *this);
}
/* do any lazy loading options want to do */
openOptions();
/* vi keys -- make these optional? */
input.set(Keyboard::Key_J, 0, true, Down);
input.set(Keyboard::Key_K, 0, true, Up);
input.set(Keyboard::Key_H, 0, true, Left);
input.set(Keyboard::Key_L, 0, true, Right);
/* regular keys */
input.set(Configuration::getUp(0), 0, true, Up);
input.set(Configuration::getDown(0), 0, true, Down);
input.set(Configuration::getLeft(0), 0, true, Left);
input.set(Configuration::getRight(0), 0, true, Right);
input.set(Configuration::getAttack1(0), 0, true, Select);
/* FIXME: use configuration keys */
input.set(Keyboard::Key_ENTER, 0, true, Select);
input.set(Keyboard::Key_ESC, 0, true, Cancel);
/* joystick */
input.set(Joystick::Up, 0, true, Up);
input.set(Joystick::Down, 0, true, Down);
input.set(Joystick::Left, 0, true, Left);
input.set(Joystick::Right, 0, true, Right);
/*! FIXME this should be changed to Select/Cancel buttons, all other buttons should be Select */
input.set(Joystick::Button1, 0, true, Select);
input.set(Joystick::Button2, 0, true, Select);
input.set(Joystick::Button3, 0, true, Select);
input.set(Joystick::Button4, 0, true, Select);
input.set(Joystick::Start, 0, true, Select);
input.set(Joystick::Quit, 0, true, Cancel);
class Logic: public Util::Logic {
public:
Logic(Menu & menu, Context & localContext, const Util::ReferenceCount<Renderer> & renderer):
menu(menu),
localContext(localContext),
renderer(renderer){
}
Menu & menu;
Context & localContext;
Util::ReferenceCount<Renderer> renderer;
void run(){
try {
menu.act(localContext);
} catch (const Exception::Return & ex){
// signaled to quit current menu, closing this one out
localContext.finish();
if (renderer != NULL){
renderer->finish();
}
}
}
double ticks(double system){
return system * Global::ticksPerSecond(90);
}
bool done(){
return localContext.getState() == Context::Completed ||
!(renderer != NULL && renderer->active());
}
};
class Draw: public Util::Draw {
public:
Draw(Menu & menu, Context & localContext):
menu(menu),
localContext(localContext){
}
Menu & menu;
Context & localContext;
void draw(const Graphics::Bitmap & buffer){
- Graphics::StretchedBitmap work(640, 480, buffer, Graphics::qualityFilterName(Configuration::getQualityFilter()));
+ Graphics::StretchedBitmap work(640, 480, buffer, Graphics::StretchedBitmap::NoClear, Graphics::qualityFilterName(Configuration::getQualityFilter()));
Util::Parameter<Util::ReferenceCount<FontInfo> > currentFont(menuFontParameter);
if (Configuration::hasMenuFont()){
currentFont.push(Configuration::getMenuFont());
}
work.start();
menu.render(localContext, work);
work.finish();
// buffer.BlitToScreen();
}
};
Logic logic(*this, localContext, renderer);
Draw draw(*this, localContext);
Util::standardLoop(logic, draw);
closeOptions();
// FIXME Menu is finished, lets return. Is this even required anymore?
throw Exception::Return(__FILE__, __LINE__);
} catch (...){
Keyboard::popRepeatState();
throw;
}
}
void Menu::Menu::act(Context & ourContext){
InputSource source(true);
// Keys
vector<InputMap<Actions>::InputEvent> events = InputManager::getEvents(input, source);
for (vector<InputMap<Actions>::InputEvent>::iterator it = events.begin(); it != events.end(); it++){
InputMap<Actions>::InputEvent event = *it;
if (!event.enabled){
continue;
}
if (event.out == Cancel){
if (renderer != NULL){
InputManager::waitForRelease(input, source, Cancel);
renderer->doAction(Cancel, ourContext);
} else {
ourContext.playSound(Cancel);
InputManager::waitForRelease(input, source, Cancel);
throw Exception::Return(__FILE__, __LINE__);
}
}
if (renderer != NULL){
switch (event.out){
case Up: renderer->doAction(Up, ourContext); break;
case Down: renderer->doAction(Down, ourContext); break;
case Left: renderer->doAction(Left, ourContext); break;
case Right: renderer->doAction(Right, ourContext); break;
case Select: renderer->doAction(Select, ourContext); break;
default: break;
}
}
}
if (renderer != NULL){
Util::Parameter<Util::ReferenceCount<FontInfo> > currentFont(menuFontParameter);
if (Configuration::hasMenuFont()){
currentFont.push(Configuration::getMenuFont());
}
renderer->act(ourContext);
}
// Act context
ourContext.act();
}
void Menu::Menu::render(const Context & ourContext, const Graphics::Bitmap & bmp) const {
// Render context
ourContext.render(renderer, bmp);
}
std::string Menu::Menu::getName(){
std::string name;
try {
if (data["name"]){
*data["name"] >> name;
}
} catch (const MenuException & ex){
}
return name;
}
std::string Menu::Menu::getInfo(){
std::string name;
try {
if (data["info"]){
*data["info"] >> name;
}
} catch (const MenuException & ex){
}
return name;
}
void Menu::Menu::addData(ValueHolder * item){
std::pair<std::map<std::string,ValueHolder *>::iterator,bool> check;
check = data.insert( std::pair<std::string,ValueHolder *>(item->getName(),item) );
if (check.second == false){
Global::debug(0,"menu") << "Value \"" << check.first->second->getName() << "\" already exists - (" << check.first->second->getValues() << ")." << endl;
Global::debug(0,"menu") << "Replacing with value \"" << item->getName() << "\" - (" << item->getValues() << ")." << endl;
data[item->getName()] = item;
}
}
void Menu::Menu::handleCurrentVersion(const Token * token){
TokenView view = token->view();
while (view.hasMore()){
try{
const Token * tok;
view >> tok;
// Newer items
if (*tok == "val" || *tok == "value"){
const Token * val;
tok->view() >> val;
ValueHolder * value = new ValueHolder(val->getName());
TokenView valueView = val->view();
try {
while (true){
*value << valueView;
}
} catch (const TokenException & ex){
}
addData(value);
} else if (*tok == "context"){
context.parseToken(tok);
} else {
Global::debug(3,"menu") <<"Unhandled menu attribute: "<<endl;
if (Global::getDebug() >= 3){
tok->print(" ");
}
}
} catch ( const TokenException & ex ) {
throw LoadException(__FILE__, __LINE__, ex, "Menu parse error");
} catch (const LoadException & ex){
throw ex;
} catch (const Filesystem::NotFound & ex){
throw LoadException(__FILE__, __LINE__, ex, "Menu parse error");
}
}
}
void Menu::Menu::handleCompatibility(const Token * token, int version, const OptionFactory & factory){
Global::debug(1,"menu") << "Trying version: " << version << endl;
if (version <= Version::getVersion(3, 3, 1)){
const Token * languages = token->findToken("_/languages");
if (languages != NULL){
try{
TokenView view = languages->view();
while (true){
string language;
view >> language;
this->languages.push_back(language);
}
} catch (const TokenException & fail){
}
}
if (this->languages.size() == 0){
this->languages.push_back("English");
}
TokenView view = token->view();
while (view.hasMore()){
try {
const Token * tok;
view >> tok;
if ( *tok == "name" ){
ValueHolder * value = new ValueHolder("name");
TokenView nameView = tok->view();
*value << nameView;
addData(value);
} else if ( *tok == "music" ) {
ValueHolder * value = new ValueHolder("music");
TokenView musicView = tok->view();
*value << musicView;
addData(value);
try {
std::string music;
*value >> music;
context.setMusic(Filesystem::RelativePath(music));
} catch (const MenuException & ex){
}
} else if( *tok == "select-sound" ) {
ValueHolder * value = new ValueHolder("select-sound");
TokenView soundView = tok->view();
*value << soundView;
addData(value);
try{
std::string sound;
*value >> sound;
context.addSound(Up, Filesystem::RelativePath(sound));
context.addSound(Down, Filesystem::RelativePath(sound));
} catch (const MenuException & ex){
}
} else if (*tok == "back-sound"){
ValueHolder * value = new ValueHolder("back-sound");
TokenView soundView = tok->view();
*value << soundView;
addData(value);
try{
std::string sound;
*value >> sound;
context.addSound(Back,Filesystem::RelativePath(sound));
context.addSound(Cancel,Filesystem::RelativePath(sound));
} catch (const MenuException & ex){
}
} else if (*tok == "ok-sound"){
ValueHolder * value = new ValueHolder("ok-sound");
TokenView okView = tok->view();
*value << okView;
addData(value);
try{
std::string sound;
*value >> sound;
context.addSound(Select,Filesystem::RelativePath(sound));
} catch (const MenuException & ex){
}
} else if ( *tok == "background" ) {
std::string temp;
tok->view() >> temp;
context.addBackground(temp);
} else if (*tok == "anim"){
context.addBackground(tok);
} else if ( *tok == "clear-color" ) {
// Not necessary ignore
} else if (renderer != NULL && renderer->readToken(tok, factory)){
// Nothing checks compatible version of renderer
} else if ( *tok == "font" ) {
ValueHolder * value = new ValueHolder("font");
TokenView fontView = tok->view();
*value << fontView << fontView << fontView;
addData(value);
try {
std::string font;
int w = 24, h = 24;
*value >> font >> w >> h;
/*context.setFont(Filesystem::RelativePath(font));
context.setFontWidth(w);
context.setFontHeight(h);*/
context.setFont(Util::ReferenceCount<FontInfo>(new RelativeFontInfo(Filesystem::RelativePath(font), w, h)));
} catch (const MenuException & ex){
}
} else if ( *tok == "action"){
// Set speed
//ActionAct(tok);
} else if ( *tok == "info-position"){
ValueHolder * value = new ValueHolder("info-position");
TokenView infoView = tok->view();
*value << infoView << infoView;
addData(value);
try {
double x=0, y=-.5;
*value >> x >> y;
context.setInfoLocation(x,y);
} catch (const MenuException & ex){
}
} else if (*tok == "menuinfo"){
ValueHolder * value = new ValueHolder("menuinfo");
TokenView infoView = tok->view();
*value << infoView;
addData(value);
try {
std::string info;
*value >> info;
context.setMenuInfoText(info);
} catch (const MenuException & ex){
}
} else if (*tok == "menuinfo-position"){
ValueHolder * value = new ValueHolder("menuinfo-position");
TokenView infoView = tok->view();
*value << infoView << infoView;
addData(value);
try {
double x=0, y=.95;
*value >> x >> y;
context.setMenuInfoLocation(x,y);
} catch (const MenuException & ex){
}
} else {
Global::debug(3,"menu") <<"Unhandled menu attribute: "<<endl;
if (Global::getDebug() >= 3){
tok->print(" ");
}
}
} catch ( const TokenException & ex ) {
throw LoadException(__FILE__, __LINE__, ex, "Menu parse error");
} catch (const LoadException & ex){
throw ex;
} catch (const Filesystem::NotFound & ex){
throw LoadException(__FILE__, __LINE__, ex, "Menu parse error");
}
}
}
}
void Menu::Menu::setRenderer(const Renderer::Type & type){
renderer = rendererType(type);
}
void Menu::Menu::setRenderer(const Util::ReferenceCount<Renderer> & renderer){
this->renderer = renderer;
}
void Menu::Menu::addOption(MenuOption * opt){
if (renderer != NULL){
this->renderer->addOption(opt);
}
}
Util::ReferenceCount<Menu::Renderer> Menu::Menu::rendererType(const Renderer::Type & type){
switch (type){
case Renderer::Tabbed: {
return Util::ReferenceCount<Renderer>(new TabRenderer());
break;
}
case Renderer::Default:
default: {
return Util::ReferenceCount<Renderer>(new DefaultRenderer());
break;
}
}
return Util::ReferenceCount<Renderer>(NULL);
}
diff --git a/util/menu/options.cpp b/util/menu/options.cpp
index 175ec7b7..ad83aacc 100644
--- a/util/menu/options.cpp
+++ b/util/menu/options.cpp
@@ -1,2926 +1,2926 @@
#include "util/graphics/bitmap.h"
#include "options.h"
#include "util/token.h"
#include "util/input/input-source.h"
#include "util/parameter.h"
#include "util/tokenreader.h"
#include "menu.h"
#include "util/configuration.h"
#include "util/exceptions/load_exception.h"
#include "menu-exception.h"
#include "util/init.h"
#include "util/events.h"
#include "util/version.h"
#include "optionfactory.h"
#include "util/sound/music.h"
#include "util/input/keyboard.h"
#include "util/funcs.h"
#include "util/file-system.h"
#include "util/system.h"
#include "util/font_factory.h"
#include "util/exceptions/shutdown_exception.h"
#include "util/exceptions/exception.h"
#include "util/font.h"
#include "util/gui/box.h"
#include "util/thread.h"
#include "util/loading.h"
#include "util/input/input-map.h"
#include "util/input/input-manager.h"
#include <sstream>
#include <algorithm>
#include <time.h>
#include <math.h>
using namespace std;
using namespace Gui;
/* true if the arguments passed in match todays date.
* pass 0 for any argument that you don't care about (it will match any date)
*/
static bool todaysDate(int month, int day, int year){
time_t result = time(NULL);
struct tm * local = localtime(&result);
return (month == 0 || month == (local->tm_mon + 1)) &&
(day == 0 || day == local->tm_mday) &&
(year == 0 || year == local->tm_year + 1900);
}
static bool jonBirthday(){
return todaysDate(3, 25, 0);
}
static bool miguelBirthday(){
return todaysDate(8, 11, 0);
}
OptionCredits::Block::Block(const std::string & title):
title(title),
titleColorOverride(false),
titleColor(Graphics::makeColor(0,255,255)),
colorOverride(false),
color(Graphics::makeColor(255,255,255)),
spacing(0){
}
OptionCredits::Block::Block(const Token * token):
titleColorOverride(false),
titleColor(Graphics::makeColor(0,255,255)),
colorOverride(false),
color(Graphics::makeColor(255,255,255)),
topWidth(0),
topHeight(0),
bottomWidth(0),
bottomHeight(0),
spacing(0){
if ( *token != "block" ){
throw LoadException(__FILE__, __LINE__, "Not a credit block");
}
TokenView view = token->view();
while (view.hasMore()){
std::string match;
try{
const Token * tok;
view >> tok;
if ( *tok == "title" ) {
tok->view() >> title;
} else if (*tok == "credit"){
std::string credit;
tok->view() >> credit;
credits.push_back(credit);
} else if ( *tok == "titlecolor" ) {
try{
int r,b,g;
tok->view() >> r >> g >> b;
titleColor = Graphics::makeColor( r, g, b );
titleColorOverride = true;
} catch (const TokenException & ex){
}
} else if ( *tok == "color" ) {
try{
int r,b,g;
tok->view() >> r >> g >> b;
color = Graphics::makeColor( r, g, b );
colorOverride = true;
} catch (const TokenException & ex){
}
} else if ( *tok == "animation" ) {
TokenView animView = tok->view();
while (animView.hasMore()){
const Token * animTok;
animView >> animTok;
if (*animTok == "top"){
tok->match("_/width", topWidth);
tok->match("_/height", topHeight);
topAnimation = Util::ReferenceCount<Gui::Animation>(new Animation(tok));
} else if (*animTok == "bottom"){
tok->match("_/width", bottomWidth);
tok->match("_/height", bottomHeight);
bottomAnimation = Util::ReferenceCount<Gui::Animation>(new Animation(tok));
}
}
} else if (*tok == "spacing"){
tok->view() >> spacing;
} else {
Global::debug( 3 ) <<"Unhandled Credit Block attribute: "<<endl;
if (Global::getDebug() >= 3){
tok->print(" ");
}
}
} catch ( const TokenException & ex ) {
throw LoadException(__FILE__, __LINE__, ex, "Credit Block parse error");
} catch ( const LoadException & ex ) {
throw ex;
}
}
}
OptionCredits::Block::Block(const OptionCredits::Block & copy):
title(copy.title),
credits(copy.credits),
titleColorOverride(copy.titleColorOverride),
titleColor(copy.titleColor),
colorOverride(copy.colorOverride),
color(copy.color),
topAnimation(copy.topAnimation),
topWidth(copy.topWidth),
topHeight(copy.topHeight),
bottomAnimation(copy.bottomAnimation),
bottomWidth(copy.bottomWidth),
bottomHeight(copy.bottomHeight),
spacing(copy.spacing){
}
OptionCredits::Block::~Block(){
}
const OptionCredits::Block & OptionCredits::Block::operator=(const OptionCredits::Block & copy){
title = copy.title;
credits = copy.credits;
titleColor = copy.titleColor;
titleColorOverride = copy.titleColorOverride;
color = copy.color;
colorOverride = copy.colorOverride;
topAnimation = copy.topAnimation;
topWidth =copy.topWidth;
topHeight = copy.topHeight;
bottomAnimation = copy.bottomAnimation;
bottomWidth = copy.bottomWidth;
bottomHeight = copy.bottomHeight;
spacing = copy.spacing;
return *this;
}
void OptionCredits::Block::addCredit(const std::string & credit){
credits.push_back(credit);
}
void OptionCredits::Block::act(){
// Top animation
if (topAnimation != NULL){
topAnimation->act();
}
// Bottom animation
if (bottomAnimation != NULL){
bottomAnimation->act();
}
}
int OptionCredits::Block::print(int x, int y, Graphics::Color defaultTitleColor, Graphics::Color defaultColor, const Font & font, const Graphics::Bitmap & work, const Justification & justification) const {
int currentY = y;
// Top animation
if (topAnimation != NULL){
int xmod = 0;
switch (justification){
default:
case Left:
xmod = 0;
break;
case Center:
xmod = topWidth/2;
break;
case Right:
xmod = topWidth;
break;
}
// FIXME temporary solution
const Graphics::Bitmap temp(topWidth, topHeight);
//topAnimation->draw(x - xmod, y, topWidth, topHeight, work);
topAnimation->draw(0, 0, topWidth, topHeight, temp);
temp.translucent().draw(x-xmod, y, work);
currentY += topHeight;
}
if (!title.empty()){
int xmod = 0;
switch (justification){
default:
case Left:
xmod = 0;
break;
case Center:
xmod = font.textLength(title.c_str())/2;
break;
case Right:
xmod = font.textLength(title.c_str());
break;
}
font.printf(x - xmod, currentY, (titleColorOverride ? titleColor : defaultTitleColor), work, title, 0);
currentY += font.getHeight();
}
for (std::vector<std::string>::const_iterator i = credits.begin(); i != credits.end(); ++i){
const std::string & credit = *i;
int xmod = 0;
switch (justification){
default:
case Left:
xmod = 0;
break;
case Center:
xmod = font.textLength(credit.c_str())/2;
break;
case Right:
xmod = font.textLength(credit.c_str());
break;
}
font.printf(x - xmod, currentY, (colorOverride ? color : defaultColor), work, credit, 0);
currentY += font.getHeight();
}
// Bottom animation
if (bottomAnimation != NULL){
int xmod = 0;
switch (justification){
default:
case Left:
xmod = 0;
break;
case Center:
xmod = bottomWidth/2;
break;
case Right:
xmod = bottomWidth;
break;
}
// FIXME temporary solution
const Graphics::Bitmap temp(topWidth, topHeight);
//bottomAnimation->draw(x - xmod, y, bottomWidth, bottomHeight, work);
bottomAnimation->draw(0, 0, bottomWidth, bottomHeight, temp);
temp.translucent().draw(x-xmod, y, work);
currentY += bottomHeight;
}
currentY += font.getHeight() + spacing;
return currentY;
}
const int OptionCredits::Block::size(const Font & font) const{
// Counts title and space in between
int total = 0;
if (topAnimation != NULL){
total += topHeight;
}
if (!title.empty()){
total+= font.getHeight();
}
total += credits.size() * font.getHeight();
if (bottomAnimation != NULL){
total += bottomHeight;
}
total += font.getHeight();
total += spacing;
return total;
}
OptionCredits::Sequence::Sequence(const Token * token):
type(Primary),
x(0),
y(0),
startx(0),
endx(0),
starty(0),
endy(0),
ticks(0),
duration(250),
speed(0),
alpha(0),
alphaMultiplier(0),
justification(Block::Center),
current(0),
done(false),
creditLength(0){
if ( *token != "sequence" ){
throw LoadException(__FILE__, __LINE__, "Not a credit sequence");
}
TokenView view = token->view();
while (view.hasMore()){
std::string match;
try{
const Token * tok;
view >> tok;
if (*tok == "type"){
std::string sequenceType;
tok->view() >> sequenceType;
if (sequenceType == "roll"){
type = Roll;
} else if (sequenceType == "primary"){
type = Primary;
}
} else if (*tok == "start-x"){
tok->view() >> startx;
} else if (*tok == "end-x"){
tok->view() >> endx;
} else if (*tok == "start-y"){
tok->view() >> starty;
} else if (*tok == "end-y"){
tok->view() >> endy;
} else if (*tok == "duration"){
tok->view() >> duration;
} else if (*tok == "speed"){
tok->view() >> speed;
} else if (*tok == "alpha-multiplier"){
tok->view() >> alphaMultiplier;
} else if ( *tok == "justification" ) {
std::string justify;
tok->view() >> justify;
if (justify == "left"){
justification = Block::Left;
} else if (justify == "center"){
justification = Block::Center;
} else if (justify == "right"){
justification = Block::Right;
}
} else if (*tok == "block"){
credits.push_back(Block(tok));
} else {
Global::debug( 3 ) <<"Unhandled Credit Sequence attribute: "<<endl;
if (Global::getDebug() >= 3){
tok->print(" ");
}
}
} catch ( const TokenException & ex ) {
throw LoadException(__FILE__, __LINE__, ex, "Credit Sequence parse error");
} catch ( const LoadException & ex ) {
throw ex;
}
}
// Initial
reset();
for (std::vector<OptionCredits::Block>::const_iterator i = credits.begin(); i != credits.end(); ++i){
const OptionCredits::Block & block = *i;
creditLength += block.size(Menu::menuFontParameter.current()->get());
}
}
OptionCredits::Sequence::Sequence(const Sequence & copy):
type(copy.type),
x(copy.x),
y(copy.y),
startx(copy.startx),
endx(copy.endx),
starty(copy.starty),
endy(copy.endy),
ticks(copy.ticks),
duration(copy.duration),
speed(copy.speed),
alpha(copy.alpha),
alphaMultiplier(copy.alphaMultiplier),
justification(copy.justification),
credits(copy.credits),
current(copy.current),
done(false),
creditLength(copy.creditLength){
}
OptionCredits::Sequence::~Sequence(){
}
const OptionCredits::Sequence & OptionCredits::Sequence::operator=(const OptionCredits::Sequence & copy){
type = copy.type;
x = copy.x;
y = copy.y;
startx = copy.startx;
endx = copy.endx;
starty = copy.starty;
endy = copy.endy;
ticks = copy.ticks;
duration = copy.duration;
speed = copy.speed;
alpha = copy.alpha;
alphaMultiplier = copy.alphaMultiplier;
justification = copy.justification;
credits = copy.credits;
current = copy.current;
done = false;
creditLength = copy.creditLength;
return *this;
}
static int alphaClamp(int x, double multiplier){
int clamp = x * multiplier;
if (clamp < 0){
clamp = 0;
} else if (clamp > 255){
clamp = 255;
}
return clamp;
}
void OptionCredits::Sequence::act(){
if (!done && !credits.empty()){
if (type == Roll){
y += speed;
if (starty > endy){
if ((y + (creditLength * 1.1)) < endy){
done = true;
}
} else if (starty < endy){
if ((y * 1.1) > endy){
done = true;
}
}
} else if (type == Primary){
credits[current].act();
if (startx != endx){
x += speed;
if (startx > endx){
const double midpoint = (startx+endx)/2;
const int mid = x > midpoint ? startx -x : x - endx;
alpha = alphaClamp(mid, alphaMultiplier);
if (x < endx){
next();
}
} else if (startx < endx){
const double midpoint = (startx+endx)/2;
const int mid = x < midpoint ? x - startx : endx - x;
alpha = alphaClamp(mid, alphaMultiplier);
//Global::debug(0) << "alpha: " << alpha << " midpoint: " << midpoint << " mid: " << mid << std::endl;
if (x > endx){
next();
}
}
} else {
const double midpoint = duration/2;
const int mid = ticks < midpoint ? ticks : duration - ticks;
alpha = alphaClamp(mid, alphaMultiplier);
ticks++;
if (ticks >= duration){
ticks = 0;
next();
}
}
}
}
}
void OptionCredits::Sequence::draw(Graphics::Color title, Graphics::Color color, const Graphics::Bitmap & work){
if (!done && !credits.empty()){
if (type == Roll){
int rollY = (int) y;
for (std::vector<OptionCredits::Block>::const_iterator i = credits.begin(); i != credits.end(); ++i){
const OptionCredits::Block & block = *i;
rollY = block.print(x, rollY, title, color, Menu::menuFontParameter.current()->get(), work, justification);
}
} else if (type == Primary){
Graphics::Bitmap::transBlender(0, 0, 0, alpha);
credits[current].print(x, y, title, color, Menu::menuFontParameter.current()->get(), work.translucent(), justification);
}
}
}
void OptionCredits::Sequence::reset(){
done = false;
current = 0;
ticks = 0;
if (!credits.empty()){
if (type == Roll){
x = startx;
y = starty;
} else if (type == Primary){
x = startx;
y = starty - (credits[current].size(Menu::menuFontParameter.current()->get())/2);
}
}
}
void OptionCredits::Sequence::next(){
if (type == Primary){
if (current < credits.size()){
current++;
if (current == credits.size()){
done = true;
} else {
x = startx;
y = starty - (credits[current].size(Menu::menuFontParameter.current()->get())/2);
}
}
}
}
static std::string defaultPositions(){
const int width = Configuration::getScreenWidth();
const int height = Configuration::getScreenHeight();
std::ostringstream out;
out << "(start-x " << width/2.3 << ") (end-x " << width/1.8 << ") (start-y " << height/2 << ") ";
//out << "(start-x " << width/2 << ") (end-x " << width/2 << ") (start-y " << height/2 << ") (duration 250) ";
return out.str();
}
OptionCredits::OptionCredits(const Gui::ContextBox & parent, const Token * token):
MenuOption(parent, token),
creditsContext(new Menu::Context()),
music(""),
color(Graphics::makeColor(255,255,255)),
title(Graphics::makeColor(0,255,255)),
clearColor(Graphics::makeColor(0,0,0)){
std::string defaultSequence = "(sequence (type primary) (speed 0.3) (alpha-multiplier 20) (justification center) " + defaultPositions();
/* Always */
if (jonBirthday()){
defaultSequence += "(block (title \"Happy birthday, Jon!\"))";
}
if (miguelBirthday()){
defaultSequence += "(block (title \"Happy birthday, Miguel!\"))";
}
if (Storage::instance().exists(Filesystem::RelativePath("sprites/paintown.png"))){
defaultSequence += "(block (animation (top) (width 350) (height 65) (image 0 \"sprites/paintown.png\") (frame (image 0) (time -1))) (credit \"Version " + Version::getVersionString() + "\"))";
} else {
defaultSequence += "(block (title \"PAINTOWN\") (credit \"Version " + Version::getVersionString() + "\"))";
}
defaultSequence += "(block (title \"Programming\") (credit \"Jon Rafkind\") (credit \"Miguel Gavidia\"))";
defaultSequence += "(block (title \"Level design\") (credit \"Jon Rafkind\") (credit \"Miguel Gavidia\"))";
defaultSequence += "(block (title \"Contact\") (credit \"Website: http://paintown.org\") (credit \"Email: jon@rafkind.com\")))";
TokenReader reader;
Sequence sequence(reader.readTokenFromString(defaultSequence));
sequences.push_back(sequence);
//Global::debug(0) << defaultSequence << std::endl;
if ( *token != "credits" ){
throw LoadException(__FILE__, __LINE__, "Not a credit menu");
}
readName(token);
TokenView view = token->view();
// NOTE Use this to handle legacy additional blocks for the time being
Block legacyAdditional("");
bool additionalTitle = true;
while (view.hasMore()){
try{
const Token * tok;
view >> tok;
if ( *tok == "music" ) {
/* Set music for credits */
tok->view() >> music;
} else if ( *tok == "background" ) {
/* Create an image and push it back on to vector */
std::string temp;
tok->view() >> temp;
creditsContext->addBackground(temp);
} else if ( *tok == "anim" || *tok == "animation" ){
creditsContext->addBackground(tok);
} else if ( *tok == "additional" ) {
std::string str;
TokenView additionalView = tok->view();
while (additionalView.hasMore()){
additionalView >> str;
if (additionalTitle){
legacyAdditional = Block(str);
additionalTitle = false;
} else {
legacyAdditional.addCredit(str);
}
}
} else if (*tok == "sequence"){
sequences.push_back(OptionCredits::Sequence(tok));
} else if ( *tok == "titlecolor" ) {
int r,b,g;
tok->view() >> r >> g >> b;
title = Graphics::makeColor( r, g, b );
} else if ( *tok == "color" ) {
int r,b,g;
tok->view() >> r >> g >> b;
color = Graphics::makeColor( r, g, b );
} else if ( *tok == "clear-color" ) {
int r,b,g;
tok->view() >> r >> g >> b;
clearColor = Graphics::makeColor( r, g, b );
} else {
Global::debug( 3 ) <<"Unhandled menu attribute: "<<endl;
if (Global::getDebug() >= 3){
tok->print(" ");
}
}
} catch ( const TokenException & ex ) {
throw LoadException(__FILE__, __LINE__, ex, "Menu parse error");
} catch ( const LoadException & ex ) {
throw ex;
}
}
if (!legacyAdditional.empty()){
//creditsRoll.push_back(legacyAdditional);
}
input.set(Keyboard::Key_ESC, 0, true, Exit);
input.set(Joystick::Button2, 0, true, Exit);
}
OptionCredits::~OptionCredits(){
}
void OptionCredits::logic(){
}
class CreditsLogicDraw : public Util::Logic, public Util::Draw {
public:
CreditsLogicDraw(std::vector<OptionCredits::Sequence> & sequences, Graphics::Color clearColor, Graphics::Color title, Graphics::Color color, const Font & font, InputMap<OptionCredits::CreditKey> & input, Menu::Context & context):
sequences(sequences),
clearColor(clearColor),
title(title),
color(color),
font(font),
input(input),
quit(false),
context(context),
current(0){
}
std::vector<OptionCredits::Sequence> & sequences;
Graphics::Color clearColor, title, color;
const Font & font;
InputMap<OptionCredits::CreditKey> & input;
bool quit;
Menu::Context & context;
unsigned int current;
void run(){
vector<InputMap<OptionCredits::CreditKey>::InputEvent> out = InputManager::getEvents(input, InputSource(true));
for (vector<InputMap<OptionCredits::CreditKey>::InputEvent>::iterator it = out.begin(); it != out.end(); it++){
const InputMap<OptionCredits::CreditKey>::InputEvent & event = *it;
if (event.enabled){
if (event.out == OptionCredits::Exit){
quit = true;
context.finish();
}
}
}
sequences[current].act();
if (sequences[current].isDone()){
sequences[current].reset();
current++;
if (current >= sequences.size()){
current = 0;
}
}
context.act();
}
bool done(){
return quit;
}
double ticks(double system){
return system * Global::ticksPerSecond(90);
}
void draw(const Graphics::Bitmap & buffer){
/* FIXME: hard coded resolution */
- Graphics::StretchedBitmap work(640, 480, buffer, Graphics::qualityFilterName(Configuration::getQualityFilter()));
+ Graphics::StretchedBitmap work(640, 480, buffer, Graphics::StretchedBitmap::NoClear, Graphics::qualityFilterName(Configuration::getQualityFilter()));
work.fill(clearColor);
work.start();
//background.Blit(work);
context.render(Util::ReferenceCount<Menu::Renderer>(NULL), work);
sequences[current].draw(title, color, work);
work.finish();
// buffer.BlitToScreen();
}
};
void OptionCredits::run(const Menu::Context & context){
Menu::Context localContext(context, *creditsContext);
localContext.initialize();
if (!music.empty()){
if (Music::loadSong(Storage::instance().find(Filesystem::RelativePath(music)).path())){
Music::pause();
Music::play();
}
}
const Font & vFont = Menu::menuFontParameter.current()->get();
CreditsLogicDraw loop(sequences, clearColor, title, color, vFont, input, localContext);
Util::standardLoop(loop, loop);
InputManager::waitForRelease(input, InputSource(true), Exit);
throw Menu::Reload(__FILE__, __LINE__);
}
OptionDummy::OptionDummy(const Gui::ContextBox & parent, const Token *token):
MenuOption(parent, token){
if ( *token != "dummy" ){
throw LoadException(__FILE__, __LINE__, "Not dummy option");
}
readName(token);
if (getText().empty()){
this->setText("Dummy");
}
setRunnable(false);
}
OptionDummy::OptionDummy(const Gui::ContextBox & parent, const std::string &name):
MenuOption(parent, 0){
if (name.empty()){
throw LoadException(__FILE__, __LINE__, "No name given to dummy");
}
this->setText(name);
setRunnable(false);
}
OptionDummy::~OptionDummy(){
}
void OptionDummy::logic(){
}
void OptionDummy::run(const Menu::Context & context){
}
OptionFullscreen::OptionFullscreen(const Gui::ContextBox & parent, const Token *token):
MenuOption(parent, token),
lblue(255),
lgreen(255),
rblue(255),
rgreen(255){
setRunnable(false);
if ( *token != "fullscreen" )
throw LoadException(__FILE__, __LINE__, "Not fullscreen option");
readName(token);
}
OptionFullscreen::~OptionFullscreen()
{
// Nothing
}
std::string OptionFullscreen::getText() const {
ostringstream out;
out << MenuOption::getText() << ": " << (Configuration::getFullscreen() ? "Yes" : "No");
return out.str();
}
void OptionFullscreen::logic(){;
}
static void changeScreenMode(){
Configuration::setFullscreen(!Configuration::getFullscreen());
int gfx = (Configuration::getFullscreen() ? Global::FULLSCREEN : Global::WINDOWED);
Graphics::changeGraphicsMode(gfx, Graphics::Bitmap::getScreenWidth(), Graphics::Bitmap::getScreenHeight());
}
void OptionFullscreen::run(const Menu::Context & context){
changeScreenMode();
}
bool OptionFullscreen::leftKey(){
changeScreenMode();
return true;
}
bool OptionFullscreen::rightKey(){
changeScreenMode();
return true;
}
OptionFps::OptionFps(const Gui::ContextBox & parent, const Token * token):
MenuOption(parent, token){
readName(token);
setRunnable(false);
}
void OptionFps::logic(){
}
void OptionFps::run(const Menu::Context & context){
}
std::string OptionFps::getText() const {
ostringstream out;
out << "Frames per second: " << Global::TICS_PER_SECOND;
return out.str();
}
bool OptionFps::leftKey(){
Global::setTicksPerSecond(Global::TICS_PER_SECOND - 1);
Configuration::setFps(Global::TICS_PER_SECOND);
return true;
}
bool OptionFps::rightKey(){
Global::setTicksPerSecond(Global::TICS_PER_SECOND + 1);
Configuration::setFps(Global::TICS_PER_SECOND);
return true;
}
OptionFps::~OptionFps(){
}
OptionQualityFilter::OptionQualityFilter(const Gui::ContextBox & parent, const Token * token):
MenuOption(parent, token){
readName(token);
setRunnable(false);
}
std::string OptionQualityFilter::getText() const {
ostringstream out;
out << MenuOption::getText() << ": " << Configuration::getQualityFilter();
return out.str();
}
void OptionQualityFilter::logic(){
}
bool OptionQualityFilter::leftKey(){
string quality = Configuration::getQualityFilter();
if (quality == "none"){
quality = "hqx";
} else if (quality == "hqx"){
quality = "xbr";
} else if (quality == "xbr"){
quality = "none";
}
Configuration::setQualityFilter(quality);
return true;
}
bool OptionQualityFilter::rightKey(){
string quality = Configuration::getQualityFilter();
if (quality == "none"){
quality = "xbr";
} else if (quality == "hqx"){
quality = "none";
} else if (quality == "xbr"){
quality = "hqx";
}
Configuration::setQualityFilter(quality);
return true;
}
void OptionQualityFilter::run(const Menu::Context & context){
}
OptionQualityFilter::~OptionQualityFilter(){
}
#if 0
static OptionJoystick::JoystickType convertToKey(const std::string &k){
std::string temp = k;
for(unsigned int i=0;i<temp.length();i++){
temp[i] = tolower(temp[i]);
}
if (temp == "up") return OptionJoystick::Up;
if (temp == "down") return OptionJoystick::Down;
if (temp == "left") return OptionJoystick::Left;
/*
if (temp == "right") return OptionJoystick::Right;
if (temp == "jump") return OptionJoystick::Jump;
if (temp == "attack1") return OptionJoystick::Attack1;
if (temp == "attack2") return OptionJoystick::Attack2;
if (temp == "attack3") return OptionJoystick::Attack3;
if (temp == "attack4") return OptionJoystick::Attack4;
if (temp == "attack5") return OptionJoystick::Attack5;
if (temp == "attack6") return OptionJoystick::Attack6;
return OptionJoystick::Invalidkey;
}
static Configuration::JoystickInput getKey(int player, OptionJoystick::JoystickType k){
switch(k){
case OptionJoystick::Up:
return Joystick::Up;
case OptionJoystick::Down:
return Joystick::Down;
case OptionJoystick::Left:
return Joystick::Left;
case OptionJoystick::Right:
return Joystick::Right;
case OptionJoystick::Jump:
return Joystick::Button4;
case OptionJoystick::Attack1:
return Joystick::Button1;
case OptionJoystick::Attack2:
return Joystick::Button2;
case OptionJoystick::Attack3:
return Joystick::Button3;
case OptionJoystick::Attack4:
return Joystick::Button4;
case OptionJoystick::Attack5:
return Joystick::Button5;
case OptionJoystick::Attack6:
return Joystick::Button6;
default:
break;
}
return Joystick::Up;
}
static void setKey(int player, OptionJoystick::JoystickType k, Configuration::JoystickInput key){
/ *
switch(k){
case OptionJoystick::Up:
Configuration::setJoystickUp(player, key);
break;
case OptionJoystick::Down:
Configuration::setJoystickDown(player, key);
break;
case OptionJoystick::Left:
Configuration::setJoystickLeft(player, key);
break;
case OptionJoystick::Right:
Configuration::setJoystickRight(player, key);
break;
case OptionJoystick::Jump:
Configuration::setJoystickJump(player, key);
break;
case OptionJoystick::Attack1:
Configuration::setJoystickAttack1(player, key);
break;
case OptionJoystick::Attack2:
Configuration::setJoystickAttack2(player, key);
break;
case OptionJoystick::Attack3:
Configuration::setJoystickAttack3(player, key);
break;
case OptionJoystick::Attack4:
Configuration::setJoystickAttack4(player, key);
break;
case OptionJoystick::Attack5:
Configuration::setJoystickAttack5(player, key);
break;
case OptionJoystick::Attack6:
Configuration::setJoystickAttack6(player, key);
break;
default:
break;
}
*/
}
static Configuration::JoystickInput readJoystick(){
vector<Joystick::Key> keys;
keys.push_back(Joystick::Up);
keys.push_back(Joystick::Down);
keys.push_back(Joystick::Left);
keys.push_back(Joystick::Right);
keys.push_back(Joystick::Button1);
keys.push_back(Joystick::Button2);
keys.push_back(Joystick::Button3);
keys.push_back(Joystick::Button4);
keys.push_back(Joystick::Button5);
keys.push_back(Joystick::Button6);
InputMap<Joystick::Key> input;
for (vector<Joystick::Key>::iterator it = keys.begin(); it != keys.end(); it++){
input.set(*it, 0, true, *it);
}
input.set(Keyboard::Key_ESC, 0, true, Joystick::Invalid);
while (true){
InputManager::poll();
vector<InputMap<Joystick::Key>::InputEvent> out = InputManager::getEvents(input, InputSource());
for (vector<InputMap<Joystick::Key>::InputEvent>::iterator it = out.begin(); it != out.end(); it++){
const InputMap<Joystick::Key>::InputEvent & event = *it;
if (event.enabled){
Global::debug(1) << "Press: " << event.out << std::endl;
if (event.out == Joystick::Invalid){
InputManager::waitForRelease(input, InputSource(), Joystick::Invalid);
throw Exception::Return(__FILE__, __LINE__);
}
return event.out;
}
}
Util::rest(1);
}
/* control probably shouldn't get here.. */
return Joystick::Up;
}
OptionJoystick::OptionJoystick(const Gui::ContextBox & parent, const Token *token):
MenuOption(parent, token),
name(""),
player(-1),
type(Invalidkey),
keyCode(0){
if (*token != "joystick"){
throw LoadException(__FILE__, __LINE__, "Not joystick option");
}
TokenView view = token->view();
while (view.hasMore()){
try{
const Token * tok;
view >> tok;
if ( *tok == "name" ){
tok->view() >> name;
} else if ( *tok == "player" ) {
tok->view() >> player;
} else if ( *tok == "type" ) {
std::string temp;
tok->view() >> temp;
type = convertToKey(temp);
} else {
Global::debug( 3 ) <<"Unhandled menu attribute: "<<endl;
tok->print(" ");
}
} catch ( const TokenException & ex ) {
throw LoadException(__FILE__, __LINE__, ex, "Menu parse error");
} catch ( const LoadException & ex ) {
// delete current;
throw ex;
}
}
if (name.empty()){
throw LoadException(__FILE__, __LINE__, "No name set, this option should have a name!");
}
if (type == Invalidkey){
throw LoadException(__FILE__, __LINE__, "Invalid joystick button, should be up, down, left, right, up, down, jump, attack1-6!");
}
if (player == -1){
throw LoadException(__FILE__, __LINE__, "Player not specified in joystick configuration");
}
ostringstream out;
out << name << ": " << Joystick::keyToName(getKey(player, type));
setText(out.str());
}
OptionJoystick::~OptionJoystick(){
// Nothing
}
void OptionJoystick::logic(){
/*
char temp[255];
sprintf( temp, "%s: %s", name.c_str(), Joystick::keyToName(getKey(player,type)));
setText(std::string(temp));
*/
}
void OptionJoystick::run(const Menu::Context & context){
/*
//int x, y, width, height;
const Font & vFont = Menu::menuFontParameter.current()->get();
const char * message = "Press a joystick button!";
const int width = vFont.textLength(message) + 10;
const int height = vFont.getHeight() + 10;
// const int x = (getParent()->getWork()->getWidth()/2) - (width/2);
// const int y = (getParent()->getWork()->getHeight()/2) - (height/2);
const int x = Menu::Menu::Width / 2 - width/2;
const int y = Menu::Menu::Height / 2 - height/2;
Box dialog;
dialog.location.setPosition(Gui::AbsolutePoint(0,0));
dialog.location.setDimensions(vFont.textLength(message) + 10, vFont.getHeight() + 10);
dialog.transforms.setRadius(0);
dialog.colors.body = Graphics::makeColor(0,0,0);
dialog.colors.bodyAlpha = 200;
dialog.colors.border = Graphics::makeColor(255,255,255);
dialog.colors.borderAlpha = 255;
Graphics::Bitmap temp = Graphics::Bitmap::temporaryBitmap(width,height);
dialog.render(temp, vFont);
vFont.printf( 5, 5, Graphics::makeColor(255,255,255), temp, message, -1);
temp.BlitToScreen(x,y);
setKey(player, type, readJoystick());
ostringstream out;
out << name << ": " << Joystick::keyToName(getKey(player, type));
setText(out.str());
*/
Graphics::Bitmap temp(Menu::Menu::Width, Menu::Menu::Height);
// Menu::Context tempContext = context;
Menu::Context tempContext(context);
tempContext.initialize();
Menu::InfoBox keyDialog;
// keyDialog.setFont(tempContext.getFont());
//keyDialog.location.set(-1,-1,1,1);
const int width = temp.getWidth();
const int height = temp.getHeight();
const Font & font = Menu::menuFontParameter.current()->get();
const int radius = 15;
keyDialog.setText("Press a joystick button!");
keyDialog.initialize(font);
keyDialog.location.setDimensions(font.textLength("Press a joystick button!") + radius, font.getHeight() + radius);
keyDialog.location.setCenterPosition(Gui::RelativePoint(0, 0));
// keyDialog.location.setPosition(Gui::AbsolutePoint((width/2)-(keyDialog.location.getWidth()/2), (height/2)-(keyDialog.location.getHeight()/2)));
// keyDialog.location.setPosition2(Gui::AbsolutePoint((
keyDialog.transforms.setRadius(radius);
keyDialog.colors.body = Graphics::makeColor(0,0,0);
keyDialog.colors.bodyAlpha = 180;
keyDialog.colors.border = Graphics::makeColor(255,255,255);
keyDialog.colors.borderAlpha = 255;
keyDialog.open();
InputManager::waitForClear();
while (!InputManager::anyInput() && keyDialog.isActive()){
InputManager::poll();
keyDialog.act(font);
/*
if (keyDialog.isActive()){
InputManager::poll();
}
*/
tempContext.act();
tempContext.render(0, temp);
keyDialog.render(temp, font);
temp.BlitToScreen();
}
tempContext.finish();
setKey(player, type, readJoystick());
InputManager::waitForClear();
ostringstream out;
out << name << ": " << Joystick::keyToName(getKey(player, type));
setText(out.str());
/*
Keyboard key;
keyCode = readKey(key);
setKey(player,type, keyCode);
*/
}
#endif
static OptionKey::keyType convertToKeyboardKey(const std::string &k){
std::string temp = k;
for(unsigned int i=0;i<temp.length();i++){
temp[i] = tolower(temp[i]);
}
if (temp == "up") return OptionKey::up;
if (temp == "down") return OptionKey::down;
if (temp == "left") return OptionKey::left;
if (temp == "right") return OptionKey::right;
if (temp == "jump") return OptionKey::jump;
if (temp == "attack1") return OptionKey::attack1;
if (temp == "attack2") return OptionKey::attack2;
if (temp == "attack3") return OptionKey::attack3;
if (temp == "attack4") return OptionKey::attack4;
if (temp == "attack5") return OptionKey::attack5;
if (temp == "attack6") return OptionKey::attack6;
return OptionKey::invalidkey;
}
static int getKey(int player, OptionKey::keyType k){
switch(k){
case OptionKey::up:
return Configuration::getUp(player);
break;
case OptionKey::down:
return Configuration::getDown(player);
break;
case OptionKey::left:
return Configuration::getLeft(player);
break;
case OptionKey::right:
return Configuration::getRight(player);
break;
case OptionKey::jump:
return Configuration::getJump(player);
break;
case OptionKey::attack1:
return Configuration::getAttack1(player);
break;
case OptionKey::attack2:
return Configuration::getAttack2(player);
break;
case OptionKey::attack3:
return Configuration::getAttack3(player);
break;
case OptionKey::attack4:
return Configuration::getAttack4(player);
case OptionKey::attack5:
return Configuration::getAttack5(player);
case OptionKey::attack6:
return Configuration::getAttack6(player);
default:
break;
}
return 0;
}
static void setKey(int player, OptionKey::keyType k, int key){
switch(k){
case OptionKey::up:
Configuration::setUp(player, key);
break;
case OptionKey::down:
Configuration::setDown(player, key);
break;
case OptionKey::left:
Configuration::setLeft(player, key);
break;
case OptionKey::right:
Configuration::setRight(player, key);
break;
case OptionKey::jump:
Configuration::setJump(player, key);
break;
case OptionKey::attack1:
Configuration::setAttack1(player, key);
break;
case OptionKey::attack2:
Configuration::setAttack2(player, key);
break;
case OptionKey::attack3:
Configuration::setAttack3(player, key);
break;
case OptionKey::attack4:
Configuration::setAttack4(player, key);
break;
case OptionKey::attack5:
Configuration::setAttack5(player, key);
break;
case OptionKey::attack6:
Configuration::setAttack6(player, key);
break;
default:
break;
}
}
/*
static int readKey( Keyboard & key ){
int k = key.readKey();
key.wait();
return k;
}
*/
OptionKey::OptionKey(const Gui::ContextBox & parent, const Token *token):
MenuOption(parent, token),
name(""),
player(-1),
type(invalidkey),
keyCode(0){
if ( *token != "key" )
throw LoadException(__FILE__, __LINE__, "Not key option");
TokenView view = token->view();
while (view.hasMore()) {
try {
const Token * tok;
view >> tok;
if ( *tok == "name" ) {
tok->view() >> name;
} else if ( *tok == "player" ) {
tok->view() >> player;
} else if ( *tok == "type" ) {
std::string temp;
tok->view() >> temp;
type = convertToKeyboardKey(temp);
} else {
Global::debug( 3 ) <<"Unhandled menu attribute: "<<endl;
tok->print(" ");
}
} catch ( const TokenException & ex ){
throw LoadException(__FILE__, __LINE__, ex, "Menu parse error");
} catch ( const LoadException & ex ) {
// delete current;
throw ex;
}
}
if(name.empty())throw LoadException(__FILE__, __LINE__, "No name set, this option should have a name!");
if(type == invalidkey)throw LoadException(__FILE__, __LINE__, "Invalid key, should be up, down, left, right, up, down, jump, attack1-6!");
if(player == -1)throw LoadException(__FILE__, __LINE__, "Player not specified in key configuration");
char temp[255];
sprintf( temp, "%s: %s", name.c_str(), Keyboard::keyToName(getKey(player,type)));
setText(std::string(temp));
}
OptionKey::~OptionKey(){
// Nothing
}
void OptionKey::logic(){
char temp[255];
sprintf( temp, "%s: %s", name.c_str(), Keyboard::keyToName(getKey(player,type)));
setText(std::string(temp));
}
void OptionKey::run(const Menu::Context & context){
// Do dialog
//Box::messageDialog(Menu::Menu::Width, Menu::Menu::Height, "Press a Key!",2);
/*
Keyboard key;
key.wait();
*/
Graphics::Bitmap temp(Menu::Menu::Width, Menu::Menu::Height);
// Menu::Context tempContext = context;
Menu::Context tempContext(context);
tempContext.initialize();
Menu::InfoBox keyDialog;
// keyDialog.setFont(tempContext.getFont());
//keyDialog.location.set(-1,-1,1,1);
const int width = temp.getWidth();
const int height = temp.getHeight();
const Font & font = Menu::menuFontParameter.current()->get();
const int radius = 15;
keyDialog.setText("Press a Key!");
keyDialog.initialize(font);
keyDialog.location.setDimensions(font.textLength("Press a Key!") + radius, font.getHeight() + radius);
keyDialog.location.setCenterPosition(Gui::RelativePoint(0, 0));
// keyDialog.location.setPosition(Gui::AbsolutePoint((width/2)-(keyDialog.location.getWidth()/2), (height/2)-(keyDialog.location.getHeight()/2)));
// keyDialog.location.setPosition2(Gui::AbsolutePoint((
keyDialog.transforms.setRadius(radius);
keyDialog.colors.body = Graphics::makeColor(0,0,0);
keyDialog.colors.bodyAlpha = 180;
keyDialog.colors.border = Graphics::makeColor(255,255,255);
keyDialog.colors.borderAlpha = 255;
keyDialog.open();
InputManager::waitForClear();
while (!InputManager::anyInput() && keyDialog.isActive()){
InputManager::poll();
keyDialog.act(font);
/*
if (keyDialog.isActive()){
InputManager::poll();
}
*/
tempContext.act();
tempContext.render(Util::ReferenceCount<Menu::Renderer>(NULL), temp);
keyDialog.render(temp, font);
temp.BlitToScreen();
}
tempContext.finish();
keyCode = InputManager::readKey();
setKey(player,type, keyCode);
InputManager::waitForClear();
}
OptionLevel::OptionLevel(const Gui::ContextBox & parent, const Token *token, int * set, int value):
MenuOption(parent, token),
set(set),
value(value){
// Nothing
}
OptionLevel::~OptionLevel(){
}
void OptionLevel::logic(){
}
void OptionLevel::run(const Menu::Context & context){
*set = value;
throw Menu::MenuException(__FILE__, __LINE__);
}
OptionMenu::OptionMenu(const Gui::ContextBox & parent, const Token *token, const Menu::OptionFactory & factory):
MenuOption(parent, token),
menu(0){
if (*token != "menu"){
throw LoadException(__FILE__, __LINE__, "Not a menu");
}
if (token->numTokens() == 1){
std::string temp;
token->view() >> temp;
menu = new Menu::Menu(Storage::instance().find(Filesystem::RelativePath(temp)), factory);
} else {
menu = new Menu::Menu(token, factory);
}
this->setText(menu->getName());
this->setInfoText(menu->getInfo());
// Lets check if this menu is going bye bye
//if ( menu->checkRemoval() ) setForRemoval(true);
}
OptionMenu::~OptionMenu(){
// Delete our menu
if (menu){
delete menu;
}
}
void OptionMenu::logic(){
// Nothing
}
void OptionMenu::run(const Menu::Context & context){
// Do our new menu
try{
menu->run(context);
} catch (const Exception::Return ignore){
throw Menu::Reload(__FILE__, __LINE__);
}
}
OptionReturn::OptionReturn(const Gui::ContextBox & parent, const Token * token):
MenuOption(parent, token){
if (*token != "return"){
throw LoadException(__FILE__, __LINE__, "Not a return option");
}
readName(token);
}
void OptionReturn::logic(){
}
/* maybe this option is misnamed, but its supposed to quit the current game
* and go back to the main menu
*/
void OptionReturn::run(const Menu::Context & context){
throw Exception::Quit(__FILE__, __LINE__);
}
OptionReturn::~OptionReturn(){
}
OptionContinue::OptionContinue(const Gui::ContextBox & parent, const Token * token):
MenuOption(parent, token){
if (*token != "continue"){
throw LoadException(__FILE__, __LINE__, "Not a continue option");
}
readName(token);
}
void OptionContinue::logic(){
}
void OptionContinue::run(const Menu::Context & context){
throw Exception::Return(__FILE__, __LINE__);
}
OptionContinue::~OptionContinue(){
}
OptionQuit::OptionQuit(const Gui::ContextBox & parent, const Token *token):
MenuOption(parent, token){
if ( *token != "quit" ){
throw LoadException(__FILE__, __LINE__, "Not quit option");
}
readName(token);
}
OptionQuit::OptionQuit(const Gui::ContextBox & parent, const std::string &name):
MenuOption(parent, 0){
if (name.empty()){
throw LoadException(__FILE__, __LINE__, "No name given to quit");
}
this->setText(name);
}
OptionQuit::~OptionQuit(){
}
void OptionQuit::logic(){
}
void OptionQuit::run(const Menu::Context & context){
throw ShutdownException();
}
#if defined(WINDOWS) && defined(doesnt_work_yet)
#include <windows.h>
#include <stdio.h>
/* contributed by Roy Underthump from allegro.cc */
static vector<ScreenSize> getScreenResolutions(){
HWND hwnd;
HDC hdc;
// int iPixelFormat;
int descerr;
int retval;
DEVMODE d;
PIXELFORMATDESCRIPTOR pfd;
hwnd = GetDesktopWindow();
hdc = GetDC(hwnd);
vector<ScreenSize> modes;
for (int i = 0;; i++){
retval = EnumDisplaySettings(0,i,&d);
if (!retval){
break;
}
descerr = DescribePixelFormat(hdc, i+1, sizeof(pfd), &pfd);
if(!descerr){
continue;
}
/*
printf("\n#%d bpp %d width %d height %d colorbits %d fps %d",i,d.dmBitsPerPel,
d.dmPelsWidth, d.dmPelsHeight,pfd.cColorBits,d.dmDisplayFrequency);
if(pfd.dwFlags & PFD_SUPPORT_OPENGL)printf(" OGL OK");
*/
modes.push_back(ScreenSize(d.dmPelsWidth, d.dmPelsHeight));
}
if (modes.empty()){
modes.push_back(ScreenSize(640,480));
}
return modes;
}
#else
static vector<ScreenSize> getScreenResolutions(){
vector<ScreenSize> modes;
modes.push_back(ScreenSize(320, 240));
modes.push_back(ScreenSize(640, 480));
modes.push_back(ScreenSize(800, 600));
modes.push_back(ScreenSize(960, 720));
modes.push_back(ScreenSize(1024, 768));
modes.push_back(ScreenSize(1280, 960));
modes.push_back(ScreenSize(1600, 1200));
return modes;
}
#endif
static bool doSort(const ScreenSize & a, const ScreenSize & b){
return (a.w * a.h) < (b.w * b.h);
}
static vector<ScreenSize> sortResolutions(const vector<ScreenSize> & modes){
vector<ScreenSize> copy(modes);
std::sort(copy.begin(), copy.end(), doSort);
return copy;
}
OptionScreenSize::OptionScreenSize(const Gui::ContextBox & parent, const Token *token):
MenuOption(parent, token),
name(""),
lblue(255),
lgreen(255),
rblue(255),
rgreen(255){
setRunnable(false);
Global::debug(1) << "Get screen resolution" << endl;
modes = sortResolutions(getScreenResolutions());
if (Global::getDebug() >= 1){
for (vector<ScreenSize>::iterator it = modes.begin(); it != modes.end(); it++){
Global::debug(1) << "Screen size: " << it->w << " x " << it->h << endl;
}
}
if ( *token != "screen-size" ){
throw LoadException(__FILE__, __LINE__, "Not a screen-size");
}
readName(token);
}
OptionScreenSize::~OptionScreenSize(){
// Nothing
}
void OptionScreenSize::logic(){
ostringstream temp;
temp << "Screen size: " << Configuration::getScreenWidth() << " x " << Configuration::getScreenHeight();
setText(temp.str());
}
void OptionScreenSize::run(const Menu::Context & context){
}
void OptionScreenSize::setMode(int width, int height){
if (width != Configuration::getScreenWidth() ||
height != Configuration::getScreenHeight()){
Global::debug(1) << "Changing mode to " << width << " x " << height << endl;
int gfx = Configuration::getFullscreen() ? Global::FULLSCREEN : Global::WINDOWED;
int ok = Graphics::changeGraphicsMode(gfx, width, height);
if (ok == 0){
Global::debug(1) << "Success" << endl;
Configuration::setScreenWidth(width);
Configuration::setScreenHeight(height);
} else {
Global::debug(1) << "Fail" << endl;
int ok = Graphics::changeGraphicsMode(gfx, Configuration::getScreenWidth(), Configuration::getScreenHeight());
Global::debug(1) << "Set mode back " << ok << endl;
}
}
}
/*
static int modes[][2] = {{640,480}, {800,600}, {1024,768}, {1280,1024}, {1600,1200}};
// static int max_modes = sizeof(modes) / sizeof(int[]);
static int max_modes = 5;
*/
int OptionScreenSize::findMode(int width, int height){
for (int mode = 0; mode < (int) modes.size(); mode++){
if (modes[mode].w == width && modes[mode].h == height){
return mode;
}
}
return -1;
}
bool OptionScreenSize::leftKey(){
int mode = findMode(Configuration::getScreenWidth(), Configuration::getScreenHeight());
if (mode >= 1 && mode < (int)modes.size()){
mode -= 1;
} else {
mode = 0;
}
setMode(modes[mode].w, modes[mode].h);
lblue = lgreen = 0;
return true;
}
bool OptionScreenSize::rightKey(){
int mode = findMode(Configuration::getScreenWidth(), Configuration::getScreenHeight());
if (mode >= 0 && mode < (int)modes.size() - 1){
mode += 1;
} else {
mode = 0;
}
setMode(modes[mode].w, modes[mode].h);
rblue = rgreen = 0;
return true;
}
static string joinPaths(const vector<Filesystem::AbsolutePath> & strings, const string & middle){
ostringstream out;
for (vector<Filesystem::AbsolutePath>::const_iterator it = strings.begin(); it != strings.end(); it++){
out << (*it).path() << middle;
}
return out.str();
}
static bool sortInfo(const Util::ReferenceCount<Menu::FontInfo> & info1,
const Util::ReferenceCount<Menu::FontInfo> & info2){
string name1 = Util::lowerCaseAll(info1->getName());
string name2 = Util::lowerCaseAll(info2->getName());
return name1 < name2;
}
static bool isWindows(){
#ifdef WINDOWS
return true;
#else
return false;
#endif
}
static bool isOSX(){
#ifdef MACOSX
return true;
#else
return false;
#endif
}
template <class X>
static vector<X> operator+(const vector<X> & v1, const vector<X> & v2){
vector<X> out;
for (typename vector<X>::const_iterator it = v1.begin(); it != v1.end(); it++){
out.push_back(*it);
}
for (typename vector<X>::const_iterator it = v2.begin(); it != v2.end(); it++){
out.push_back(*it);
}
return out;
}
static vector<Filesystem::AbsolutePath> findSystemFonts(){
if (isWindows()){
const char * windows = getenv("windir");
if (windows != NULL){
return Storage::instance().getFilesRecursive(Filesystem::AbsolutePath(string(windows) + "/fonts"), "*.ttf");
}
return vector<Filesystem::AbsolutePath>();
} else if (isOSX()){
return Storage::instance().getFilesRecursive(Filesystem::AbsolutePath("/Library/Fonts"), "*.ttf");
} else {
/* assume unix/linux conventions */
return Storage::instance().getFilesRecursive(Filesystem::AbsolutePath("/usr/share/fonts/truetype"), "*.ttf") +
Storage::instance().getFilesRecursive(Filesystem::AbsolutePath("/usr/local/share/fonts/truetype"), "*.ttf");
}
}
static vector<Util::ReferenceCount<Menu::FontInfo> > findFonts(){
vector<Util::ReferenceCount<Menu::FontInfo> > fonts;
try{
Filesystem::AbsolutePath fontsDirectory = Storage::instance().find(Filesystem::RelativePath("fonts"));
Global::debug(1, "fonts") << "Font directory " << fontsDirectory.path() << endl;
vector<Filesystem::AbsolutePath> ttfFonts = Storage::instance().getFiles(fontsDirectory, "*.ttf");
Global::debug(1, "fonts") << "Found ttf fonts " << joinPaths(ttfFonts, ", ") << endl;
vector<Filesystem::AbsolutePath> otfFonts = Storage::instance().getFiles(fontsDirectory, "*.otf");
Global::debug(1, "fonts") << "Found otf fonts " << joinPaths(otfFonts, ", ") << endl;
for (vector<Filesystem::AbsolutePath>::iterator it = ttfFonts.begin(); it != ttfFonts.end(); it++){
fonts.push_back(Util::ReferenceCount<Menu::FontInfo>(new Menu::RelativeFontInfo(Storage::instance().cleanse(*it), Configuration::getMenuFontWidth(), Configuration::getMenuFontHeight())));
}
for (vector<Filesystem::AbsolutePath>::iterator it = otfFonts.begin(); it != otfFonts.end(); it++){
fonts.push_back(Util::ReferenceCount<Menu::FontInfo>(new Menu::RelativeFontInfo(Storage::instance().cleanse(*it), Configuration::getMenuFontWidth(), Configuration::getMenuFontHeight())));
}
/* linux specific fonts */
vector<Filesystem::AbsolutePath> systemFonts = findSystemFonts();
for (vector<Filesystem::AbsolutePath>::iterator it = systemFonts.begin(); it != systemFonts.end(); it++){
Global::debug(1) << "Adding system font `" << (*it).path() << "'" << endl;
fonts.push_back(Util::ReferenceCount<Menu::FontInfo>(new Menu::AbsoluteFontInfo(*it, Configuration::getMenuFontWidth(), Configuration::getMenuFontHeight())));
}
sort(fonts.begin(), fonts.end(), sortInfo);
// DEFAULT (blank)
// fonts.insert(fonts.begin(), new Menu::DefaultFontInfo());
fonts.insert(fonts.begin(), Util::ReferenceCount<Menu::FontInfo>(NULL));
} catch (const Filesystem::NotFound & e){
throw LoadException(__FILE__, __LINE__, e, "Could not load font");
}
return fonts;
}
OptionSelectFont::OptionSelectFont(const Gui::ContextBox & parent, const Token *token):
MenuOption(parent, token),
typeAdjust(fontName),
lblue(255),
lgreen(255),
rblue(255),
rgreen(255){
setRunnable(false);
if ( *token != "font-select" ){
throw LoadException(__FILE__, __LINE__, "Not a font selector");
}
TokenView view = token->view();
while (view.hasMore()){
try{
const Token * tok;
view >> tok;
if ( *tok == "adjust" ){
std::string temp;
tok->view() >> temp;
if ( temp == "name" ) typeAdjust = fontName;
else if ( temp == "width" ) typeAdjust = fontWidth;
else if ( temp == "height" ) typeAdjust = fontHeight;
else throw LoadException(__FILE__, __LINE__, "Incorrect value \"" + temp + "\" in font-select");
} else {
Global::debug(3) << "Unhandled menu attribute: " << endl;
if (Global::getDebug() >= 3){
tok->print(" ");
}
}
} catch ( const TokenException & ex ) {
throw LoadException(__FILE__, __LINE__, ex, "Menu parse error");
} catch ( const LoadException & ex ) {
// delete current;
throw ex;
}
}
}
void OptionSelectFont::open(){
// Find and set fonts now
if (typeAdjust == fontName){
fonts = findFonts();
}
}
void OptionSelectFont::close(){
if (typeAdjust == fontName){
/* the user probably loaded a bunch of different fonts that will
* never be used again, so clear the font cache
* TODO: dont clear the currently selected font
*/
FontFactory::clear();
}
}
OptionSelectFont::~OptionSelectFont(){
// Nothing
}
void OptionSelectFont::logic(){
/* FIXME Get current font and display info */
switch (typeAdjust){
case fontName:{
std::string name;
if (Configuration::hasMenuFont()){
name = Configuration::getMenuFont()->getName();
} else {
name = "Default";
}
setText("Current Font: " + name);
break;
}
case fontWidth:{
ostringstream temp;
temp << "Font Width: " << Configuration::getMenuFontWidth();
setText(temp.str());
break;
}
case fontHeight:{
ostringstream temp;
temp << "Font Height: " << Configuration::getMenuFontHeight();
setText(temp.str());
break;
}
default: break;
}
if (lblue < 255){
lblue += 5;
}
if (rblue < 255){
rblue += 5;
}
if (lgreen < 255){
lgreen += 5;
}
if (rgreen < 255){
rgreen += 5;
}
}
void OptionSelectFont::run(const Menu::Context & context){
// throw Menu::MenuException(__FILE__, __LINE__);
/* throw something to quit back to the previous menu */
}
bool OptionSelectFont::leftKey(){
switch (typeAdjust){
case fontName:
nextIndex(false);
break;
case fontWidth:
Configuration::setMenuFontWidth(Configuration::getMenuFontWidth() - 1);
break;
case fontHeight:
Configuration::setMenuFontHeight(Configuration::getMenuFontHeight() - 1);
break;
default:
break;
}
lblue = lgreen = 0;
return true;
}
bool OptionSelectFont::rightKey(){
switch (typeAdjust){
case fontName:
nextIndex(true);
break;
case fontWidth:
Configuration::setMenuFontWidth(Configuration::getMenuFontWidth() + 1);
break;
case fontHeight:
Configuration::setMenuFontHeight(Configuration::getMenuFontHeight() + 1);
break;
default:
break;
}
rblue = rgreen = 0;
return true;
}
static bool saneFont(const Util::ReferenceCount<Menu::FontInfo> & info){
class Context: public Loader::LoadingContext {
public:
Context(const Util::ReferenceCount<Menu::FontInfo> & info):
info(info),
isok(false){
}
bool ok(){
try{
const Font & font = info->get();
return font.textLength("A") != 0 &&
font.getHeight() != 0;
} catch (const Exception::Base & ignore){
return true;
}
}
virtual void load(){
isok = ok();
}
const Util::ReferenceCount<Menu::FontInfo> & info;
bool isok;
};
if (info == NULL){
return true;
}
Context context(info);
/* an empty Info object, we don't really care about it */
Loader::Info level("Loading Font", Filesystem::AbsolutePath());
Loader::loadScreen(context, level, Loader::SimpleCircle);
return context.isok;
}
void OptionSelectFont::nextIndex(bool forward){
if (fonts.size() == 0){
return;
}
int index = 0;
for (unsigned int i = 0 ; i < fonts.size() ; ++i){
if ((Configuration::getMenuFont() == NULL && fonts[i] == NULL) ||
((Configuration::getMenuFont() != NULL && fonts[i] != NULL) &&
(*Configuration::getMenuFont() == *fonts[i]))){
index = i;
}
}
if (forward){
index++;
if (index >= (int) fonts.size()){
index = 0;
}
} else {
index--;
if (index < 0){
index = (int)fonts.size()-1;
}
}
while (!saneFont(fonts[index])){
Global::debug(0) << "Warning: erasing font `" << fonts[index]->getName() << "'" << endl;
int where = 0;
vector<Util::ReferenceCount<Menu::FontInfo> >::iterator it;
for (it = fonts.begin(); it != fonts.end() && where != index; it++, where++){
}
fonts.erase(it);
if (index >= (int) fonts.size()){
index = fonts.size() - 1;
}
}
Configuration::setMenuFont(fonts[index]);
/* FIXME */
/*
if (fonts[index] == "Default"){
Configuration::setMenuFont("");
} else {
Configuration::setMenuFont(fonts[index]);
}
*/
}
OptionSpeed::OptionSpeed(const Gui::ContextBox & parent, const Token *token):
MenuOption(parent, token),
name(""),
lblue(255),
lgreen(255),
rblue(255),
rgreen(255){
setRunnable(false);
if ( *token != "speed" )
throw LoadException(__FILE__, __LINE__, "Not speed option");
readName(token);
}
OptionSpeed::~OptionSpeed(){
// Nothing
}
std::string OptionSpeed::getText() const {
ostringstream out;
out << MenuOption::getText() << ": " << Configuration::getGameSpeed();
return out.str();
}
void OptionSpeed::logic(){
/*
//ostringstream temp;
char temp[255];
sprintf( temp, "%s: %0.2f", name.c_str(), MenuGlobals::getGameSpeed() );
setText(std::string(temp));
*/
}
void OptionSpeed::run(const Menu::Context & context){
}
bool OptionSpeed::leftKey(){
Configuration::setGameSpeed(Configuration::getGameSpeed() - 0.05);
if (Configuration::getGameSpeed() < 0.1){
Configuration::setGameSpeed(0.1);
}
return false;
}
bool OptionSpeed::rightKey(){
Configuration::setGameSpeed(Configuration::getGameSpeed() + 0.05);
rblue = rgreen = 0;
return false;
}
OptionTabMenu::OptionTabMenu(const Gui::ContextBox & parent, const Token *token, const Menu::OptionFactory & factory):
MenuOption(parent, token),
menu(0){
if (token->numTokens() == 1){
std::string temp;
token->view() >> temp;
menu = new Menu::Menu(Storage::instance().find(Filesystem::RelativePath(temp)), factory, Menu::Renderer::Tabbed);
} else {
menu = new Menu::Menu(token, factory, Menu::Renderer::Tabbed);
}
// this->setText(menu->getName());
// token->print("Menu: ");
const Token * tok = token->findToken("_/name");
if (tok != NULL){
std::string name;
tok->view() >> name;
// Global::debug(0, "menu") << "Menu name: " << name << endl;
this->setText(name);
} else {
// No name?
throw LoadException(__FILE__, __LINE__, "Menu has no name");
}
}
OptionTabMenu::~OptionTabMenu(){
// Delete our menu
if (menu){
delete menu;
}
}
void OptionTabMenu::logic(){
// Nothing
}
void OptionTabMenu::run(const Menu::Context & context){
// Do our new menu
// menu->run(context);
try{
menu->run(context);
} catch (const Exception::Return ignore){
throw Menu::Reload(__FILE__, __LINE__);
}
}
OptionSound::OptionSound(const Gui::ContextBox & parent, const Token *token):
MenuOption(parent, token),
lblue(255),
lgreen(255),
rblue(255),
rgreen(255){
setRunnable(false);
if (*token != "sound" ){
throw LoadException(__FILE__, __LINE__, "Not a sound option");
}
readName(token);
originalName = getName();
}
OptionSound::~OptionSound(){
}
void OptionSound::logic(){
ostringstream temp;
temp << originalName << ": " << Configuration::getSoundVolume();
setText(temp.str());
}
void OptionSound::run(const Menu::Context & context){
}
void OptionSound::changeSound(int much){
int volume = Configuration::getSoundVolume();
volume += much;
if (volume < 0){
volume = 0;
}
if (volume > 100){
volume = 100;
}
Configuration::setSoundVolume(volume);
}
bool OptionSound::leftKey(){
changeSound(-1);
lblue = lgreen = 0;
return true;
}
bool OptionSound::rightKey(){
changeSound(+1);
rblue = rgreen = 0;
return true;
}
OptionMusic::OptionMusic(const Gui::ContextBox & parent, const Token *token):
MenuOption(parent, token),
lblue(255),
lgreen(255),
rblue(255),
rgreen(255){
setRunnable(false);
if (*token != "music" ){
throw LoadException(__FILE__, __LINE__, "Not a music option");
}
readName(token);
originalName = getName();
}
void OptionMusic::logic(){
ostringstream temp;
temp << originalName << ": " << Configuration::getMusicVolume();
setText(temp.str());
}
void OptionMusic::run(const Menu::Context & context){
}
void OptionMusic::changeMusic(int much){
int volume = Configuration::getMusicVolume();
volume += much;
if (volume < 0){
volume = 0;
}
if (volume > 100){
volume = 100;
}
Configuration::setMusicVolume(volume);
Music::setVolume((double) volume / 100.0);
}
bool OptionMusic::leftKey(){
changeMusic(-1);
lblue = lgreen = 0;
return true;
}
bool OptionMusic::rightKey(){
changeMusic(+1);
lblue = lgreen = 0;
return true;
}
OptionMusic::~OptionMusic(){
}
OptionLanguage::OptionLanguage(const Gui::ContextBox & parent, const Token * token):
MenuOption(parent, token){
readName(token);
#if 0
const Token * start = token->getRootParent();
vector<const Token*> tokens = start->findTokens("*/language");
vector<string> all;
for (vector<const Token*>::iterator it = tokens.begin(); it != tokens.end(); it++){
string language;
const Token * token = *it;
if (token->match("language", language)){
all.push_back(language);
}
}
sort(all.begin(), all.end());
unique_copy(all.begin(), all.end(), back_insert_iterator<vector<string> >(languages));
// Global::debug(0) << "Found " << languages.size() << " languages" << endl;
#endif
}
void OptionLanguage::run(const Menu::Context & context){
class LanguageOption: public MenuOption {
public:
LanguageOption(const Gui::ContextBox & parent, const string & language):
MenuOption(parent, NULL){
setText(language);
setInfoText(language);
}
virtual void logic(){
}
virtual void run(const ::Menu::Context & context){
Configuration::setLanguage(getText());
Configuration::saveConfiguration();
throw ::Menu::MenuException(__FILE__, __LINE__);
}
};
Util::NewReferenceCount<Menu::DefaultRenderer> renderer;
Menu::Menu temp(renderer.convert<Menu::Renderer>());
Util::ReferenceCount<Menu::FontInfo> info(new Menu::RelativeFontInfo(Font::getDefaultFontPath(), 24, 24));
temp.setFont(info);
const Gui::ContextBox & box = renderer->getBox();
vector<string> languages = context.getLanguages();
for (vector<string>::iterator it = languages.begin(); it != languages.end(); it++){
temp.addOption(new LanguageOption(box, *it));
}
try {
temp.run(context);
} catch (const Exception::Return & ignore){
} catch (const Menu::MenuException & ex){
}
throw Menu::Reload(__FILE__, __LINE__);
// throw Exception::Return(__FILE__, __LINE__);
}
void OptionLanguage::logic(){
}
OptionJoystick::OptionJoystick(const Gui::ContextBox & parent, const Token *token):
MenuOption(parent, token){
setRunnable(true);
if (*token != "joystick" ){
throw LoadException(__FILE__, __LINE__, "Not a joystick option");
}
readName(token);
}
void OptionJoystick::logic(){
}
class JoystickLogicDraw: public Util::Logic, public Util::Draw {
public:
enum Inputs{
Exit
};
static const int marginX = 20;
JoystickLogicDraw(int id, const Util::ReferenceCount<Joystick> & joystick, const ::Menu::Context & context):
id(id),
joystick(joystick),
quit(false),
context(context, Menu::Context()){
input.set(Keyboard::Key_ESC, Exit);
}
const int id;
Util::ReferenceCount<Joystick> joystick;
bool quit;
Menu::Context context;
InputMap<Inputs> input;
void doInput(){
vector<InputMap<Inputs>::InputEvent> out = InputManager::getEvents(input, InputSource(true));
for (vector<InputMap<Inputs>::InputEvent>::iterator it = out.begin(); it != out.end(); it++){
const InputMap<Inputs>::InputEvent & event = *it;
if (event.enabled){
if (event.out == Exit){
quit = true;
// context.finish();
}
}
}
}
virtual void run(){
doInput();
context.act();
}
bool done(){
return quit;
}
double ticks(double system){
return system * Global::ticksPerSecond(60);
}
void drawButtons(const Font & font, const Graphics::Bitmap & buffer, int y){
Graphics::Color color = Graphics::makeColor(255, 255, 255);
font.printf(marginX, y, color, buffer, "Up: ", 0); y += font.getHeight() + 5;
font.printf(marginX, y, color, buffer, "Down: ", 0); y += font.getHeight() + 5;
font.printf(marginX, y, color, buffer, "Left: ", 0); y += font.getHeight() + 5;
font.printf(marginX, y, color, buffer, "Right: ", 0); y += font.getHeight() + 5;
font.printf(marginX, y, color, buffer, "Button1: ", 0); y += font.getHeight() + 5;
font.printf(marginX, y, color, buffer, "Button2: ", 0); y += font.getHeight() + 5;
font.printf(marginX, y, color, buffer, "Button3: ", 0); y += font.getHeight() + 5;
font.printf(marginX, y, color, buffer, "Button4: ", 0); y += font.getHeight() + 5;
font.printf(marginX, y, color, buffer, "Button5: ", 0); y += font.getHeight() + 5;
font.printf(marginX, y, color, buffer, "Button6: ", 0); y += font.getHeight() + 5;
font.printf(marginX, y, color, buffer, "Select: ", 0); y += font.getHeight() + 5;
font.printf(marginX, y, color, buffer, "Quit: ", 0); y += font.getHeight() + 5;
}
void draw(const Graphics::Bitmap & buffer){
const Font & font = Menu::menuFontParameter.current()->get();
- Graphics::StretchedBitmap work(640, 480, buffer, Graphics::qualityFilterName(Configuration::getQualityFilter()));
+ Graphics::StretchedBitmap work(640, 480, buffer, Graphics::StretchedBitmap::NoClear, Graphics::qualityFilterName(Configuration::getQualityFilter()));
work.start();
context.renderBackground(work);
/* FIXME: scale the joystck name down to fit */
font.printf(marginX, 1, Graphics::makeColor(255, 255, 255), work, "Joystick %d: %s", 0, id, joystick->getName().c_str());
drawButtons(font, work, 1 + font.getHeight() + 5);
context.renderForeground(work);
work.finish();
}
};
namespace{
struct Axis{
Axis():
stick(0),
axis(0),
first(0),
set(false),
last(0),
lastMotion(0){
}
int stick;
int axis;
/* first value from this axis. we assume that
* the first value is sufficiently close to 'zero' which
* can be any value, but most likely will either be
* -1, 0, or 1
*/
double first;
/* true if first has been set */
bool set;
/* keep track of last value for this axis */
double last;
/* last time an event was produced (or at least last
* time we read it). it might be better to get the actual
* time from the event itself.
*/
uint64_t lastMotion;
};
}
static void runJoystickMenu(int joystickId, const Util::ReferenceCount<Joystick> & joystick, const ::Menu::Context & context){
Util::NewReferenceCount<Menu::DefaultRenderer> renderer;
Menu::Menu menu(renderer.convert<Menu::Renderer>());
Gui::ContextBox & box = renderer->getBox();
box.setListType(ContextBox::Normal);
Gui::ListValues attributes(box.getListValues());
attributes.setDistanceFade(false);
box.setListValues(attributes);
menu.setPosition(Gui::Coordinate(Gui::RelativePoint(-0.8, -0.3),
Gui::RelativePoint(0, 0.8)));
#define WAIT_TIME_MS (0.7 * 1000)
#define WAIT_TIME_AXIS_MS (1 * 1000)
#define AXIS_THRESHOLD 0.7
class JoystickButton: public MenuOption {
public:
JoystickButton(const Menu::Menu & menu, const Gui::ContextBox & parent, const Util::ReferenceCount<Joystick> & joystick, const string & name, Joystick::Key key):
MenuOption(parent, NULL),
menu(menu),
name(name),
joystick(joystick),
key(key){
setText(name);
setInfoText(name);
}
const Menu::Menu & menu;
string name;
Util::ReferenceCount<Joystick> joystick;
Joystick::Key key;
class ButtonListener: public JoystickListener {
public:
ButtonListener(const Util::ReferenceCount<Joystick> & joystick):
done(false),
chosen(-1),
chosenAxis(NULL){
map<int, map<int, double> > axisValues = joystick->getCurrentAxisValues();
for (map<int, map<int, double> >::iterator it = axisValues.begin(); it != axisValues.end(); it++){
int stick = it->first;
const map<int, double> & subMap = it->second;
for (map<int, double>::const_iterator it = subMap.begin(); it != subMap.end(); it++){
int axis = it->first;
double value = it->second;
Axis use;
use.stick = stick;
use.axis = axis;
use.first = value;
use.set = true;
use.last = value;
use.lastMotion = 0;
this->axis.push_back(use);
}
}
}
map<int, uint64_t> presses;
map<int, bool> pressed;
vector<Axis> axis;
bool done;
int chosen;
Axis * chosenAxis;
Axis & getAxis(int stick, int axis){
for (vector<Axis>::iterator it = this->axis.begin(); it != this->axis.end(); it++){
Axis & use = *it;
if (use.stick == stick && use.axis == axis){
return use;
}
}
Axis out;
out.stick = stick;
out.axis = axis;
this->axis.push_back(out);
return getAxis(stick, axis);
}
const vector<Axis> & getAllAxis() const {
return axis;
}
const map<int, uint64_t> & getPresses() const {
return presses;
}
int getButton() const {
return chosen;
}
Axis * getChosenAxis() const {
return chosenAxis;
}
virtual ~ButtonListener(){
}
bool isDone() const {
return (getButton() != -1 && !anyPressed()) ||
(getChosenAxis() != NULL);
}
void choose(){
uint64_t now = System::currentMilliseconds();
for (map<int, uint64_t>::const_iterator it = presses.begin(); it != presses.end(); it++){
uint64_t what = it->second;
if (what != 0 && now - what > WAIT_TIME_MS){
chosen = it->first;
}
}
for (vector<Axis>::iterator it = axis.begin(); it != axis.end(); it++){
Axis & use = *it;
if (fabs(use.last - use.first) > AXIS_THRESHOLD &&
now - use.lastMotion > WAIT_TIME_AXIS_MS){
chosenAxis = &use;
}
}
}
bool anyPressed() const {
for (map<int, bool>::const_iterator it = pressed.begin(); it != pressed.end(); it++){
if (it->second){
return true;
}
}
return false;
}
virtual void pressButton(Joystick * from, int button){
pressed[button] = true;
presses[button] = System::currentMilliseconds();
}
virtual void releaseButton(Joystick * from, int button){
presses[button] = 0;
pressed[button] = false;
}
/* either all increasing or all decreasing */
bool monotonic(const vector<double> & what){
if (what.size() == 0){
return true;
}
double first = what[0];
int direction = -1;
vector<double>::const_iterator it = what.begin();
it++;
for (/**/; it != what.end(); it++){
switch (direction){
case -1: {
if (*it < first){
direction = 1;
} else if (*it > first){
direction = 2;
}
first = *it;
break;
}
case 1: {
if (*it > first){
return false;
}
break;
}
case 2: {
if (*it < first){
return false;
}
break;
}
}
}
return true;
}
virtual void axisMotion(Joystick * from, int stick, int axis, double motion){
Axis & use = getAxis(stick, axis);
if (!use.set){
use.first = motion;
use.set = true;
}
use.last = motion;
use.lastMotion = System::currentMilliseconds();
/*
const double AXIS_THRESHOLD = 0.5;
Global::debug(0) << "stick " << stick << " axis " << axis << " first " << use.first << " last " << use.last << " diff " << fabs(use.last - use.first) << std::endl;
if (fabs(use.last - use.first) > AXIS_THRESHOLD){
Global::debug(0) << "stick " << stick << " axis " << axis << " motion " << motion << std::endl;
}
*/
}
virtual void hatMotion(Joystick * from, int motion){
}
};
void logic(){
ostringstream out;
int stick, axis;
double low, high;
if (joystick->getAxis(key, stick, axis, low, high)){
out << name << ": " << stick << "/" << axis << "/";
if (low < 0){
out << "-";
} else {
out << "+";
}
} else {
int button = joystick->getButton(key);
out << name << ": ";
if (button != -1){
out << joystick->getButton(key);
} else {
out << "unset";
}
}
setText(out.str());
}
void run(const Menu::Context & context){
class SetButton: public Util::Logic, public Util::Draw {
public:
SetButton(const Menu::Context & context, const Menu::Menu & menu, const string & name, const Util::ReferenceCount<Joystick> & joystick):
context(context),
menu(menu),
name(name),
listener(joystick),
joystick(joystick){
input.set(Keyboard::Key_ESC, 0);
joystick->addListener(&listener);
}
const Menu::Context & context;
const Menu::Menu & menu;
string name;
ButtonListener listener;
InputMap<int> input;
Util::ReferenceCount<Joystick> joystick;
~SetButton(){
joystick->removeListener(&listener);
}
void setButton(Joystick::Key key){
int button = listener.getButton();
if (button != -1){
Global::debug(1) << "Chosen button " << listener.getButton() << std::endl;
joystick->setCustomButton(listener.getButton(), key);
} else {
Axis * axis = listener.getChosenAxis();
double rangeLow = 0;
double rangeHigh = 0;
/* stick went negative and went to -1 */
if (axis->first <= 0 && axis->last < axis->first){
rangeLow = -1;
rangeHigh = -AXIS_THRESHOLD;
/* stick started at negative and went positive, possibly
* not above 0.
*/
} else if (axis->first < 0 && axis->last > axis->first){
rangeLow = 0;
rangeHigh = 1;
/* stick started positive and went to 1 */
} else if (axis->first >= 0 && axis->last > axis->first){
rangeLow = AXIS_THRESHOLD;
rangeHigh = 1;
/* stick started positive and went towards -1 */
} else if (axis->first > 0 && axis->last < axis->first){
rangeLow = -1;
rangeHigh = 0;
}
Global::debug(0) << "Set stick " << axis->stick << " axis " << axis->axis << " [" << rangeLow << ", " << rangeHigh << "]" << std::endl;
joystick->setCustomAxis(key, axis->stick, axis->axis, rangeLow, rangeHigh);
}
}
double ticks(double system){
return system * Global::ticksPerSecond(60);
}
bool done(){
return listener.isDone();
}
void run(){
vector<InputMap<int>::InputEvent> out = InputManager::getEvents(input, InputSource(true));
for (vector<InputMap<int>::InputEvent>::iterator it = out.begin(); it != out.end(); it++){
const InputMap<int>::InputEvent & event = *it;
if (event.enabled){
if (event.out == 0){
throw Exception::Return(__FILE__, __LINE__);
}
}
}
listener.choose();
}
void draw(const Graphics::Bitmap & buffer){
- Graphics::StretchedBitmap work(640, 480, buffer, Graphics::qualityFilterName(Configuration::getQualityFilter()));
+ Graphics::StretchedBitmap work(640, 480, buffer, Graphics::StretchedBitmap::NoClear, Graphics::qualityFilterName(Configuration::getQualityFilter()));
work.start();
menu.render(context, work);
const Font & font = Menu::menuFontParameter.current()->get();
Gui::RelativePoint start(0.2, -0.3);
Gui::RelativePoint end(0.85, 0.8);
int x = start.getX();
int y = start.getY();
// font.printfWrap(x, y - font.getHeight() * 2 - 5, Graphics::makeColor(255, 255, 255), work, end.getX() - start.getX(), "Press and hold a button", 0);
font.printf(x, y - 18 - 5, 18, 18, Graphics::makeColor(255, 255, 255), work, "Press and hold a button", 0);
work.translucent(0, 0, 0, 128).rectangleFill(x, y, end.getX(), end.getY(), Graphics::makeColor(0, 0, 0));
uint64_t now = System::currentMilliseconds();
const map<int, uint64_t> & presses = listener.getPresses();
for (map<int, uint64_t>::const_iterator it = presses.begin(); it != presses.end(); it++){
int button = it->first;
uint64_t time = it->second;
if (time > 0){
int delta = now - time;
if (delta > WAIT_TIME_MS){
delta = WAIT_TIME_MS;
}
/* this shouldn't happen... */
if (delta < 0){
delta = 0;
}
Graphics::Color color;
color = Graphics::makeColor((int)(255.0 * (double) delta / (WAIT_TIME_MS)),
0, 255);
if (button == listener.getButton()){
color = Graphics::makeColor(255, 255, 255);
}
ostringstream text;
text << name << ": " << button;
font.printf(x, y, color, work, text.str(), 0);
y += font.getHeight() + 5;
}
}
const vector<Axis> & axis = listener.getAllAxis();
for (vector<Axis>::const_iterator it = axis.begin(); it != axis.end(); it++){
const Axis & use = *it;
if (fabs(use.last - use.first) > AXIS_THRESHOLD){
int delta = now - use.lastMotion;
if (delta > WAIT_TIME_AXIS_MS){
delta = WAIT_TIME_AXIS_MS;
}
/* this shouldn't happen... */
if (delta < 0){
delta = 0;
}
Graphics::Color color;
color = Graphics::makeColor((int)(255.0 * (double) delta / (WAIT_TIME_AXIS_MS)),
0, 255);
if (&use == listener.getChosenAxis()){
color = Graphics::makeColor(255, 255, 255);
}
ostringstream text;
text << name << ": stick " << use.stick << " axis " << use.axis;
if (use.last > use.first){
text << " +";
} else {
text << " -";
}
font.printf(x, y, 18, 18, color, work, text.str(), 0);
y += font.getHeight() + 5;
}
}
work.finish();
}
void wait(){
while (listener.anyPressed()){
InputManager::poll();
Util::rest(1);
}
}
};
Global::debug(1) << "Set button " << getName() << std::endl;
SetButton set(context, menu, name, joystick);
try{
Util::standardLoop(set, set);
set.setButton(key);
} catch (const Exception::Return & quit){
}
set.wait();
}
};
menu.addOption(new JoystickButton(menu, box, joystick, "Up", Joystick::Up));
menu.addOption(new JoystickButton(menu, box, joystick, "Down", Joystick::Down));
menu.addOption(new JoystickButton(menu, box, joystick, "Left", Joystick::Left));
menu.addOption(new JoystickButton(menu, box, joystick, "Right", Joystick::Right));
menu.addOption(new JoystickButton(menu, box, joystick, "Button1", Joystick::Button1));
menu.addOption(new JoystickButton(menu, box, joystick, "Button2", Joystick::Button2));
menu.addOption(new JoystickButton(menu, box, joystick, "Button3", Joystick::Button3));
menu.addOption(new JoystickButton(menu, box, joystick, "Button4", Joystick::Button4));
menu.addOption(new JoystickButton(menu, box, joystick, "Button5", Joystick::Button5));
menu.addOption(new JoystickButton(menu, box, joystick, "Button6", Joystick::Button6));
menu.addOption(new JoystickButton(menu, box, joystick, "Start", Joystick::Start));
menu.addOption(new JoystickButton(menu, box, joystick, "Quit", Joystick::Quit));
try {
menu.run(context);
} catch (const Exception::Return & ignore){
} catch (const Menu::MenuException & ex){
}
}
void OptionJoystick::run(const Menu::Context & context){
class JoystickOption: public MenuOption {
public:
JoystickOption(const Gui::ContextBox & parent, int id, const Util::ReferenceCount<Joystick> & joystick):
MenuOption(parent, NULL),
joystick(joystick),
id(id){
ostringstream out;
out << "Joystick " << (id + 1);
setText(out.str());
setInfoText(joystick->getName());
}
const Util::ReferenceCount<Joystick> joystick;
const int id;
virtual void logic(){
}
virtual void run(const ::Menu::Context & context){
runJoystickMenu(id, joystick, context);
/*
JoystickLogicDraw mainLoop(id, joystick, context);
Util::standardLoop(mainLoop, mainLoop);
*/
throw ::Menu::MenuException(__FILE__, __LINE__);
}
};
Util::NewReferenceCount<Menu::DefaultRenderer> renderer;
Menu::Menu menu(renderer.convert<Menu::Renderer>());
/*
Util::ReferenceCount<Menu::FontInfo> info(new Menu::RelativeFontInfo(Global::DEFAULT_FONT, 24, 24));
temp.setFont(info);
*/
Gui::ContextBox & box = renderer->getBox();
box.setListType(ContextBox::Normal);
map<int, Util::ReferenceCount<Joystick> > joysticks = InputManager::getJoysticks();
for (map<int, Util::ReferenceCount<Joystick> >::iterator it = joysticks.begin(); it != joysticks.end(); it++){
menu.addOption(new JoystickOption(box, it->first, it->second));
}
if (joysticks.size() == 0){
menu.addOption(new OptionDummy(box, "No joysticks found!"));
}
try {
menu.run(context);
} catch (const Exception::Return & ignore){
} catch (const Menu::MenuException & ex){
}
}
OptionJoystick::~OptionJoystick(){
}

File Metadata

Mime Type
text/x-diff
Expires
Thu, Jun 18, 12:15 AM (1 w, 4 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
70362
Default Alt Text
(304 KB)

Event Timeline