/*
Copyright (C) 2009 Facundo DomÃnguez
This file is part of Spacejunk.
Spacejunk is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Foobar is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar. If not, see .
*/
#include "graphic.h"
#include "global.h"
#include
#include "resourcemanager.h"
#include
#include
#include "clipping.h"
#include "gem-uta.h"
using namespace std;
int Graphic::toScreenx(int x){ return int(round(Graphic::screenx+Graphic::getBaseScaleFactorX()*x)); }
int Graphic::toScreeny(int y){ return int(round(Graphic::screeny+Graphic::getBaseScaleFactorY()*y)); }
int Graphic::toScreenw(int w){ return int(round(Graphic::getBaseScaleFactorX()*w)); }
int Graphic::toScreenh(int h){ return int(round(Graphic::getBaseScaleFactorY()*h)); }
int Graphic::fromScreenx(int x){ return int(round((x-screenx)/getBaseScaleFactorX())); }
int Graphic::fromScreeny(int y){ return int(round((y-screeny)/getBaseScaleFactorY())); }
class UpdateSystemUta {
private:
GemUta * uta;
GemUta * newrects;
public:
int UPDW;
int UPDH;
UpdateSystemUta() {
UPDW=640;
UPDH=480;
uta = NULL;
newrects = NULL;
}
void init(int x,int y,int w,int h) {
UPDW=w;
UPDH=h;
uta = gem_uta_new_coords (x, y, UPDW, UPDH);
newrects = gem_uta_new_coords (x, y, UPDW, UPDH);
}
~UpdateSystemUta() {
if (uta!=NULL)
gem_uta_free (uta);
if (newrects!=NULL)
gem_uta_free (newrects);
}
void clear() {
gem_uta_clear (uta);
gem_uta_clear (newrects);
}
void commit() {
int num_rects;
gem_uta_union(uta,newrects);
SDL_Rect *rects = gem_uta_get_rects (uta, &num_rects, 0, 0, UPDW, UPDH);
SDL_UpdateRects (Graphic::getScreen(), num_rects, rects);
free (rects);
gem_uta_copy(uta,newrects);
gem_uta_clear(newrects);
}
void update(const SDL_Rect & r) {
GemRect grect={r.x,r.x + r.w - 1,r.y,r.y + r.h-1};
gem_uta_add_rect (newrects, &grect);
}
};
UpdateSystemUta upd;
void Graphic::updateRect(SDL_Surface * s,SDL_Rect * r) {
if (!r->w || !r->h) return;
SDL_Rect rs = {toScreenx(r->x),toScreeny(r->y),toScreenw(r->w),toScreenh(r->h)};
if (clipRectangle(&rs,screenx,screeny,screenw,screenh))
upd.update(rs);
}
void Graphic::mUpdateRect(SDL_Surface * s,SDL_Rect * r) {
if (!r->w || !r->h) return;
if (clipRectangle(r,screenx,screeny,screenw,screenh)) {
upd.update(*r);
}
}
void Graphic::fullUpdate() {
fullupdate=true;
};
bool Graphic::alwaysupdate=false;
void Graphic::setAlwaysUpdate(bool always) {
alwaysupdate=(getScreen()->flags & SDL_DOUBLEBUF)!=SDL_DOUBLEBUF && always;
};
Uint32 Graphic::sdlflags= /*SDL_HWPALETTE | SDL_HWSURFACE | SDL_DOUBLEBUF*/ 0;
Vector2d Graphic::screencenter;
int Graphic::screenx;
int Graphic::screeny;
int Graphic::screenw;
int Graphic::screenh;
float inline min(float x,float y) {
return xflags & SDL_DOUBLEBUF)!=SDL_DOUBLEBUF && on);
}
SDL_Surface * Graphic::screen=NULL;
float Graphic::getBaseScaleFactorX(){ return base_scalefactor; };
float Graphic::getBaseScaleFactorY(){ return base_scalefactor; };
void Graphic::setScaleFactors() {
if (getScreen()->w!=LOGICAL_WIDTH || getScreen()->h!=LOGICAL_HEIGHT)
base_scalefactor=min(getScreen()->w/float(LOGICAL_WIDTH),getScreen()->h/float(LOGICAL_HEIGHT));
else
base_scalefactor=1.0f;
if (getScreen()->w>int(base_scalefactor*LOGICAL_WIDTH))
screenw = int(base_scalefactor*LOGICAL_WIDTH);
else
screenw = getScreen()->w;
if (getScreen()->h>int(base_scalefactor*LOGICAL_HEIGHT))
screenh = int(base_scalefactor*LOGICAL_HEIGHT);
else
screenh = getScreen()->h;
screenx = (getScreen()->w-screenw)/2;
screeny = (getScreen()->h-screenh)/2;
}
void Graphic::set_video_mode(int w,int h) {
screen = SDL_SetVideoMode(w, h, 8, sdlflags|SDL_HWPALETTE|SDL_ANYFORMAT);
if (screen == NULL) {
CHERROR<<"Graphic: Unable to set video mode "<flags & SDL_HWPALETTE)
CHERROR<<"Graphic: hwpalette set\n";
else
CHERROR<<"Graphic: hwpalette not set\n";
if (screen->format->palette)
CHERROR<<"Graphic: there's palette"<w,screen->h);
sge_Update_OFF();
CHERROR<<"Graphic: Screen is at "<w<<"x"<h<<"@"<format->BitsPerPixel)<format->palette) {
pal=RMPALETTERef(id);
if (!SDL_SetPalette(getScreen(), flags, pal->colors, 0,pal->ncolors)) {
CHERROR<<"Unable to set "<format->palette && pal) {
if (!SDL_SetColors(getScreen(), pal->colors, 0,pal->ncolors)) {
CHERROR<<"Unable to set main palette: "<format->palette : NULL;
}
void Graphic::flip(bool full) {
if ((getScreen()->flags & SDL_DOUBLEBUF) ==SDL_DOUBLEBUF || full || fullupdate || !updates) {
SDL_Flip(getScreen());
fullupdate=false;
} else {
upd.commit();
}
}
void Graphic::setFullscreenFlag(bool full) {
if (full)
sdlflags |= SDL_FULLSCREEN;
else
sdlflags &= ~SDL_FULLSCREEN;
}
void Graphic::toggleFullscreen() {
sdlflags ^= SDL_FULLSCREEN;
set_video_mode(getScreen()->w,getScreen()->h);
}
void Graphic::clearScreen(Uint32 color) {
SDL_Rect r={0,0,getScreen()->w,getScreen()->h};
SDL_FillRect(getScreen(),&r,color);
}
Graphic::Graphic() : cache(NULL),images(Ref(),0),bmp() {
ROT_FLAG=0;
};
int Graphic::getWidth() const {
return int(bmp->w/getBaseScaleFactorX());
}
int Graphic::getHeight() const {
return int(bmp->h/getBaseScaleFactorY());
}
//#define ROT_FLAG 0
Graphic::Graphic(const std::string & id,bool usecache,bool isopaque,bool prerotate) : cache(NULL),images(Ref(),0) {
if (!id.length()) {
return;
}
images=getImageSet(id);
if (!images.second) {
fprintf(stderr,"Unable to load graphic %s: %s\n",id.c_str(),SDL_GetError());
exit(1);
}
if (isopaque) { // Clear color key
for (int i=0;i(SDL_DisplayFormat(&*bmp));
cache_rect.x=cache_rect.y=0;
cache_rect.w=cache->w;
cache_rect.h=cache->h;
hotspot.x=getWidth()/2;
hotspot.y=getHeight()/2;
if (!(&*cache)) {
CHERROR<<"Graphic error: Could not get a cache surface."<w;
cache_rect.h=bmp->h;
hotspot=Vector2d();
}
if (getScreen()->format->palette || bmp->w*bmp->h>64*64) ROT_FLAG=0;
else ROT_FLAG=SGE_TAA;
if (prerotate) {
hotspot.x=getWidth()/2;
hotspot.y=getHeight()/2;
rot=RMROTRef(id);
}
}
Graphic::~Graphic() {};
void Graphic::draw(Vector2d pos,real zoom,real angle) {
draw(int(pos.x),int(pos.y),zoom,angle);
}
void Graphic::draw(int x,int y,SDL_Rect * r) {
SDL_Rect trec={toScreenx(x),toScreeny(y),0,0};
SDL_Surface * s= cache_zoom ? &*cache : &*bmp;
SDL_Surface * screen=getScreen();
if (r) {
trec.w=toScreenw(r->w);
trec.h=toScreenh(r->h);
} else {
trec.w=cache_rect.w;
trec.h=cache_rect.h;
}
SDL_BlitSurface(s,r,screen,&trec);
if (updates)
mUpdateRect(screen,&trec);
};
void Graphic::draw(int x,int y,real zoom,real angle) {
if (!&*bmp) return;
SDL_Surface * screen=getScreen();
if (!cache_zoom) {
if (&*rot) {
x -= int(zoom*hotspot.x);
y -= int(zoom*hotspot.y);
rot->draw(x,y,angle,zoom);
} else {
x -= int(hotspot.x);
y -= int(hotspot.y);
SDL_Rect srcrec={0,0,bmp->w,bmp->h};
SDL_Rect trec={toScreenx(x),toScreeny(y),bmp->w,bmp->h};
SDL_BlitSurface(&*bmp,&srcrec,screen,&trec);
if (updates)
mUpdateRect(screen,&trec);
}
return;
}
x -= int(zoom*hotspot.x);
y -= int(zoom*hotspot.y);
if (0<=x+zoom*getWidth() && xEPS || fabsf(cache_zoom-zoom)>EPS) {
// Recompute cache if necesary.
cache_angle=angle;
cache_zoom=zoom;
SDL_FillRect(&*cache,&cache_rect,SDL_MapRGB(bmp->format,0,0, 0));
real hx = hotspot.x*Graphic::getBaseScaleFactorX();
real hy = hotspot.y*Graphic::getBaseScaleFactorY();
sge_transform(&*bmp,&*cache,cache_angle,zoom,zoom,
Uint16(hx),Uint16(hy),int(zoom*hx), int(zoom*hy),ROT_FLAG);
cache_rect.w = int(zoom*bmp->w);
cache_rect.h = int(zoom*bmp->h);
cache_rect.x = 0;
cache_rect.y = 0;
}
// Blit cache.
SDL_Rect trec={toScreenx(x),toScreeny(y),0,0};
SDL_BlitSurface(&*cache,&cache_rect,screen,&trec);
if (updates)
mUpdateRect(screen,&trec);
}
}
const real SQRT2 = sqrt(2);
void Graphic::drawCircle(Vector2d center,int radius,Uint32 color) {
SDL_Surface* s=getScreen();
float rx=radius*getBaseScaleFactorX();
float ry=radius*getBaseScaleFactorY();
int cx = toScreenx(int(center.x));
int cy = toScreeny(int(center.y));
sge_Ellipse(s,cx,cy,int(rx),int(ry),color);
if (updates) {
real dx=rx/SQRT2;
real dy=ry/SQRT2;
SDL_Rect trec={Sint16(cx-rx),Sint16(cy-ry),int(2*rx),int(2*ry)};
SDL_Rect trec1={trec.x,Sint16(cy-dy-1),Sint16(rx-dx+3),Sint16(2*dy+3)};
SDL_Rect trec2=trec1;
mUpdateRect(s,&trec1);
trec2.x=Sint16(cx+dx);
mUpdateRect(s,&trec2);
SDL_Rect trec3={Sint16(cx-dx),trec.y,Sint16(2*dx+3),Sint16(ry-dy+4)};
SDL_Rect trec4=trec3;
mUpdateRect(s,&trec3);
trec4.y=Sint16(cy+dy-1);
mUpdateRect(s,&trec4);
}
}
void Graphic::drawSurroundingCircle(Vector2d center,real zoom,Uint32 color) {
drawCircle(center,int(zoom*getWidth()/2+2),color);
}
void Graphic::drawSurroundingCross(Vector2d center,real zoom,Uint32 color) {
float lx=(getWidth()*zoom/2+12)*Graphic::getBaseScaleFactorX();
float ly=(getWidth()*zoom/2+12)*Graphic::getBaseScaleFactorY();
float slx = 6*Graphic::getBaseScaleFactorX();
float sly = 6*Graphic::getBaseScaleFactorY();
SDL_Surface *s=getScreen();
center.x = toScreenx(int(center.x));
center.y = toScreeny(int(center.y));
sge_Line(s,Sint16(center.x-lx),Sint16(center.y),Sint16(center.x-(lx-slx)),Sint16(center.y),color);
sge_Line(s,Sint16(center.x+lx),Sint16(center.y),Sint16(center.x+(lx-slx)),Sint16(center.y),color);
sge_Line(s,Sint16(center.x),Sint16(center.y-ly),Sint16(center.x),Sint16(center.y-(ly-sly)),color);
sge_Line(s,Sint16(center.x),Sint16(center.y+ly),Sint16(center.x),Sint16(center.y+(ly-sly)),color);
if (updates) {
SDL_Rect trec={Sint16(center.x-lx),Sint16(center.y),Sint16(2*lx+2),1};
mUpdateRect(s,&trec);
trec.x=Sint16(center.x);
trec.y=Sint16(center.y-ly);
trec.w=1;
trec.h=Sint16(2*ly+2);
mUpdateRect(s,&trec);
}
}
void Graphic::drawArrow(int x1,int y1,int x2,int y2,Uint32 color) {
SDL_Surface *s=getScreen();
x1 = toScreenx(x1);
y1 = toScreeny(y1);
x2 = toScreenx(x2);
y2 = toScreeny(y2);
sge_Line(s,x1,y1,x2,y2,color);
if (updates) {
SDL_Rect trec;
bbFromLine(&trec,x1,y1,x2,y2);
mUpdateRect(s,&trec);
}
}
void Graphic::drawArrow(int x1,int y1,int x2,int y2) {
SDL_Surface *s=getScreen();
x1 = toScreenx(x1);
y1 = toScreeny(y1);
x2 = toScreenx(x2);
y2 = toScreeny(y2);
sge_Line(s,x1,y1,x2,y2,SDL_MapRGB(s->format, 128,0,0));
if (updates) {
SDL_Rect trec;
bbFromLine(&trec,x1,y1,x2,y2);
mUpdateRect(s,&trec);
}
}
void Graphic::drawRect(Sint16 x1,Sint16 y1,Sint16 x2,Sint16 y2,Uint32 color) {
SDL_Surface * s=getScreen();
x1 = toScreenx(x1);
y1 = toScreeny(y1);
x2 = toScreenx(x2);
y2 = toScreeny(y2);
sge_Rect(s,x1,y1,x2,y2,color);
if (updates) {
SDL_Rect trec;
bbFromLine(&trec,x1,y1,x2,y2);
if (trec.w<=2 || trec.h<=2)
mUpdateRect(s,&trec);
else {
SDL_Rect r={trec.x,trec.y,1,trec.h};
mUpdateRect(s,&r);
r.x+=trec.w-1;
mUpdateRect(s,&r);
r.x=trec.x+1;
r.w=trec.w-2;
r.h=1;
mUpdateRect(s,&r);
r.y+=trec.h-1;
mUpdateRect(s,&r);
}
}
};
void Graphic::drawFilledRect(Sint16 x1,Sint16 y1,Sint16 x2,Sint16 y2,Uint32 color) {
SDL_Surface * s=getScreen();
SDL_Rect trec;
x1 = toScreenx(x1);
y1 = toScreeny(y1);
x2 = toScreenx(x2);
y2 = toScreeny(y2);
bbFromLine(&trec,x1,y1,x2,y2);
SDL_FillRect(s,&trec,color);
if (updates)
mUpdateRect(s,&trec);
};
void Graphic::setFrame(int i) {
bmp = (i>=getImageCount()) ? RMIMGRef() : (&*images.first)[i];
cache_angle++;
}
/*
* Return the pixel value at (x, y)
* NOTE: The surface must be locked before calling this!
*/
Uint32 getpixel(SDL_Surface *surface, int x, int y) {
int bpp = surface->format->BytesPerPixel;
/* Here p is the address to the pixel we want to retrieve */
Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
switch (bpp) {
case 1:
return *p;
case 2:
return *(Uint16 *)p;
case 3:
if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
return p[0] << 16 | p[1] << 8 | p[2];
else
return p[0] | p[1] << 8 | p[2] << 16;
case 4:
return *(Uint32 *)p;
default:
return 0; /* shouldn't happen, but avoids warnings */
}
}
bool SDLinsideImage(SDL_Surface * surface,int x,int y) {
x = Graphic::toScreenx(x);
y = Graphic::toScreeny(y);
if (!surface || x<0 || y<0 || x>=(int)surface->w || y>= (int)surface->h) return false;
SDL_LockSurface( surface );
Uint32 color=getpixel(surface,x,y);
SDL_UnlockSurface( surface );
if (surface->format->palette && surface->format->palette->colors) {
SDL_Color c=surface->format->palette->colors[color];
color=SDL_MapRGB(surface->format,c.r,c.g,c.b);
}
if (surface->flags & SDL_SRCCOLORKEY)
return color!=surface->format->colorkey;
else return color!=SDL_MapRGB(surface->format,0,0,0);
};
bool Graphic::insideImage(int x,int y) const {
return SDLinsideImage(&*bmp,x,y);
}
GraphicSeq::GraphicSeq(Graphic * g) : seqg(g) {
current=sstart=0;
send=0;
sloop=false;
hold=false;
sstep=1;
framestep=30;
remaining=framestep;
};
void GraphicSeq::setFrameSequence(int start,int end,bool loop,bool forward) {
current=(startgetImageCount()) ? start : seqg->getImageCount()-1;
sstart=current;
send=(endgetImageCount()) ? end : seqg->getImageCount()-1;
sloop=loop;
sstep=forward ? 1 : -1;
}
void GraphicSeq::stepSeq(int delta) {
if (current==send && !sloop) {
if (!hold)
seqg->setFrame(seqg->getImageCount());
return;
}
delta+=remaining;
while (delta>=framestep) {
delta-=framestep;
nextFrame();
}
remaining=delta;
}
void GraphicSeq::nextFrame() {
if (current==send && sloop) {
sstep*=-1;
int temp=sstart;
sstart=send;
send=temp;
}
if (current!=send) {
if (current+sstep<0)
current=seqg->getImageCount()-1;
else if ((int(current))+sstep==int(seqg->getImageCount()))
current=0;
else
current+=sstep;
seqg->setFrame(current);
} else if (!hold)
seqg->setFrame(seqg->getImageCount());
}
SDL_Surface * Graphic::createSurface(Uint16 w,Uint16 h) {
SDL_Surface * s=SDL_CreateRGBSurface(SDL_SWSURFACE,w,h,
screen->format->BitsPerPixel,
screen->format->Rmask,
screen->format->Bmask,
screen->format->Gmask,
screen->format->Amask);
if (!s) {
CHERROR<<"Graphic: Error requesting temporal surface: "<format, 0x00, 0x00, 0x00));
SDL_Surface * t=SDL_DisplayFormat(s);
if (!t) {
CHERROR<<"Graphic: Error converting temporal surface to display format: "<::getInstance()->create(id);
img=(&*imgs.first)[0];
updated=new bool[NUM_ROT_FRAMES];
memset(updated,0,sizeof(bool)*NUM_ROT_FRAMES);
rot=new SDL_Surface*[NUM_ROT_FRAMES];
for (int i=0;iw,img->h);
if (Graphic::getScreen()->format->palette)
ROT_FLAG=0;
else
ROT_FLAG=SGE_TAA;
zoom=0;
};
void ZoomRotator::draw(int x,int y,float angle,float zoom) {
x = Graphic::toScreenx(x);
y = Graphic::toScreeny(y);
if (!(Graphic::screenx<=x+zoom*img->w && xh && yzoom-zoom)>EPS) {
memset(updated,0,sizeof(bool)*NUM_ROT_FRAMESX*NUM_ROT_FRAMESY);
this->zoom=zoom;
}
int i=int(angle*NUM_ROT_FRAMESX*NUM_ROT_FRAMESY/360.0);
SDL_Rect clip={0,0,img->w,img->h};
if (!updated[i]) {
Uint16 hotspotx=img->w>>1;
Uint16 hotspoty=img->h>>1;
SDL_FillRect(rot[i],NULL,SDL_MapRGB(rot[i]->format,0,0, 0));
sge_transform(&*img,rot[i],360.0*i/(NUM_ROT_FRAMESX*NUM_ROT_FRAMESY),zoom,zoom,hotspotx,hotspoty,
clip.x+Uint16(zoom*hotspotx),clip.y+Uint16(zoom*hotspoty),ROT_FLAG);
updated[i]=true;
}
clip.w=Uint16(clip.w*zoom);
clip.h=Uint16(clip.h*zoom);
SDL_Rect trec={x,y,clip.w,clip.h};
SDL_BlitSurface(rot[i],&clip,Graphic::getScreen(),&trec);
if (Graphic::isUpdating())
Graphic::mUpdateRect(Graphic::getScreen(),&trec);
};
ZoomRotator::~ZoomRotator() {
for (int i=0;i");
img_paths.insert(imagepath);
if (id) *id=imagepath;
PARSEend;
return p;
}
SDL_Surface * ImageLoader::create(const string & path) {
if (img_paths.find(path)==img_paths.end()) {
CHERROR<<"ResourceManager error: Image file "<format, 0x00, 0x00, 0x00));
SDL_Surface * t2=SDL_DisplayFormat(s);
SDL_FreeSurface(s);
if (!t2) {
CHERROR<<"ResourceManager error: Image "<<(GetDataPath()+path)<<" could not be converted to screen format."<w),Graphic::toScreenh(t2->h));
SDL_FillRect(t3,NULL,SDL_MapRGB(t3->format,0,0,0));
sge_transform(t2,t3,0.0f, Graphic::getBaseScaleFactorX(),Graphic::getBaseScaleFactorY(),0,0,0,0,
(Uint8)(t3->format->palette ? 0:SGE_TAA));
SDL_FreeSurface(t2);
if (!t3) {
CHERROR<<"ResourceManager error: Image "< * vstring=NULL;
PARSEbegin(Parser,p);
LS("");
MANYbegin
TRY(S("");
if (img_sets[name]) {
CHERROR<<"Image resource duplicated: "<();
MANYbegin
TRY(S("::getInstance()->parse,&imagepath);
vstring->push_back(imagepath);
MANYend
S("");
WS;
img_sets[name]=vstring;
vstring=NULL;
MANYend
S("");
PARSEend;
if (vstring)
delete vstring;
return p;
}
std::pair[ ,int> ImageSetLoader::create(const string & id) {
vector * set=img_sets[id];
if (!set || set->size()==0) {
CHERROR<<"ResourceManager error: Resource image set "<,int> buf;
buf.first = Ref(new RMIMGRef[set->size()]);
buf.second = set->size();
int counter=0;
for (vector::iterator i=set->begin();i!=set->end();i++)
(&*buf.first)[counter++]=Resource(*i);
return buf;
};
ImageSetLoader::~ImageSetLoader() {
for (std::map*>::iterator i=img_sets.begin();
i!=img_sets.end();i++) delete i->second;
};
ZoomRotator * RotateImageLoader::create(const std::string & id) {
return new ZoomRotator(id);
};
void RotateImageLoader::free(ZoomRotator * p) {
delete p;
};
FontRenderer * FontLoader::create(const std::string & name) {
string path=font_paths[name];
if (!path.length()) {
CHERROR<<"ResourceManager error: Font "<format,0,0,0),0.0f,
Graphic::getBaseScaleFactorX(),Graphic::getBaseScaleFactorY(),
(Uint8)SGE_TAA);
SDL_FreeSurface(temp);
if (!ss) {
CHERROR<<"ResourceManager error: Font "<<(GetDataPath()+path)<<" could not be scaled."<"));
MANYbegin;
TRY(S("");
font_paths[name]=path;
MANYend;
S("");
PARSEend;
return p;
};
]