/* 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; };