Graphics.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119
  1. /**
  2. * Copyright (c) 2006-2011 LOVE Development Team
  3. *
  4. * This software is provided 'as-is', without any express or implied
  5. * warranty. In no event will the authors be held liable for any damages
  6. * arising from the use of this software.
  7. *
  8. * Permission is granted to anyone to use this software for any purpose,
  9. * including commercial applications, and to alter it and redistribute it
  10. * freely, subject to the following restrictions:
  11. *
  12. * 1. The origin of this software must not be misrepresented; you must not
  13. * claim that you wrote the original software. If you use this software
  14. * in a product, an acknowledgment in the product documentation would be
  15. * appreciated but is not required.
  16. * 2. Altered source versions must be plainly marked as such, and must not be
  17. * misrepresented as being the original software.
  18. * 3. This notice may not be removed or altered from any source distribution.
  19. **/
  20. #include <common/config.h>
  21. #include <common/math.h>
  22. #include <common/Vector.h>
  23. #include "Graphics.h"
  24. #include <window/sdl/Window.h>
  25. #include <vector>
  26. #include <sstream>
  27. #include <algorithm>
  28. #include <iterator>
  29. namespace love
  30. {
  31. namespace graphics
  32. {
  33. namespace opengl
  34. {
  35. Graphics::Graphics()
  36. : currentFont(0), currentImageFilter(), lineStyle(LINE_SMOOTH), lineWidth(1), matrixLimit(0), userMatrices(0)
  37. {
  38. currentWindow = love::window::sdl::Window::getSingleton();
  39. }
  40. Graphics::~Graphics()
  41. {
  42. if (currentFont != 0)
  43. currentFont->release();
  44. currentWindow->release();
  45. }
  46. const char * Graphics::getName() const
  47. {
  48. return "love.graphics.opengl";
  49. }
  50. bool Graphics::checkMode(int width, int height, bool fullscreen)
  51. {
  52. return currentWindow->checkWindowSize(width, height, fullscreen);
  53. }
  54. DisplayState Graphics::saveState()
  55. {
  56. DisplayState s;
  57. s.color = getColor();
  58. s.backgroundColor = getBackgroundColor();
  59. //store modes here
  60. GLint mode;
  61. //get blend mode
  62. glGetIntegerv(GL_BLEND_DST, &mode);
  63. //following syntax seems better than if-else every time
  64. s.blendMode = (mode == GL_ONE) ? Graphics::BLEND_ADDITIVE : Graphics::BLEND_ALPHA;
  65. //get color mode
  66. glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &mode);
  67. s.colorMode = (mode == GL_MODULATE) ? Graphics::COLOR_MODULATE : Graphics::COLOR_REPLACE;
  68. //get line style
  69. s.lineStyle = lineStyle;
  70. //get the point size
  71. glGetFloatv(GL_POINT_SIZE, &s.pointSize);
  72. //get point style
  73. s.pointStyle = (glIsEnabled(GL_POINT_SMOOTH) == GL_TRUE) ? Graphics::POINT_SMOOTH : Graphics::POINT_ROUGH;
  74. //get scissor status
  75. s.scissor = (glIsEnabled(GL_SCISSOR_TEST) == GL_TRUE);
  76. //do we have scissor, if so, store the box
  77. if (s.scissor)
  78. glGetIntegerv(GL_SCISSOR_BOX, s.scissorBox);
  79. return s;
  80. }
  81. void Graphics::restoreState(const DisplayState & s)
  82. {
  83. setColor(s.color);
  84. setBackgroundColor(s.backgroundColor);
  85. setBlendMode(s.blendMode);
  86. setColorMode(s.colorMode);
  87. setLine(lineWidth, s.lineStyle);
  88. setPoint(s.pointSize, s.pointStyle);
  89. if (s.scissor)
  90. setScissor(s.scissorBox[0], s.scissorBox[1], s.scissorBox[2], s.scissorBox[3]);
  91. else
  92. setScissor();
  93. }
  94. bool Graphics::setMode(int width, int height, bool fullscreen, bool vsync, int fsaa)
  95. {
  96. // This operation destroys the OpenGL context, so
  97. // we must save the state.
  98. DisplayState tempState;
  99. if (isCreated())
  100. tempState = saveState();
  101. // Unlad all volatile objects. These must be reloaded after
  102. // the display mode change.
  103. Volatile::unloadAll();
  104. currentWindow->setWindow(width, height, fullscreen, vsync, fsaa);
  105. // Okay, setup OpenGL.
  106. // Enable blending
  107. glEnable(GL_BLEND);
  108. // "Normal" blending
  109. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  110. // Enable line/point smoothing.
  111. setLineStyle(LINE_SMOOTH);
  112. glEnable(GL_POINT_SMOOTH);
  113. glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
  114. // Enable textures
  115. glEnable(GL_TEXTURE_2D);
  116. // Set the viewport to top-left corner
  117. glViewport(0,0, width, height);
  118. // Reset the projection matrix
  119. glMatrixMode(GL_PROJECTION);
  120. glLoadIdentity();
  121. // Set up orthographic view (no depth)
  122. glOrtho(0.0, width, height,0.0, -1.0, 1.0);
  123. // Reset modelview matrix
  124. glMatrixMode(GL_MODELVIEW);
  125. glLoadIdentity();
  126. // Set pixel row alignment
  127. glPixelStorei(GL_UNPACK_ALIGNMENT, 2);
  128. // Reload all volatile objects.
  129. if (!Volatile::loadAll())
  130. std::cerr << "Could not reload all volatile objects." << std::endl;
  131. // Restore the display state.
  132. restoreState(tempState);
  133. // Get the maximum number of matrices
  134. // subtract a few to give the engine some room.
  135. glGetIntegerv(GL_MAX_MODELVIEW_STACK_DEPTH, &matrixLimit);
  136. matrixLimit -= 5;
  137. return true;
  138. }
  139. void Graphics::getMode(int &width, int &height, bool &fullscreen, bool &vsync, int &fsaa)
  140. {
  141. currentWindow->getWindow(width, height, fullscreen, vsync, fsaa);
  142. }
  143. bool Graphics::toggleFullscreen()
  144. {
  145. int width, height, fsaa;
  146. bool fullscreen, vsync;
  147. currentWindow->getWindow(width, height, fullscreen, vsync, fsaa);
  148. return currentWindow->setWindow(width, height, !fullscreen, vsync, fsaa);
  149. }
  150. void Graphics::reset()
  151. {
  152. DisplayState s;
  153. discardStencil();
  154. Canvas::bindDefaultCanvas();
  155. restoreState(s);
  156. }
  157. void Graphics::clear()
  158. {
  159. glClear(GL_COLOR_BUFFER_BIT);
  160. glLoadIdentity();
  161. PixelEffect::detach();
  162. }
  163. void Graphics::present()
  164. {
  165. currentWindow->swapBuffers();
  166. }
  167. void Graphics::setIcon(Image * image)
  168. {
  169. currentWindow->setIcon(image->getData());
  170. }
  171. void Graphics::setCaption(const char * caption)
  172. {
  173. std::string title(caption);
  174. currentWindow->setWindowTitle(title);
  175. }
  176. int Graphics::getCaption(lua_State * L)
  177. {
  178. std::string title = currentWindow->getWindowTitle();
  179. lua_pushstring(L, title.c_str());
  180. return 1;
  181. }
  182. int Graphics::getWidth()
  183. {
  184. return currentWindow->getWidth();
  185. }
  186. int Graphics::getHeight()
  187. {
  188. return currentWindow->getHeight();
  189. }
  190. int Graphics::getRenderHeight()
  191. {
  192. if (Canvas::current)
  193. return Canvas::current->getHeight();
  194. return getHeight();
  195. }
  196. bool Graphics::isCreated()
  197. {
  198. return currentWindow->isCreated();
  199. }
  200. int Graphics::getModes(lua_State * L)
  201. {
  202. int n;
  203. love::window::Window::WindowSize ** modes = currentWindow->getFullscreenSizes(n);
  204. if (modes == 0)
  205. return 0;
  206. lua_newtable(L);
  207. for (int i = 0; i < n ; i++)
  208. {
  209. lua_pushinteger(L, i+1);
  210. lua_newtable(L);
  211. // Inner table attribs.
  212. lua_pushstring(L, "width");
  213. lua_pushinteger(L, modes[i]->width);
  214. lua_settable(L, -3);
  215. lua_pushstring(L, "height");
  216. lua_pushinteger(L, modes[i]->height);
  217. lua_settable(L, -3);
  218. // Inner table attribs end.
  219. lua_settable(L, -3);
  220. delete modes[i];
  221. }
  222. delete[] modes;
  223. return 1;
  224. }
  225. void Graphics::setScissor(int x, int y, int width, int height)
  226. {
  227. glEnable(GL_SCISSOR_TEST);
  228. glScissor(x, getRenderHeight() - (y + height), width, height); // Compensates for the fact that our y-coordinate is reverse of OpenGLs.
  229. }
  230. void Graphics::setScissor()
  231. {
  232. glDisable(GL_SCISSOR_TEST);
  233. }
  234. int Graphics::getScissor(lua_State * L)
  235. {
  236. if (glIsEnabled(GL_SCISSOR_TEST) == GL_FALSE)
  237. return 0;
  238. GLint scissor[4];
  239. glGetIntegerv(GL_SCISSOR_BOX, scissor);
  240. lua_pushnumber(L, scissor[0]);
  241. lua_pushnumber(L, getRenderHeight() - (scissor[1] + scissor[3])); // Compensates for the fact that our y-coordinate is reverse of OpenGLs.
  242. lua_pushnumber(L, scissor[2]);
  243. lua_pushnumber(L, scissor[3]);
  244. return 4;
  245. }
  246. void Graphics::defineStencil()
  247. {
  248. glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
  249. glEnable(GL_STENCIL_TEST);
  250. glClear(GL_STENCIL_BUFFER_BIT);
  251. glStencilFunc(GL_ALWAYS, 1, 1);
  252. glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
  253. }
  254. void Graphics::useStencil(bool invert)
  255. {
  256. glStencilFunc(GL_EQUAL, (int)(!invert), 1); // invert ? 0 : 1
  257. glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
  258. glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
  259. }
  260. void Graphics::discardStencil()
  261. {
  262. glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
  263. glDisable(GL_STENCIL_TEST);
  264. }
  265. Image * Graphics::newImage(love::image::ImageData * data)
  266. {
  267. // Create the image.
  268. Image * image = new Image(data);
  269. bool success;
  270. try
  271. {
  272. success = image->load();
  273. }
  274. catch (love::Exception & e)
  275. {
  276. image->release();
  277. throw love::Exception(e.what());
  278. }
  279. if (!success)
  280. {
  281. image->release();
  282. return 0;
  283. }
  284. image->setFilter(currentImageFilter);
  285. return image;
  286. }
  287. Quad * Graphics::newQuad(float x, float y, float w, float h, float sw, float sh)
  288. {
  289. Quad::Viewport v;
  290. v.x = x;
  291. v.y = y;
  292. v.w = w;
  293. v.h = h;
  294. return new Quad(v, sw, sh);
  295. }
  296. Font * Graphics::newFont(love::font::Rasterizer * r, const Image::Filter& filter)
  297. {
  298. Font * font = new Font(r, filter);
  299. // Load it and check for errors.
  300. if (!font)
  301. {
  302. delete font;
  303. return 0;
  304. }
  305. return font;
  306. }
  307. SpriteBatch * Graphics::newSpriteBatch(Image * image, int size, int usage)
  308. {
  309. SpriteBatch * t = NULL;
  310. try
  311. {
  312. t = new SpriteBatch(image, size, usage);
  313. }
  314. catch (love::Exception& e)
  315. {
  316. if (t) delete t;
  317. throw e;
  318. }
  319. return t;
  320. }
  321. ParticleSystem * Graphics::newParticleSystem(Image * image, int size)
  322. {
  323. return new ParticleSystem(image, size);
  324. }
  325. Canvas * Graphics::newCanvas(int width, int height)
  326. {
  327. Canvas * canvas = new Canvas(width, height);
  328. GLenum err = canvas->getStatus();
  329. // everything ok, reaturn canvas (early out)
  330. if (err == GL_FRAMEBUFFER_COMPLETE)
  331. return canvas;
  332. // create error message
  333. std::stringstream error_string;
  334. error_string << "Cannot create canvas: ";
  335. switch (err) {
  336. case GL_FRAMEBUFFER_UNSUPPORTED:
  337. error_string << "Not supported by your OpenGL implementation.";
  338. break;
  339. // remaining error codes are highly unlikely:
  340. case GL_FRAMEBUFFER_UNDEFINED:
  341. case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
  342. case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
  343. case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
  344. case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
  345. case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
  346. error_string << "Error in implementation. Possible fix: Make canvas width and height powers of two.";
  347. break;
  348. default:
  349. // my intel hda card wrongly returns 0 to glCheckFramebufferStatus() but sets
  350. // no error flag. I think it meant to return GL_FRAMEBUFFER_UNSUPPORTED, but who
  351. // knows.
  352. if (glGetError() == GL_NO_ERROR)
  353. error_string << "May not be supported by your OpenGL implementation.";
  354. // the remaining error is an indication of a serious fuckup since it should
  355. // only be returned if glCheckFramebufferStatus() was called with the wrong
  356. // arguments.
  357. else
  358. error_string << "Cannot create canvas: Aliens did it (OpenGL error code: " << glGetError() << ")";
  359. }
  360. canvas->release();
  361. throw Exception(error_string.str().c_str());
  362. return NULL; // never reached
  363. }
  364. PixelEffect * Graphics::newPixelEffect(const std::string& code)
  365. {
  366. PixelEffect * effect = NULL;
  367. try
  368. {
  369. effect = new PixelEffect(code);
  370. }
  371. catch (love::Exception& e)
  372. {
  373. if (effect)
  374. delete effect;
  375. throw(e);
  376. }
  377. return effect;
  378. }
  379. void Graphics::setColor(const Color& c)
  380. {
  381. glColor4ubv(&c.r);
  382. }
  383. Color Graphics::getColor()
  384. {
  385. float c[4];
  386. glGetFloatv(GL_CURRENT_COLOR, c);
  387. Color t;
  388. t.r = (unsigned char)(255.0f*c[0]);
  389. t.g = (unsigned char)(255.0f*c[1]);
  390. t.b = (unsigned char)(255.0f*c[2]);
  391. t.a = (unsigned char)(255.0f*c[3]);
  392. return t;
  393. }
  394. void Graphics::setBackgroundColor(const Color& c)
  395. {
  396. glClearColor((float)c.r/255.0f, (float)c.g/255.0f, (float)c.b/255.0f, (float)c.a/255.0f);
  397. }
  398. Color Graphics::getBackgroundColor()
  399. {
  400. float c[4];
  401. glGetFloatv(GL_COLOR_CLEAR_VALUE, c);
  402. Color t;
  403. t.r = (unsigned char)(255.0f*c[0]);
  404. t.g = (unsigned char)(255.0f*c[1]);
  405. t.b = (unsigned char)(255.0f*c[2]);
  406. t.a = (unsigned char)(255.0f*c[3]);
  407. return t;
  408. }
  409. void Graphics::setFont( Font * font )
  410. {
  411. if (currentFont != 0)
  412. currentFont->release();
  413. currentFont = font;
  414. if (font != 0)
  415. currentFont->retain();
  416. }
  417. Font * Graphics::getFont()
  418. {
  419. return currentFont;
  420. }
  421. void Graphics::setBlendMode( Graphics::BlendMode mode )
  422. {
  423. glAlphaFunc(GL_GEQUAL, 0);
  424. if (mode == BLEND_SUBTRACTIVE)
  425. glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
  426. else
  427. glBlendEquation(GL_FUNC_ADD);
  428. if (mode == BLEND_ALPHA)
  429. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  430. else if (mode == BLEND_MULTIPLICATIVE)
  431. glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA);
  432. else if (mode == BLEND_PREMULTIPLIED)
  433. glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
  434. else // mode == BLEND_ADDITIVE || mode == BLEND_SUBTRACTIVE
  435. glBlendFunc(GL_SRC_ALPHA, GL_ONE);
  436. }
  437. void Graphics::setColorMode ( Graphics::ColorMode mode )
  438. {
  439. if (mode == COLOR_MODULATE)
  440. glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  441. else // mode = COLOR_REPLACE
  442. glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
  443. }
  444. void Graphics::setDefaultImageFilter(const Image::Filter& f)
  445. {
  446. currentImageFilter = f;
  447. }
  448. Graphics::BlendMode Graphics::getBlendMode ()
  449. {
  450. GLint dst, src, equation;
  451. glGetIntegerv(GL_BLEND_DST, &dst);
  452. glGetIntegerv(GL_BLEND_SRC, &src);
  453. glGetIntegerv(GL_BLEND_EQUATION, &equation);
  454. if (equation == GL_FUNC_REVERSE_SUBTRACT) // && src == GL_SRC_ALPHA && dst == GL_ONE
  455. return BLEND_SUBTRACTIVE;
  456. else if (src == GL_SRC_ALPHA && dst == GL_ONE) // && equation == GL_FUNC_ADD
  457. return BLEND_ADDITIVE;
  458. else if (src == GL_SRC_ALPHA && dst == GL_ONE_MINUS_SRC_ALPHA) // && equation == GL_FUNC_ADD
  459. return BLEND_ALPHA;
  460. else if (src == GL_DST_COLOR && dst == GL_ONE_MINUS_SRC_ALPHA) // && equation == GL_FUNC_ADD
  461. return BLEND_MULTIPLICATIVE;
  462. else if (src == GL_ONE && dst == GL_ONE_MINUS_SRC_ALPHA) // && equation == GL_FUNC_ADD
  463. return BLEND_PREMULTIPLIED;
  464. return BLEND_MAX_ENUM; // Should never be reached.
  465. }
  466. Graphics::ColorMode Graphics::getColorMode()
  467. {
  468. GLint mode;
  469. glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &mode);
  470. if (mode == GL_MODULATE)
  471. return COLOR_MODULATE;
  472. else // // mode == GL_REPLACE
  473. return COLOR_REPLACE;
  474. }
  475. const Image::Filter& Graphics::getDefaultImageFilter() const
  476. {
  477. return currentImageFilter;
  478. }
  479. void Graphics::setLineWidth( float width )
  480. {
  481. lineWidth = width;
  482. }
  483. void Graphics::setLineStyle(Graphics::LineStyle style)
  484. {
  485. lineStyle = style;
  486. }
  487. void Graphics::setLine( float width, Graphics::LineStyle style )
  488. {
  489. setLineWidth(width);
  490. if (style == 0)
  491. return;
  492. setLineStyle(style);
  493. }
  494. float Graphics::getLineWidth()
  495. {
  496. float w;
  497. glGetFloatv(GL_LINE_WIDTH, &w);
  498. return w;
  499. }
  500. Graphics::LineStyle Graphics::getLineStyle()
  501. {
  502. return lineStyle;
  503. }
  504. void Graphics::setPointSize( float size )
  505. {
  506. glPointSize((GLfloat)size);
  507. }
  508. void Graphics::setPointStyle( Graphics::PointStyle style )
  509. {
  510. if ( style == POINT_SMOOTH )
  511. glEnable(GL_POINT_SMOOTH);
  512. else // love::POINT_ROUGH
  513. glDisable(GL_POINT_SMOOTH);
  514. }
  515. void Graphics::setPoint( float size, Graphics::PointStyle style )
  516. {
  517. if ( style == POINT_SMOOTH )
  518. glEnable(GL_POINT_SMOOTH);
  519. else // POINT_ROUGH
  520. glDisable(GL_POINT_SMOOTH);
  521. glPointSize((GLfloat)size);
  522. }
  523. float Graphics::getPointSize()
  524. {
  525. GLfloat size;
  526. glGetFloatv(GL_POINT_SIZE, &size);
  527. return (float)size;
  528. }
  529. Graphics::PointStyle Graphics::getPointStyle()
  530. {
  531. if (glIsEnabled(GL_POINT_SMOOTH) == GL_TRUE)
  532. return POINT_SMOOTH;
  533. else
  534. return POINT_ROUGH;
  535. }
  536. int Graphics::getMaxPointSize()
  537. {
  538. GLint max;
  539. glGetIntegerv(GL_POINT_SIZE_MAX, &max);
  540. return (int)max;
  541. }
  542. void Graphics::print( const char * str, float x, float y , float angle, float sx, float sy, float ox, float oy, float kx, float ky)
  543. {
  544. if (currentFont != 0)
  545. {
  546. std::string text(str);
  547. currentFont->print(text, x, y, angle, sx, sy, ox, oy, kx, ky);
  548. }
  549. }
  550. void Graphics::printf( const char * str, float x, float y, float wrap, AlignMode align)
  551. {
  552. if (currentFont == 0)
  553. return;
  554. using namespace std;
  555. string text(str);
  556. vector<string> lines_to_draw = currentFont->getWrap(text, wrap);
  557. // now for the actual printing
  558. vector<string>::const_iterator line_iter, line_end = lines_to_draw.end();
  559. for (line_iter = lines_to_draw.begin(); line_iter != line_end; ++line_iter)
  560. {
  561. float width = static_cast<float>(currentFont->getWidth( *line_iter ));
  562. switch (align) {
  563. case ALIGN_RIGHT:
  564. currentFont->print(*line_iter, ceil(x + wrap - width), ceil(y));
  565. break;
  566. case ALIGN_CENTER:
  567. currentFont->print(*line_iter, ceil(x + (wrap - width) / 2), ceil(y));
  568. break;
  569. case ALIGN_LEFT:
  570. default:
  571. currentFont->print(*line_iter, ceil(x), ceil(y));
  572. break;
  573. }
  574. y += currentFont->getHeight() * currentFont->getLineHeight();
  575. }
  576. }
  577. /**
  578. * Primitives
  579. **/
  580. void Graphics::point( float x, float y )
  581. {
  582. glDisable(GL_TEXTURE_2D);
  583. glBegin(GL_POINTS);
  584. glVertex2f(x, y);
  585. glEnd();
  586. glEnable(GL_TEXTURE_2D);
  587. }
  588. // Calculate line boundary points u1 and u2. Sketch:
  589. // u1
  590. // -------------+---...___
  591. // | ```'''-- ---
  592. // p- - - - - - q- - . _ _ | w/2
  593. // | ` ' ' r +
  594. // -------------+---...___ | w/2
  595. // u2 ```'''-- ---
  596. //
  597. // u1 and u2 depend on four things:
  598. // - the half line width w/2
  599. // - the previous line vertex p
  600. // - the current line vertex q
  601. // - the next line vertex r
  602. //
  603. // u1/u2 are the intersection points of the parallel lines to p-q and q-r,
  604. // i.e. the point where
  605. //
  606. // (p + w/2 * n1) + mu * (q - p) = (q + w/2 * n2) + lambda * (r - q) (u1)
  607. // (p - w/2 * n1) + mu * (q - p) = (q - w/2 * n2) + lambda * (r - q) (u2)
  608. //
  609. // with n1,n2 being the normals on the segments p-q and q-r:
  610. //
  611. // n1 = perp(q - p) / |q - p|
  612. // n2 = perp(r - q) / |r - q|
  613. //
  614. // The intersection points can be calculated using cramers rule.
  615. static void pushIntersectionPoints(Vector *vertices, Vector* overdraw,
  616. int pos, int count, float hw, float inv_hw,
  617. const Vector& p, const Vector& q, const Vector& r)
  618. {
  619. // calculate line directions
  620. Vector s = (q - p);
  621. Vector t = (r - q);
  622. // calculate vertex displacement vectors
  623. Vector n1 = s.getNormal();
  624. Vector n2 = t.getNormal();
  625. n1.normalize();
  626. n2.normalize();
  627. float det_norm = n1 ^ n2; // will be close to zero if the angle between the normals is sharp
  628. n1 *= hw;
  629. n2 *= hw;
  630. // lines parallel -> assume intersection at displacement points
  631. if (fabs(det_norm) <= .03)
  632. {
  633. vertices[pos] = q - n2;
  634. vertices[pos+1] = q + n2;
  635. }
  636. // real intersection -> calculate boundary intersection points with cramers rule
  637. else
  638. {
  639. float det = s ^ t;
  640. Vector d = n1 - n2;
  641. Vector b = s - d; // s = q - p
  642. Vector c = s + d;
  643. float lambda = (b ^ t) / det;
  644. float mu = (c ^ t) / det;
  645. // ordering for GL_TRIANGLE_STRIP
  646. vertices[pos] = p + s*mu - n1; // u1
  647. vertices[pos+1] = p + s*lambda + n1; // u2
  648. }
  649. if (overdraw)
  650. {
  651. // displacement of the overdraw vertices (works by magic).
  652. Vector x = (vertices[pos] - q) * inv_hw;
  653. overdraw[pos] = vertices[pos];
  654. overdraw[pos+1] = vertices[pos] + x;
  655. overdraw[2*count-pos-2] = vertices[pos+1];
  656. overdraw[2*count-pos-1] = vertices[pos+1] - x;
  657. }
  658. }
  659. // precondition:
  660. // glEnableClientState(GL_VERTEX_ARRAY);
  661. static void draw_overdraw(Vector* overdraw, size_t count, bool looping)
  662. {
  663. // if not looping, the outer overdraw vertices need to be displaced
  664. // to cover the line endings, i.e.:
  665. // +- - - - //- - + +- - - - - //- - - +
  666. // +-------//-----+ : +-------//-----+ :
  667. // | core // line | --> : | core // line | :
  668. // +-----//-------+ : +-----//-------+ :
  669. // +- - //- - - - + +- - - //- - - - - +
  670. if (!looping)
  671. {
  672. Vector s = overdraw[1] - overdraw[3];
  673. s.normalize();
  674. overdraw[1] += s;
  675. overdraw[2*count-1] += s;
  676. Vector t = overdraw[count-1] - overdraw[count-3];
  677. t.normalize();
  678. overdraw[count-1] += t;
  679. overdraw[count+1] += t;
  680. // we need to draw two more triangles to close the
  681. // overdraw at the line start.
  682. overdraw[2*count] = overdraw[0];
  683. overdraw[2*count+1] = overdraw[1];
  684. }
  685. // prepare colors:
  686. // even indices in overdraw* point to inner vertices => alpha = current-alpha,
  687. // odd indices point to outer vertices => alpha = 0.
  688. GLfloat c[4];
  689. glGetFloatv(GL_CURRENT_COLOR, c);
  690. Color *colors = new Color[2*count+1];
  691. for (size_t i = 0; i < 2*count+1; ++i)
  692. {
  693. colors[i] = Color(GLubyte(c[0] * 255.f),
  694. GLubyte(c[1] * 255.f),
  695. GLubyte(c[2] * 255.f),
  696. // avoids branching. equiv to if (i%2 == 1) colors[i].a = 0;
  697. GLubyte(c[3] * 255.f) * GLubyte(i%2 == 0));
  698. }
  699. // draw faded out line halos
  700. glEnableClientState(GL_COLOR_ARRAY);
  701. glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors);
  702. glVertexPointer(2, GL_FLOAT, 0, (const GLvoid*)overdraw);
  703. glDrawArrays(GL_TRIANGLE_STRIP, 0, 2*count + 2 * int(!looping));
  704. glDisableClientState(GL_COLOR_ARRAY);
  705. // "if GL_COLOR_ARRAY is enabled, the value of the current color is
  706. // undefined after glDrawArrays executes"
  707. glColor4fv(c);
  708. delete[] colors;
  709. }
  710. void Graphics::polyline(const float* coords, size_t count)
  711. {
  712. Vector *vertices = new Vector[count]; // two vertices for every line end-point
  713. Vector *overdraw = NULL;
  714. Vector p,q,r;
  715. bool looping = (coords[0] == coords[count-2]) && (coords[1] == coords[count-1]);
  716. if (lineStyle == LINE_SMOOTH)
  717. overdraw = new Vector[2*count+2];
  718. float halfwidth = lineWidth/2.f;
  719. float inv_hw = 1.f / halfwidth;
  720. // Overdraw changes visible line width. account for that.
  721. // Value of 0.15 chosen empirically.
  722. if (lineStyle == LINE_SMOOTH)
  723. halfwidth -= .15f;
  724. // get line vertex boundaries
  725. // if not looping, extend the line at the beginning, else use last point as `p'
  726. r = Vector(coords[0], coords[1]);
  727. if (!looping)
  728. q = r * 2 - Vector(coords[2], coords[3]);
  729. else
  730. q = Vector(coords[count-4], coords[count-3]);
  731. for (size_t i = 0; i+3 < count; i += 2)
  732. {
  733. p = q; q = r;
  734. r = Vector(coords[i+2], coords[i+3]);
  735. pushIntersectionPoints(vertices, overdraw, i, count, halfwidth, inv_hw, p,q,r);
  736. }
  737. // if not looping, extend the line at the end, else use first point as `r'
  738. p = q; q = r;
  739. if (!looping)
  740. r += q - p;
  741. else
  742. r = Vector(coords[2], coords[3]);
  743. pushIntersectionPoints(vertices, overdraw, count-2, count, halfwidth, inv_hw, p,q,r);
  744. // end get line vertex boundaries
  745. // draw the core line
  746. glDisable(GL_TEXTURE_2D);
  747. glEnableClientState(GL_VERTEX_ARRAY);
  748. glVertexPointer(2, GL_FLOAT, 0, (const GLvoid*)vertices);
  749. glDrawArrays(GL_TRIANGLE_STRIP, 0, count);
  750. // draw the line halo (antialiasing)
  751. if (lineStyle == LINE_SMOOTH)
  752. draw_overdraw(overdraw, count, looping);
  753. glDisableClientState(GL_VERTEX_ARRAY);
  754. glEnable(GL_TEXTURE_2D);
  755. // cleanup
  756. delete[] vertices;
  757. if (lineStyle == LINE_SMOOTH)
  758. delete[] overdraw;
  759. }
  760. void Graphics::triangle(DrawMode mode, float x1, float y1, float x2, float y2, float x3, float y3 )
  761. {
  762. float coords[] = { x1,y1, x2,y2, x3,y3, x1,y1 };
  763. polygon(mode, coords, 4 * 2);
  764. }
  765. void Graphics::rectangle(DrawMode mode, float x, float y, float w, float h)
  766. {
  767. quad(mode, x,y, x,y+h, x+w,y+h, x+w,y);
  768. }
  769. void Graphics::quad(DrawMode mode, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4 )
  770. {
  771. float coords[] = { x1,y1, x2,y2, x3,y3, x4,y4, x1,y1 };
  772. polygon(mode, coords, 5 * 2);
  773. }
  774. void Graphics::circle(DrawMode mode, float x, float y, float radius, int points)
  775. {
  776. float two_pi = static_cast<float>(LOVE_M_PI * 2);
  777. if (points <= 0) points = 1;
  778. float angle_shift = (two_pi / points);
  779. float phi = .0f;
  780. float *coords = new float[2 * (points + 1)];
  781. for (int i = 0; i < points; ++i, phi += angle_shift)
  782. {
  783. coords[2*i] = x + radius * cos(phi);
  784. coords[2*i+1] = y + radius * sin(phi);
  785. }
  786. coords[2*points] = coords[0];
  787. coords[2*points+1] = coords[1];
  788. polygon(mode, coords, (points + 1) * 2);
  789. delete[] coords;
  790. }
  791. void Graphics::arc(DrawMode mode, float x, float y, float radius, float angle1, float angle2, int points)
  792. {
  793. // Nothing to display with no points or equal angles. (Or is there with line mode?)
  794. if (points <= 0 || angle1 == angle2)
  795. return;
  796. // Oh, you want to draw a circle?
  797. if (fabs(angle1 - angle2) >= 2.0f * (float) LOVE_M_PI)
  798. {
  799. circle(mode, x, y, radius, points);
  800. return;
  801. }
  802. float angle_shift = (angle2 - angle1) / points;
  803. // Bail on precision issues.
  804. if (angle_shift == 0.0)
  805. return;
  806. float phi = angle1;
  807. int num_coords = (points + 3) * 2;
  808. float * coords = new float[num_coords];
  809. coords[0] = coords[num_coords - 2] = x;
  810. coords[1] = coords[num_coords - 1] = y;
  811. for (int i = 0; i <= points; ++i, phi += angle_shift)
  812. {
  813. coords[2 * (i+1)] = x + radius * cos(phi);
  814. coords[2 * (i+1) + 1] = y + radius * sin(phi);
  815. }
  816. // GL_POLYGON can only fill-draw convex polygons, so we need to do stuff manually here
  817. if (mode == DRAW_LINE)
  818. {
  819. polyline(coords, num_coords); // Artifacts at sharp angles if set to looping.
  820. }
  821. else
  822. {
  823. glDisable(GL_TEXTURE_2D);
  824. glEnableClientState(GL_VERTEX_ARRAY);
  825. glVertexPointer(2, GL_FLOAT, 0, (const GLvoid *) coords);
  826. glDrawArrays(GL_TRIANGLE_FAN, 0, points + 2);
  827. glDisableClientState(GL_VERTEX_ARRAY);
  828. glEnable(GL_TEXTURE_2D);
  829. }
  830. delete[] coords;
  831. }
  832. /// @param mode the draw mode
  833. /// @param coords the coordinate array
  834. /// @param count the number of coordinates/size of the array
  835. void Graphics::polygon(DrawMode mode, const float* coords, size_t count)
  836. {
  837. // coords is an array of a closed loop of vertices, i.e.
  838. // coords[count-2] = coords[0], coords[count-1] = coords[1]
  839. if (mode == DRAW_LINE)
  840. {
  841. polyline(coords, count);
  842. }
  843. else
  844. {
  845. glDisable(GL_TEXTURE_2D);
  846. glEnableClientState(GL_VERTEX_ARRAY);
  847. glVertexPointer(2, GL_FLOAT, 0, (const GLvoid*)coords);
  848. glDrawArrays(GL_POLYGON, 0, count/2-1); // opengl will close the polygon for us
  849. glDisableClientState(GL_VERTEX_ARRAY);
  850. glEnable(GL_TEXTURE_2D);
  851. }
  852. }
  853. love::image::ImageData * Graphics::newScreenshot(love::image::Image * image)
  854. {
  855. int w = getWidth();
  856. int h = getHeight();
  857. int row = 4*w;
  858. int size = row*h;
  859. GLubyte * pixels = new GLubyte[size];
  860. GLubyte * screenshot = new GLubyte[size];
  861. glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
  862. // OpenGL sucks and reads pixels from the lower-left. Let's fix that.
  863. GLubyte *src = pixels - row, *dst = screenshot + size;
  864. for (int i = 0; i < h; ++i)
  865. {
  866. memcpy(dst-=row, src+=row, row);
  867. }
  868. love::image::ImageData * img = image->newImageData(w, h, (void*)screenshot);
  869. delete [] pixels;
  870. delete [] screenshot;
  871. return img;
  872. }
  873. void Graphics::push()
  874. {
  875. if (userMatrices == matrixLimit)
  876. throw Exception("Maximum stack depth reached.");
  877. glPushMatrix();
  878. ++userMatrices;
  879. }
  880. void Graphics::pop()
  881. {
  882. if (userMatrices < 1)
  883. throw Exception("Minimum stack depth reached. (More pops than pushes?)");
  884. glPopMatrix();
  885. --userMatrices;
  886. }
  887. void Graphics::rotate(float r)
  888. {
  889. glRotatef(LOVE_TODEG(r), 0, 0, 1);
  890. }
  891. void Graphics::scale(float x, float y)
  892. {
  893. glScalef(x, y, 1);
  894. }
  895. void Graphics::translate(float x, float y)
  896. {
  897. glTranslatef(x, y, 0);
  898. }
  899. void Graphics::shear(float kx, float ky)
  900. {
  901. Matrix t;
  902. t.setShear(kx, ky);
  903. glMultMatrixf((const GLfloat*)t.getElements());
  904. }
  905. void Graphics::drawTest(Image * image, float x, float y, float a, float sx, float sy, float ox, float oy)
  906. {
  907. image->bind();
  908. // Buffer for transforming the image.
  909. vertex buf[4];
  910. Matrix t;
  911. t.translate(x, y);
  912. t.rotate(a);
  913. t.scale(sx, sy);
  914. t.translate(ox, oy);
  915. t.transform(buf, image->getVertices(), 4);
  916. const vertex * vertices = image->getVertices();
  917. glEnableClientState(GL_VERTEX_ARRAY);
  918. glEnableClientState(GL_TEXTURE_COORD_ARRAY);
  919. glVertexPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid*)&buf[0].x);
  920. glTexCoordPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid*)&vertices[0].s);
  921. glDrawArrays(GL_QUADS, 0, 4);
  922. glDisableClientState(GL_TEXTURE_COORD_ARRAY);
  923. glDisableClientState(GL_VERTEX_ARRAY);
  924. }
  925. bool Graphics::hasFocus()
  926. {
  927. return currentWindow->hasFocus();
  928. }
  929. } // opengl
  930. } // graphics
  931. } // love