Image.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721
  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. #include "Image.h"
  21. // STD
  22. #include <cstring> // For memcpy
  23. #include <algorithm> // for min/max
  24. namespace love
  25. {
  26. namespace graphics
  27. {
  28. namespace opengl
  29. {
  30. int Image::imageCount = 0;
  31. float Image::maxMipmapSharpness = 0.0f;
  32. Texture::FilterMode Image::defaultMipmapFilter = Texture::FILTER_NONE;
  33. float Image::defaultMipmapSharpness = 0.0f;
  34. Image::Image(love::image::ImageData *data, Format format)
  35. : data(data)
  36. , cdata(nullptr)
  37. , paddedWidth(width)
  38. , paddedHeight(height)
  39. , texture(0)
  40. , mipmapSharpness(defaultMipmapSharpness)
  41. , mipmapsCreated(false)
  42. , compressed(false)
  43. , format(format)
  44. , usingDefaultTexture(false)
  45. , textureMemorySize(0)
  46. {
  47. width = data->getWidth();
  48. height = data->getHeight();
  49. preload();
  50. ++imageCount;
  51. }
  52. Image::Image(love::image::CompressedData *cdata, Format format)
  53. : data(nullptr)
  54. , cdata(cdata)
  55. , paddedWidth(width)
  56. , paddedHeight(height)
  57. , texture(0)
  58. , mipmapSharpness(defaultMipmapSharpness)
  59. , mipmapsCreated(false)
  60. , compressed(true)
  61. , format(format)
  62. , usingDefaultTexture(false)
  63. , textureMemorySize(0)
  64. {
  65. width = cdata->getWidth(0);
  66. height = cdata->getHeight(0);
  67. preload();
  68. ++imageCount;
  69. }
  70. Image::~Image()
  71. {
  72. unload();
  73. --imageCount;
  74. }
  75. love::image::ImageData *Image::getImageData() const
  76. {
  77. return data.get();
  78. }
  79. love::image::CompressedData *Image::getCompressedData() const
  80. {
  81. return cdata.get();
  82. }
  83. void Image::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
  84. {
  85. Matrix t;
  86. t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
  87. drawv(t, vertices);
  88. }
  89. void Image::drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
  90. {
  91. Matrix t;
  92. t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
  93. drawv(t, quad->getVertices());
  94. }
  95. void Image::predraw()
  96. {
  97. bind();
  98. if (width != paddedWidth || height != paddedHeight)
  99. {
  100. // NPOT image padded to POT size, so the texcoords should be scaled.
  101. glMatrixMode(GL_TEXTURE);
  102. glPushMatrix();
  103. glScalef(float(width) / float(paddedWidth), float(height) / float(paddedHeight), 0.0f);
  104. glMatrixMode(GL_MODELVIEW);
  105. }
  106. }
  107. void Image::postdraw()
  108. {
  109. if (width != paddedWidth || height != paddedHeight)
  110. {
  111. glMatrixMode(GL_TEXTURE);
  112. glPopMatrix();
  113. glMatrixMode(GL_MODELVIEW);
  114. }
  115. }
  116. GLuint Image::getGLTexture() const
  117. {
  118. return texture;
  119. }
  120. void Image::uploadCompressedMipmaps()
  121. {
  122. if (!isCompressed() || !cdata.get() || !hasCompressedTextureSupport(cdata->getFormat()))
  123. return;
  124. bind();
  125. int count = cdata->getMipmapCount();
  126. // We have to inform OpenGL if the image doesn't have all mipmap levels.
  127. if (GLEE_VERSION_1_2 || GLEE_SGIS_texture_lod)
  128. {
  129. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, count - 1);
  130. }
  131. else if (cdata->getWidth(count-1) > 1 || cdata->getHeight(count-1) > 1)
  132. {
  133. // Telling OpenGL to ignore certain levels isn't always supported.
  134. throw love::Exception("Cannot load mipmaps: "
  135. "compressed image does not have all required levels.");
  136. }
  137. size_t totalsize = cdata->getSize(0);
  138. for (int i = 1; i < count; i++)
  139. {
  140. totalsize += cdata->getSize(i);
  141. glCompressedTexImage2DARB(GL_TEXTURE_2D,
  142. i,
  143. getCompressedFormat(cdata->getFormat()),
  144. cdata->getWidth(i),
  145. cdata->getHeight(i),
  146. 0,
  147. GLsizei(cdata->getSize(i)),
  148. cdata->getData(i));
  149. }
  150. size_t prevmemsize = textureMemorySize;
  151. textureMemorySize = totalsize;
  152. gl.updateTextureMemorySize(prevmemsize, textureMemorySize);
  153. }
  154. void Image::createMipmaps()
  155. {
  156. // Only valid for Images created with ImageData.
  157. if (!data.get() || isCompressed())
  158. return;
  159. if (!hasMipmapSupport())
  160. throw love::Exception("Mipmap filtering is not supported on this system.");
  161. // Some old drivers claim support for NPOT textures, but fail when creating
  162. // mipmaps. We can't detect which systems will do this, so we fail gracefully
  163. // for all NPOT images.
  164. int w = int(width), h = int(height);
  165. if (w != next_p2(w) || h != next_p2(h))
  166. {
  167. throw love::Exception("Cannot create mipmaps: "
  168. "image does not have power of two dimensions.");
  169. }
  170. bind();
  171. // Prevent other threads from changing the ImageData while we upload it.
  172. love::thread::Lock lock(data->getMutex());
  173. if (hasNpot() && (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_object))
  174. {
  175. if (gl.getVendor() == OpenGL::VENDOR_ATI_AMD)
  176. {
  177. // AMD/ATI drivers have several bugs when generating mipmaps,
  178. // re-uploading the entire base image seems to be required.
  179. uploadTexture();
  180. // More bugs: http://www.opengl.org/wiki/Common_Mistakes#Automatic_mipmap_generation
  181. glEnable(GL_TEXTURE_2D);
  182. }
  183. glGenerateMipmap(GL_TEXTURE_2D);
  184. }
  185. else
  186. {
  187. glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
  188. glTexSubImage2D(GL_TEXTURE_2D,
  189. 0,
  190. 0,
  191. 0,
  192. (GLsizei)width,
  193. (GLsizei)height,
  194. GL_RGBA,
  195. GL_UNSIGNED_BYTE,
  196. data->getData());
  197. glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE);
  198. }
  199. size_t prevmemsize = textureMemorySize;
  200. textureMemorySize = paddedWidth * paddedHeight * 4 * 1.3333;
  201. gl.updateTextureMemorySize(prevmemsize, textureMemorySize);
  202. }
  203. void Image::checkMipmapsCreated()
  204. {
  205. if (mipmapsCreated || filter.mipmap == FILTER_NONE || usingDefaultTexture)
  206. return;
  207. if (isCompressed() && cdata.get() && hasCompressedTextureSupport(cdata->getFormat()))
  208. uploadCompressedMipmaps();
  209. else if (data.get())
  210. createMipmaps();
  211. else
  212. return;
  213. mipmapsCreated = true;
  214. }
  215. void Image::setFilter(const Texture::Filter &f)
  216. {
  217. filter = f;
  218. // We don't want filtering or (attempted) mipmaps on the default texture.
  219. if (usingDefaultTexture)
  220. {
  221. filter.mipmap = FILTER_NONE;
  222. filter.min = filter.mag = FILTER_NEAREST;
  223. }
  224. bind();
  225. gl.setTextureFilter(filter);
  226. checkMipmapsCreated();
  227. }
  228. void Image::setWrap(const Texture::Wrap &w)
  229. {
  230. wrap = w;
  231. bind();
  232. gl.setTextureWrap(w);
  233. }
  234. void Image::setMipmapSharpness(float sharpness)
  235. {
  236. if (hasMipmapSharpnessSupport())
  237. {
  238. // LOD bias has the range (-maxbias, maxbias)
  239. mipmapSharpness = std::min(std::max(sharpness, -maxMipmapSharpness + 0.01f), maxMipmapSharpness - 0.01f);
  240. bind();
  241. // negative bias is sharper
  242. glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -mipmapSharpness);
  243. }
  244. else
  245. mipmapSharpness = 0.0f;
  246. }
  247. float Image::getMipmapSharpness() const
  248. {
  249. return mipmapSharpness;
  250. }
  251. void Image::bind() const
  252. {
  253. if (texture == 0)
  254. return;
  255. gl.bindTexture(texture);
  256. }
  257. void Image::preload()
  258. {
  259. memset(vertices, 255, sizeof(Vertex)*4);
  260. vertices[0].x = 0;
  261. vertices[0].y = 0;
  262. vertices[1].x = 0;
  263. vertices[1].y = (float) height;
  264. vertices[2].x = (float) width;
  265. vertices[2].y = (float) height;
  266. vertices[3].x = (float) width;
  267. vertices[3].y = 0;
  268. vertices[0].s = 0;
  269. vertices[0].t = 0;
  270. vertices[1].s = 0;
  271. vertices[1].t = 1;
  272. vertices[2].s = 1;
  273. vertices[2].t = 1;
  274. vertices[3].s = 1;
  275. vertices[3].t = 0;
  276. filter.mipmap = defaultMipmapFilter;
  277. }
  278. bool Image::load()
  279. {
  280. return loadVolatile();
  281. }
  282. void Image::unload()
  283. {
  284. return unloadVolatile();
  285. }
  286. bool Image::loadVolatile()
  287. {
  288. if (format == FORMAT_SRGB && !hasSRGBSupport())
  289. throw love::Exception("sRGB images are not supported on this system.");
  290. if (isCompressed() && cdata.get() && !hasCompressedTextureSupport(cdata->getFormat()))
  291. {
  292. const char *str;
  293. if (image::CompressedData::getConstant(cdata->getFormat(), str))
  294. {
  295. throw love::Exception("Cannot create image: "
  296. "%s compressed images are not supported on this system.", str);
  297. }
  298. else
  299. throw love::Exception("cannot create image: format is not supported on this system.");
  300. }
  301. if (hasMipmapSharpnessSupport() && maxMipmapSharpness == 0.0f)
  302. glGetFloatv(GL_MAX_TEXTURE_LOD_BIAS, &maxMipmapSharpness);
  303. glGenTextures(1, &texture);
  304. gl.bindTexture(texture);
  305. filter.anisotropy = gl.setTextureFilter(filter);
  306. gl.setTextureWrap(wrap);
  307. setMipmapSharpness(mipmapSharpness);
  308. paddedWidth = width;
  309. paddedHeight = height;
  310. if (!hasNpot())
  311. {
  312. // NPOT textures will be padded to POT dimensions if necessary.
  313. paddedWidth = next_p2(width);
  314. paddedHeight = next_p2(height);
  315. }
  316. // Use a default texture if the size is too big for the system.
  317. if (paddedWidth > gl.getMaxTextureSize() || paddedHeight > gl.getMaxTextureSize())
  318. {
  319. uploadDefaultTexture();
  320. return true;
  321. }
  322. // Mutex lock will potentially cover texture loading and mipmap creation.
  323. love::thread::EmptyLock lock;
  324. if (data.get())
  325. lock.setLock(data->getMutex());
  326. while (glGetError() != GL_NO_ERROR); // Clear errors.
  327. if (hasNpot() || (width == paddedWidth && height == paddedHeight))
  328. uploadTexture();
  329. else
  330. uploadTexturePadded();
  331. GLenum glerr = glGetError();
  332. if (glerr != GL_NO_ERROR)
  333. throw love::Exception("Cannot create image (error code 0x%x)", glerr);
  334. if (isCompressed())
  335. textureMemorySize = cdata->getSize(0);
  336. else
  337. textureMemorySize = paddedWidth * paddedHeight * 4;
  338. gl.updateTextureMemorySize(0, textureMemorySize);
  339. usingDefaultTexture = false;
  340. mipmapsCreated = false;
  341. checkMipmapsCreated();
  342. return true;
  343. }
  344. void Image::uploadTexturePadded()
  345. {
  346. if (isCompressed() && cdata.get())
  347. {
  348. // Padded textures don't really work if they're compressed...
  349. throw love::Exception("Cannot create image: "
  350. "compressed NPOT images are not supported on this system.");
  351. }
  352. else if (data.get())
  353. {
  354. GLenum iformat = (format == FORMAT_SRGB) ? GL_SRGB8_ALPHA8 : GL_RGBA8;
  355. glTexImage2D(GL_TEXTURE_2D,
  356. 0,
  357. iformat,
  358. (GLsizei)paddedWidth,
  359. (GLsizei)paddedHeight,
  360. 0,
  361. GL_RGBA,
  362. GL_UNSIGNED_BYTE,
  363. 0);
  364. glTexSubImage2D(GL_TEXTURE_2D,
  365. 0,
  366. 0, 0,
  367. (GLsizei)width,
  368. (GLsizei)height,
  369. GL_RGBA,
  370. GL_UNSIGNED_BYTE,
  371. data->getData());
  372. }
  373. }
  374. void Image::uploadTexture()
  375. {
  376. if (isCompressed() && cdata.get())
  377. {
  378. GLenum format = getCompressedFormat(cdata->getFormat());
  379. glCompressedTexImage2DARB(GL_TEXTURE_2D,
  380. 0,
  381. format,
  382. cdata->getWidth(0),
  383. cdata->getHeight(0),
  384. 0,
  385. GLsizei(cdata->getSize(0)),
  386. cdata->getData(0));
  387. }
  388. else if (data.get())
  389. {
  390. GLenum iformat = (format == FORMAT_SRGB) ? GL_SRGB8_ALPHA8 : GL_RGBA8;
  391. glTexImage2D(GL_TEXTURE_2D,
  392. 0,
  393. iformat,
  394. (GLsizei)width,
  395. (GLsizei)height,
  396. 0,
  397. GL_RGBA,
  398. GL_UNSIGNED_BYTE,
  399. data->getData());
  400. }
  401. }
  402. void Image::unloadVolatile()
  403. {
  404. // Delete the hardware texture.
  405. if (texture != 0)
  406. {
  407. gl.deleteTexture(texture);
  408. texture = 0;
  409. gl.updateTextureMemorySize(textureMemorySize, 0);
  410. textureMemorySize = 0;
  411. }
  412. }
  413. bool Image::refresh()
  414. {
  415. // No effect if the texture hasn't been created yet.
  416. if (texture == 0)
  417. return false;
  418. if (usingDefaultTexture)
  419. {
  420. uploadDefaultTexture();
  421. return true;
  422. }
  423. // We want this lock to potentially cover mipmap creation as well.
  424. love::thread::EmptyLock lock;
  425. bind();
  426. if (data.get() && !isCompressed())
  427. lock.setLock(data->getMutex());
  428. while (glGetError() != GL_NO_ERROR); // Clear errors.
  429. if (hasNpot() || (width == paddedWidth && height == paddedHeight))
  430. uploadTexture();
  431. else
  432. uploadTexturePadded();
  433. if (glGetError() != GL_NO_ERROR)
  434. uploadDefaultTexture();
  435. else
  436. usingDefaultTexture = false;
  437. mipmapsCreated = false;
  438. checkMipmapsCreated();
  439. return true;
  440. }
  441. Image::Format Image::getFormat() const
  442. {
  443. return format;
  444. }
  445. void Image::uploadDefaultTexture()
  446. {
  447. usingDefaultTexture = true;
  448. bind();
  449. setFilter(filter);
  450. // A nice friendly checkerboard to signify invalid textures...
  451. GLubyte px[] = {0xFF,0xFF,0xFF,0xFF, 0xC0,0xC0,0xC0,0xFF,
  452. 0xC0,0xC0,0xC0,0xFF, 0xFF,0xFF,0xFF,0xFF};
  453. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, px);
  454. }
  455. void Image::drawv(const Matrix &t, const Vertex *v)
  456. {
  457. OpenGL::TempTransform transform(gl);
  458. transform.get() *= t;
  459. predraw();
  460. glEnableClientState(GL_VERTEX_ARRAY);
  461. glEnableClientState(GL_TEXTURE_COORD_ARRAY);
  462. glVertexPointer(2, GL_FLOAT, sizeof(Vertex), (GLvoid *)&v[0].x);
  463. glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), (GLvoid *)&v[0].s);
  464. gl.prepareDraw();
  465. gl.drawArrays(GL_QUADS, 0, 4);
  466. glDisableClientState(GL_TEXTURE_COORD_ARRAY);
  467. glDisableClientState(GL_VERTEX_ARRAY);
  468. postdraw();
  469. }
  470. void Image::setDefaultMipmapSharpness(float sharpness)
  471. {
  472. defaultMipmapSharpness = sharpness;
  473. }
  474. float Image::getDefaultMipmapSharpness()
  475. {
  476. return defaultMipmapSharpness;
  477. }
  478. void Image::setDefaultMipmapFilter(Texture::FilterMode f)
  479. {
  480. defaultMipmapFilter = f;
  481. }
  482. Texture::FilterMode Image::getDefaultMipmapFilter()
  483. {
  484. return defaultMipmapFilter;
  485. }
  486. bool Image::isCompressed() const
  487. {
  488. return compressed;
  489. }
  490. GLenum Image::getCompressedFormat(image::CompressedData::Format cformat) const
  491. {
  492. bool srgb = format == FORMAT_SRGB;
  493. switch (cformat)
  494. {
  495. case image::CompressedData::FORMAT_DXT1:
  496. if (srgb)
  497. return GL_COMPRESSED_SRGB_S3TC_DXT1_EXT;
  498. else
  499. return GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
  500. case image::CompressedData::FORMAT_DXT3:
  501. if (srgb)
  502. return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT;
  503. else
  504. return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
  505. case image::CompressedData::FORMAT_DXT5:
  506. if (srgb)
  507. return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
  508. else
  509. return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
  510. case image::CompressedData::FORMAT_BC4:
  511. return GL_COMPRESSED_RED_RGTC1;
  512. case image::CompressedData::FORMAT_BC4s:
  513. return GL_COMPRESSED_SIGNED_RED_RGTC1;
  514. case image::CompressedData::FORMAT_BC5:
  515. return GL_COMPRESSED_RG_RGTC2;
  516. case image::CompressedData::FORMAT_BC5s:
  517. return GL_COMPRESSED_SIGNED_RG_RGTC2;
  518. case image::CompressedData::FORMAT_BC6H:
  519. return GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB;
  520. case image::CompressedData::FORMAT_BC6Hs:
  521. return GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB;
  522. case image::CompressedData::FORMAT_BC7:
  523. if (srgb)
  524. return GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB;
  525. else
  526. return GL_COMPRESSED_RGBA_BPTC_UNORM_ARB;
  527. case image::CompressedData::FORMAT_BC7SRGB:
  528. return GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB;
  529. default:
  530. if (srgb)
  531. return GL_SRGB8_ALPHA8;
  532. else
  533. return GL_RGBA8;
  534. }
  535. }
  536. bool Image::hasNpot()
  537. {
  538. return GLEE_VERSION_2_0 || GLEE_ARB_texture_non_power_of_two;
  539. }
  540. bool Image::hasAnisotropicFilteringSupport()
  541. {
  542. return GLEE_EXT_texture_filter_anisotropic;
  543. }
  544. bool Image::hasMipmapSupport()
  545. {
  546. return GLEE_VERSION_1_4 || GLEE_SGIS_generate_mipmap;
  547. }
  548. bool Image::hasMipmapSharpnessSupport()
  549. {
  550. return GLEE_VERSION_1_4;
  551. }
  552. bool Image::hasCompressedTextureSupport()
  553. {
  554. return GLEE_VERSION_1_3 || GLEE_ARB_texture_compression;
  555. }
  556. bool Image::hasCompressedTextureSupport(image::CompressedData::Format format)
  557. {
  558. if (!hasCompressedTextureSupport())
  559. return false;
  560. switch (format)
  561. {
  562. case image::CompressedData::FORMAT_DXT1:
  563. case image::CompressedData::FORMAT_DXT3:
  564. case image::CompressedData::FORMAT_DXT5:
  565. return GLEE_EXT_texture_compression_s3tc;
  566. case image::CompressedData::FORMAT_BC4:
  567. case image::CompressedData::FORMAT_BC4s:
  568. case image::CompressedData::FORMAT_BC5:
  569. case image::CompressedData::FORMAT_BC5s:
  570. return (GLEE_VERSION_3_0 || GLEE_ARB_texture_compression_rgtc || GLEE_EXT_texture_compression_rgtc);
  571. case image::CompressedData::FORMAT_BC6H:
  572. case image::CompressedData::FORMAT_BC6Hs:
  573. case image::CompressedData::FORMAT_BC7:
  574. case image::CompressedData::FORMAT_BC7SRGB:
  575. return GLEE_VERSION_4_2 || GLEE_ARB_texture_compression_bptc;
  576. default:
  577. return false;
  578. }
  579. }
  580. bool Image::hasSRGBSupport()
  581. {
  582. return GLEE_VERSION_2_1 || GLEE_EXT_texture_sRGB;
  583. }
  584. bool Image::getConstant(const char *in, Format &out)
  585. {
  586. return formats.find(in, out);
  587. }
  588. bool Image::getConstant(Format in, const char *&out)
  589. {
  590. return formats.find(in, out);
  591. }
  592. StringMap<Image::Format, Image::FORMAT_MAX_ENUM>::Entry Image::formatEntries[] =
  593. {
  594. {"normal", Image::FORMAT_NORMAL},
  595. {"srgb", Image::FORMAT_SRGB},
  596. };
  597. StringMap<Image::Format, Image::FORMAT_MAX_ENUM> Image::formats(Image::formatEntries, sizeof(Image::formatEntries));
  598. } // opengl
  599. } // graphics
  600. } // love