Image.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  1. /**
  2. * Copyright (c) 2006-2017 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. namespace love
  26. {
  27. namespace graphics
  28. {
  29. namespace opengl
  30. {
  31. Image::Image(TextureType textype, PixelFormat format, int width, int height, int slices, const Settings &settings)
  32. : love::graphics::Image(Slices(textype), settings, false)
  33. , texture(0)
  34. , compressed(false)
  35. , usingDefaultTexture(false)
  36. , textureMemorySize(0)
  37. {
  38. if (isPixelFormatCompressed(format))
  39. throw love::Exception("This constructor is only supported for non-compressed pixel formats.");
  40. if (textype == TEXTURE_VOLUME)
  41. depth = slices;
  42. else if (textype == TEXTURE_2D_ARRAY)
  43. layers = slices;
  44. init(format, width, height, settings);
  45. }
  46. Image::Image(const Slices &slices, const Settings &settings)
  47. : love::graphics::Image(slices, settings, true)
  48. , texture(0)
  49. , compressed(false)
  50. , usingDefaultTexture(false)
  51. , textureMemorySize(0)
  52. {
  53. if (texType == TEXTURE_2D_ARRAY)
  54. this->layers = data.getSliceCount();
  55. else if (texType == TEXTURE_VOLUME)
  56. this->depth = data.getSliceCount();
  57. love::image::ImageDataBase *slice = data.get(0, 0);
  58. init(slice->getFormat(), slice->getWidth(), slice->getHeight(), settings);
  59. }
  60. Image::~Image()
  61. {
  62. unloadVolatile();
  63. }
  64. void Image::init(PixelFormat fmt, int w, int h, const Settings &settings)
  65. {
  66. pixelWidth = w;
  67. pixelHeight = h;
  68. width = (int) (pixelWidth / settings.pixeldensity + 0.5);
  69. height = (int) (pixelHeight / settings.pixeldensity + 0.5);
  70. mipmapCount = mipmapsType == MIPMAPS_NONE ? 1 : getMipmapCount(w, h);
  71. format = fmt;
  72. compressed = isPixelFormatCompressed(format);
  73. if (compressed && mipmapsType == MIPMAPS_GENERATED)
  74. mipmapsType = MIPMAPS_NONE;
  75. if (getMipmapCount() > 1)
  76. filter.mipmap = defaultMipmapFilter;
  77. initQuad();
  78. loadVolatile();
  79. }
  80. void Image::generateMipmaps()
  81. {
  82. if (getMipmapCount() > 1 && !isCompressed() &&
  83. (GLAD_ES_VERSION_2_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object || GLAD_EXT_framebuffer_object))
  84. {
  85. GLenum gltextype = OpenGL::getGLTextureType(texType);
  86. if (gl.bugs.generateMipmapsRequiresTexture2DEnable)
  87. glEnable(gltextype);
  88. glGenerateMipmap(gltextype);
  89. }
  90. }
  91. void Image::loadDefaultTexture()
  92. {
  93. usingDefaultTexture = true;
  94. gl.bindTextureToUnit(this, 0, false);
  95. setFilter(filter);
  96. bool isSRGB = false;
  97. gl.rawTexStorage(texType, 1, PIXELFORMAT_RGBA8, isSRGB, 2, 2, 1);
  98. // A nice friendly checkerboard to signify invalid textures...
  99. GLubyte px[] = {0xFF,0xFF,0xFF,0xFF, 0xFF,0xA0,0xA0,0xFF,
  100. 0xFF,0xA0,0xA0,0xFF, 0xFF,0xFF,0xFF,0xFF};
  101. int slices = texType == TEXTURE_CUBE ? 6 : 1;
  102. Rect rect = {0, 0, 2, 2};
  103. for (int slice = 0; slice < slices; slice++)
  104. uploadByteData(PIXELFORMAT_RGBA8, px, sizeof(px), rect, 0, slice);
  105. }
  106. void Image::loadData()
  107. {
  108. int mipcount = getMipmapCount();
  109. int slicecount = 1;
  110. if (texType == TEXTURE_VOLUME)
  111. slicecount = getDepth();
  112. else if (texType == TEXTURE_2D_ARRAY)
  113. slicecount = getLayerCount();
  114. else if (texType == TEXTURE_CUBE)
  115. slicecount = 6;
  116. if (!isCompressed())
  117. gl.rawTexStorage(texType, mipcount, format, sRGB, pixelWidth, pixelHeight, texType == TEXTURE_VOLUME ? depth : layers);
  118. if (mipmapsType == MIPMAPS_GENERATED)
  119. mipcount = 1;
  120. int w = pixelWidth;
  121. int h = pixelHeight;
  122. int d = depth;
  123. OpenGL::TextureFormat fmt = gl.convertPixelFormat(format, false, sRGB);
  124. for (int mip = 0; mip < mipcount; mip++)
  125. {
  126. if (isCompressed() && (texType == TEXTURE_2D_ARRAY || texType == TEXTURE_VOLUME))
  127. {
  128. size_t mipsize = 0;
  129. if (texType == TEXTURE_2D_ARRAY || texType == TEXTURE_VOLUME)
  130. {
  131. for (int slice = 0; slice < data.getSliceCount(mip); slice++)
  132. mipsize += data.get(slice, mip)->getSize();
  133. }
  134. GLenum gltarget = OpenGL::getGLTextureType(texType);
  135. glCompressedTexImage3D(gltarget, mip, fmt.internalformat, w, h, d, 0, mipsize, nullptr);
  136. }
  137. for (int slice = 0; slice < slicecount; slice++)
  138. {
  139. love::image::ImageDataBase *id = data.get(slice, mip);
  140. if (id != nullptr)
  141. uploadImageData(id, mip, slice);
  142. }
  143. w = std::max(w / 2, 1);
  144. h = std::max(h / 2, 1);
  145. if (texType == TEXTURE_VOLUME)
  146. d = std::max(d / 2, 1);
  147. }
  148. if (mipmapsType == MIPMAPS_GENERATED)
  149. generateMipmaps();
  150. }
  151. void Image::uploadByteData(PixelFormat pixelformat, const void *data, size_t size, const Rect &r, int level, int slice)
  152. {
  153. OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(pixelformat, false, sRGB);
  154. GLenum gltarget = OpenGL::getGLTextureType(texType);
  155. if (texType == TEXTURE_CUBE)
  156. gltarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + slice;
  157. if (isPixelFormatCompressed(pixelformat))
  158. {
  159. if (r.x != 0 || r.y != 0)
  160. throw love::Exception("x and y parameters must be 0 for compressed images.");
  161. if (texType == TEXTURE_2D || texType == TEXTURE_CUBE)
  162. glCompressedTexImage2D(gltarget, level, fmt.internalformat, r.w, r.h, 0, size, data);
  163. else if (texType == TEXTURE_2D_ARRAY || texType == TEXTURE_VOLUME)
  164. glCompressedTexSubImage3D(gltarget, level, 0, 0, slice, r.w, r.h, 1, fmt.internalformat, size, data);
  165. }
  166. else
  167. {
  168. if (texType == TEXTURE_2D || texType == TEXTURE_CUBE)
  169. glTexSubImage2D(gltarget, level, r.x, r.y, r.w, r.h, fmt.externalformat, fmt.type, data);
  170. else if (texType == TEXTURE_2D_ARRAY || texType == TEXTURE_VOLUME)
  171. glTexSubImage3D(gltarget, level, r.x, r.y, slice, r.w, r.h, 1, fmt.externalformat, fmt.type, data);
  172. }
  173. }
  174. void Image::uploadImageData(love::image::ImageDataBase *d, int level, int slice)
  175. {
  176. love::image::ImageData *id = dynamic_cast<love::image::ImageData *>(d);
  177. love::thread::EmptyLock lock;
  178. if (id != nullptr)
  179. lock.setLock(id->getMutex());
  180. Rect rect = {0, 0, d->getWidth(), d->getHeight()};
  181. uploadByteData(d->getFormat(), d->getData(), d->getSize(), rect, level, slice);
  182. }
  183. bool Image::loadVolatile()
  184. {
  185. if (texture != 0)
  186. return true;
  187. OpenGL::TempDebugGroup debuggroup("Image load");
  188. if (!OpenGL::isPixelFormatSupported(format, false, true, sRGB))
  189. {
  190. const char *str;
  191. if (love::getConstant(format, str))
  192. {
  193. throw love::Exception("Cannot create image: "
  194. "%s%s images are not supported on this system.", sRGB ? "sRGB " : "", str);
  195. }
  196. else
  197. throw love::Exception("cannot create image: format is not supported on this system.");
  198. }
  199. else if (!isCompressed())
  200. {
  201. if (sRGB && !hasSRGBSupport())
  202. throw love::Exception("sRGB images are not supported on this system.");
  203. // GL_EXT_sRGB doesn't support glGenerateMipmap for sRGB textures.
  204. if (sRGB && (GLAD_ES_VERSION_2_0 && GLAD_EXT_sRGB && !GLAD_ES_VERSION_3_0)
  205. && mipmapsType != MIPMAPS_DATA)
  206. {
  207. mipmapsType = MIPMAPS_NONE;
  208. filter.mipmap = FILTER_NONE;
  209. }
  210. }
  211. // NPOT textures don't support mipmapping without full NPOT support.
  212. if ((GLAD_ES_VERSION_2_0 && !(GLAD_ES_VERSION_3_0 || GLAD_OES_texture_npot))
  213. && (pixelWidth != nextP2(pixelWidth) || pixelHeight != nextP2(pixelHeight)))
  214. {
  215. mipmapsType = MIPMAPS_NONE;
  216. filter.mipmap = FILTER_NONE;
  217. }
  218. glGenTextures(1, &texture);
  219. gl.bindTextureToUnit(this, 0, false);
  220. bool loaddefault = false;
  221. int max2Dsize = gl.getMax2DTextureSize();
  222. int max3Dsize = gl.getMax3DTextureSize();
  223. if ((texType == TEXTURE_2D || texType == TEXTURE_2D_ARRAY) && (pixelWidth > max2Dsize || pixelHeight > max2Dsize))
  224. loaddefault = true;
  225. else if (texType == TEXTURE_2D_ARRAY && layers > gl.getMaxTextureLayers())
  226. loaddefault = true;
  227. else if (texType == TEXTURE_CUBE && (pixelWidth > gl.getMaxCubeTextureSize() || pixelWidth != pixelHeight))
  228. loaddefault = true;
  229. else if (texType == TEXTURE_VOLUME && (pixelWidth > max3Dsize || pixelHeight > max3Dsize || depth > max3Dsize))
  230. loaddefault = true;
  231. // Use a default texture if the size is too big for the system.
  232. if (loaddefault)
  233. {
  234. loadDefaultTexture();
  235. return true;
  236. }
  237. setFilter(filter);
  238. setWrap(wrap);
  239. setMipmapSharpness(mipmapSharpness);
  240. GLenum gltextype = OpenGL::getGLTextureType(texType);
  241. if (mipmapsType == MIPMAPS_NONE && (GLAD_ES_VERSION_3_0 || GLAD_VERSION_1_0))
  242. glTexParameteri(gltextype, GL_TEXTURE_MAX_LEVEL, 0);
  243. while (glGetError() != GL_NO_ERROR); // Clear errors.
  244. try
  245. {
  246. loadData();
  247. GLenum glerr = glGetError();
  248. if (glerr != GL_NO_ERROR)
  249. throw love::Exception("Cannot create image (OpenGL error: %s)", OpenGL::errorString(glerr));
  250. }
  251. catch (love::Exception &)
  252. {
  253. gl.deleteTexture(texture);
  254. texture = 0;
  255. throw;
  256. }
  257. size_t prevmemsize = textureMemorySize;
  258. textureMemorySize = 0;
  259. for (int slice = 0; slice < data.getSliceCount(0); slice++)
  260. textureMemorySize += data.get(slice, 0)->getSize();
  261. if (getMipmapCount() > 1)
  262. textureMemorySize *= 1.33334;
  263. gl.updateTextureMemorySize(prevmemsize, textureMemorySize);
  264. usingDefaultTexture = false;
  265. return true;
  266. }
  267. void Image::unloadVolatile()
  268. {
  269. if (texture == 0)
  270. return;
  271. gl.deleteTexture(texture);
  272. texture = 0;
  273. gl.updateTextureMemorySize(textureMemorySize, 0);
  274. textureMemorySize = 0;
  275. }
  276. void Image::replacePixels(love::image::ImageDataBase *d, int slice, int mipmap, bool reloadmipmaps)
  277. {
  278. // No effect if the texture hasn't been created yet.
  279. if (getHandle() == 0 || usingDefaultTexture)
  280. return;
  281. if (d->getFormat() != getPixelFormat())
  282. throw love::Exception("Pixel formats must match.");
  283. if (mipmap < 0 || (mipmapsType != MIPMAPS_DATA && mipmap > 0) || mipmap >= getMipmapCount())
  284. throw love::Exception("Invalid image mipmap index.");
  285. if (slice < 0 || (texType == TEXTURE_CUBE && slice >= 6)
  286. || (texType == TEXTURE_VOLUME && slice >= std::max(getDepth() >> mipmap, 1))
  287. || (texType == TEXTURE_2D_ARRAY && slice >= getLayerCount()))
  288. {
  289. throw love::Exception("Invalid image slice index.");
  290. }
  291. love::image::ImageDataBase *oldd = data.get(slice, mipmap);
  292. if (oldd == nullptr)
  293. throw love::Exception("Image does not store ImageData!");
  294. int w = d->getWidth();
  295. int h = d->getHeight();
  296. if (w != oldd->getWidth() || h != oldd->getHeight())
  297. throw love::Exception("Dimensions must match the texture's dimensions for the specified mipmap level.");
  298. Graphics::flushStreamDrawsGlobal();
  299. d->retain();
  300. oldd->release();
  301. data.set(slice, mipmap, d);
  302. OpenGL::TempDebugGroup debuggroup("Image replace pixels");
  303. gl.bindTextureToUnit(this, 0, false);
  304. uploadImageData(d, mipmap, slice);
  305. if (reloadmipmaps && mipmap == 0 && getMipmapCount() > 1)
  306. generateMipmaps();
  307. }
  308. void Image::replacePixels(const void *data, size_t size, const Rect &rect, int slice, int mipmap, bool reloadmipmaps)
  309. {
  310. Graphics::flushStreamDrawsGlobal();
  311. OpenGL::TempDebugGroup debuggroup("Image replace pixels");
  312. gl.bindTextureToUnit(this, 0, false);
  313. uploadByteData(format, data, size, rect, mipmap, slice);
  314. if (reloadmipmaps && mipmap == 0 && getMipmapCount() > 1)
  315. generateMipmaps();
  316. }
  317. ptrdiff_t Image::getHandle() const
  318. {
  319. return texture;
  320. }
  321. void Image::setFilter(const Texture::Filter &f)
  322. {
  323. Texture::setFilter(f);
  324. if (!OpenGL::hasTextureFilteringSupport(getPixelFormat()))
  325. {
  326. filter.mag = filter.min = FILTER_NEAREST;
  327. if (filter.mipmap == FILTER_LINEAR)
  328. filter.mipmap = FILTER_NEAREST;
  329. }
  330. // We don't want filtering or (attempted) mipmaps on the default texture.
  331. if (usingDefaultTexture)
  332. {
  333. filter.mipmap = FILTER_NONE;
  334. filter.min = filter.mag = FILTER_NEAREST;
  335. }
  336. gl.bindTextureToUnit(this, 0, false);
  337. gl.setTextureFilter(texType, filter);
  338. }
  339. bool Image::setWrap(const Texture::Wrap &w)
  340. {
  341. Graphics::flushStreamDrawsGlobal();
  342. bool success = true;
  343. bool forceclamp = texType == TEXTURE_CUBE;
  344. wrap = w;
  345. // If we only have limited NPOT support then the wrap mode must be CLAMP.
  346. if ((GLAD_ES_VERSION_2_0 && !(GLAD_ES_VERSION_3_0 || GLAD_OES_texture_npot))
  347. && (pixelWidth != nextP2(pixelWidth) || pixelHeight != nextP2(pixelHeight) || depth != nextP2(depth)))
  348. {
  349. forceclamp = true;
  350. }
  351. if (forceclamp)
  352. {
  353. if (wrap.s != WRAP_CLAMP || wrap.t != WRAP_CLAMP || wrap.r != WRAP_CLAMP)
  354. success = false;
  355. wrap.s = wrap.t = wrap.r = WRAP_CLAMP;
  356. }
  357. if (!gl.isClampZeroTextureWrapSupported())
  358. {
  359. if (wrap.s == WRAP_CLAMP_ZERO) wrap.s = WRAP_CLAMP;
  360. if (wrap.t == WRAP_CLAMP_ZERO) wrap.t = WRAP_CLAMP;
  361. if (wrap.r == WRAP_CLAMP_ZERO) wrap.r = WRAP_CLAMP;
  362. }
  363. gl.bindTextureToUnit(this, 0, false);
  364. gl.setTextureWrap(texType, wrap);
  365. return success;
  366. }
  367. bool Image::setMipmapSharpness(float sharpness)
  368. {
  369. if (!gl.isSamplerLODBiasSupported())
  370. return false;
  371. Graphics::flushStreamDrawsGlobal();
  372. float maxbias = gl.getMaxLODBias();
  373. if (maxbias > 0.01f)
  374. maxbias -= 0.0f;
  375. mipmapSharpness = std::min(std::max(sharpness, -maxbias), maxbias);
  376. gl.bindTextureToUnit(this, 0, false);
  377. // negative bias is sharper
  378. glTexParameterf(gl.getGLTextureType(texType), GL_TEXTURE_LOD_BIAS, -mipmapSharpness);
  379. return true;
  380. }
  381. bool Image::isFormatLinear() const
  382. {
  383. return isGammaCorrect() && !sRGB;
  384. }
  385. bool Image::isCompressed() const
  386. {
  387. return compressed;
  388. }
  389. Image::MipmapsType Image::getMipmapsType() const
  390. {
  391. return mipmapsType;
  392. }
  393. bool Image::isFormatSupported(PixelFormat pixelformat)
  394. {
  395. return OpenGL::isPixelFormatSupported(pixelformat, false, true, false);
  396. }
  397. bool Image::hasSRGBSupport()
  398. {
  399. return GLAD_ES_VERSION_3_0 || GLAD_EXT_sRGB || GLAD_VERSION_2_1 || GLAD_EXT_texture_sRGB;
  400. }
  401. } // opengl
  402. } // graphics
  403. } // love