Image.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785
  1. /**
  2. * Copyright (c) 2006-2015 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. #include "graphics/Graphics.h"
  22. #include "common/int.h"
  23. // STD
  24. #include <algorithm> // for min/max
  25. #ifdef LOVE_ANDROID
  26. // log2 is not declared in the math.h shipped with the Android NDK
  27. #include <cmath>
  28. inline double log2(double n)
  29. {
  30. // log(n)/log(2) is log2.
  31. return std::log(n) / std::log(2);
  32. }
  33. #endif
  34. namespace love
  35. {
  36. namespace graphics
  37. {
  38. namespace opengl
  39. {
  40. int Image::imageCount = 0;
  41. float Image::maxMipmapSharpness = 0.0f;
  42. Texture::FilterMode Image::defaultMipmapFilter = Texture::FILTER_LINEAR;
  43. float Image::defaultMipmapSharpness = 0.0f;
  44. static int getMipmapCount(int basewidth, int baseheight)
  45. {
  46. return (int) log2(std::max(basewidth, baseheight)) + 1;
  47. }
  48. template <typename T>
  49. static bool verifyMipmapLevels(const std::vector<T> &miplevels)
  50. {
  51. int numlevels = (int) miplevels.size();
  52. if (numlevels == 1)
  53. return false;
  54. int width = miplevels[0]->getWidth();
  55. int height = miplevels[0]->getHeight();
  56. int expectedlevels = getMipmapCount(width, height);
  57. // All mip levels must be present when not using auto-generated mipmaps.
  58. if (numlevels != expectedlevels)
  59. throw love::Exception("Image does not have all required mipmap levels (expected %d, got %d)", expectedlevels, numlevels);
  60. // Verify the size of each mip level.
  61. for (int i = 1; i < numlevels; i++)
  62. {
  63. width = std::max(width / 2, 1);
  64. height = std::max(height / 2, 1);
  65. if (miplevels[i]->getWidth() != width)
  66. throw love::Exception("Width of image mipmap level %d is incorrect (expected %d, got %d)", i+1, width, miplevels[i]->getWidth());
  67. if (miplevels[i]->getHeight() != height)
  68. throw love::Exception("Height of image mipmap level %d is incorrect (expected %d, got %d)", i+1, height, miplevels[i]->getHeight());
  69. }
  70. return true;
  71. }
  72. Image::Image(const std::vector<love::image::ImageData *> &imagedata, const Flags &flags)
  73. : texture(0)
  74. , mipmapSharpness(defaultMipmapSharpness)
  75. , compressed(false)
  76. , flags(flags)
  77. , sRGB(false)
  78. , usingDefaultTexture(false)
  79. , textureMemorySize(0)
  80. {
  81. if (imagedata.empty())
  82. throw love::Exception("");
  83. width = imagedata[0]->getWidth();
  84. height = imagedata[0]->getHeight();
  85. if (verifyMipmapLevels(imagedata))
  86. this->flags.mipmaps = true;
  87. for (const auto &id : imagedata)
  88. data.push_back(id);
  89. preload();
  90. loadVolatile();
  91. ++imageCount;
  92. }
  93. Image::Image(const std::vector<love::image::CompressedImageData *> &compresseddata, const Flags &flags)
  94. : texture(0)
  95. , mipmapSharpness(defaultMipmapSharpness)
  96. , compressed(true)
  97. , flags(flags)
  98. , sRGB(false)
  99. , usingDefaultTexture(false)
  100. , textureMemorySize(0)
  101. {
  102. width = compresseddata[0]->getWidth(0);
  103. height = compresseddata[0]->getHeight(0);
  104. if (verifyMipmapLevels(compresseddata))
  105. this->flags.mipmaps = true;
  106. else if (flags.mipmaps && getMipmapCount(width, height) != compresseddata[0]->getMipmapCount())
  107. {
  108. if (compresseddata[0]->getMipmapCount() == 1)
  109. this->flags.mipmaps = false;
  110. else
  111. throw love::Exception("Image cannot have mipmaps: compressed image data does not have all required mipmap levels.");
  112. }
  113. for (const auto &cd : compresseddata)
  114. {
  115. cdata.push_back(cd);
  116. if (cd->getFormat() != cdata[0]->getFormat())
  117. throw love::Exception("All image mipmap levels must have the same format.");
  118. }
  119. preload();
  120. loadVolatile();
  121. ++imageCount;
  122. }
  123. Image::~Image()
  124. {
  125. unloadVolatile();
  126. --imageCount;
  127. }
  128. void Image::preload()
  129. {
  130. for (int i = 0; i < 4; i++)
  131. vertices[i].r = vertices[i].g = vertices[i].b = vertices[i].a = 255;
  132. // Vertices are ordered for use with triangle strips:
  133. // 0----2
  134. // | / |
  135. // | / |
  136. // 1----3
  137. vertices[0].x = 0.0f;
  138. vertices[0].y = 0.0f;
  139. vertices[1].x = 0.0f;
  140. vertices[1].y = (float) height;
  141. vertices[2].x = (float) width;
  142. vertices[2].y = 0.0f;
  143. vertices[3].x = (float) width;
  144. vertices[3].y = (float) height;
  145. vertices[0].s = 0.0f;
  146. vertices[0].t = 0.0f;
  147. vertices[1].s = 0.0f;
  148. vertices[1].t = 1.0f;
  149. vertices[2].s = 1.0f;
  150. vertices[2].t = 0.0f;
  151. vertices[3].s = 1.0f;
  152. vertices[3].t = 1.0f;
  153. if (flags.mipmaps)
  154. filter.mipmap = defaultMipmapFilter;
  155. if (!isGammaCorrect())
  156. flags.linear = false;
  157. if (isGammaCorrect() && !flags.linear)
  158. sRGB = true;
  159. else
  160. sRGB = false;
  161. }
  162. void Image::generateMipmaps()
  163. {
  164. // The GL_GENERATE_MIPMAP texparameter is set in loadVolatile if we don't
  165. // have support for glGenerateMipmap.
  166. if (flags.mipmaps && !isCompressed() &&
  167. (GLAD_ES_VERSION_2_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object))
  168. {
  169. // Driver bug: http://www.opengl.org/wiki/Common_Mistakes#Automatic_mipmap_generation
  170. #if defined(LOVE_WINDOWS) || defined(LOVE_LINUX)
  171. if (gl.getVendor() == OpenGL::VENDOR_AMD)
  172. glEnable(GL_TEXTURE_2D);
  173. #endif
  174. glGenerateMipmap(GL_TEXTURE_2D);
  175. }
  176. }
  177. void Image::loadDefaultTexture()
  178. {
  179. usingDefaultTexture = true;
  180. gl.bindTexture(texture);
  181. setFilter(filter);
  182. // A nice friendly checkerboard to signify invalid textures...
  183. GLubyte px[] = {0xFF,0xFF,0xFF,0xFF, 0xFF,0xC0,0xC0,0xFF,
  184. 0xFF,0xC0,0xC0,0xFF, 0xFF,0xFF,0xFF,0xFF};
  185. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, px);
  186. }
  187. void Image::loadFromCompressedData()
  188. {
  189. GLenum iformat = getCompressedFormat(cdata[0]->getFormat(), sRGB);
  190. if (isGammaCorrect() && !sRGB)
  191. flags.linear = true;
  192. int count = 1;
  193. if (flags.mipmaps && cdata.size() > 1)
  194. count = (int) cdata.size();
  195. else if (flags.mipmaps)
  196. count = cdata[0]->getMipmapCount();
  197. for (int i = 0; i < count; i++)
  198. {
  199. // Compressed image mipmaps can come from separate CompressedImageData
  200. // objects, or all from a single object.
  201. auto cd = cdata.size() > 1 ? cdata[i].get() : cdata[0].get();
  202. int datamip = cdata.size() > 1 ? 0 : i;
  203. glCompressedTexImage2D(GL_TEXTURE_2D, i, iformat, cd->getWidth(datamip),
  204. cd->getHeight(datamip), 0,
  205. (GLsizei) cd->getSize(datamip), cd->getData(datamip));
  206. }
  207. }
  208. void Image::loadFromImageData()
  209. {
  210. GLenum iformat = sRGB ? GL_SRGB8_ALPHA8 : GL_RGBA8;
  211. GLenum format = GL_RGBA;
  212. // in GLES2, the internalformat and format params of TexImage have to match.
  213. if (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0)
  214. {
  215. format = sRGB ? GL_SRGB_ALPHA : GL_RGBA;
  216. iformat = format;
  217. }
  218. int mipcount = flags.mipmaps ? (int) data.size() : 1;
  219. for (int i = 0; i < mipcount; i++)
  220. {
  221. love::image::ImageData *id = data[i].get();
  222. love::thread::Lock lock(id->getMutex());
  223. glTexImage2D(GL_TEXTURE_2D, i, iformat, id->getWidth(), id->getHeight(),
  224. 0, format, GL_UNSIGNED_BYTE, id->getData());
  225. }
  226. if (data.size() <= 1)
  227. generateMipmaps();
  228. }
  229. bool Image::loadVolatile()
  230. {
  231. OpenGL::TempDebugGroup debuggroup("Image load");
  232. if (isCompressed() && !hasCompressedTextureSupport(cdata[0]->getFormat(), sRGB))
  233. {
  234. const char *str;
  235. if (image::CompressedImageData::getConstant(cdata[0]->getFormat(), str))
  236. {
  237. throw love::Exception("Cannot create image: "
  238. "%s%s compressed images are not supported on this system.", sRGB ? "sRGB " : "", str);
  239. }
  240. else
  241. throw love::Exception("cannot create image: format is not supported on this system.");
  242. }
  243. else if (!isCompressed())
  244. {
  245. if (sRGB && !hasSRGBSupport())
  246. throw love::Exception("sRGB images are not supported on this system.");
  247. // GL_EXT_sRGB doesn't support glGenerateMipmap for sRGB textures.
  248. if (sRGB && (GLAD_ES_VERSION_2_0 && GLAD_EXT_sRGB && !GLAD_ES_VERSION_3_0)
  249. && data.size() <= 1)
  250. {
  251. flags.mipmaps = false;
  252. filter.mipmap = FILTER_NONE;
  253. }
  254. }
  255. // NPOT textures don't support mipmapping without full NPOT support.
  256. if ((GLAD_ES_VERSION_2_0 && !(GLAD_ES_VERSION_3_0 || GLAD_OES_texture_npot))
  257. && (width != next_p2(width) || height != next_p2(height)))
  258. {
  259. flags.mipmaps = false;
  260. filter.mipmap = FILTER_NONE;
  261. }
  262. if (maxMipmapSharpness == 0.0f && GLAD_VERSION_1_4)
  263. glGetFloatv(GL_MAX_TEXTURE_LOD_BIAS, &maxMipmapSharpness);
  264. glGenTextures(1, &texture);
  265. gl.bindTexture(texture);
  266. setFilter(filter);
  267. setWrap(wrap);
  268. setMipmapSharpness(mipmapSharpness);
  269. // Use a default texture if the size is too big for the system.
  270. if (width > gl.getMaxTextureSize() || height > gl.getMaxTextureSize())
  271. {
  272. loadDefaultTexture();
  273. return true;
  274. }
  275. if (!flags.mipmaps && (GLAD_ES_VERSION_3_0 || GLAD_VERSION_1_0))
  276. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
  277. if (flags.mipmaps && !isCompressed() && data.size() <= 1 &&
  278. !(GLAD_ES_VERSION_2_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object))
  279. {
  280. // Auto-generate mipmaps every time the texture is modified, if
  281. // glGenerateMipmap isn't supported.
  282. glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
  283. }
  284. while (glGetError() != GL_NO_ERROR); // Clear errors.
  285. try
  286. {
  287. if (isCompressed())
  288. loadFromCompressedData();
  289. else
  290. loadFromImageData();
  291. GLenum glerr = glGetError();
  292. if (glerr != GL_NO_ERROR)
  293. throw love::Exception("Cannot create image (OpenGL error: %s)", OpenGL::errorString(glerr));
  294. }
  295. catch (love::Exception &)
  296. {
  297. gl.deleteTexture(texture);
  298. texture = 0;
  299. throw;
  300. }
  301. size_t prevmemsize = textureMemorySize;
  302. if (isCompressed())
  303. textureMemorySize = cdata[0]->getSize();
  304. else
  305. textureMemorySize = data[0]->getSize();
  306. if (flags.mipmaps)
  307. textureMemorySize *= 1.33334;
  308. gl.updateTextureMemorySize(prevmemsize, textureMemorySize);
  309. usingDefaultTexture = false;
  310. return true;
  311. }
  312. void Image::unloadVolatile()
  313. {
  314. if (texture == 0)
  315. return;
  316. gl.deleteTexture(texture);
  317. texture = 0;
  318. gl.updateTextureMemorySize(textureMemorySize, 0);
  319. textureMemorySize = 0;
  320. }
  321. bool Image::refresh(int xoffset, int yoffset, int w, int h)
  322. {
  323. // No effect if the texture hasn't been created yet.
  324. if (texture == 0 || usingDefaultTexture)
  325. return false;
  326. if (xoffset < 0 || yoffset < 0 || w <= 0 || h <= 0 ||
  327. (xoffset + w) > width || (yoffset + h) > height)
  328. {
  329. throw love::Exception("Invalid rectangle dimensions.");
  330. }
  331. OpenGL::TempDebugGroup debuggroup("Image refresh");
  332. gl.bindTexture(texture);
  333. if (isCompressed())
  334. {
  335. loadFromCompressedData();
  336. return true;
  337. }
  338. GLenum format = GL_RGBA;
  339. // In ES2, the format parameter of TexSubImage must match the internal
  340. // format of the texture.
  341. if (sRGB && (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0))
  342. format = GL_SRGB_ALPHA;
  343. int mipcount = flags.mipmaps ? (int) data.size() : 1;
  344. // Reupload the sub-rectangle of each mip level (if we have custom mipmaps.)
  345. for (int i = 0; i < mipcount; i++)
  346. {
  347. const image::pixel *pdata = (const image::pixel *) data[i]->getData();
  348. pdata += yoffset * data[i]->getWidth() + xoffset;
  349. thread::Lock lock(data[i]->getMutex());
  350. glTexSubImage2D(GL_TEXTURE_2D, i, xoffset, yoffset, w, h, format,
  351. GL_UNSIGNED_BYTE, pdata);
  352. xoffset /= 2;
  353. yoffset /= 2;
  354. w = std::max(w / 2, 1);
  355. h = std::max(h / 2, 1);
  356. }
  357. if (data.size() <= 1)
  358. generateMipmaps();
  359. return true;
  360. }
  361. void Image::drawv(const Matrix4 &t, const Vertex *v)
  362. {
  363. OpenGL::TempDebugGroup debuggroup("Image draw");
  364. OpenGL::TempTransform transform(gl);
  365. transform.get() *= t;
  366. gl.bindTexture(texture);
  367. gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD);
  368. glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &v[0].x);
  369. glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &v[0].s);
  370. gl.prepareDraw();
  371. gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4);
  372. }
  373. void Image::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
  374. {
  375. Matrix4 t(x, y, angle, sx, sy, ox, oy, kx, ky);
  376. drawv(t, vertices);
  377. }
  378. void Image::drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
  379. {
  380. Matrix4 t(x, y, angle, sx, sy, ox, oy, kx, ky);
  381. drawv(t, quad->getVertices());
  382. }
  383. const void *Image::getHandle() const
  384. {
  385. return &texture;
  386. }
  387. const std::vector<StrongRef<love::image::ImageData>> &Image::getImageData() const
  388. {
  389. return data;
  390. }
  391. const std::vector<StrongRef<love::image::CompressedImageData>> &Image::getCompressedData() const
  392. {
  393. return cdata;
  394. }
  395. void Image::setFilter(const Texture::Filter &f)
  396. {
  397. if (!validateFilter(f, flags.mipmaps))
  398. {
  399. if (f.mipmap != FILTER_NONE && !flags.mipmaps)
  400. throw love::Exception("Non-mipmapped image cannot have mipmap filtering.");
  401. else
  402. throw love::Exception("Invalid texture filter.");
  403. }
  404. filter = f;
  405. // We don't want filtering or (attempted) mipmaps on the default texture.
  406. if (usingDefaultTexture)
  407. {
  408. filter.mipmap = FILTER_NONE;
  409. filter.min = filter.mag = FILTER_NEAREST;
  410. }
  411. gl.bindTexture(texture);
  412. gl.setTextureFilter(filter);
  413. }
  414. bool Image::setWrap(const Texture::Wrap &w)
  415. {
  416. bool success = true;
  417. wrap = w;
  418. if ((GLAD_ES_VERSION_2_0 && !(GLAD_ES_VERSION_3_0 || GLAD_OES_texture_npot))
  419. && (width != next_p2(width) || height != next_p2(height)))
  420. {
  421. if (wrap.s != WRAP_CLAMP || wrap.t != WRAP_CLAMP)
  422. success = false;
  423. // If we only have limited NPOT support then the wrap mode must be CLAMP.
  424. wrap.s = wrap.t = WRAP_CLAMP;
  425. }
  426. if (!gl.isClampZeroTextureWrapSupported())
  427. {
  428. if (wrap.s == WRAP_CLAMP_ZERO)
  429. wrap.s = WRAP_CLAMP;
  430. if (wrap.t == WRAP_CLAMP_ZERO)
  431. wrap.t = WRAP_CLAMP;
  432. }
  433. gl.bindTexture(texture);
  434. gl.setTextureWrap(wrap);
  435. return success;
  436. }
  437. void Image::setMipmapSharpness(float sharpness)
  438. {
  439. // OpenGL ES doesn't support LOD bias via glTexParameter.
  440. if (!GLAD_VERSION_1_4)
  441. return;
  442. // LOD bias has the range (-maxbias, maxbias)
  443. mipmapSharpness = std::min(std::max(sharpness, -maxMipmapSharpness + 0.01f), maxMipmapSharpness - 0.01f);
  444. gl.bindTexture(texture);
  445. // negative bias is sharper
  446. glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -mipmapSharpness);
  447. }
  448. float Image::getMipmapSharpness() const
  449. {
  450. return mipmapSharpness;
  451. }
  452. const Image::Flags &Image::getFlags() const
  453. {
  454. return flags;
  455. }
  456. void Image::setDefaultMipmapSharpness(float sharpness)
  457. {
  458. defaultMipmapSharpness = sharpness;
  459. }
  460. float Image::getDefaultMipmapSharpness()
  461. {
  462. return defaultMipmapSharpness;
  463. }
  464. void Image::setDefaultMipmapFilter(Texture::FilterMode f)
  465. {
  466. defaultMipmapFilter = f;
  467. }
  468. Texture::FilterMode Image::getDefaultMipmapFilter()
  469. {
  470. return defaultMipmapFilter;
  471. }
  472. bool Image::isCompressed() const
  473. {
  474. return compressed;
  475. }
  476. GLenum Image::getCompressedFormat(image::CompressedImageData::Format cformat, bool &isSRGB) const
  477. {
  478. using image::CompressedImageData;
  479. switch (cformat)
  480. {
  481. case CompressedImageData::FORMAT_DXT1:
  482. return isSRGB ? GL_COMPRESSED_SRGB_S3TC_DXT1_EXT : GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
  483. case CompressedImageData::FORMAT_DXT3:
  484. return isSRGB ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT : GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
  485. case CompressedImageData::FORMAT_DXT5:
  486. return isSRGB ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT : GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
  487. case CompressedImageData::FORMAT_BC4:
  488. isSRGB = false;
  489. return GL_COMPRESSED_RED_RGTC1;
  490. case CompressedImageData::FORMAT_BC4s:
  491. isSRGB = false;
  492. return GL_COMPRESSED_SIGNED_RED_RGTC1;
  493. case CompressedImageData::FORMAT_BC5:
  494. isSRGB = false;
  495. return GL_COMPRESSED_RG_RGTC2;
  496. case CompressedImageData::FORMAT_BC5s:
  497. isSRGB = false;
  498. return GL_COMPRESSED_SIGNED_RG_RGTC2;
  499. case CompressedImageData::FORMAT_BC6H:
  500. isSRGB = false;
  501. return GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT;
  502. case CompressedImageData::FORMAT_BC6Hs:
  503. isSRGB = false;
  504. return GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT;
  505. case CompressedImageData::FORMAT_BC7:
  506. return isSRGB ? GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM : GL_COMPRESSED_RGBA_BPTC_UNORM;
  507. case CompressedImageData::FORMAT_PVR1_RGB2:
  508. return isSRGB ? GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT : GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
  509. case CompressedImageData::FORMAT_PVR1_RGB4:
  510. return isSRGB ? GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT : GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
  511. case CompressedImageData::FORMAT_PVR1_RGBA2:
  512. return isSRGB ? GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT : GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
  513. case CompressedImageData::FORMAT_PVR1_RGBA4:
  514. return isSRGB ? GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT : GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
  515. case CompressedImageData::FORMAT_ETC1:
  516. // The ETC2 format can load ETC1 textures.
  517. if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_4_3 || GLAD_ARB_ES3_compatibility)
  518. return isSRGB ? GL_COMPRESSED_SRGB8_ETC2 : GL_COMPRESSED_RGB8_ETC2;
  519. else
  520. {
  521. isSRGB = false;
  522. return GL_ETC1_RGB8_OES;
  523. }
  524. case CompressedImageData::FORMAT_ETC2_RGB:
  525. return isSRGB ? GL_COMPRESSED_SRGB8_ETC2 : GL_COMPRESSED_RGB8_ETC2;
  526. case CompressedImageData::FORMAT_ETC2_RGBA:
  527. return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC : GL_COMPRESSED_RGBA8_ETC2_EAC;
  528. case CompressedImageData::FORMAT_ETC2_RGBA1:
  529. return isSRGB ? GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 : GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2;
  530. case CompressedImageData::FORMAT_EAC_R:
  531. isSRGB = false;
  532. return GL_COMPRESSED_R11_EAC;
  533. case CompressedImageData::FORMAT_EAC_Rs:
  534. isSRGB = false;
  535. return GL_COMPRESSED_SIGNED_R11_EAC;
  536. case CompressedImageData::FORMAT_EAC_RG:
  537. isSRGB = false;
  538. return GL_COMPRESSED_RG11_EAC;
  539. case CompressedImageData::FORMAT_EAC_RGs:
  540. isSRGB = false;
  541. return GL_COMPRESSED_SIGNED_RG11_EAC;
  542. case CompressedImageData::FORMAT_ASTC_4x4:
  543. return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR : GL_COMPRESSED_RGBA_ASTC_4x4_KHR;
  544. case CompressedImageData::FORMAT_ASTC_5x4:
  545. return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR : GL_COMPRESSED_RGBA_ASTC_5x4_KHR;
  546. case CompressedImageData::FORMAT_ASTC_5x5:
  547. return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR : GL_COMPRESSED_RGBA_ASTC_5x5_KHR;
  548. case CompressedImageData::FORMAT_ASTC_6x5:
  549. return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR : GL_COMPRESSED_RGBA_ASTC_6x5_KHR;
  550. case CompressedImageData::FORMAT_ASTC_6x6:
  551. return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR : GL_COMPRESSED_RGBA_ASTC_6x6_KHR;
  552. case CompressedImageData::FORMAT_ASTC_8x5:
  553. return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR : GL_COMPRESSED_RGBA_ASTC_8x5_KHR;
  554. case CompressedImageData::FORMAT_ASTC_8x6:
  555. return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR : GL_COMPRESSED_RGBA_ASTC_8x6_KHR;
  556. case CompressedImageData::FORMAT_ASTC_8x8:
  557. return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR : GL_COMPRESSED_RGBA_ASTC_8x8_KHR;
  558. case CompressedImageData::FORMAT_ASTC_10x5:
  559. return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR : GL_COMPRESSED_RGBA_ASTC_10x5_KHR;
  560. case CompressedImageData::FORMAT_ASTC_10x6:
  561. return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR : GL_COMPRESSED_RGBA_ASTC_10x6_KHR;
  562. case CompressedImageData::FORMAT_ASTC_10x8:
  563. return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR : GL_COMPRESSED_RGBA_ASTC_10x8_KHR;
  564. case CompressedImageData::FORMAT_ASTC_10x10:
  565. return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR : GL_COMPRESSED_RGBA_ASTC_10x10_KHR;
  566. case CompressedImageData::FORMAT_ASTC_12x10:
  567. return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR : GL_COMPRESSED_RGBA_ASTC_12x10_KHR;
  568. case CompressedImageData::FORMAT_ASTC_12x12:
  569. return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR : GL_COMPRESSED_RGBA_ASTC_12x12_KHR;
  570. default:
  571. return isSRGB ? GL_SRGB8_ALPHA8 : GL_RGBA8;
  572. }
  573. }
  574. bool Image::hasAnisotropicFilteringSupport()
  575. {
  576. return GLAD_EXT_texture_filter_anisotropic != GL_FALSE;
  577. }
  578. bool Image::hasCompressedTextureSupport(image::CompressedImageData::Format format, bool sRGB)
  579. {
  580. using image::CompressedImageData;
  581. switch (format)
  582. {
  583. case CompressedImageData::FORMAT_DXT1:
  584. return GLAD_EXT_texture_compression_s3tc || GLAD_EXT_texture_compression_dxt1;
  585. case CompressedImageData::FORMAT_DXT3:
  586. return GLAD_EXT_texture_compression_s3tc || GLAD_ANGLE_texture_compression_dxt3;
  587. case CompressedImageData::FORMAT_DXT5:
  588. return GLAD_EXT_texture_compression_s3tc || GLAD_ANGLE_texture_compression_dxt5;
  589. case CompressedImageData::FORMAT_BC4:
  590. case CompressedImageData::FORMAT_BC4s:
  591. case CompressedImageData::FORMAT_BC5:
  592. case CompressedImageData::FORMAT_BC5s:
  593. return (GLAD_VERSION_3_0 || GLAD_ARB_texture_compression_rgtc || GLAD_EXT_texture_compression_rgtc);
  594. case CompressedImageData::FORMAT_BC6H:
  595. case CompressedImageData::FORMAT_BC6Hs:
  596. case CompressedImageData::FORMAT_BC7:
  597. return GLAD_VERSION_4_2 || GLAD_ARB_texture_compression_bptc;
  598. case CompressedImageData::FORMAT_PVR1_RGB2:
  599. case CompressedImageData::FORMAT_PVR1_RGB4:
  600. case CompressedImageData::FORMAT_PVR1_RGBA2:
  601. case CompressedImageData::FORMAT_PVR1_RGBA4:
  602. return sRGB ? GLAD_EXT_pvrtc_sRGB : GLAD_IMG_texture_compression_pvrtc;
  603. case CompressedImageData::FORMAT_ETC1:
  604. // ETC2 support guarantees ETC1 support as well.
  605. return GLAD_ES_VERSION_3_0 || GLAD_VERSION_4_3 || GLAD_ARB_ES3_compatibility || GLAD_OES_compressed_ETC1_RGB8_texture;
  606. case CompressedImageData::FORMAT_ETC2_RGB:
  607. case CompressedImageData::FORMAT_ETC2_RGBA:
  608. case CompressedImageData::FORMAT_ETC2_RGBA1:
  609. case CompressedImageData::FORMAT_EAC_R:
  610. case CompressedImageData::FORMAT_EAC_Rs:
  611. case CompressedImageData::FORMAT_EAC_RG:
  612. case CompressedImageData::FORMAT_EAC_RGs:
  613. return GLAD_ES_VERSION_3_0 || GLAD_VERSION_4_3 || GLAD_ARB_ES3_compatibility;
  614. case CompressedImageData::FORMAT_ASTC_4x4:
  615. case CompressedImageData::FORMAT_ASTC_5x4:
  616. case CompressedImageData::FORMAT_ASTC_5x5:
  617. case CompressedImageData::FORMAT_ASTC_6x5:
  618. case CompressedImageData::FORMAT_ASTC_6x6:
  619. case CompressedImageData::FORMAT_ASTC_8x5:
  620. case CompressedImageData::FORMAT_ASTC_8x6:
  621. case CompressedImageData::FORMAT_ASTC_8x8:
  622. case CompressedImageData::FORMAT_ASTC_10x5:
  623. case CompressedImageData::FORMAT_ASTC_10x6:
  624. case CompressedImageData::FORMAT_ASTC_10x8:
  625. case CompressedImageData::FORMAT_ASTC_10x10:
  626. case CompressedImageData::FORMAT_ASTC_12x10:
  627. case CompressedImageData::FORMAT_ASTC_12x12:
  628. return GLAD_ES_VERSION_3_2 || GLAD_KHR_texture_compression_astc_ldr;
  629. default:
  630. return false;
  631. }
  632. }
  633. bool Image::hasSRGBSupport()
  634. {
  635. return GLAD_ES_VERSION_3_0 || GLAD_EXT_sRGB || GLAD_VERSION_2_1 || GLAD_EXT_texture_sRGB;
  636. }
  637. bool Image::getConstant(const char *in, FlagType &out)
  638. {
  639. return flagNames.find(in, out);
  640. }
  641. bool Image::getConstant(FlagType in, const char *&out)
  642. {
  643. return flagNames.find(in, out);
  644. }
  645. StringMap<Image::FlagType, Image::FLAG_TYPE_MAX_ENUM>::Entry Image::flagNameEntries[] =
  646. {
  647. {"mipmaps", FLAG_TYPE_MIPMAPS},
  648. {"linear", FLAG_TYPE_LINEAR},
  649. };
  650. StringMap<Image::FlagType, Image::FLAG_TYPE_MAX_ENUM> Image::flagNames(Image::flagNameEntries, sizeof(Image::flagNameEntries));
  651. } // opengl
  652. } // graphics
  653. } // love