Image.cpp 15 KB

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