Canvas.cpp 11 KB

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