Canvas.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799
  1. /**
  2. * Copyright (c) 2006-2013 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 "Canvas.h"
  21. #include "Image.h"
  22. #include "Graphics.h"
  23. #include "common/Matrix.h"
  24. #include "common/config.h"
  25. #include <cstring> // For memcpy
  26. #include <limits>
  27. namespace love
  28. {
  29. namespace graphics
  30. {
  31. namespace opengl
  32. {
  33. // strategy for fbo creation, interchangable at runtime:
  34. // none, opengl >= 3.0, extensions
  35. struct FramebufferStrategy
  36. {
  37. virtual ~FramebufferStrategy() {}
  38. /// create a new framebuffer and texture
  39. /**
  40. * @param[out] framebuffer Framebuffer name
  41. * @param[out] img Texture name
  42. * @param[in] width Width of framebuffer
  43. * @param[in] height Height of framebuffer
  44. * @param[in] texture_type Type of the canvas texture.
  45. * @return Creation status
  46. */
  47. virtual GLenum createFBO(GLuint &, GLuint &, int, int, Canvas::TextureType)
  48. {
  49. return GL_FRAMEBUFFER_UNSUPPORTED;
  50. }
  51. /// Create a stencil buffer and attach it to the active framebuffer object
  52. /**
  53. * @param[in] width Width of the stencil buffer
  54. * @param[in] height Height of the stencil buffer
  55. * @param[out] stencil Name for stencil buffer
  56. * @return Whether the stencil buffer was successfully created
  57. **/
  58. virtual bool createStencil(int, int, GLuint &)
  59. {
  60. return false;
  61. }
  62. /// remove objects
  63. /**
  64. * @param[in] framebuffer Framebuffer name
  65. * @param[in] depth_stencil Name for packed depth and stencil buffer
  66. * @param[in] img Texture name
  67. */
  68. virtual void deleteFBO(GLuint, GLuint, GLuint) {}
  69. virtual void bindFBO(GLuint) {}
  70. /// attach additional canvases to the active framebuffer for rendering
  71. /**
  72. * @param[in] canvases List of canvases to attach
  73. **/
  74. virtual void setAttachments(const std::vector<Canvas *> &) {}
  75. /// stop using all additional attached canvases
  76. virtual void setAttachments() {}
  77. };
  78. struct FramebufferStrategyGL3 : public FramebufferStrategy
  79. {
  80. virtual GLenum createFBO(GLuint &framebuffer, GLuint &img, int width, int height, Canvas::TextureType texture_type)
  81. {
  82. // get currently bound fbo to reset to it later
  83. GLint current_fbo;
  84. glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &current_fbo);
  85. // create framebuffer
  86. glGenFramebuffers(1, &framebuffer);
  87. glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
  88. // generate texture save target
  89. GLint internalFormat;
  90. GLenum format;
  91. switch (texture_type)
  92. {
  93. case Canvas::TYPE_HDR:
  94. internalFormat = GL_RGBA16F;
  95. format = GL_FLOAT;
  96. break;
  97. case Canvas::TYPE_NORMAL:
  98. default:
  99. internalFormat = GL_RGBA8;
  100. format = GL_UNSIGNED_BYTE;
  101. }
  102. glGenTextures(1, &img);
  103. gl.bindTexture(img);
  104. gl.setTextureFilter(Image::getDefaultFilter());
  105. glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height,
  106. 0, GL_RGBA, format, NULL);
  107. gl.bindTexture(0);
  108. glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
  109. GL_TEXTURE_2D, img, 0);
  110. // check status
  111. GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
  112. // unbind framebuffer
  113. glBindFramebuffer(GL_FRAMEBUFFER, (GLuint) current_fbo);
  114. return status;
  115. }
  116. virtual bool createStencil(int width, int height, GLuint &stencil)
  117. {
  118. // create combined depth/stencil buffer
  119. glDeleteRenderbuffers(1, &stencil);
  120. glGenRenderbuffers(1, &stencil);
  121. glBindRenderbuffer(GL_RENDERBUFFER, stencil);
  122. glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL, width, height);
  123. glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
  124. GL_RENDERBUFFER, stencil);
  125. glBindRenderbuffer(GL_RENDERBUFFER, 0);
  126. // check status
  127. return glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE;
  128. }
  129. virtual void deleteFBO(GLuint framebuffer, GLuint depth_stencil, GLuint img)
  130. {
  131. gl.deleteTexture(img);
  132. if (depth_stencil != 0)
  133. glDeleteRenderbuffers(1, &depth_stencil);
  134. if (framebuffer != 0)
  135. glDeleteFramebuffers(1, &framebuffer);
  136. }
  137. virtual void bindFBO(GLuint framebuffer)
  138. {
  139. glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
  140. }
  141. virtual void setAttachments()
  142. {
  143. // set a single render target
  144. glDrawBuffer(GL_COLOR_ATTACHMENT0);
  145. }
  146. virtual void setAttachments(const std::vector<Canvas *> &canvases)
  147. {
  148. if (canvases.size() == 0)
  149. {
  150. setAttachments();
  151. return;
  152. }
  153. std::vector<GLenum> drawbuffers;
  154. drawbuffers.push_back(GL_COLOR_ATTACHMENT0);
  155. // Attach the canvas textures to the currently bound framebuffer.
  156. for (size_t i = 0; i < canvases.size(); i++)
  157. {
  158. glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1 + i,
  159. GL_TEXTURE_2D, canvases[i]->getTextureName(), 0);
  160. drawbuffers.push_back(GL_COLOR_ATTACHMENT1 + i);
  161. }
  162. // set up multiple render targets
  163. if (GLEE_VERSION_2_0)
  164. glDrawBuffers(drawbuffers.size(), &drawbuffers[0]);
  165. else if (GLEE_ARB_draw_buffers)
  166. glDrawBuffersARB(drawbuffers.size(), &drawbuffers[0]);
  167. }
  168. };
  169. struct FramebufferStrategyPackedEXT : public FramebufferStrategy
  170. {
  171. virtual GLenum createFBO(GLuint &framebuffer, GLuint &img, int width, int height, Canvas::TextureType texture_type)
  172. {
  173. GLint current_fbo;
  174. glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING_EXT, &current_fbo);
  175. // create framebuffer
  176. glGenFramebuffersEXT(1, &framebuffer);
  177. glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer);
  178. // generate texture save target
  179. GLint internalFormat;
  180. GLenum format;
  181. switch (texture_type)
  182. {
  183. case Canvas::TYPE_HDR:
  184. internalFormat = GL_RGBA16F;
  185. format = GL_FLOAT;
  186. break;
  187. case Canvas::TYPE_NORMAL:
  188. default:
  189. internalFormat = GL_RGBA8;
  190. format = GL_UNSIGNED_BYTE;
  191. }
  192. glGenTextures(1, &img);
  193. gl.bindTexture(img);
  194. gl.setTextureFilter(Image::getDefaultFilter());
  195. glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height,
  196. 0, GL_RGBA, format, NULL);
  197. gl.bindTexture(0);
  198. glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
  199. GL_TEXTURE_2D, img, 0);
  200. // check status
  201. GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
  202. // unbind framebuffer
  203. glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, (GLuint) current_fbo);
  204. return status;
  205. }
  206. virtual bool createStencil(int width, int height, GLuint &stencil)
  207. {
  208. // create combined depth/stencil buffer
  209. glDeleteRenderbuffers(1, &stencil);
  210. glGenRenderbuffersEXT(1, &stencil);
  211. glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, stencil);
  212. glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_STENCIL_EXT,
  213. width, height);
  214. glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
  215. GL_RENDERBUFFER_EXT, stencil);
  216. glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
  217. // check status
  218. return glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT;
  219. }
  220. virtual void deleteFBO(GLuint framebuffer, GLuint depth_stencil, GLuint img)
  221. {
  222. gl.deleteTexture(img);
  223. if (depth_stencil != 0)
  224. glDeleteRenderbuffersEXT(1, &depth_stencil);
  225. if (framebuffer != 0)
  226. glDeleteFramebuffersEXT(1, &framebuffer);
  227. }
  228. virtual void bindFBO(GLuint framebuffer)
  229. {
  230. glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer);
  231. }
  232. virtual void setAttachments()
  233. {
  234. // set a single render target
  235. glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
  236. }
  237. virtual void setAttachments(const std::vector<Canvas *> &canvases)
  238. {
  239. if (canvases.size() == 0)
  240. {
  241. setAttachments();
  242. return;
  243. }
  244. std::vector<GLenum> drawbuffers;
  245. drawbuffers.push_back(GL_COLOR_ATTACHMENT0_EXT);
  246. // Attach the canvas textures to the currently bound framebuffer.
  247. for (size_t i = 0; i < canvases.size(); i++)
  248. {
  249. glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1_EXT + i,
  250. GL_TEXTURE_2D, canvases[i]->getTextureName(), 0);
  251. drawbuffers.push_back(GL_COLOR_ATTACHMENT1_EXT + i);
  252. }
  253. // set up multiple render targets
  254. if (GLEE_VERSION_2_0)
  255. glDrawBuffers(drawbuffers.size(), &drawbuffers[0]);
  256. else if (GLEE_ARB_draw_buffers)
  257. glDrawBuffersARB(drawbuffers.size(), &drawbuffers[0]);
  258. }
  259. };
  260. struct FramebufferStrategyEXT : public FramebufferStrategyPackedEXT
  261. {
  262. virtual bool createStencil(int width, int height, GLuint &stencil)
  263. {
  264. // create stencil buffer
  265. glDeleteRenderbuffers(1, &stencil);
  266. glGenRenderbuffersEXT(1, &stencil);
  267. glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, stencil);
  268. glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_STENCIL_INDEX,
  269. width, height);
  270. glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
  271. GL_RENDERBUFFER_EXT, stencil);
  272. glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
  273. // check status
  274. return glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT;
  275. }
  276. bool isSupported()
  277. {
  278. GLuint fb = 0, stencil = 0, img = 0;
  279. GLenum status = createFBO(fb, img, 2, 2, Canvas::TYPE_NORMAL);
  280. deleteFBO(fb, stencil, img);
  281. return status == GL_FRAMEBUFFER_COMPLETE;
  282. }
  283. };
  284. FramebufferStrategy *strategy = NULL;
  285. FramebufferStrategy strategyNone;
  286. FramebufferStrategyGL3 strategyGL3;
  287. FramebufferStrategyPackedEXT strategyPackedEXT;
  288. FramebufferStrategyEXT strategyEXT;
  289. Canvas *Canvas::current = NULL;
  290. OpenGL::Viewport Canvas::systemViewport = OpenGL::Viewport();
  291. static void getStrategy()
  292. {
  293. if (!strategy)
  294. {
  295. if (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_object)
  296. strategy = &strategyGL3;
  297. else if (GLEE_EXT_framebuffer_object && GLEE_EXT_packed_depth_stencil)
  298. strategy = &strategyPackedEXT;
  299. else if (GLEE_EXT_framebuffer_object && strategyEXT.isSupported())
  300. strategy = &strategyEXT;
  301. else
  302. strategy = &strategyNone;
  303. }
  304. }
  305. static int maxFBOColorAttachments = 0;
  306. static int maxDrawBuffers = 0;
  307. Canvas::Canvas(int width, int height, TextureType texture_type)
  308. : width(width)
  309. , height(height)
  310. , fbo(0)
  311. , depth_stencil(0)
  312. , img(0)
  313. , texture_type(texture_type)
  314. {
  315. float w = static_cast<float>(width);
  316. float h = static_cast<float>(height);
  317. // world coordinates
  318. vertices[0].x = 0;
  319. vertices[0].y = h;
  320. vertices[1].x = w;
  321. vertices[1].y = h;
  322. vertices[2].x = w;
  323. vertices[2].y = 0;
  324. vertices[3].x = 0;
  325. vertices[3].y = 0;
  326. // texture coordinates
  327. vertices[0].s = 0;
  328. vertices[0].t = 0;
  329. vertices[1].s = 1;
  330. vertices[1].t = 0;
  331. vertices[2].s = 1;
  332. vertices[2].t = 1;
  333. vertices[3].s = 0;
  334. vertices[3].t = 1;
  335. settings.filter = Image::getDefaultFilter();
  336. getStrategy();
  337. loadVolatile();
  338. }
  339. Canvas::~Canvas()
  340. {
  341. // reset framebuffer if still using this one
  342. if (current == this)
  343. stopGrab();
  344. unloadVolatile();
  345. }
  346. bool Canvas::isSupported()
  347. {
  348. getStrategy();
  349. return (strategy != &strategyNone);
  350. }
  351. bool Canvas::isHDRSupported()
  352. {
  353. return GLEE_VERSION_3_0 || (isSupported() && GLEE_ARB_texture_float);
  354. }
  355. bool Canvas::isMultiCanvasSupported()
  356. {
  357. if (!(isSupported() && (GLEE_VERSION_2_0 || GLEE_ARB_draw_buffers)))
  358. return false;
  359. if (maxFBOColorAttachments == 0 || maxDrawBuffers == 0)
  360. {
  361. glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxFBOColorAttachments);
  362. glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
  363. }
  364. // system must support at least 4 simultanious active canvases
  365. return maxFBOColorAttachments >= 4 && maxDrawBuffers >= 4;
  366. }
  367. void Canvas::bindDefaultCanvas()
  368. {
  369. if (current != NULL)
  370. current->stopGrab();
  371. }
  372. void Canvas::setupGrab()
  373. {
  374. // already grabbing
  375. if (current == this)
  376. return;
  377. // cleanup after previous fbo
  378. if (current != NULL)
  379. current->stopGrab();
  380. // bind the framebuffer object.
  381. systemViewport = gl.getViewport();
  382. strategy->bindFBO(fbo);
  383. gl.setViewport(OpenGL::Viewport(0, 0, width, height));
  384. // Reset the projection matrix
  385. glMatrixMode(GL_PROJECTION);
  386. glPushMatrix();
  387. glLoadIdentity();
  388. // Set up orthographic view (no depth)
  389. glOrtho(0.0, width, height, 0.0, -1.0, 1.0);
  390. // Switch back to modelview matrix
  391. glMatrixMode(GL_MODELVIEW);
  392. // indicate we are using this fbo
  393. current = this;
  394. }
  395. void Canvas::startGrab(const std::vector<Canvas *> &canvases)
  396. {
  397. // Whether the new canvas list is different from the old one.
  398. // A more thorough check is done below.
  399. bool canvaseschanged = canvases.size() != attachedCanvases.size();
  400. if (canvases.size() > 0)
  401. {
  402. if (!isMultiCanvasSupported())
  403. throw love::Exception("Multi-canvas rendering is not supported on this system.");
  404. if (canvases.size()+1 > size_t(maxDrawBuffers) || canvases.size()+1 > size_t(maxFBOColorAttachments))
  405. throw love::Exception("This system can't simultaniously render to %d canvases.", canvases.size()+1);
  406. }
  407. for (size_t i = 0; i < canvases.size(); i++)
  408. {
  409. if (canvases[i]->getWidth() != width || canvases[i]->getHeight() != height)
  410. throw love::Exception("All canvas arguments must have the same dimensions.");
  411. if (canvases[i]->getTextureType() != texture_type)
  412. throw love::Exception("All canvas arguments must have the same texture type.");
  413. if (!canvaseschanged && canvases[i] != attachedCanvases[i])
  414. canvaseschanged = true;
  415. }
  416. setupGrab();
  417. // Don't attach anything if there's nothing to change.
  418. if (!canvaseschanged)
  419. return;
  420. // Attach the canvas textures to the active FBO and set up MRTs.
  421. strategy->setAttachments(canvases);
  422. for (size_t i = 0; i < canvases.size(); i++)
  423. canvases[i]->retain();
  424. for (size_t i = 0; i < attachedCanvases.size(); i++)
  425. attachedCanvases[i]->release();
  426. attachedCanvases = canvases;
  427. }
  428. void Canvas::startGrab()
  429. {
  430. setupGrab();
  431. if (attachedCanvases.size() == 0)
  432. return;
  433. // make sure the FBO is only using a single canvas
  434. strategy->setAttachments();
  435. // release any previously attached canvases
  436. for (size_t i = 0; i < attachedCanvases.size(); i++)
  437. attachedCanvases[i]->release();
  438. attachedCanvases.clear();
  439. }
  440. void Canvas::stopGrab()
  441. {
  442. // i am not grabbing. leave me alone
  443. if (current != this)
  444. return;
  445. // bind default
  446. strategy->bindFBO(0);
  447. glMatrixMode(GL_PROJECTION);
  448. glPopMatrix();
  449. glMatrixMode(GL_MODELVIEW);
  450. gl.setViewport(systemViewport);
  451. current = NULL;
  452. }
  453. void Canvas::clear(Color c)
  454. {
  455. if (strategy == &strategyNone)
  456. return;
  457. GLuint previous = 0;
  458. if (current != this)
  459. {
  460. if (current != NULL)
  461. previous = current->fbo;
  462. strategy->bindFBO(fbo);
  463. }
  464. GLfloat glcolor[] = {c.r/255.f, c.g/255.f, c.b/255.f, c.a/255.f};
  465. // We don't need to worry about multiple FBO attachments or global clear
  466. // color state when OpenGL 3.0+ is supported.
  467. if (GLEE_VERSION_3_0)
  468. {
  469. glClearBufferfv(GL_COLOR, 0, glcolor);
  470. if (depth_stencil != 0)
  471. {
  472. GLint stencilvalue = 0;
  473. glClearBufferiv(GL_STENCIL, 0, &stencilvalue);
  474. }
  475. }
  476. else
  477. {
  478. // glClear will clear all active draw buffers, so we need to temporarily
  479. // detach any other canvases (when MRT is being used.)
  480. if (attachedCanvases.size() > 0)
  481. strategy->setAttachments();
  482. // Don't use the state-shadowed gl.setClearColor because we want to save
  483. // the previous clear color.
  484. glClearColor(glcolor[0], glcolor[1], glcolor[2], glcolor[3]);
  485. glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
  486. if (attachedCanvases.size() > 0)
  487. strategy->setAttachments(attachedCanvases);
  488. // Restore the global clear color.
  489. gl.setClearColor(gl.getClearColor());
  490. }
  491. if (current != this)
  492. strategy->bindFBO(previous);
  493. }
  494. void Canvas::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
  495. {
  496. static Matrix t;
  497. t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
  498. drawv(t, vertices);
  499. }
  500. void Canvas::drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
  501. {
  502. static Matrix t;
  503. t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
  504. const Vertex *v = quad->getVertices();
  505. // flip texture coordinates vertically.
  506. Vertex w[4];
  507. memcpy(w, v, sizeof(Vertex) * 4);
  508. for (size_t i = 0; i < 4; i++)
  509. w[i].t = 1.0f - w[i].t;
  510. drawv(t, w);
  511. }
  512. bool Canvas::checkCreateStencil()
  513. {
  514. // Do nothing if we've already created the stencil buffer.
  515. if (depth_stencil != 0)
  516. return true;
  517. if (current != this)
  518. strategy->bindFBO(fbo);
  519. bool success = strategy->createStencil(width, height, depth_stencil);
  520. if (current && current != this)
  521. strategy->bindFBO(current->fbo);
  522. else if (!current)
  523. strategy->bindFBO(0);
  524. return success;
  525. }
  526. love::image::ImageData *Canvas::getImageData(love::image::Image *image)
  527. {
  528. int row = 4 * width;
  529. int size = row * height;
  530. GLubyte *pixels = new GLubyte[size];
  531. GLubyte *flipped = new GLubyte[size];
  532. strategy->bindFBO(fbo);
  533. glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
  534. if (current)
  535. strategy->bindFBO(current->fbo);
  536. else
  537. strategy->bindFBO(0);
  538. GLubyte *src = pixels, *dst = flipped + size - row;
  539. for (int i = 0; i < height; ++i, dst -= row, src += row)
  540. memcpy(dst, src, row);
  541. love::image::ImageData *img = image->newImageData(width, height, (void *)flipped, true);
  542. // The new ImageData now owns the flipped data, so we don't delete it here.
  543. delete[] pixels;
  544. return img;
  545. }
  546. void Canvas::getPixel(unsigned char* pixel_rgba, int x, int y)
  547. {
  548. if (current != this)
  549. strategy->bindFBO(fbo);
  550. glReadPixels(x, height - y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel_rgba);
  551. if (current && current != this)
  552. strategy->bindFBO(current->fbo);
  553. else if (!current)
  554. strategy->bindFBO(0);
  555. }
  556. const std::vector<Canvas *> &Canvas::getAttachedCanvases() const
  557. {
  558. return attachedCanvases;
  559. }
  560. void Canvas::setFilter(const Image::Filter &f)
  561. {
  562. settings.filter = f;
  563. gl.bindTexture(img);
  564. settings.filter.anisotropy = gl.setTextureFilter(f);
  565. }
  566. Image::Filter Canvas::getFilter() const
  567. {
  568. gl.bindTexture(img);
  569. return gl.getTextureFilter();
  570. }
  571. void Canvas::setWrap(const Image::Wrap &w)
  572. {
  573. settings.wrap = w;
  574. gl.bindTexture(img);
  575. gl.setTextureWrap(w);
  576. }
  577. Image::Wrap Canvas::getWrap() const
  578. {
  579. return settings.wrap;
  580. }
  581. bool Canvas::loadVolatile()
  582. {
  583. fbo = depth_stencil = img = 0;
  584. // glTexImage2D is guaranteed to error in this case.
  585. if (width > gl.getMaxTextureSize() || height > gl.getMaxTextureSize())
  586. {
  587. status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
  588. return false;
  589. }
  590. status = strategy->createFBO(fbo, img, width, height, texture_type);
  591. if (status != GL_FRAMEBUFFER_COMPLETE)
  592. return false;
  593. setFilter(settings.filter);
  594. setWrap(settings.wrap);
  595. clear(Color(0, 0, 0, 0));
  596. return true;
  597. }
  598. void Canvas::unloadVolatile()
  599. {
  600. strategy->deleteFBO(fbo, depth_stencil, img);
  601. fbo = depth_stencil = img = 0;
  602. for (size_t i = 0; i < attachedCanvases.size(); i++)
  603. attachedCanvases[i]->release();
  604. attachedCanvases.clear();
  605. }
  606. int Canvas::getWidth()
  607. {
  608. return width;
  609. }
  610. int Canvas::getHeight()
  611. {
  612. return height;
  613. }
  614. void Canvas::drawv(const Matrix &t, const Vertex *v) const
  615. {
  616. glPushMatrix();
  617. glMultMatrixf((const GLfloat *)t.getElements());
  618. gl.bindTexture(img);
  619. glEnableClientState(GL_VERTEX_ARRAY);
  620. glEnableClientState(GL_TEXTURE_COORD_ARRAY);
  621. glVertexPointer(2, GL_FLOAT, sizeof(Vertex), (GLvoid *)&v[0].x);
  622. glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), (GLvoid *)&v[0].s);
  623. glDrawArrays(GL_QUADS, 0, 4);
  624. glDisableClientState(GL_TEXTURE_COORD_ARRAY);
  625. glDisableClientState(GL_VERTEX_ARRAY);
  626. glPopMatrix();
  627. }
  628. bool Canvas::getConstant(const char *in, Canvas::TextureType &out)
  629. {
  630. return textureTypes.find(in, out);
  631. }
  632. bool Canvas::getConstant(Canvas::TextureType in, const char *&out)
  633. {
  634. return textureTypes.find(in, out);
  635. }
  636. StringMap<Canvas::TextureType, Canvas::TYPE_MAX_ENUM>::Entry Canvas::textureTypeEntries[] =
  637. {
  638. {"normal", Canvas::TYPE_NORMAL},
  639. {"hdr", Canvas::TYPE_HDR},
  640. };
  641. StringMap<Canvas::TextureType, Canvas::TYPE_MAX_ENUM> Canvas::textureTypes(Canvas::textureTypeEntries, sizeof(Canvas::textureTypeEntries));
  642. } // opengl
  643. } // graphics
  644. } // love