Canvas.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645
  1. /**
  2. * Copyright (c) 2006-2016 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 <cstring> // For memcpy
  25. #include <algorithm> // For min/max
  26. namespace love
  27. {
  28. namespace graphics
  29. {
  30. namespace opengl
  31. {
  32. static GLenum createFBO(GLuint &framebuffer, GLuint texture)
  33. {
  34. // get currently bound fbo to reset to it later
  35. GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL);
  36. glGenFramebuffers(1, &framebuffer);
  37. gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, framebuffer);
  38. if (texture != 0)
  39. {
  40. glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
  41. // Initialize the texture to transparent black.
  42. glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  43. glClear(GL_COLOR_BUFFER_BIT);
  44. }
  45. GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
  46. gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo);
  47. return status;
  48. }
  49. static bool createMSAABuffer(int width, int height, int &samples, GLenum iformat, GLuint &buffer)
  50. {
  51. GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL);
  52. // Temporary FBO used to clear the renderbuffer.
  53. GLuint fbo = 0;
  54. glGenFramebuffers(1, &fbo);
  55. gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, fbo);
  56. glGenRenderbuffers(1, &buffer);
  57. glBindRenderbuffer(GL_RENDERBUFFER, buffer);
  58. glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, iformat, width, height);
  59. glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, buffer);
  60. glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
  61. glBindRenderbuffer(GL_RENDERBUFFER, 0);
  62. GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
  63. if (status == GL_FRAMEBUFFER_COMPLETE && samples > 1)
  64. {
  65. // Initialize the buffer to transparent black.
  66. glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  67. glClear(GL_COLOR_BUFFER_BIT);
  68. }
  69. else
  70. {
  71. glDeleteRenderbuffers(1, &buffer);
  72. buffer = 0;
  73. samples = 0;
  74. }
  75. gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo);
  76. gl.deleteFramebuffer(fbo);
  77. return status == GL_FRAMEBUFFER_COMPLETE && samples > 1;
  78. }
  79. int Canvas::canvasCount = 0;
  80. Canvas::Canvas(int width, int height, Format format, int msaa)
  81. : fbo(0)
  82. , texture(0)
  83. , msaa_buffer(0)
  84. , format(format)
  85. , requested_samples(msaa)
  86. , actual_samples(0)
  87. , texture_memory(0)
  88. {
  89. this->width = width;
  90. this->height = height;
  91. float w = static_cast<float>(width);
  92. float h = static_cast<float>(height);
  93. // Vertices are ordered for use with triangle strips:
  94. // 0----2
  95. // | / |
  96. // | / |
  97. // 1----3
  98. // world coordinates
  99. vertices[0].x = 0;
  100. vertices[0].y = 0;
  101. vertices[1].x = 0;
  102. vertices[1].y = h;
  103. vertices[2].x = w;
  104. vertices[2].y = 0;
  105. vertices[3].x = w;
  106. vertices[3].y = h;
  107. // texture coordinates
  108. vertices[0].s = 0;
  109. vertices[0].t = 0;
  110. vertices[1].s = 0;
  111. vertices[1].t = 1;
  112. vertices[2].s = 1;
  113. vertices[2].t = 0;
  114. vertices[3].s = 1;
  115. vertices[3].t = 1;
  116. loadVolatile();
  117. ++canvasCount;
  118. }
  119. Canvas::~Canvas()
  120. {
  121. --canvasCount;
  122. unloadVolatile();
  123. }
  124. bool Canvas::loadVolatile()
  125. {
  126. if (texture != 0)
  127. return true;
  128. OpenGL::TempDebugGroup debuggroup("Canvas load");
  129. fbo = texture = 0;
  130. msaa_buffer = 0;
  131. status = GL_FRAMEBUFFER_COMPLETE;
  132. // glTexImage2D is guaranteed to error in this case.
  133. if (width > gl.getMaxTextureSize() || height > gl.getMaxTextureSize())
  134. {
  135. status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
  136. return false;
  137. }
  138. // getMaxRenderbufferSamples will be 0 on systems that don't support
  139. // multisampled renderbuffers / don't export FBO multisample extensions.
  140. requested_samples = std::min(requested_samples, gl.getMaxRenderbufferSamples());
  141. requested_samples = std::max(requested_samples, 0);
  142. glGenTextures(1, &texture);
  143. gl.bindTextureToUnit(texture, 0, false);
  144. if (GLAD_ANGLE_texture_usage)
  145. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_USAGE_ANGLE, GL_FRAMEBUFFER_ATTACHMENT_ANGLE);
  146. setFilter(filter);
  147. setWrap(wrap);
  148. GLenum internalformat = GL_RGBA;
  149. GLenum externalformat = GL_RGBA;
  150. GLenum textype = GL_UNSIGNED_BYTE;
  151. convertFormat(format, internalformat, externalformat, textype);
  152. // in GLES2, the internalformat and format params of TexImage have to match.
  153. GLint iformat = (GLint) internalformat;
  154. if (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0)
  155. iformat = (GLint) externalformat;
  156. while (glGetError() != GL_NO_ERROR)
  157. /* Clear the error buffer. */;
  158. glTexImage2D(GL_TEXTURE_2D, 0, iformat, width, height, 0, externalformat,
  159. textype, nullptr);
  160. if (glGetError() != GL_NO_ERROR)
  161. {
  162. gl.deleteTexture(texture);
  163. texture = 0;
  164. status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
  165. return false;
  166. }
  167. // Create a canvas-local FBO used for glReadPixels as well as MSAA blitting.
  168. status = createFBO(fbo, texture);
  169. if (status != GL_FRAMEBUFFER_COMPLETE)
  170. {
  171. if (fbo != 0)
  172. {
  173. gl.deleteFramebuffer(fbo);
  174. fbo = 0;
  175. }
  176. return false;
  177. }
  178. actual_samples = requested_samples == 1 ? 0 : requested_samples;
  179. if (actual_samples > 0 && !createMSAABuffer(width, height, actual_samples, internalformat, msaa_buffer))
  180. actual_samples = 0;
  181. size_t prevmemsize = texture_memory;
  182. texture_memory = ((getFormatBitsPerPixel(format) * width) / 8) * height;
  183. if (msaa_buffer != 0)
  184. texture_memory += (texture_memory * actual_samples);
  185. gl.updateTextureMemorySize(prevmemsize, texture_memory);
  186. return true;
  187. }
  188. void Canvas::unloadVolatile()
  189. {
  190. if (fbo != 0)
  191. gl.deleteFramebuffer(fbo);
  192. if (msaa_buffer != 0)
  193. glDeleteRenderbuffers(1, &msaa_buffer);
  194. if (texture != 0)
  195. gl.deleteTexture(texture);
  196. fbo = 0;
  197. msaa_buffer = 0;
  198. texture = 0;
  199. gl.updateTextureMemorySize(texture_memory, 0);
  200. texture_memory = 0;
  201. }
  202. void Canvas::drawv(const Matrix4 &t, const Vertex *v)
  203. {
  204. auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
  205. if (gfx != nullptr)
  206. {
  207. const PassInfo &info = gfx->getActivePass();
  208. for (const auto &attachment : info.colorAttachments)
  209. {
  210. if (attachment.canvas == this)
  211. throw love::Exception("Cannot render a Canvas to itself!");
  212. }
  213. }
  214. OpenGL::TempDebugGroup debuggroup("Canvas draw");
  215. OpenGL::TempTransform transform(gl);
  216. transform.get() *= t;
  217. gl.bindTextureToUnit(texture, 0, false);
  218. gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD);
  219. gl.bindBuffer(BUFFER_VERTEX, 0);
  220. glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &v[0].x);
  221. glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &v[0].s);
  222. gl.prepareDraw();
  223. gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4);
  224. }
  225. void Canvas::draw(const Matrix4 &m)
  226. {
  227. drawv(m, vertices);
  228. }
  229. void Canvas::drawq(Quad *quad, const Matrix4 &m)
  230. {
  231. drawv(m, quad->getVertices());
  232. }
  233. void Canvas::setFilter(const Texture::Filter &f)
  234. {
  235. if (!validateFilter(f, false))
  236. throw love::Exception("Invalid texture filter.");
  237. filter = f;
  238. gl.bindTextureToUnit(texture, 0, false);
  239. gl.setTextureFilter(filter);
  240. }
  241. bool Canvas::setWrap(const Texture::Wrap &w)
  242. {
  243. bool success = true;
  244. wrap = w;
  245. if ((GLAD_ES_VERSION_2_0 && !(GLAD_ES_VERSION_3_0 || GLAD_OES_texture_npot))
  246. && (width != nextP2(width) || height != nextP2(height)))
  247. {
  248. if (wrap.s != WRAP_CLAMP || wrap.t != WRAP_CLAMP)
  249. success = false;
  250. // If we only have limited NPOT support then the wrap mode must be CLAMP.
  251. wrap.s = wrap.t = WRAP_CLAMP;
  252. }
  253. if (!gl.isClampZeroTextureWrapSupported())
  254. {
  255. if (wrap.s == WRAP_CLAMP_ZERO)
  256. wrap.s = WRAP_CLAMP;
  257. if (wrap.t == WRAP_CLAMP_ZERO)
  258. wrap.t = WRAP_CLAMP;
  259. }
  260. gl.bindTextureToUnit(texture, 0, false);
  261. gl.setTextureWrap(wrap);
  262. return success;
  263. }
  264. const void *Canvas::getHandle() const
  265. {
  266. return &texture;
  267. }
  268. Canvas::Format Canvas::getSizedFormat(Canvas::Format format)
  269. {
  270. switch (format)
  271. {
  272. case FORMAT_NORMAL:
  273. if (isGammaCorrect())
  274. return FORMAT_SRGB;
  275. else if (GLAD_ES_VERSION_2_0 && !(GLAD_ES_VERSION_3_0 || GLAD_OES_rgb8_rgba8 || GLAD_ARM_rgba8))
  276. // 32-bit render targets don't have guaranteed support on GLES2.
  277. return FORMAT_RGBA4;
  278. else
  279. return FORMAT_RGBA8;
  280. case FORMAT_HDR:
  281. return FORMAT_RGBA16F;
  282. default:
  283. return format;
  284. }
  285. }
  286. void Canvas::convertFormat(Canvas::Format format, GLenum &internalformat, GLenum &externalformat, GLenum &type)
  287. {
  288. format = getSizedFormat(format);
  289. externalformat = GL_RGBA;
  290. switch (format)
  291. {
  292. case FORMAT_RGBA4:
  293. internalformat = GL_RGBA4;
  294. type = GL_UNSIGNED_SHORT_4_4_4_4;
  295. break;
  296. case FORMAT_RGB5A1:
  297. internalformat = GL_RGB5_A1;
  298. type = GL_UNSIGNED_SHORT_5_5_5_1;
  299. break;
  300. case FORMAT_RGB565:
  301. internalformat = GL_RGB565;
  302. externalformat = GL_RGB;
  303. type = GL_UNSIGNED_SHORT_5_6_5;
  304. break;
  305. case FORMAT_R8:
  306. internalformat = GL_R8;
  307. externalformat = GL_RED;
  308. type = GL_UNSIGNED_BYTE;
  309. break;
  310. case FORMAT_RG8:
  311. internalformat = GL_RG8;
  312. externalformat = GL_RG;
  313. type = GL_UNSIGNED_BYTE;
  314. break;
  315. case FORMAT_RGBA8:
  316. default:
  317. internalformat = GL_RGBA8;
  318. type = GL_UNSIGNED_BYTE;
  319. break;
  320. case FORMAT_RGB10A2:
  321. internalformat = GL_RGB10_A2;
  322. type = GL_UNSIGNED_INT_2_10_10_10_REV;
  323. break;
  324. case FORMAT_RG11B10F:
  325. internalformat = GL_R11F_G11F_B10F;
  326. externalformat = GL_RGB;
  327. type = GL_UNSIGNED_INT_10F_11F_11F_REV;
  328. break;
  329. case FORMAT_R16F:
  330. internalformat = GL_R16F;
  331. externalformat = GL_RED;
  332. if (GLAD_OES_texture_half_float)
  333. type = GL_HALF_FLOAT_OES;
  334. else if (GLAD_VERSION_1_0)
  335. type = GL_FLOAT;
  336. else
  337. type = GL_HALF_FLOAT;
  338. break;
  339. case FORMAT_RG16F:
  340. internalformat = GL_RG16F;
  341. externalformat = GL_RG;
  342. if (GLAD_OES_texture_half_float)
  343. type = GL_HALF_FLOAT_OES;
  344. else if (GLAD_VERSION_1_0)
  345. type = GL_FLOAT;
  346. else
  347. type = GL_HALF_FLOAT;
  348. break;
  349. case FORMAT_RGBA16F:
  350. internalformat = GL_RGBA16F;
  351. if (GLAD_OES_texture_half_float)
  352. type = GL_HALF_FLOAT_OES;
  353. else if (GLAD_VERSION_1_0)
  354. type = GL_FLOAT;
  355. else
  356. type = GL_HALF_FLOAT;
  357. break;
  358. case FORMAT_R32F:
  359. internalformat = GL_R32F;
  360. externalformat = GL_RED;
  361. type = GL_FLOAT;
  362. break;
  363. case FORMAT_RG32F:
  364. internalformat = GL_RG32F;
  365. externalformat = GL_RG;
  366. type = GL_FLOAT;
  367. break;
  368. case FORMAT_RGBA32F:
  369. internalformat = GL_RGBA32F;
  370. type = GL_FLOAT;
  371. break;
  372. case FORMAT_SRGB:
  373. internalformat = GL_SRGB8_ALPHA8;
  374. type = GL_UNSIGNED_BYTE;
  375. if (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0)
  376. externalformat = GL_SRGB_ALPHA;
  377. break;
  378. }
  379. }
  380. size_t Canvas::getFormatBitsPerPixel(Format format)
  381. {
  382. switch (getSizedFormat(format))
  383. {
  384. case FORMAT_R8:
  385. return 8;
  386. case FORMAT_RGBA4:
  387. case FORMAT_RGB5A1:
  388. case FORMAT_RGB565:
  389. case FORMAT_RG8:
  390. case FORMAT_R16F:
  391. return 16;
  392. case FORMAT_RGBA8:
  393. case FORMAT_RGB10A2:
  394. case FORMAT_RG11B10F:
  395. case FORMAT_RG16F:
  396. case FORMAT_R32F:
  397. case FORMAT_SRGB:
  398. default:
  399. return 32;
  400. case FORMAT_RGBA16F:
  401. case FORMAT_RG32F:
  402. return 64;
  403. case FORMAT_RGBA32F:
  404. return 128;
  405. }
  406. }
  407. bool Canvas::isSupported()
  408. {
  409. return GLAD_ES_VERSION_2_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object || GLAD_EXT_framebuffer_object;
  410. }
  411. bool Canvas::isMultiFormatMultiCanvasSupported()
  412. {
  413. return gl.getMaxRenderTargets() > 1 && (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object);
  414. }
  415. bool Canvas::supportedFormats[] = {false};
  416. bool Canvas::checkedFormats[] = {false};
  417. bool Canvas::isFormatSupported(Canvas::Format format)
  418. {
  419. if (!isSupported())
  420. return false;
  421. bool supported = true;
  422. format = getSizedFormat(format);
  423. switch (format)
  424. {
  425. case FORMAT_RGBA4:
  426. case FORMAT_RGB5A1:
  427. supported = true;
  428. break;
  429. case FORMAT_RGB565:
  430. supported = GLAD_ES_VERSION_2_0 || GLAD_VERSION_4_2 || GLAD_ARB_ES2_compatibility;
  431. break;
  432. case FORMAT_R8:
  433. case FORMAT_RG8:
  434. if (GLAD_VERSION_1_0)
  435. supported = GLAD_VERSION_3_0 || GLAD_ARB_texture_rg;
  436. else if (GLAD_ES_VERSION_2_0)
  437. supported = GLAD_ES_VERSION_3_0 || GLAD_EXT_texture_rg;
  438. break;
  439. case FORMAT_RGBA8:
  440. supported = GLAD_VERSION_1_0 || GLAD_ES_VERSION_3_0 || GLAD_OES_rgb8_rgba8 || GLAD_ARM_rgba8;
  441. break;
  442. case FORMAT_RGB10A2:
  443. supported = GLAD_ES_VERSION_3_0 || GLAD_VERSION_1_0;
  444. break;
  445. case FORMAT_RG11B10F:
  446. supported = GLAD_VERSION_3_0 || GLAD_EXT_packed_float || GLAD_APPLE_color_buffer_packed_float;
  447. break;
  448. case FORMAT_R16F:
  449. case FORMAT_RG16F:
  450. if (GLAD_VERSION_1_0)
  451. supported = GLAD_VERSION_3_0 || (GLAD_ARB_texture_float && GLAD_ARB_texture_rg);
  452. else
  453. supported = GLAD_EXT_color_buffer_half_float && (GLAD_ES_VERSION_3_0 || (GLAD_OES_texture_half_float && GLAD_EXT_texture_rg));
  454. break;
  455. case FORMAT_RGBA16F:
  456. if (GLAD_VERSION_1_0)
  457. supported = GLAD_VERSION_3_0 || GLAD_ARB_texture_float;
  458. else if (GLAD_ES_VERSION_2_0)
  459. supported = GLAD_EXT_color_buffer_half_float && (GLAD_ES_VERSION_3_0 || GLAD_OES_texture_half_float);
  460. break;
  461. case FORMAT_R32F:
  462. case FORMAT_RG32F:
  463. supported = GLAD_VERSION_3_0 || (GLAD_ARB_texture_float && GLAD_ARB_texture_rg);
  464. break;
  465. case FORMAT_RGBA32F:
  466. supported = GLAD_VERSION_3_0 || GLAD_ARB_texture_float;
  467. break;
  468. case FORMAT_SRGB:
  469. if (GLAD_VERSION_1_0)
  470. {
  471. supported = GLAD_VERSION_3_0 || ((GLAD_ARB_framebuffer_sRGB || GLAD_EXT_framebuffer_sRGB)
  472. && (GLAD_VERSION_2_1 || GLAD_EXT_texture_sRGB));
  473. }
  474. else
  475. supported = GLAD_ES_VERSION_3_0 || GLAD_EXT_sRGB;
  476. break;
  477. default:
  478. supported = false;
  479. break;
  480. }
  481. if (!supported)
  482. return false;
  483. if (checkedFormats[format])
  484. return supportedFormats[format];
  485. // Even though we might have the necessary OpenGL version or extension,
  486. // drivers are still allowed to throw FRAMEBUFFER_UNSUPPORTED when attaching
  487. // a texture to a FBO whose format the driver doesn't like. So we should
  488. // test with an actual FBO.
  489. GLenum internalformat = GL_RGBA;
  490. GLenum externalformat = GL_RGBA;
  491. GLenum textype = GL_UNSIGNED_BYTE;
  492. convertFormat(format, internalformat, externalformat, textype);
  493. // in GLES2, the internalformat and format params of TexImage have to match.
  494. if (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0)
  495. internalformat = externalformat;
  496. GLuint texture = 0;
  497. glGenTextures(1, &texture);
  498. gl.bindTextureToUnit(texture, 0, false);
  499. Texture::Filter f;
  500. f.min = f.mag = Texture::FILTER_NEAREST;
  501. gl.setTextureFilter(f);
  502. Texture::Wrap w;
  503. gl.setTextureWrap(w);
  504. glTexImage2D(GL_TEXTURE_2D, 0, internalformat, 2, 2, 0, externalformat, textype, nullptr);
  505. GLuint fbo = 0;
  506. supported = (createFBO(fbo, texture) == GL_FRAMEBUFFER_COMPLETE);
  507. gl.deleteFramebuffer(fbo);
  508. gl.deleteTexture(texture);
  509. // Cache the result so we don't do this for every isFormatSupported call.
  510. checkedFormats[format] = true;
  511. supportedFormats[format] = supported;
  512. return supported;
  513. }
  514. bool Canvas::getConstant(const char *in, Format &out)
  515. {
  516. return formats.find(in, out);
  517. }
  518. bool Canvas::getConstant(Format in, const char *&out)
  519. {
  520. return formats.find(in, out);
  521. }
  522. StringMap<Canvas::Format, Canvas::FORMAT_MAX_ENUM>::Entry Canvas::formatEntries[] =
  523. {
  524. {"normal", FORMAT_NORMAL},
  525. {"hdr", FORMAT_HDR},
  526. {"rgba4", FORMAT_RGBA4},
  527. {"rgb5a1", FORMAT_RGB5A1},
  528. {"rgb565", FORMAT_RGB565},
  529. {"r8", FORMAT_R8},
  530. {"rg8", FORMAT_RG8},
  531. {"rgba8", FORMAT_RGBA8},
  532. {"rgb10a2", FORMAT_RGB10A2},
  533. {"rg11b10f", FORMAT_RG11B10F},
  534. {"r16f", FORMAT_R16F},
  535. {"rg16f", FORMAT_RG16F},
  536. {"rgba16f", FORMAT_RGBA16F},
  537. {"r32f", FORMAT_R32F},
  538. {"rg32f", FORMAT_RG32F},
  539. {"rgba32f", FORMAT_RGBA32F},
  540. {"srgb", FORMAT_SRGB},
  541. };
  542. StringMap<Canvas::Format, Canvas::FORMAT_MAX_ENUM> Canvas::formats(Canvas::formatEntries, sizeof(Canvas::formatEntries));
  543. } // opengl
  544. } // graphics
  545. } // love