/*** Copyright (c) 2006-2012 LOVE Development Team** This software is provided 'as-is', without any express or implied* warranty. In no event will the authors be held liable for any damages* arising from the use of this software.** Permission is granted to anyone to use this software for any purpose,* including commercial applications, and to alter it and redistribute it* freely, subject to the following restrictions:** 1. The origin of this software must not be misrepresented; you must not* claim that you wrote the original software. If you use this software* in a product, an acknowledgment in the product documentation would be* appreciated but is not required.* 2. Altered source versions must be plainly marked as such, and must not be* misrepresented as being the original software.* 3. This notice may not be removed or altered from any source distribution.**/#include <common/config.h>#include <common/math.h>#include <common/Vector.h>#include "Graphics.h"#include <window/sdl/Window.h>#include <vector>#include <sstream>#include <algorithm>#include <iterator>namespacelove{namespacegraphics{namespaceopengl{Graphics::Graphics():currentFont(0),currentImageFilter(),lineStyle(LINE_SMOOTH),lineWidth(1),matrixLimit(0),userMatrices(0){currentWindow=love::window::sdl::Window::getSingleton();resetBoundTexture();}Graphics::~Graphics(){if(currentFont!=0)currentFont->release();currentWindow->release();}constchar*Graphics::getName()const{return"love.graphics.opengl";}boolGraphics::checkMode(intwidth,intheight,boolfullscreen){returncurrentWindow->checkWindowSize(width,height,fullscreen);}DisplayStateGraphics::saveState(){DisplayStates;s.color=getColor();s.backgroundColor=getBackgroundColor();//store modes hereGLintmode;//get blend modeglGetIntegerv(GL_BLEND_DST,&mode);//following syntax seems better than if-else every times.blendMode=(mode==GL_ONE)?Graphics::BLEND_ADDITIVE:Graphics::BLEND_ALPHA;//get color modeglGetTexEnviv(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,&mode);s.colorMode=(mode==GL_MODULATE)?Graphics::COLOR_MODULATE:Graphics::COLOR_REPLACE;//get line styles.lineStyle=lineStyle;//get the point sizeglGetFloatv(GL_POINT_SIZE,&s.pointSize);//get point styles.pointStyle=(glIsEnabled(GL_POINT_SMOOTH)==GL_TRUE)?Graphics::POINT_SMOOTH:Graphics::POINT_ROUGH;//get scissor statuss.scissor=(glIsEnabled(GL_SCISSOR_TEST)==GL_TRUE);//do we have scissor, if so, store the boxif(s.scissor)glGetIntegerv(GL_SCISSOR_BOX,s.scissorBox);returns;}voidGraphics::restoreState(constDisplayState&s){setColor(s.color);setBackgroundColor(s.backgroundColor);setBlendMode(s.blendMode);setColorMode(s.colorMode);setLine(lineWidth,s.lineStyle);setPoint(s.pointSize,s.pointStyle);if(s.scissor)setScissor(s.scissorBox[0],s.scissorBox[1],s.scissorBox[2],s.scissorBox[3]);elsesetScissor();}boolGraphics::setMode(intwidth,intheight,boolfullscreen,boolvsync,intfsaa){// This operation destroys the OpenGL context, so// we must save the state.DisplayStatetempState;if(isCreated())tempState=saveState();// Unlad all volatile objects. These must be reloaded after// the display mode change.Volatile::unloadAll();currentWindow->setWindow(width,height,fullscreen,vsync,fsaa);// Okay, setup OpenGL.// Enable blendingglEnable(GL_BLEND);// "Normal" blendingglBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);// Enable line/point smoothing.setLineStyle(LINE_SMOOTH);glEnable(GL_POINT_SMOOTH);glHint(GL_POINT_SMOOTH_HINT,GL_NICEST);// Enable texturesglEnable(GL_TEXTURE_2D);// Set the viewport to top-left cornerglViewport(0,0,width,height);// Reset the projection matrixglMatrixMode(GL_PROJECTION);glLoadIdentity();// Set up orthographic view (no depth)glOrtho(0.0,width,height,0.0,-1.0,1.0);// Reset modelview matrixglMatrixMode(GL_MODELVIEW);glLoadIdentity();// Set pixel row alignmentglPixelStorei(GL_UNPACK_ALIGNMENT,2);// Reload all volatile objects.if(!Volatile::loadAll())std::cerr<<"Could not reload all volatile objects."<<std::endl;// Restore the display state.restoreState(tempState);// Get the maximum number of matrices// subtract a few to give the engine some room.glGetIntegerv(GL_MAX_MODELVIEW_STACK_DEPTH,&matrixLimit);matrixLimit-=5;returntrue;}voidGraphics::getMode(int&width,int&height,bool&fullscreen,bool&vsync,int&fsaa){currentWindow->getWindow(width,height,fullscreen,vsync,fsaa);}boolGraphics::toggleFullscreen(){intwidth,height,fsaa;boolfullscreen,vsync;currentWindow->getWindow(width,height,fullscreen,vsync,fsaa);returncurrentWindow->setWindow(width,height,!fullscreen,vsync,fsaa);}voidGraphics::reset(){DisplayStates;discardStencil();Canvas::bindDefaultCanvas();restoreState(s);}voidGraphics::clear(){glClear(GL_COLOR_BUFFER_BIT);glLoadIdentity();PixelEffect::detach();}voidGraphics::present(){currentWindow->swapBuffers();}voidGraphics::setIcon(Image*image){currentWindow->setIcon(image->getData());}voidGraphics::setCaption(constchar*caption){std::stringtitle(caption);currentWindow->setWindowTitle(title);}intGraphics::getCaption(lua_State*L){std::stringtitle=currentWindow->getWindowTitle();lua_pushstring(L,title.c_str());return1;}intGraphics::getWidth(){returncurrentWindow->getWidth();}intGraphics::getHeight(){returncurrentWindow->getHeight();}intGraphics::getRenderHeight(){if(Canvas::current)returnCanvas::current->getHeight();returngetHeight();}boolGraphics::isCreated(){returncurrentWindow->isCreated();}intGraphics::getModes(lua_State*L){intn;love::window::Window::WindowSize**modes=currentWindow->getFullscreenSizes(n);if(modes==0)return0;lua_newtable(L);for(inti=0;i<n;i++){lua_pushinteger(L,i+1);lua_newtable(L);// Inner table attribs.lua_pushstring(L,"width");lua_pushinteger(L,modes[i]->width);lua_settable(L,-3);lua_pushstring(L,"height");lua_pushinteger(L,modes[i]->height);lua_settable(L,-3);// Inner table attribs end.lua_settable(L,-3);deletemodes[i];}delete[]modes;return1;}voidGraphics::setScissor(intx,inty,intwidth,intheight){glEnable(GL_SCISSOR_TEST);glScissor(x,getRenderHeight()-(y+height),width,height);// Compensates for the fact that our y-coordinate is reverse of OpenGLs.}voidGraphics::setScissor(){glDisable(GL_SCISSOR_TEST);}intGraphics::getScissor(lua_State*L){if(glIsEnabled(GL_SCISSOR_TEST)==GL_FALSE)return0;GLintscissor[4];glGetIntegerv(GL_SCISSOR_BOX,scissor);lua_pushnumber(L,scissor[0]);lua_pushnumber(L,getRenderHeight()-(scissor[1]+scissor[3]));// Compensates for the fact that our y-coordinate is reverse of OpenGLs.lua_pushnumber(L,scissor[2]);lua_pushnumber(L,scissor[3]);return4;}voidGraphics::defineStencil(){glColorMask(GL_FALSE,GL_FALSE,GL_FALSE,GL_FALSE);glEnable(GL_STENCIL_TEST);glClear(GL_STENCIL_BUFFER_BIT);glStencilFunc(GL_ALWAYS,1,1);glStencilOp(GL_KEEP,GL_KEEP,GL_REPLACE);}voidGraphics::useStencil(boolinvert){glStencilFunc(GL_EQUAL,(int)(!invert),1);// invert ? 0 : 1glStencilOp(GL_KEEP,GL_KEEP,GL_KEEP);glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);}voidGraphics::discardStencil(){glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);glDisable(GL_STENCIL_TEST);}Image*Graphics::newImage(love::image::ImageData*data){// Create the image.Image*image=newImage(data);boolsuccess;try{success=image->load();}catch(love::Exception&e){image->release();throwlove::Exception(e.what());}if(!success){image->release();return0;}image->setFilter(currentImageFilter);returnimage;}Quad*Graphics::newQuad(floatx,floaty,floatw,floath,floatsw,floatsh){Quad::Viewportv;v.x=x;v.y=y;v.w=w;v.h=h;returnnewQuad(v,sw,sh);}Font*Graphics::newFont(love::font::Rasterizer*r,constImage::Filter&filter){Font*font=newFont(r,filter);// Load it and check for errors.if(!font){deletefont;return0;}returnfont;}SpriteBatch*Graphics::newSpriteBatch(Image*image,intsize,intusage){SpriteBatch*t=NULL;try{t=newSpriteBatch(image,size,usage);}catch(love::Exception&e){if(t)deletet;throwe;}returnt;}ParticleSystem*Graphics::newParticleSystem(Image*image,intsize){returnnewParticleSystem(image,size);}Canvas*Graphics::newCanvas(intwidth,intheight){Canvas*canvas=newCanvas(width,height);GLenumerr=canvas->getStatus();// everything ok, reaturn canvas (early out)if(err==GL_FRAMEBUFFER_COMPLETE)returncanvas;// create error messagestd::stringstreamerror_string;error_string<<"Cannot create canvas: ";switch(err){caseGL_FRAMEBUFFER_UNSUPPORTED:error_string<<"Not supported by your OpenGL implementation.";break;// remaining error codes are highly unlikely:caseGL_FRAMEBUFFER_UNDEFINED:caseGL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:caseGL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:caseGL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:caseGL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:caseGL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:error_string<<"Error in implementation. Possible fix: Make canvas width and height powers of two.";break;default:// my intel hda card wrongly returns 0 to glCheckFramebufferStatus() but sets// no error flag. I think it meant to return GL_FRAMEBUFFER_UNSUPPORTED, but who// knows.if(glGetError()==GL_NO_ERROR)error_string<<"May not be supported by your OpenGL implementation.";// the remaining error is an indication of a serious fuckup since it should// only be returned if glCheckFramebufferStatus() was called with the wrong// arguments.elseerror_string<<"Cannot create canvas: Aliens did it (OpenGL error code: "<<glGetError()<<")";}canvas->release();throwException(error_string.str().c_str());returnNULL;// never reached}PixelEffect*Graphics::newPixelEffect(conststd::string&code){PixelEffect*effect=NULL;try{effect=newPixelEffect(code);}catch(love::Exception&e){if(effect)deleteeffect;throw(e);}returneffect;}voidGraphics::setColor(constColor&c){glColor4ubv(&c.r);}ColorGraphics::getColor(){floatc[4];glGetFloatv(GL_CURRENT_COLOR,c);Colort;t.r=(unsignedchar)(255.0f*c[0]);t.g=(unsignedchar)(255.0f*c[1]);t.b=(unsignedchar)(255.0f*c[2]);t.a=(unsignedchar)(255.0f*c[3]);returnt;}voidGraphics::setBackgroundColor(constColor&c){glClearColor((float)c.r/255.0f,(float)c.g/255.0f,(float)c.b/255.0f,(float)c.a/255.0f);}ColorGraphics::getBackgroundColor(){floatc[4];glGetFloatv(GL_COLOR_CLEAR_VALUE,c);Colort;t.r=(unsignedchar)(255.0f*c[0]);t.g=(unsignedchar)(255.0f*c[1]);t.b=(unsignedchar)(255.0f*c[2]);t.a=(unsignedchar)(255.0f*c[3]);returnt;}voidGraphics::setFont(Font*font){if(currentFont!=0)currentFont->release();currentFont=font;if(font!=0)currentFont->retain();}Font*Graphics::getFont(){returncurrentFont;}voidGraphics::setBlendMode(Graphics::BlendModemode){glAlphaFunc(GL_GEQUAL,0);if(mode==BLEND_SUBTRACTIVE)glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);elseglBlendEquation(GL_FUNC_ADD);if(mode==BLEND_ALPHA)glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);elseif(mode==BLEND_MULTIPLICATIVE)glBlendFunc(GL_DST_COLOR,GL_ONE_MINUS_SRC_ALPHA);elseif(mode==BLEND_PREMULTIPLIED)glBlendFunc(GL_ONE,GL_ONE_MINUS_SRC_ALPHA);else// mode == BLEND_ADDITIVE || mode == BLEND_SUBTRACTIVEglBlendFunc(GL_SRC_ALPHA,GL_ONE);}voidGraphics::setColorMode(Graphics::ColorModemode){if(mode==COLOR_MODULATE)glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);else// mode = COLOR_REPLACEglTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE);}voidGraphics::setDefaultImageFilter(constImage::Filter&f){currentImageFilter=f;}Graphics::BlendModeGraphics::getBlendMode(){GLintdst,src,equation;glGetIntegerv(GL_BLEND_DST,&dst);glGetIntegerv(GL_BLEND_SRC,&src);glGetIntegerv(GL_BLEND_EQUATION,&equation);if(equation==GL_FUNC_REVERSE_SUBTRACT)// && src == GL_SRC_ALPHA && dst == GL_ONEreturnBLEND_SUBTRACTIVE;elseif(src==GL_SRC_ALPHA&&dst==GL_ONE)// && equation == GL_FUNC_ADDreturnBLEND_ADDITIVE;elseif(src==GL_SRC_ALPHA&&dst==GL_ONE_MINUS_SRC_ALPHA)// && equation == GL_FUNC_ADDreturnBLEND_ALPHA;elseif(src==GL_DST_COLOR&&dst==GL_ONE_MINUS_SRC_ALPHA)// && equation == GL_FUNC_ADDreturnBLEND_MULTIPLICATIVE;elseif(src==GL_ONE&&dst==GL_ONE_MINUS_SRC_ALPHA)// && equation == GL_FUNC_ADDreturnBLEND_PREMULTIPLIED;returnBLEND_MAX_ENUM;// Should never be reached.}Graphics::ColorModeGraphics::getColorMode(){GLintmode;glGetTexEnviv(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,&mode);if(mode==GL_MODULATE)returnCOLOR_MODULATE;else// // mode == GL_REPLACEreturnCOLOR_REPLACE;}constImage::Filter&Graphics::getDefaultImageFilter()const{returncurrentImageFilter;}voidGraphics::setLineWidth(floatwidth){lineWidth=width;}voidGraphics::setLineStyle(Graphics::LineStylestyle){lineStyle=style;}voidGraphics::setLine(floatwidth,Graphics::LineStylestyle){setLineWidth(width);if(style==0)return;setLineStyle(style);}floatGraphics::getLineWidth(){floatw;glGetFloatv(GL_LINE_WIDTH,&w);returnw;}Graphics::LineStyleGraphics::getLineStyle(){returnlineStyle;}voidGraphics::setPointSize(floatsize){glPointSize((GLfloat)size);}voidGraphics::setPointStyle(Graphics::PointStylestyle){if(style==POINT_SMOOTH)glEnable(GL_POINT_SMOOTH);else// love::POINT_ROUGHglDisable(GL_POINT_SMOOTH);}voidGraphics::setPoint(floatsize,Graphics::PointStylestyle){if(style==POINT_SMOOTH)glEnable(GL_POINT_SMOOTH);else// POINT_ROUGHglDisable(GL_POINT_SMOOTH);glPointSize((GLfloat)size);}floatGraphics::getPointSize(){GLfloatsize;glGetFloatv(GL_POINT_SIZE,&size);return(float)size;}Graphics::PointStyleGraphics::getPointStyle(){if(glIsEnabled(GL_POINT_SMOOTH)==GL_TRUE)returnPOINT_SMOOTH;elsereturnPOINT_ROUGH;}intGraphics::getMaxPointSize(){GLintmax;glGetIntegerv(GL_POINT_SIZE_MAX,&max);return(int)max;}voidGraphics::print(constchar*str,floatx,floaty,floatangle,floatsx,floatsy,floatox,floatoy,floatkx,floatky){if(currentFont!=0){std::stringtext(str);currentFont->print(text,x,y,angle,sx,sy,ox,oy,kx,ky);}}voidGraphics::printf(constchar*str,floatx,floaty,floatwrap,AlignModealign){if(currentFont==0)return;usingnamespacestd;stringtext(str);vector<string>lines_to_draw=currentFont->getWrap(text,wrap);// now for the actual printingvector<string>::const_iteratorline_iter,line_end=lines_to_draw.end();for(line_iter=lines_to_draw.begin();line_iter!=line_end;++line_iter){floatwidth=static_cast<float>(currentFont->getWidth(*line_iter));switch(align){caseALIGN_RIGHT:currentFont->print(*line_iter,ceil(x+wrap-width),ceil(y));break;caseALIGN_CENTER:currentFont->print(*line_iter,ceil(x+(wrap-width)/2),ceil(y));break;caseALIGN_LEFT:default:currentFont->print(*line_iter,ceil(x),ceil(y));break;}y+=currentFont->getHeight()*currentFont->getLineHeight();}}/** * Primitives **/voidGraphics::point(floatx,floaty){glDisable(GL_TEXTURE_2D);glBegin(GL_POINTS);glVertex2f(x,y);glEnd();glEnable(GL_TEXTURE_2D);}// Calculate line boundary points u1 and u2. Sketch:// u1// -------------+---...___// | ```'''-- ---// p- - - - - - q- - . _ _ | w/2// | ` ' ' r +// -------------+---...___ | w/2// u2 ```'''-- ---//// u1 and u2 depend on four things:// - the half line width w/2// - the previous line vertex p// - the current line vertex q// - the next line vertex r//// u1/u2 are the intersection points of the parallel lines to p-q and q-r,// i.e. the point where//// (p + w/2 * n1) + mu * (q - p) = (q + w/2 * n2) + lambda * (r - q) (u1)// (p - w/2 * n1) + mu * (q - p) = (q - w/2 * n2) + lambda * (r - q) (u2)//// with n1,n2 being the normals on the segments p-q and q-r://// n1 = perp(q - p) / |q - p|// n2 = perp(r - q) / |r - q|//// The intersection points can be calculated using cramers rule.staticvoidpushIntersectionPoints(Vector*vertices,Vector*overdraw,intpos,intcount,floathw,floatinv_hw,constVector&p,constVector&q,constVector&r){// calculate line directionsVectors=(q-p);Vectort=(r-q);// calculate vertex displacement vectorsVectorn1=s.getNormal();Vectorn2=t.getNormal();n1.normalize();n2.normalize();floatdet_norm=n1^n2;// will be close to zero if the angle between the normals is sharpn1*=hw;n2*=hw;// lines parallel -> assume intersection at displacement pointsif(fabs(det_norm)<=.03){vertices[pos]=q-n2;vertices[pos+1]=q+n2;}// real intersection -> calculate boundary intersection points with cramers ruleelse{floatdet=s^t;Vectord=n1-n2;Vectorb=s-d;// s = q - pVectorc=s+d;floatlambda=(b^t)/det;floatmu=(c^t)/det;// ordering for GL_TRIANGLE_STRIPvertices[pos]=p+s*mu-n1;// u1vertices[pos+1]=p+s*lambda+n1;// u2}if(overdraw){// displacement of the overdraw vertices (works by magic).Vectorx=(vertices[pos]-q)*inv_hw;overdraw[pos]=vertices[pos];overdraw[pos+1]=vertices[pos]+x;overdraw[2*count-pos-2]=vertices[pos+1];overdraw[2*count-pos-1]=vertices[pos+1]-x;}}// precondition:// glEnableClientState(GL_VERTEX_ARRAY);staticvoiddraw_overdraw(Vector*overdraw,size_tcount,boollooping){// if not looping, the outer overdraw vertices need to be displaced// to cover the line endings, i.e.:// +- - - - //- - + +- - - - - //- - - +// +-------//-----+ : +-------//-----+ :// | core // line | --> : | core // line | :// +-----//-------+ : +-----//-------+ :// +- - //- - - - + +- - - //- - - - - +if(!looping){Vectors=overdraw[1]-overdraw[3];s.normalize();overdraw[1]+=s;overdraw[2*count-1]+=s;Vectort=overdraw[count-1]-overdraw[count-3];t.normalize();overdraw[count-1]+=t;overdraw[count+1]+=t;// we need to draw two more triangles to close the// overdraw at the line start.overdraw[2*count]=overdraw[0];overdraw[2*count+1]=overdraw[1];}// prepare colors:// even indices in overdraw* point to inner vertices => alpha = current-alpha,// odd indices point to outer vertices => alpha = 0.GLfloatc[4];glGetFloatv(GL_CURRENT_COLOR,c);Color*colors=newColor[2*count+2];for(size_ti=0;i<2*count+2;++i){colors[i]=Color(GLubyte(c[0]*255.f),GLubyte(c[1]*255.f),GLubyte(c[2]*255.f),// avoids branching. equiv to if (i%2 == 1) colors[i].a = 0;GLubyte(c[3]*255.f)*GLubyte(i%2==0));}// draw faded out line halosglEnableClientState(GL_COLOR_ARRAY);glColorPointer(4,GL_UNSIGNED_BYTE,0,colors);glVertexPointer(2,GL_FLOAT,0,(constGLvoid*)overdraw);glDrawArrays(GL_TRIANGLE_STRIP,0,2*count+2*int(!looping));glDisableClientState(GL_COLOR_ARRAY);// "if GL_COLOR_ARRAY is enabled, the value of the current color is// undefined after glDrawArrays executes"glColor4fv(c);delete[]colors;}voidGraphics::polyline(constfloat*coords,size_tcount){Vector*vertices=newVector[count];// two vertices for every line end-pointVector*overdraw=NULL;Vectorp,q,r;boollooping=(coords[0]==coords[count-2])&&(coords[1]==coords[count-1]);floathalfwidth=lineWidth/2.f;floatinv_hw=1.f/halfwidth;if(lineStyle==LINE_SMOOTH){overdraw=newVector[2*count+2];// Overdraw changes visible line width. account for that.// Value of 0.2 chosen empirically.halfwidth-=.2f;}// get line vertex boundaries// if not looping, extend the line at the beginning, else use last point as `p'r=Vector(coords[0],coords[1]);if(!looping)q=r*2-Vector(coords[2],coords[3]);elseq=Vector(coords[count-4],coords[count-3]);for(size_ti=0;i+3<count;i+=2){p=q;q=r;r=Vector(coords[i+2],coords[i+3]);pushIntersectionPoints(vertices,overdraw,i,count,halfwidth,inv_hw,p,q,r);}// if not looping, extend the line at the end, else use first point as `r'p=q;q=r;if(!looping)r+=q-p;elser=Vector(coords[2],coords[3]);pushIntersectionPoints(vertices,overdraw,count-2,count,halfwidth,inv_hw,p,q,r);// end get line vertex boundaries// draw the core lineglDisable(GL_TEXTURE_2D);glEnableClientState(GL_VERTEX_ARRAY);glVertexPointer(2,GL_FLOAT,0,(constGLvoid*)vertices);glDrawArrays(GL_TRIANGLE_STRIP,0,count);// draw the line halo (antialiasing)if(lineStyle==LINE_SMOOTH)draw_overdraw(overdraw,count,looping);glDisableClientState(GL_VERTEX_ARRAY);glEnable(GL_TEXTURE_2D);// cleanupdelete[]vertices;if(lineStyle==LINE_SMOOTH)delete[]overdraw;}voidGraphics::triangle(DrawModemode,floatx1,floaty1,floatx2,floaty2,floatx3,floaty3){floatcoords[]={x1,y1,x2,y2,x3,y3,x1,y1};polygon(mode,coords,4*2);}voidGraphics::rectangle(DrawModemode,floatx,floaty,floatw,floath){quad(mode,x,y,x,y+h,x+w,y+h,x+w,y);}voidGraphics::quad(DrawModemode,floatx1,floaty1,floatx2,floaty2,floatx3,floaty3,floatx4,floaty4){floatcoords[]={x1,y1,x2,y2,x3,y3,x4,y4,x1,y1};polygon(mode,coords,5*2);}voidGraphics::circle(DrawModemode,floatx,floaty,floatradius,intpoints){floattwo_pi=static_cast<float>(LOVE_M_PI*2);if(points<=0)points=1;floatangle_shift=(two_pi/points);floatphi=.0f;float*coords=newfloat[2*(points+1)];for(inti=0;i<points;++i,phi+=angle_shift){coords[2*i]=x+radius*cos(phi);coords[2*i+1]=y+radius*sin(phi);}coords[2*points]=coords[0];coords[2*points+1]=coords[1];polygon(mode,coords,(points+1)*2);delete[]coords;}voidGraphics::arc(DrawModemode,floatx,floaty,floatradius,floatangle1,floatangle2,intpoints){// Nothing to display with no points or equal angles. (Or is there with line mode?)if(points<=0||angle1==angle2)return;// Oh, you want to draw a circle?if(fabs(angle1-angle2)>=2.0f*(float)LOVE_M_PI){circle(mode,x,y,radius,points);return;}floatangle_shift=(angle2-angle1)/points;// Bail on precision issues.if(angle_shift==0.0)return;floatphi=angle1;intnum_coords=(points+3)*2;float*coords=newfloat[num_coords];coords[0]=coords[num_coords-2]=x;coords[1]=coords[num_coords-1]=y;for(inti=0;i<=points;++i,phi+=angle_shift){coords[2*(i+1)]=x+radius*cos(phi);coords[2*(i+1)+1]=y+radius*sin(phi);}// GL_POLYGON can only fill-draw convex polygons, so we need to do stuff manually hereif(mode==DRAW_LINE){polyline(coords,num_coords);// Artifacts at sharp angles if set to looping.}else{glDisable(GL_TEXTURE_2D);glEnableClientState(GL_VERTEX_ARRAY);glVertexPointer(2,GL_FLOAT,0,(constGLvoid*)coords);glDrawArrays(GL_TRIANGLE_FAN,0,points+2);glDisableClientState(GL_VERTEX_ARRAY);glEnable(GL_TEXTURE_2D);}delete[]coords;}/// @param mode the draw mode/// @param coords the coordinate array/// @param count the number of coordinates/size of the arrayvoidGraphics::polygon(DrawModemode,constfloat*coords,size_tcount){// coords is an array of a closed loop of vertices, i.e.// coords[count-2] = coords[0], coords[count-1] = coords[1]if(mode==DRAW_LINE){polyline(coords,count);}else{glDisable(GL_TEXTURE_2D);glEnableClientState(GL_VERTEX_ARRAY);glVertexPointer(2,GL_FLOAT,0,(constGLvoid*)coords);glDrawArrays(GL_POLYGON,0,count/2-1);// opengl will close the polygon for usglDisableClientState(GL_VERTEX_ARRAY);glEnable(GL_TEXTURE_2D);}}love::image::ImageData*Graphics::newScreenshot(love::image::Image*image){intw=getWidth();inth=getHeight();introw=4*w;intsize=row*h;GLubyte*pixels=newGLubyte[size];GLubyte*screenshot=newGLubyte[size];glReadPixels(0,0,w,h,GL_RGBA,GL_UNSIGNED_BYTE,pixels);// OpenGL sucks and reads pixels from the lower-left. Let's fix that.GLubyte*src=pixels-row,*dst=screenshot+size;for(inti=0;i<h;++i){memcpy(dst-=row,src+=row,row);}love::image::ImageData*img=image->newImageData(w,h,(void*)screenshot);delete[]pixels;delete[]screenshot;returnimg;}voidGraphics::push(){if(userMatrices==matrixLimit)throwException("Maximum stack depth reached.");glPushMatrix();++userMatrices;}voidGraphics::pop(){if(userMatrices<1)throwException("Minimum stack depth reached. (More pops than pushes?)");glPopMatrix();--userMatrices;}voidGraphics::rotate(floatr){glRotatef(LOVE_TODEG(r),0,0,1);}voidGraphics::scale(floatx,floaty){glScalef(x,y,1);}voidGraphics::translate(floatx,floaty){glTranslatef(x,y,0);}voidGraphics::shear(floatkx,floatky){Matrixt;t.setShear(kx,ky);glMultMatrixf((constGLfloat*)t.getElements());}voidGraphics::drawTest(Image*image,floatx,floaty,floata,floatsx,floatsy,floatox,floatoy){image->bind();// Buffer for transforming the image.vertexbuf[4];Matrixt;t.translate(x,y);t.rotate(a);t.scale(sx,sy);t.translate(ox,oy);t.transform(buf,image->getVertices(),4);constvertex*vertices=image->getVertices();glEnableClientState(GL_VERTEX_ARRAY);glEnableClientState(GL_TEXTURE_COORD_ARRAY);glVertexPointer(2,GL_FLOAT,sizeof(vertex),(GLvoid*)&buf[0].x);glTexCoordPointer(2,GL_FLOAT,sizeof(vertex),(GLvoid*)&vertices[0].s);glDrawArrays(GL_QUADS,0,4);glDisableClientState(GL_TEXTURE_COORD_ARRAY);glDisableClientState(GL_VERTEX_ARRAY);}boolGraphics::hasFocus(){returncurrentWindow->hasFocus();}}// opengl}// graphics}// love