Graphics.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179
  1. /**
  2. * Copyright (c) 2006-2014 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. // LOVE
  21. #include "common/config.h"
  22. #include "common/math.h"
  23. #include "common/Vector.h"
  24. #include "Graphics.h"
  25. #include "window/sdl/Window.h"
  26. #include "Polyline.h"
  27. // C++
  28. #include <vector>
  29. #include <sstream>
  30. #include <algorithm>
  31. #include <iterator>
  32. // C
  33. #include <cmath>
  34. namespace love
  35. {
  36. namespace graphics
  37. {
  38. namespace opengl
  39. {
  40. Graphics::Graphics()
  41. : currentFont(0)
  42. , lineStyle(LINE_SMOOTH)
  43. , lineWidth(1)
  44. , matrixLimit(0)
  45. , userMatrices(0)
  46. , colorMask()
  47. , width(0)
  48. , height(0)
  49. , created(false)
  50. , activeStencil(false)
  51. , savedState()
  52. , displayedMinReqWarning(false)
  53. {
  54. currentWindow = love::window::sdl::Window::createSingleton();
  55. int w, h;
  56. love::window::WindowSettings wsettings;
  57. currentWindow->getWindow(w, h, wsettings);
  58. if (currentWindow->isCreated())
  59. setMode(w, h, wsettings.sRGB);
  60. }
  61. Graphics::~Graphics()
  62. {
  63. if (currentFont != 0)
  64. currentFont->release();
  65. currentWindow->release();
  66. }
  67. const char *Graphics::getName() const
  68. {
  69. return "love.graphics.opengl";
  70. }
  71. DisplayState Graphics::saveState()
  72. {
  73. DisplayState s;
  74. s.color = getColor();
  75. s.backgroundColor = getBackgroundColor();
  76. s.blendMode = getBlendMode();
  77. //get line style
  78. s.lineStyle = lineStyle;
  79. //get the point size
  80. glGetFloatv(GL_POINT_SIZE, &s.pointSize);
  81. //get scissor status
  82. s.scissor = (glIsEnabled(GL_SCISSOR_TEST) == GL_TRUE);
  83. //do we have scissor, if so, store the box
  84. if (s.scissor)
  85. s.scissorBox = gl.getScissor();
  86. for (int i = 0; i < 4; i++)
  87. s.colorMask[i] = colorMask[i];
  88. wireframe = isWireframe();
  89. return s;
  90. }
  91. void Graphics::restoreState(const DisplayState &s)
  92. {
  93. setColor(s.color);
  94. setBackgroundColor(s.backgroundColor);
  95. setBlendMode(s.blendMode);
  96. setLineWidth(lineWidth);
  97. setLineStyle(s.lineStyle);
  98. setPointSize(s.pointSize);
  99. if (s.scissor)
  100. setScissor(s.scissorBox.x, s.scissorBox.y, s.scissorBox.w, s.scissorBox.h);
  101. else
  102. setScissor();
  103. setColorMask(s.colorMask[0], s.colorMask[1], s.colorMask[2], s.colorMask[3]);
  104. setWireframe(s.wireframe);
  105. }
  106. void Graphics::setViewportSize(int width, int height)
  107. {
  108. this->width = width;
  109. this->height = height;
  110. if (!isCreated())
  111. return;
  112. // We want to affect the main screen, not any Canvas that's currently active
  113. // (not that any *should* be active when this is called.)
  114. Canvas *c = Canvas::current;
  115. Canvas::bindDefaultCanvas();
  116. // Set the viewport to top-left corner.
  117. gl.setViewport(OpenGL::Viewport(0, 0, width, height));
  118. // If a canvas was bound before this function was called, it needs to be
  119. // made aware of the new system viewport size.
  120. Canvas::systemViewport = gl.getViewport();
  121. // Reset the projection matrix
  122. glMatrixMode(GL_PROJECTION);
  123. glLoadIdentity();
  124. // Set up orthographic view (no depth)
  125. glOrtho(0.0, width, height, 0.0, -1.0, 1.0);
  126. glMatrixMode(GL_MODELVIEW);
  127. // Restore the previously active Canvas.
  128. if (c != nullptr)
  129. c->startGrab(c->getAttachedCanvases());
  130. }
  131. bool Graphics::setMode(int width, int height, bool &sRGB)
  132. {
  133. this->width = width;
  134. this->height = height;
  135. gl.initContext();
  136. // Does the system meet LOVE's minimum requirements for graphics?
  137. if (!(GLEE_VERSION_2_0 && Shader::isSupported() && Canvas::isSupported())
  138. && !displayedMinReqWarning)
  139. {
  140. love::window::MessageBoxType type = love::window::MESSAGEBOX_ERROR;
  141. const char *title = "Minimum system requirements not met!";
  142. std::string message;
  143. message += "Detected OpenGL version: ";
  144. message += (const char *) glGetString(GL_VERSION);
  145. message += "\nRequired OpenGL version: 2.1."; // -ish
  146. message += "\nThe program may crash or have graphical issues.";
  147. ::printf("%s\n%s\n", title, message.c_str());
  148. currentWindow->showMessageBox(type, title, message.c_str());
  149. // We should only show the message once, instead of after every setMode.
  150. displayedMinReqWarning = true;
  151. }
  152. // Okay, setup OpenGL.
  153. gl.setupContext();
  154. created = true;
  155. setViewportSize(width, height);
  156. // Make sure antialiasing works when set elsewhere
  157. glEnable(GL_MULTISAMPLE);
  158. // Enable blending
  159. glEnable(GL_BLEND);
  160. // Enable all color component writes.
  161. setColorMask(true, true, true, true);
  162. // Auto-generated mipmaps should be the best quality possible
  163. glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);
  164. // Enable textures
  165. glEnable(GL_TEXTURE_2D);
  166. gl.setTextureUnit(0);
  167. // Reset modelview matrix
  168. glMatrixMode(GL_MODELVIEW);
  169. glLoadIdentity();
  170. // Set pixel row alignment
  171. glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  172. // Reload all volatile objects.
  173. if (!Volatile::loadAll())
  174. std::cerr << "Could not reload all volatile objects." << std::endl;
  175. // Restore the display state.
  176. restoreState(savedState);
  177. pixel_size_stack.clear();
  178. pixel_size_stack.reserve(5);
  179. pixel_size_stack.push_back(1);
  180. // Get the maximum number of matrices
  181. // subtract a few to give the engine some room.
  182. glGetIntegerv(GL_MAX_MODELVIEW_STACK_DEPTH, &matrixLimit);
  183. matrixLimit -= 5;
  184. // Set whether drawing converts input from linear -> sRGB colorspace.
  185. if (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_sRGB || GLEE_EXT_framebuffer_sRGB)
  186. {
  187. if (sRGB)
  188. glEnable(GL_FRAMEBUFFER_SRGB);
  189. else
  190. glDisable(GL_FRAMEBUFFER_SRGB);
  191. }
  192. else
  193. sRGB = false;
  194. Canvas::screenHasSRGB = sRGB;
  195. bool enabledebug = false;
  196. if (GLEE_VERSION_3_0)
  197. {
  198. // Enable OpenGL's debug output if a debug context has been created.
  199. GLint flags = 0;
  200. glGetIntegerv(GL_CONTEXT_FLAGS, &flags);
  201. enabledebug = (flags & GL_CONTEXT_FLAG_DEBUG_BIT) != 0;
  202. }
  203. setDebug(enabledebug);
  204. return true;
  205. }
  206. void Graphics::unSetMode()
  207. {
  208. if (!isCreated())
  209. return;
  210. // Window re-creation may destroy the GL context, so we must save the state.
  211. savedState = saveState();
  212. // Unload all volatile objects. These must be reloaded after the display
  213. // mode change.
  214. Volatile::unloadAll();
  215. gl.deInitContext();
  216. created = false;
  217. }
  218. static void APIENTRY debugCB(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei /*len*/, const GLchar *msg, GLvoid* /*usr*/)
  219. {
  220. // Human-readable strings for the debug info.
  221. const char *sourceStr = OpenGL::debugSourceString(source);
  222. const char *typeStr = OpenGL::debugTypeString(type);
  223. const char *severityStr = OpenGL::debugSeverityString(severity);
  224. const char *fmt = "OpenGL: %s [source=%s, type=%s, severity=%s, id=%d]\n";
  225. printf(fmt, msg, sourceStr, typeStr, severityStr, id);
  226. }
  227. void Graphics::setDebug(bool enable)
  228. {
  229. // Make sure debug output is supported. The AMD ext. is a bit different
  230. // so we don't make use of it, since AMD drivers now support KHR_debug.
  231. if (!(GLEE_VERSION_4_3 || GLEE_KHR_debug || GLEE_ARB_debug_output))
  232. return;
  233. // Ugly hack to reduce code duplication.
  234. if (GLEE_ARB_debug_output && !(GLEE_VERSION_4_3 || GLEE_KHR_debug))
  235. {
  236. glDebugMessageCallback = (GLEEPFNGLDEBUGMESSAGECALLBACKPROC) glDebugMessageCallbackARB;
  237. glDebugMessageControl = (GLEEPFNGLDEBUGMESSAGECONTROLPROC) glDebugMessageControlARB;
  238. }
  239. if (!enable)
  240. {
  241. // Disable the debug callback function.
  242. glDebugMessageCallback(nullptr, nullptr);
  243. // We can disable debug output entirely with KHR_debug.
  244. if (GLEE_VERSION_4_3 || GLEE_KHR_debug)
  245. glDisable(GL_DEBUG_OUTPUT);
  246. return;
  247. }
  248. // We don't want asynchronous debug output.
  249. glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
  250. glDebugMessageCallback(debugCB, nullptr);
  251. // Initially, enable everything.
  252. glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, 0, GL_TRUE);
  253. // Disable messages about deprecated OpenGL functionality.
  254. glDebugMessageControl(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR, GL_DONT_CARE, 0, 0, GL_FALSE);
  255. glDebugMessageControl(GL_DEBUG_SOURCE_SHADER_COMPILER, GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR, GL_DONT_CARE, 0, 0, GL_FALSE);
  256. if (GLEE_VERSION_4_3 || GLEE_KHR_debug)
  257. glEnable(GL_DEBUG_OUTPUT);
  258. ::printf("OpenGL debug output enabled (LOVE_GRAPHICS_DEBUG=1)\n");
  259. }
  260. void Graphics::reset()
  261. {
  262. DisplayState s;
  263. discardStencil();
  264. Canvas::bindDefaultCanvas();
  265. Shader::detach();
  266. origin();
  267. restoreState(s);
  268. }
  269. void Graphics::clear()
  270. {
  271. glClear(GL_COLOR_BUFFER_BIT);
  272. }
  273. void Graphics::present()
  274. {
  275. currentWindow->swapBuffers();
  276. }
  277. int Graphics::getWidth() const
  278. {
  279. return width;
  280. }
  281. int Graphics::getHeight() const
  282. {
  283. return height;
  284. }
  285. bool Graphics::isCreated() const
  286. {
  287. return created;
  288. }
  289. void Graphics::setScissor(int x, int y, int width, int height)
  290. {
  291. glEnable(GL_SCISSOR_TEST);
  292. // OpenGL's reversed y-coordinate is compensated for in OpenGL::setScissor.
  293. gl.setScissor(OpenGL::Viewport(x, y, width, height));
  294. }
  295. void Graphics::setScissor()
  296. {
  297. glDisable(GL_SCISSOR_TEST);
  298. }
  299. bool Graphics::getScissor(int &x, int &y, int &width, int &height) const
  300. {
  301. OpenGL::Viewport scissor = gl.getScissor();
  302. x = scissor.x;
  303. y = scissor.y;
  304. width = scissor.w;
  305. height = scissor.h;
  306. return glIsEnabled(GL_SCISSOR_TEST) == GL_TRUE;
  307. }
  308. void Graphics::defineStencil()
  309. {
  310. // Make sure the active canvas has a stencil buffer.
  311. if (Canvas::current)
  312. Canvas::current->checkCreateStencil();
  313. // Disable color writes but don't save the mask values.
  314. glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
  315. glClear(GL_STENCIL_BUFFER_BIT);
  316. glEnable(GL_STENCIL_TEST);
  317. glStencilFunc(GL_ALWAYS, 1, 1);
  318. glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
  319. activeStencil = true;
  320. }
  321. void Graphics::useStencil(bool invert)
  322. {
  323. glStencilFunc(GL_EQUAL, (GLint)(!invert), 1); // invert ? 0 : 1
  324. glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
  325. setColorMask(colorMask[0], colorMask[1], colorMask[2], colorMask[3]);
  326. }
  327. void Graphics::discardStencil()
  328. {
  329. if (!activeStencil)
  330. return;
  331. setColorMask(colorMask[0], colorMask[1], colorMask[2], colorMask[3]);
  332. glDisable(GL_STENCIL_TEST);
  333. activeStencil = false;
  334. }
  335. Image *Graphics::newImage(love::image::ImageData *data, const Image::Flags &flags)
  336. {
  337. // Create the image.
  338. Image *image = new Image(data, flags);
  339. if (!isCreated())
  340. return image;
  341. bool success = false;
  342. try
  343. {
  344. success = image->load();
  345. }
  346. catch(love::Exception &)
  347. {
  348. image->release();
  349. throw;
  350. }
  351. if (!success)
  352. {
  353. image->release();
  354. return nullptr;
  355. }
  356. return image;
  357. }
  358. Image *Graphics::newImage(love::image::CompressedData *cdata, const Image::Flags &flags)
  359. {
  360. // Create the image.
  361. Image *image = new Image(cdata, flags);
  362. if (!isCreated())
  363. return image;
  364. bool success = false;
  365. try
  366. {
  367. success = image->load();
  368. }
  369. catch(love::Exception &)
  370. {
  371. image->release();
  372. throw;
  373. }
  374. if (!success)
  375. {
  376. image->release();
  377. return nullptr;
  378. }
  379. return image;
  380. }
  381. Quad *Graphics::newQuad(Quad::Viewport v, float sw, float sh)
  382. {
  383. return new Quad(v, sw, sh);
  384. }
  385. Font *Graphics::newFont(love::font::Rasterizer *r, const Texture::Filter &filter)
  386. {
  387. return new Font(r, filter);
  388. }
  389. SpriteBatch *Graphics::newSpriteBatch(Texture *texture, int size, int usage)
  390. {
  391. return new SpriteBatch(texture, size, usage);
  392. }
  393. ParticleSystem *Graphics::newParticleSystem(Texture *texture, int size)
  394. {
  395. return new ParticleSystem(texture, size);
  396. }
  397. Canvas *Graphics::newCanvas(int width, int height, Canvas::Format format, int msaa)
  398. {
  399. if (!Canvas::isFormatSupported(format))
  400. {
  401. const char *fstr = "rgba8";
  402. Canvas::getConstant(format, fstr);
  403. throw love::Exception("The %s canvas format is not supported by your OpenGL implementation.", fstr);
  404. }
  405. if (width > gl.getMaxTextureSize())
  406. throw Exception("Cannot create canvas: width of %d pixels is too large for this system.", width);
  407. else if (height > gl.getMaxTextureSize())
  408. throw Exception("Cannot create canvas: height of %d pixels is too large for this system.", height);
  409. while (GL_NO_ERROR != glGetError())
  410. /* clear opengl error flag */;
  411. Canvas *canvas = new Canvas(width, height, format, msaa);
  412. GLenum err = canvas->getStatus();
  413. // everything ok, return canvas (early out)
  414. if (err == GL_FRAMEBUFFER_COMPLETE)
  415. return canvas;
  416. // create error message
  417. std::stringstream error_string;
  418. error_string << "Cannot create canvas: ";
  419. switch (err)
  420. {
  421. case GL_FRAMEBUFFER_UNSUPPORTED:
  422. error_string << "Not supported by your OpenGL implementation.";
  423. break;
  424. // remaining error codes are highly unlikely:
  425. case GL_FRAMEBUFFER_UNDEFINED:
  426. case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
  427. case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
  428. case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
  429. case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
  430. case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
  431. error_string << "Error in implementation. Possible fix: Make canvas width and height powers of two.";
  432. break;
  433. default:
  434. // my intel hda card wrongly returns 0 to glCheckFramebufferStatus() but sets
  435. // no error flag. I think it meant to return GL_FRAMEBUFFER_UNSUPPORTED, but who
  436. // knows.
  437. if (glGetError() == GL_NO_ERROR)
  438. error_string << "May not be supported by your OpenGL implementation.";
  439. // the remaining error is an indication of a serious fuckup since it should
  440. // only be returned if glCheckFramebufferStatus() was called with the wrong
  441. // arguments.
  442. else
  443. error_string << "Cannot create canvas: Aliens did it (OpenGL error code: " << glGetError() << ")";
  444. }
  445. canvas->release();
  446. throw Exception("%s", error_string.str().c_str());
  447. return nullptr; // never reached
  448. }
  449. Shader *Graphics::newShader(const Shader::ShaderSources &sources)
  450. {
  451. return new Shader(sources);
  452. }
  453. Mesh *Graphics::newMesh(const std::vector<Vertex> &vertices, Mesh::DrawMode mode)
  454. {
  455. return new Mesh(vertices, mode);
  456. }
  457. Mesh *Graphics::newMesh(int vertexcount, Mesh::DrawMode mode)
  458. {
  459. return new Mesh(vertexcount, mode);
  460. }
  461. void Graphics::setColor(const Color &c)
  462. {
  463. gl.setColor(c);
  464. }
  465. Color Graphics::getColor() const
  466. {
  467. return gl.getColor();
  468. }
  469. void Graphics::setBackgroundColor(const Color &c)
  470. {
  471. gl.setClearColor(c);
  472. }
  473. Color Graphics::getBackgroundColor() const
  474. {
  475. return gl.getClearColor();
  476. }
  477. void Graphics::setFont(Font *font)
  478. {
  479. Object::AutoRelease fontrelease(currentFont);
  480. currentFont = font;
  481. if (font != 0)
  482. currentFont->retain();
  483. }
  484. Font *Graphics::getFont() const
  485. {
  486. return currentFont;
  487. }
  488. void Graphics::setColorMask(bool r, bool g, bool b, bool a)
  489. {
  490. colorMask[0] = r;
  491. colorMask[1] = g;
  492. colorMask[2] = b;
  493. colorMask[3] = a;
  494. glColorMask((GLboolean) r, (GLboolean) g, (GLboolean) b, (GLboolean) a);
  495. }
  496. const bool *Graphics::getColorMask() const
  497. {
  498. return colorMask;
  499. }
  500. void Graphics::setBlendMode(Graphics::BlendMode mode)
  501. {
  502. OpenGL::BlendState state = {GL_ONE, GL_ONE, GL_ZERO, GL_ZERO, GL_FUNC_ADD};
  503. switch (mode)
  504. {
  505. case BLEND_ALPHA:
  506. state.srcRGB = GL_SRC_ALPHA;
  507. state.srcA = GL_ONE;
  508. state.dstRGB = state.dstA = GL_ONE_MINUS_SRC_ALPHA;
  509. break;
  510. case BLEND_MULTIPLY:
  511. state.srcRGB = state.srcA = GL_DST_COLOR;
  512. state.dstRGB = state.dstA = GL_ZERO;
  513. break;
  514. case BLEND_PREMULTIPLIED:
  515. state.srcRGB = state.srcA = GL_ONE;
  516. state.dstRGB = state.dstA = GL_ONE_MINUS_SRC_ALPHA;
  517. break;
  518. case BLEND_SUBTRACT:
  519. state.func = GL_FUNC_REVERSE_SUBTRACT;
  520. case BLEND_ADD:
  521. state.srcRGB = state.srcA = GL_SRC_ALPHA;
  522. state.dstRGB = state.dstA = GL_ONE;
  523. break;
  524. case BLEND_SCREEN:
  525. state.srcRGB = state.srcA = GL_ONE;
  526. state.dstRGB = state.dstA = GL_ONE_MINUS_SRC_COLOR;
  527. break;
  528. case BLEND_REPLACE:
  529. default:
  530. state.srcRGB = state.srcA = GL_ONE;
  531. state.dstRGB = state.dstA = GL_ZERO;
  532. break;
  533. }
  534. gl.setBlendState(state);
  535. }
  536. Graphics::BlendMode Graphics::getBlendMode() const
  537. {
  538. OpenGL::BlendState state = gl.getBlendState();
  539. if (state.func == GL_FUNC_REVERSE_SUBTRACT) // && src == GL_SRC_ALPHA && dst == GL_ONE
  540. return BLEND_SUBTRACT;
  541. // Everything else has equation == GL_FUNC_ADD.
  542. else if (state.srcRGB == state.srcA && state.dstRGB == state.dstA)
  543. {
  544. if (state.srcRGB == GL_SRC_ALPHA && state.dstRGB == GL_ONE)
  545. return BLEND_ADD;
  546. else if (state.srcRGB == GL_SRC_ALPHA && state.dstRGB == GL_ONE_MINUS_SRC_ALPHA)
  547. return BLEND_ALPHA; // alpha blend mode fallback for very old OpenGL versions.
  548. else if (state.srcRGB == GL_DST_COLOR && state.dstRGB == GL_ZERO)
  549. return BLEND_MULTIPLY;
  550. else if (state.srcRGB == GL_ONE && state.dstRGB == GL_ONE_MINUS_SRC_ALPHA)
  551. return BLEND_PREMULTIPLIED;
  552. else if (state.srcRGB == GL_ONE && state.dstRGB == GL_ONE_MINUS_SRC_COLOR)
  553. return BLEND_SCREEN;
  554. else if (state.srcRGB == GL_ONE && state.dstRGB == GL_ZERO)
  555. return BLEND_REPLACE;
  556. }
  557. else if (state.srcRGB == GL_SRC_ALPHA && state.srcA == GL_ONE &&
  558. state.dstRGB == GL_ONE_MINUS_SRC_ALPHA && state.dstA == GL_ONE_MINUS_SRC_ALPHA)
  559. return BLEND_ALPHA;
  560. throw Exception("Unknown blend mode");
  561. }
  562. void Graphics::setDefaultFilter(const Texture::Filter &f)
  563. {
  564. Texture::setDefaultFilter(f);
  565. }
  566. const Texture::Filter &Graphics::getDefaultFilter() const
  567. {
  568. return Texture::getDefaultFilter();
  569. }
  570. void Graphics::setDefaultMipmapFilter(Texture::FilterMode filter, float sharpness)
  571. {
  572. Image::setDefaultMipmapFilter(filter);
  573. Image::setDefaultMipmapSharpness(sharpness);
  574. }
  575. void Graphics::getDefaultMipmapFilter(Texture::FilterMode *filter, float *sharpness) const
  576. {
  577. *filter = Image::getDefaultMipmapFilter();
  578. *sharpness = Image::getDefaultMipmapSharpness();
  579. }
  580. void Graphics::setLineWidth(float width)
  581. {
  582. lineWidth = width;
  583. }
  584. void Graphics::setLineStyle(Graphics::LineStyle style)
  585. {
  586. lineStyle = style;
  587. }
  588. void Graphics::setLineJoin(Graphics::LineJoin join)
  589. {
  590. lineJoin = join;
  591. }
  592. float Graphics::getLineWidth() const
  593. {
  594. return lineWidth;
  595. }
  596. Graphics::LineStyle Graphics::getLineStyle() const
  597. {
  598. return lineStyle;
  599. }
  600. Graphics::LineJoin Graphics::getLineJoin() const
  601. {
  602. return lineJoin;
  603. }
  604. void Graphics::setPointSize(float size)
  605. {
  606. glPointSize((GLfloat)size);
  607. }
  608. float Graphics::getPointSize() const
  609. {
  610. GLfloat size;
  611. glGetFloatv(GL_POINT_SIZE, &size);
  612. return (float)size;
  613. }
  614. void Graphics::setWireframe(bool enable)
  615. {
  616. wireframe = enable;
  617. glPolygonMode(GL_FRONT_AND_BACK, enable ? GL_LINE : GL_FILL);
  618. }
  619. bool Graphics::isWireframe() const
  620. {
  621. return wireframe;
  622. }
  623. void Graphics::print(const std::string &str, float x, float y , float angle, float sx, float sy, float ox, float oy, float kx, float ky)
  624. {
  625. if (currentFont != nullptr)
  626. currentFont->print(str, x, y, 0.0, angle, sx, sy, ox, oy, kx, ky);
  627. }
  628. void Graphics::printf(const std::string &str, float x, float y, float wrap, AlignMode align, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
  629. {
  630. if (currentFont == nullptr)
  631. return;
  632. if (wrap < 0.0f)
  633. throw love::Exception("Horizontal wrap limit cannot be negative.");
  634. using std::string;
  635. using std::vector;
  636. // wrappedlines indicates which lines were automatically wrapped. It's
  637. // guaranteed to have the same number of elements as lines_to_draw.
  638. vector<bool> wrappedlines;
  639. vector<string> lines_to_draw = currentFont->getWrap(str, wrap, 0, &wrappedlines);
  640. glPushMatrix();
  641. static Matrix t;
  642. t.setTransformation(ceilf(x), ceilf(y), angle, sx, sy, ox, oy, kx, ky);
  643. glMultMatrixf((const GLfloat *)t.getElements());
  644. x = y = 0.0f;
  645. try
  646. {
  647. // now for the actual printing
  648. vector<string>::const_iterator line_iter, line_end = lines_to_draw.end();
  649. float extra_spacing = 0.0f;
  650. int num_spaces = 0;
  651. int i = 0;
  652. for (line_iter = lines_to_draw.begin(); line_iter != line_end; ++line_iter)
  653. {
  654. float width = static_cast<float>(currentFont->getWidth(*line_iter));
  655. switch (align)
  656. {
  657. case ALIGN_RIGHT:
  658. currentFont->print(*line_iter, ceilf(x + (wrap - width)), ceilf(y), 0.0f);
  659. break;
  660. case ALIGN_CENTER:
  661. currentFont->print(*line_iter, ceilf(x + (wrap - width) / 2), ceilf(y), 0.0f);
  662. break;
  663. case ALIGN_JUSTIFY:
  664. num_spaces = std::count(line_iter->begin(), line_iter->end(), ' ');
  665. if (wrappedlines[i] && num_spaces >= 1)
  666. extra_spacing = (wrap - width) / float(num_spaces);
  667. else
  668. extra_spacing = 0.0f;
  669. currentFont->print(*line_iter, ceilf(x), ceilf(y), extra_spacing);
  670. break;
  671. case ALIGN_LEFT:
  672. default:
  673. currentFont->print(*line_iter, ceilf(x), ceilf(y), 0.0f);
  674. break;
  675. }
  676. y += currentFont->getHeight() * currentFont->getLineHeight();
  677. i++;
  678. }
  679. }
  680. catch (love::Exception &)
  681. {
  682. glPopMatrix();
  683. throw;
  684. }
  685. glPopMatrix();
  686. }
  687. /**
  688. * Primitives
  689. **/
  690. void Graphics::point(float x, float y)
  691. {
  692. gl.prepareDraw();
  693. gl.bindTexture(0);
  694. glBegin(GL_POINTS);
  695. glVertex2f(x, y);
  696. glEnd();
  697. }
  698. void Graphics::polyline(const float *coords, size_t count)
  699. {
  700. if (lineJoin == LINE_JOIN_NONE)
  701. {
  702. NoneJoinPolyline line;
  703. line.render(coords, count, lineWidth * .5f, float(pixel_size_stack.back()), lineStyle == LINE_SMOOTH);
  704. line.draw();
  705. }
  706. else if (lineJoin == LINE_JOIN_BEVEL)
  707. {
  708. BevelJoinPolyline line;
  709. line.render(coords, count, lineWidth * .5f, float(pixel_size_stack.back()), lineStyle == LINE_SMOOTH);
  710. line.draw();
  711. }
  712. else // LINE_JOIN_MITER
  713. {
  714. MiterJoinPolyline line;
  715. line.render(coords, count, lineWidth * .5f, float(pixel_size_stack.back()), lineStyle == LINE_SMOOTH);
  716. line.draw();
  717. }
  718. }
  719. void Graphics::rectangle(DrawMode mode, float x, float y, float w, float h)
  720. {
  721. float coords[] = {x,y, x,y+h, x+w,y+h, x+w,y, x,y};
  722. polygon(mode, coords, 5 * 2);
  723. }
  724. void Graphics::circle(DrawMode mode, float x, float y, float radius, int points)
  725. {
  726. float two_pi = static_cast<float>(LOVE_M_PI * 2);
  727. if (points <= 0) points = 1;
  728. float angle_shift = (two_pi / points);
  729. float phi = .0f;
  730. float *coords = new float[2 * (points + 1)];
  731. for (int i = 0; i < points; ++i, phi += angle_shift)
  732. {
  733. coords[2*i] = x + radius * cosf(phi);
  734. coords[2*i+1] = y + radius * sinf(phi);
  735. }
  736. coords[2*points] = coords[0];
  737. coords[2*points+1] = coords[1];
  738. polygon(mode, coords, (points + 1) * 2);
  739. delete[] coords;
  740. }
  741. void Graphics::arc(DrawMode mode, float x, float y, float radius, float angle1, float angle2, int points)
  742. {
  743. // Nothing to display with no points or equal angles. (Or is there with line mode?)
  744. if (points <= 0 || angle1 == angle2)
  745. return;
  746. // Oh, you want to draw a circle?
  747. if (fabs(angle1 - angle2) >= 2.0f * (float) LOVE_M_PI)
  748. {
  749. circle(mode, x, y, radius, points);
  750. return;
  751. }
  752. float angle_shift = (angle2 - angle1) / points;
  753. // Bail on precision issues.
  754. if (angle_shift == 0.0)
  755. return;
  756. float phi = angle1;
  757. int num_coords = (points + 3) * 2;
  758. float *coords = new float[num_coords];
  759. coords[0] = coords[num_coords - 2] = x;
  760. coords[1] = coords[num_coords - 1] = y;
  761. for (int i = 0; i <= points; ++i, phi += angle_shift)
  762. {
  763. coords[2 * (i+1)] = x + radius * cosf(phi);
  764. coords[2 * (i+1) + 1] = y + radius * sinf(phi);
  765. }
  766. // GL_POLYGON can only fill-draw convex polygons, so we need to do stuff manually here
  767. if (mode == DRAW_LINE)
  768. {
  769. polyline(coords, num_coords); // Artifacts at sharp angles if set to looping.
  770. }
  771. else
  772. {
  773. gl.prepareDraw();
  774. gl.bindTexture(0);
  775. glEnableClientState(GL_VERTEX_ARRAY);
  776. glVertexPointer(2, GL_FLOAT, 0, (const GLvoid *) coords);
  777. glDrawArrays(GL_TRIANGLE_FAN, 0, points + 2);
  778. glDisableClientState(GL_VERTEX_ARRAY);
  779. }
  780. delete[] coords;
  781. }
  782. /// @param mode the draw mode
  783. /// @param coords the coordinate array
  784. /// @param count the number of coordinates/size of the array
  785. void Graphics::polygon(DrawMode mode, const float *coords, size_t count)
  786. {
  787. // coords is an array of a closed loop of vertices, i.e.
  788. // coords[count-2] = coords[0], coords[count-1] = coords[1]
  789. if (mode == DRAW_LINE)
  790. {
  791. polyline(coords, count);
  792. }
  793. else
  794. {
  795. gl.prepareDraw();
  796. gl.bindTexture(0);
  797. glEnableClientState(GL_VERTEX_ARRAY);
  798. glVertexPointer(2, GL_FLOAT, 0, (const GLvoid *)coords);
  799. glDrawArrays(GL_POLYGON, 0, count/2-1); // opengl will close the polygon for us
  800. glDisableClientState(GL_VERTEX_ARRAY);
  801. }
  802. }
  803. love::image::ImageData *Graphics::newScreenshot(love::image::Image *image, bool copyAlpha)
  804. {
  805. // Temporarily unbind the currently active canvas (glReadPixels reads the
  806. // active framebuffer, not the main one.)
  807. Canvas *curcanvas = Canvas::current;
  808. if (curcanvas)
  809. Canvas::bindDefaultCanvas();
  810. int w = getWidth();
  811. int h = getHeight();
  812. int row = 4*w;
  813. int size = row*h;
  814. GLubyte *pixels = 0;
  815. GLubyte *screenshot = 0;
  816. try
  817. {
  818. pixels = new GLubyte[size];
  819. screenshot = new GLubyte[size];
  820. }
  821. catch (std::exception &)
  822. {
  823. delete[] pixels;
  824. delete[] screenshot;
  825. if (curcanvas)
  826. curcanvas->startGrab(curcanvas->getAttachedCanvases());
  827. throw love::Exception("Out of memory.");
  828. }
  829. glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
  830. if (!copyAlpha)
  831. {
  832. // Replace alpha values with full opacity.
  833. for (int i = 3; i < size; i += 4)
  834. pixels[i] = 255;
  835. }
  836. // OpenGL sucks and reads pixels from the lower-left. Let's fix that.
  837. GLubyte *src = pixels - row, *dst = screenshot + size;
  838. for (int i = 0; i < h; ++i)
  839. memcpy(dst-=row, src+=row, row);
  840. delete[] pixels;
  841. love::image::ImageData *img = 0;
  842. try
  843. {
  844. // Tell the new ImageData that it owns the screenshot data, so we don't
  845. // need to delete it here.
  846. img = image->newImageData(w, h, (void *) screenshot, true);
  847. }
  848. catch (love::Exception &)
  849. {
  850. delete[] screenshot;
  851. if (curcanvas)
  852. curcanvas->startGrab(curcanvas->getAttachedCanvases());
  853. throw;
  854. }
  855. // Re-bind the active canvas, if necessary.
  856. if (curcanvas)
  857. curcanvas->startGrab(curcanvas->getAttachedCanvases());
  858. return img;
  859. }
  860. Graphics::RendererInfo Graphics::getRendererInfo() const
  861. {
  862. RendererInfo info;
  863. info.name = "OpenGL";
  864. const char *str = (const char *) glGetString(GL_VERSION);
  865. if (str)
  866. info.version = str;
  867. else
  868. throw love::Exception("Cannot retrieve renderer version information.");
  869. str = (const char *) glGetString(GL_VENDOR);
  870. if (str)
  871. info.vendor = str;
  872. else
  873. throw love::Exception("Cannot retrieve renderer vendor information.");
  874. str = (const char *) glGetString(GL_RENDERER);
  875. if (str)
  876. info.device = str;
  877. else
  878. throw love::Exception("Cannot retrieve renderer device information.");
  879. return info;
  880. }
  881. double Graphics::getSystemLimit(SystemLimit limittype) const
  882. {
  883. double limit = 0.0;
  884. switch (limittype)
  885. {
  886. case Graphics::LIMIT_POINT_SIZE:
  887. {
  888. GLfloat limits[2];
  889. glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, limits);
  890. limit = limits[1];
  891. }
  892. break;
  893. case Graphics::LIMIT_TEXTURE_SIZE:
  894. limit = (double) gl.getMaxTextureSize();
  895. break;
  896. case Graphics::LIMIT_MULTI_CANVAS:
  897. limit = (double) gl.getMaxRenderTargets();
  898. break;
  899. case Graphics::LIMIT_CANVAS_MSAA:
  900. if (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_object
  901. || GLEE_EXT_framebuffer_multisample)
  902. {
  903. GLint intlimit = 0;
  904. glGetIntegerv(GL_MAX_SAMPLES, &intlimit);
  905. limit = (double) intlimit;
  906. }
  907. break;
  908. default:
  909. break;
  910. }
  911. return limit;
  912. }
  913. bool Graphics::isSupported(Support feature) const
  914. {
  915. switch (feature)
  916. {
  917. case SUPPORT_MULTI_CANVAS:
  918. return Canvas::isMultiCanvasSupported();
  919. case SUPPORT_SRGB:
  920. // sRGB support for the screen is guaranteed if it's supported as a
  921. // Canvas format.
  922. return Canvas::isFormatSupported(Canvas::FORMAT_SRGB);
  923. default:
  924. return false;
  925. }
  926. }
  927. void Graphics::push()
  928. {
  929. if (userMatrices == matrixLimit)
  930. throw Exception("Maximum stack depth reached. (More pushes than pops?)");
  931. glPushMatrix();
  932. ++userMatrices;
  933. pixel_size_stack.push_back(pixel_size_stack.back());
  934. }
  935. void Graphics::pop()
  936. {
  937. if (userMatrices < 1)
  938. throw Exception("Minimum stack depth reached. (More pops than pushes?)");
  939. glPopMatrix();
  940. --userMatrices;
  941. pixel_size_stack.pop_back();
  942. }
  943. void Graphics::rotate(float r)
  944. {
  945. glRotatef(LOVE_TODEG(r), 0, 0, 1);
  946. }
  947. void Graphics::scale(float x, float y)
  948. {
  949. glScalef(x, y, 1);
  950. pixel_size_stack.back() *= 2. / (fabs(x) + fabs(y));
  951. }
  952. void Graphics::translate(float x, float y)
  953. {
  954. glTranslatef(x, y, 0);
  955. }
  956. void Graphics::shear(float kx, float ky)
  957. {
  958. Matrix t;
  959. t.setShear(kx, ky);
  960. glMultMatrixf((const GLfloat *)t.getElements());
  961. }
  962. void Graphics::origin()
  963. {
  964. glLoadIdentity();
  965. pixel_size_stack.back() = 1;
  966. }
  967. } // opengl
  968. } // graphics
  969. } // love