Canvas.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. #include "Canvas.h"
  2. #include "Graphics.h"
  3. #include <common/Matrix.h>
  4. #include <cstring> // For memcpy
  5. namespace love
  6. {
  7. namespace graphics
  8. {
  9. namespace opengl
  10. {
  11. // strategy for fbo creation, interchangable at runtime:
  12. // none, opengl >= 3.0, extensions
  13. struct FramebufferStrategy {
  14. /// create a new framebuffer, depth_stencil and texture
  15. /**
  16. * @param[out] framebuffer Framebuffer name
  17. * @param[out] depth_stencil Name for packed depth and stencil buffer
  18. * @param[out] img Texture name
  19. * @param[in] width Width of framebuffer
  20. * @param[in] height Height of framebuffer
  21. * @return Creation status
  22. */
  23. virtual GLenum createFBO(GLuint&, GLuint&, GLuint&, int, int)
  24. { return GL_FRAMEBUFFER_UNSUPPORTED; }
  25. /// remove objects
  26. /**
  27. * @param[in] framebuffer Framebuffer name
  28. * @param[in] depth_stencil Name for packed depth and stencil buffer
  29. * @param[in] img Texture name
  30. */
  31. virtual void deleteFBO(GLuint, GLuint, GLuint) {}
  32. virtual void bindFBO(GLuint) {}
  33. };
  34. struct FramebufferStrategyGL3 : public FramebufferStrategy {
  35. virtual GLenum createFBO(GLuint& framebuffer, GLuint& depth_stencil, GLuint& img, int width, int height)
  36. {
  37. // get currently bound fbo to reset to it later
  38. GLint current_fbo;
  39. glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &current_fbo);
  40. // create framebuffer
  41. glGenFramebuffers(1, &framebuffer);
  42. glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
  43. // create stencil buffer
  44. glGenRenderbuffers(1, &depth_stencil);
  45. glBindRenderbuffer(GL_RENDERBUFFER, depth_stencil);
  46. glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
  47. glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
  48. GL_RENDERBUFFER, depth_stencil);
  49. glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
  50. GL_RENDERBUFFER, depth_stencil);
  51. // generate texture save target
  52. glGenTextures(1, &img);
  53. glBindTexture(GL_TEXTURE_2D, img);
  54. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  55. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  56. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height,
  57. 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
  58. glBindTexture(GL_TEXTURE_2D, 0);
  59. glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
  60. GL_TEXTURE_2D, img, 0);
  61. // check status
  62. GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
  63. // unbind framebuffer
  64. glBindRenderbuffer(GL_RENDERBUFFER, 0);
  65. glBindFramebuffer(GL_FRAMEBUFFER, (GLuint)current_fbo);
  66. return status;
  67. }
  68. virtual void deleteFBO(GLuint framebuffer, GLuint depth_stencil, GLuint img)
  69. {
  70. glDeleteTextures(1, &img);
  71. glDeleteRenderbuffers(1, &depth_stencil);
  72. glDeleteFramebuffers(1, &framebuffer);
  73. }
  74. virtual void bindFBO(GLuint framebuffer)
  75. {
  76. glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
  77. }
  78. };
  79. struct FramebufferStrategyEXT : public FramebufferStrategy {
  80. virtual GLenum createFBO(GLuint& framebuffer, GLuint& depth_stencil, GLuint& img, int width, int height)
  81. {
  82. GLint current_fbo;
  83. glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING_EXT, &current_fbo);
  84. // create framebuffer
  85. glGenFramebuffersEXT(1, &framebuffer);
  86. glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer);
  87. // create stencil buffer
  88. glGenRenderbuffersEXT(1, &depth_stencil);
  89. glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depth_stencil);
  90. glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, width, height);
  91. glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
  92. GL_RENDERBUFFER_EXT, depth_stencil);
  93. glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
  94. GL_RENDERBUFFER_EXT, depth_stencil);
  95. // generate texture save target
  96. glGenTextures(1, &img);
  97. glBindTexture(GL_TEXTURE_2D, img);
  98. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  99. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  100. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height,
  101. 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
  102. glBindTexture(GL_TEXTURE_2D, 0);
  103. glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
  104. GL_TEXTURE_2D, img, 0);
  105. // check status
  106. GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
  107. // unbind framebuffer
  108. glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
  109. glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, (GLuint)current_fbo);
  110. return status;
  111. }
  112. virtual void deleteFBO(GLuint framebuffer, GLuint depth_stencil, GLuint img)
  113. {
  114. glDeleteTextures(1, &img);
  115. glDeleteRenderbuffersEXT(1, &depth_stencil);
  116. glDeleteFramebuffersEXT(1, &framebuffer);
  117. }
  118. virtual void bindFBO(GLuint framebuffer)
  119. {
  120. glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer);
  121. }
  122. };
  123. FramebufferStrategy* strategy;
  124. FramebufferStrategy strategyNone;
  125. FramebufferStrategyGL3 strategyGL3;
  126. FramebufferStrategyEXT strategyEXT;
  127. Canvas* Canvas::current = NULL;
  128. static void loadStrategy()
  129. {
  130. if (!strategy) {
  131. if (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_object)
  132. strategy = &strategyGL3;
  133. else if (GLEE_EXT_framebuffer_object && GLEE_EXT_packed_depth_stencil)
  134. strategy = &strategyEXT;
  135. else
  136. strategy = &strategyNone;
  137. }
  138. }
  139. Canvas::Canvas(int width, int height) :
  140. width(width), height(height)
  141. {
  142. strategy = NULL;
  143. float w = static_cast<float>(width);
  144. float h = static_cast<float>(height);
  145. // world coordinates
  146. vertices[0].x = 0; vertices[0].y = 0;
  147. vertices[1].x = 0; vertices[1].y = h;
  148. vertices[2].x = w; vertices[2].y = h;
  149. vertices[3].x = w; vertices[3].y = 0;
  150. // texture coordinates
  151. vertices[0].s = 0; vertices[0].t = 0;
  152. vertices[1].s = 0; vertices[1].t = 1;
  153. vertices[2].s = 1; vertices[2].t = 1;
  154. vertices[3].s = 1; vertices[3].t = 0;
  155. loadStrategy();
  156. loadVolatile();
  157. }
  158. Canvas::~Canvas()
  159. {
  160. // reset framebuffer if still using this one
  161. if (current == this)
  162. stopGrab();
  163. unloadVolatile();
  164. }
  165. bool Canvas::isSupported()
  166. {
  167. loadStrategy();
  168. return (strategy != &strategyNone);
  169. }
  170. void Canvas::bindDefaultCanvas()
  171. {
  172. if (current != NULL)
  173. current->stopGrab();
  174. }
  175. void Canvas::startGrab()
  176. {
  177. // already grabbing
  178. if (current == this)
  179. return;
  180. // cleanup after previous fbo
  181. if (current != NULL)
  182. current->stopGrab();
  183. // bind buffer and clear screen
  184. glPushAttrib(GL_VIEWPORT_BIT | GL_TRANSFORM_BIT);
  185. strategy->bindFBO(fbo);
  186. glViewport(0, 0, width, height);
  187. // Reset the projection matrix
  188. glMatrixMode(GL_PROJECTION);
  189. glPushMatrix();
  190. glLoadIdentity();
  191. // Set up upside-down orthographic view (no depth)
  192. glOrtho(0.0, width, 0.0, height, -1.0, 1.0);
  193. // Switch back to modelview matrix
  194. glMatrixMode(GL_MODELVIEW);
  195. // indicate we are using this fbo
  196. current = this;
  197. }
  198. void Canvas::stopGrab()
  199. {
  200. // i am not grabbing. leave me alone
  201. if (current != this)
  202. return;
  203. // bind default
  204. strategy->bindFBO( 0 );
  205. glMatrixMode(GL_PROJECTION);
  206. glPopMatrix();
  207. glPopAttrib();
  208. current = NULL;
  209. }
  210. void Canvas::clear(const Color& c)
  211. {
  212. GLuint previous = 0;
  213. if (current != NULL)
  214. previous = current->fbo;
  215. strategy->bindFBO(fbo);
  216. glPushAttrib(GL_COLOR_BUFFER_BIT);
  217. glClearColor((float)c.r/255.0f, (float)c.g/255.0f, (float)c.b/255.0f, (float)c.a/255.0f);
  218. glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
  219. glPopAttrib();
  220. strategy->bindFBO(previous);
  221. }
  222. void Canvas::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
  223. {
  224. static Matrix t;
  225. t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
  226. drawv(t, vertices);
  227. }
  228. void Canvas::drawq(love::graphics::Quad * quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
  229. {
  230. static Matrix t;
  231. const vertex * v = quad->getVertices();
  232. t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
  233. drawv(t, v);
  234. }
  235. love::image::ImageData * Canvas::getImageData(love::image::Image * image)
  236. {
  237. GLubyte pixels[4*width*height];
  238. strategy->bindFBO( fbo );
  239. glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
  240. if (current)
  241. strategy->bindFBO( current->fbo );
  242. else
  243. strategy->bindFBO( 0 );
  244. love::image::ImageData * img = image->newImageData(width, height, (void*)pixels);
  245. return img;
  246. }
  247. void Canvas::setFilter(const Image::Filter &f)
  248. {
  249. GLint gmin = (f.min == Image::FILTER_NEAREST) ? GL_NEAREST : GL_LINEAR;
  250. GLint gmag = (f.mag == Image::FILTER_NEAREST) ? GL_NEAREST : GL_LINEAR;
  251. glBindTexture(GL_TEXTURE_2D, img);
  252. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gmin);
  253. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gmag);
  254. }
  255. Image::Filter Canvas::getFilter() const
  256. {
  257. GLint gmin, gmag;
  258. glBindTexture(GL_TEXTURE_2D, img);
  259. glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, &gmin);
  260. glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, &gmag);
  261. Image::Filter f;
  262. f.min = (gmin == GL_NEAREST) ? Image::FILTER_NEAREST : Image::FILTER_LINEAR;
  263. f.mag = (gmag == GL_NEAREST) ? Image::FILTER_NEAREST : Image::FILTER_LINEAR;
  264. return f;
  265. }
  266. void Canvas::setWrap(const Image::Wrap &w)
  267. {
  268. GLint wrap_s = (w.s == Image::WRAP_CLAMP) ? GL_CLAMP_TO_EDGE : GL_REPEAT;
  269. GLint wrap_t = (w.t == Image::WRAP_CLAMP) ? GL_CLAMP_TO_EDGE : GL_REPEAT;
  270. glBindTexture(GL_TEXTURE_2D, img);
  271. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_s);
  272. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_t);
  273. }
  274. Image::Wrap Canvas::getWrap() const
  275. {
  276. GLint wrap_s, wrap_t;
  277. glBindTexture(GL_TEXTURE_2D, img);
  278. glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, &wrap_s);
  279. glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, &wrap_t);
  280. Image::Wrap w;
  281. w.s = (wrap_s == GL_CLAMP_TO_EDGE) ? Image::WRAP_CLAMP : Image::WRAP_REPEAT;
  282. w.t = (wrap_t == GL_CLAMP_TO_EDGE) ? Image::WRAP_CLAMP : Image::WRAP_REPEAT;
  283. return w;
  284. }
  285. bool Canvas::loadVolatile()
  286. {
  287. status = strategy->createFBO(fbo, depth_stencil, img, width, height);
  288. if (status != GL_FRAMEBUFFER_COMPLETE)
  289. return false;
  290. setFilter(settings.filter);
  291. setWrap(settings.wrap);
  292. Color c;
  293. c.r = c.g = c.b = c.a = 0;
  294. clear(c);
  295. return true;
  296. }
  297. void Canvas::unloadVolatile()
  298. {
  299. settings.filter = getFilter();
  300. settings.wrap = getWrap();
  301. strategy->deleteFBO(fbo, depth_stencil, img);
  302. }
  303. int Canvas::getWidth()
  304. {
  305. return width;
  306. }
  307. int Canvas::getHeight()
  308. {
  309. return height;
  310. }
  311. void Canvas::drawv(const Matrix & t, const vertex * v) const
  312. {
  313. glPushMatrix();
  314. glMultMatrixf((const GLfloat*)t.getElements());
  315. glBindTexture(GL_TEXTURE_2D, img);
  316. glEnableClientState(GL_VERTEX_ARRAY);
  317. glEnableClientState(GL_TEXTURE_COORD_ARRAY);
  318. glVertexPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid*)&v[0].x);
  319. glTexCoordPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid*)&v[0].s);
  320. glDrawArrays(GL_QUADS, 0, 4);
  321. glDisableClientState(GL_TEXTURE_COORD_ARRAY);
  322. glDisableClientState(GL_VERTEX_ARRAY);
  323. glPopMatrix();
  324. }
  325. } // opengl
  326. } // graphics
  327. } // love