Image.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625
  1. /**
  2. * Copyright (c) 2006-2013 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. Image::FilterMode Image::defaultMipmapFilter = Image::FILTER_NONE;
  32. float Image::defaultMipmapSharpness = 0.0f;
  33. Image::Image(love::image::ImageData *data)
  34. : cdata(0)
  35. , width((float)(data->getWidth()))
  36. , height((float)(data->getHeight()))
  37. , texture(0)
  38. , mipmapSharpness(defaultMipmapSharpness)
  39. , mipmapsCreated(false)
  40. , compressed(false)
  41. {
  42. data->retain();
  43. this->data = data;
  44. preload();
  45. }
  46. Image::Image(love::image::CompressedData *cdata)
  47. : data(0)
  48. , width((float)(cdata->getWidth(0)))
  49. , height((float)(cdata->getHeight(0)))
  50. , texture(0)
  51. , mipmapSharpness(defaultMipmapSharpness)
  52. , mipmapsCreated(false)
  53. , compressed(true)
  54. {
  55. cdata->retain();
  56. this->cdata = cdata;
  57. preload();
  58. }
  59. Image::~Image()
  60. {
  61. if (data != 0)
  62. data->release();
  63. if (cdata != 0)
  64. cdata->release();
  65. unload();
  66. }
  67. float Image::getWidth() const
  68. {
  69. return width;
  70. }
  71. float Image::getHeight() const
  72. {
  73. return height;
  74. }
  75. const vertex *Image::getVertices() const
  76. {
  77. return vertices;
  78. }
  79. love::image::ImageData *Image::getData() const
  80. {
  81. return data;
  82. }
  83. void Image::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
  84. {
  85. static Matrix t;
  86. t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
  87. drawv(t, vertices);
  88. }
  89. void Image::drawg(love::graphics::Geometry *geom, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
  90. {
  91. static Matrix t;
  92. t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
  93. // use colors stored in geometry (horrible, horrible hack)
  94. const vertex *v = geom->getVertexArray();
  95. glEnableClientState(GL_COLOR_ARRAY);
  96. glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(vertex), (GLvoid *)&v->r);
  97. drawv(t, v, geom->getVertexArraySize(), GL_TRIANGLES);
  98. glDisableClientState(GL_COLOR_ARRAY);
  99. }
  100. void Image::uploadCompressedMipmaps()
  101. {
  102. if (!isCompressed() || !cdata || !hasCompressedTextureSupport(cdata->getType()))
  103. return;
  104. bind();
  105. int numMipmaps = cdata->getNumMipmaps();
  106. // We have to inform OpenGL if the image doesn't have all mipmap levels.
  107. if (GLEE_VERSION_1_2 || GLEE_SGIS_texture_lod)
  108. {
  109. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, numMipmaps - 1);
  110. }
  111. else if (cdata->getWidth(numMipmaps-1) > 1 || cdata->getHeight(numMipmaps-1) > 1)
  112. {
  113. // Telling OpenGL to ignore certain levels isn't always supported.
  114. throw love::Exception("Cannot load mipmaps: "
  115. "compressed image does not have all required levels.");
  116. }
  117. for (int i = 1; i < numMipmaps; i++)
  118. {
  119. glCompressedTexImage2DARB(GL_TEXTURE_2D,
  120. i,
  121. getCompressedFormat(cdata->getType()),
  122. cdata->getWidth(i),
  123. cdata->getHeight(i),
  124. 0,
  125. GLsizei(cdata->getSize(i)),
  126. cdata->getData(i));
  127. }
  128. }
  129. void Image::createMipmaps()
  130. {
  131. if (!data)
  132. return;
  133. if (!hasMipmapSupport())
  134. throw love::Exception("Mipmap filtering is not supported on this system.");
  135. // Some old drivers claim support for NPOT textures, but fail when creating
  136. // mipmaps. We can't detect which systems will do this, so we fail gracefully
  137. // for all NPOT images.
  138. int w = int(width), h = int(height);
  139. if (w != next_p2(w) || h != next_p2(h))
  140. {
  141. throw love::Exception("Cannot create mipmaps: "
  142. "image does not have power of two dimensions.");
  143. }
  144. bind();
  145. if (hasNpot() && (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_object))
  146. {
  147. // AMD/ATI drivers have several bugs when generating mipmaps,
  148. // re-uploading the entire base image seems to be required.
  149. glTexImage2D(GL_TEXTURE_2D,
  150. 0,
  151. GL_RGBA8,
  152. (GLsizei)width,
  153. (GLsizei)height,
  154. 0,
  155. GL_RGBA,
  156. GL_UNSIGNED_BYTE,
  157. data->getData());
  158. // More bugs: http://www.opengl.org/wiki/Common_Mistakes#Automatic_mipmap_generation
  159. glEnable(GL_TEXTURE_2D);
  160. glGenerateMipmap(GL_TEXTURE_2D);
  161. }
  162. else
  163. {
  164. glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
  165. glTexSubImage2D(GL_TEXTURE_2D,
  166. 0,
  167. 0,
  168. 0,
  169. (GLsizei)width,
  170. (GLsizei)height,
  171. GL_RGBA,
  172. GL_UNSIGNED_BYTE,
  173. data->getData());
  174. }
  175. }
  176. void Image::checkMipmapsCreated()
  177. {
  178. if (mipmapsCreated || filter.mipmap == FILTER_NONE)
  179. return;
  180. if (isCompressed() && cdata && hasCompressedTextureSupport(cdata->getType()))
  181. uploadCompressedMipmaps();
  182. else if (data)
  183. createMipmaps();
  184. else
  185. return;
  186. mipmapsCreated = true;
  187. }
  188. void Image::setFilter(const Image::Filter &f)
  189. {
  190. filter = f;
  191. bind();
  192. filter.anisotropy = gl.setTextureFilter(f);
  193. checkMipmapsCreated();
  194. }
  195. const Image::Filter &Image::getFilter() const
  196. {
  197. return filter;
  198. }
  199. void Image::setWrap(const Image::Wrap &w)
  200. {
  201. wrap = w;
  202. bind();
  203. gl.setTextureWrap(w);
  204. }
  205. const Image::Wrap &Image::getWrap() const
  206. {
  207. return wrap;
  208. }
  209. void Image::setMipmapSharpness(float sharpness)
  210. {
  211. if (hasMipmapSharpnessSupport())
  212. {
  213. // LOD bias has the range (-maxbias, maxbias)
  214. mipmapSharpness = std::min(std::max(sharpness, -maxMipmapSharpness + 0.01f), maxMipmapSharpness - 0.01f);
  215. bind();
  216. // negative bias is sharper
  217. glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -mipmapSharpness);
  218. }
  219. else
  220. mipmapSharpness = 0.0f;
  221. }
  222. float Image::getMipmapSharpness() const
  223. {
  224. return mipmapSharpness;
  225. }
  226. void Image::bind() const
  227. {
  228. if (texture == 0)
  229. return;
  230. gl.bindTexture(texture);
  231. }
  232. void Image::preload()
  233. {
  234. memset(vertices, 255, sizeof(vertex)*4);
  235. vertices[0].x = 0;
  236. vertices[0].y = 0;
  237. vertices[1].x = 0;
  238. vertices[1].y = height;
  239. vertices[2].x = width;
  240. vertices[2].y = height;
  241. vertices[3].x = width;
  242. vertices[3].y = 0;
  243. vertices[0].s = 0;
  244. vertices[0].t = 0;
  245. vertices[1].s = 0;
  246. vertices[1].t = 1;
  247. vertices[2].s = 1;
  248. vertices[2].t = 1;
  249. vertices[3].s = 1;
  250. vertices[3].t = 0;
  251. filter = getDefaultFilter();
  252. filter.mipmap = defaultMipmapFilter;
  253. }
  254. bool Image::load()
  255. {
  256. return loadVolatile();
  257. }
  258. void Image::unload()
  259. {
  260. return unloadVolatile();
  261. }
  262. bool Image::loadVolatile()
  263. {
  264. if (isCompressed() && cdata && !hasCompressedTextureSupport(cdata->getType()))
  265. {
  266. const char *str;
  267. if (image::CompressedData::getConstant(cdata->getType(), str))
  268. {
  269. throw love::Exception("Cannot create image: "
  270. "%s compressed images are not supported on this system.", str);
  271. }
  272. else
  273. throw love::Exception("cannot create image: format is not supported on this system.");
  274. }
  275. if (hasMipmapSharpnessSupport() && maxMipmapSharpness == 0.0f)
  276. glGetFloatv(GL_MAX_TEXTURE_LOD_BIAS, &maxMipmapSharpness);
  277. if (hasNpot())
  278. return loadVolatileNPOT();
  279. else
  280. return loadVolatilePOT();
  281. }
  282. bool Image::loadVolatilePOT()
  283. {
  284. glGenTextures(1,(GLuint *)&texture);
  285. gl.bindTexture(texture);
  286. filter.anisotropy = gl.setTextureFilter(filter);
  287. gl.setTextureWrap(wrap);
  288. float p2width = next_p2(width);
  289. float p2height = next_p2(height);
  290. float s = width/p2width;
  291. float t = height/p2height;
  292. vertices[1].t = t;
  293. vertices[2].t = t;
  294. vertices[2].s = s;
  295. vertices[3].s = s;
  296. while (glGetError() != GL_NO_ERROR); // clear errors
  297. if (isCompressed() && cdata)
  298. {
  299. if (s < 1.0f || t < 1.0f)
  300. {
  301. throw love::Exception("Cannot create image: "
  302. "compressed NPOT images are not supported on this system.");
  303. }
  304. glCompressedTexImage2DARB(GL_TEXTURE_2D,
  305. 0,
  306. getCompressedFormat(cdata->getType()),
  307. cdata->getWidth(0),
  308. cdata->getHeight(0),
  309. 0,
  310. GLsizei(cdata->getSize(0)),
  311. cdata->getData(0));
  312. }
  313. else if (data)
  314. {
  315. glTexImage2D(GL_TEXTURE_2D,
  316. 0,
  317. GL_RGBA8,
  318. (GLsizei)p2width,
  319. (GLsizei)p2height,
  320. 0,
  321. GL_RGBA,
  322. GL_UNSIGNED_BYTE,
  323. 0);
  324. glTexSubImage2D(GL_TEXTURE_2D,
  325. 0,
  326. 0, 0,
  327. (GLsizei)width,
  328. (GLsizei)height,
  329. GL_RGBA,
  330. GL_UNSIGNED_BYTE,
  331. data->getData());
  332. }
  333. if (glGetError() != GL_NO_ERROR)
  334. throw love::Exception("Cannot create image: size may be too large for this system.");
  335. mipmapsCreated = false;
  336. checkMipmapsCreated();
  337. setMipmapSharpness(mipmapSharpness);
  338. return true;
  339. }
  340. bool Image::loadVolatileNPOT()
  341. {
  342. glGenTextures(1,(GLuint *)&texture);
  343. gl.bindTexture(texture);
  344. filter.anisotropy = gl.setTextureFilter(filter);
  345. gl.setTextureWrap(wrap);
  346. while (glGetError() != GL_NO_ERROR); // clear errors
  347. if (isCompressed() && cdata)
  348. {
  349. GLenum format = getCompressedFormat(cdata->getType());
  350. glCompressedTexImage2DARB(GL_TEXTURE_2D,
  351. 0,
  352. format,
  353. cdata->getWidth(0),
  354. cdata->getHeight(0),
  355. 0,
  356. GLsizei(cdata->getSize(0)),
  357. cdata->getData(0));
  358. }
  359. else if (data)
  360. {
  361. glTexImage2D(GL_TEXTURE_2D,
  362. 0,
  363. GL_RGBA8,
  364. (GLsizei)width,
  365. (GLsizei)height,
  366. 0,
  367. GL_RGBA,
  368. GL_UNSIGNED_BYTE,
  369. data->getData());
  370. }
  371. if (glGetError() != GL_NO_ERROR)
  372. throw love::Exception("Cannot create image: size may be too large for this system.");
  373. mipmapsCreated = false;
  374. checkMipmapsCreated();
  375. setMipmapSharpness(mipmapSharpness);
  376. return true;
  377. }
  378. void Image::unloadVolatile()
  379. {
  380. // Delete the hardware texture.
  381. if (texture != 0)
  382. {
  383. gl.deleteTexture(texture);
  384. texture = 0;
  385. }
  386. }
  387. bool Image::refresh()
  388. {
  389. // No effect if the texture hasn't been created yet.
  390. if (texture == 0)
  391. return false;
  392. bind();
  393. if (isCompressed() && cdata)
  394. {
  395. GLenum format = getCompressedFormat(cdata->getType());
  396. glCompressedTexSubImage2DARB(GL_TEXTURE_2D,
  397. 0,
  398. 0, 0,
  399. cdata->getWidth(0),
  400. cdata->getHeight(0),
  401. format,
  402. GLsizei(cdata->getSize(0)),
  403. cdata->getData(0));
  404. }
  405. else if (data)
  406. {
  407. glTexSubImage2D(GL_TEXTURE_2D,
  408. 0,
  409. 0, 0,
  410. (GLsizei)width,
  411. (GLsizei)height,
  412. GL_RGBA,
  413. GL_UNSIGNED_BYTE,
  414. data->getData());
  415. }
  416. mipmapsCreated = false;
  417. checkMipmapsCreated();
  418. return true;
  419. }
  420. void Image::drawv(const Matrix &t, const vertex *v, GLsizei count, GLenum mode) const
  421. {
  422. bind();
  423. glPushMatrix();
  424. glMultMatrixf((const GLfloat *)t.getElements());
  425. glEnableClientState(GL_VERTEX_ARRAY);
  426. glEnableClientState(GL_TEXTURE_COORD_ARRAY);
  427. // XXX: drawg() enables/disables GL_COLOR_ARRAY in order to use the color
  428. // defined in the geometry to draw itself.
  429. // if the drawing method below is changed to use something other than
  430. // glDrawArrays(), drawg() needs to be updated accordingly!
  431. glVertexPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid *)&v[0].x);
  432. glTexCoordPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid *)&v[0].s);
  433. glDrawArrays(mode, 0, count);
  434. glDisableClientState(GL_TEXTURE_COORD_ARRAY);
  435. glDisableClientState(GL_VERTEX_ARRAY);
  436. glPopMatrix();
  437. }
  438. void Image::setDefaultMipmapSharpness(float sharpness)
  439. {
  440. defaultMipmapSharpness = sharpness;
  441. }
  442. float Image::getDefaultMipmapSharpness()
  443. {
  444. return defaultMipmapSharpness;
  445. }
  446. void Image::setDefaultMipmapFilter(Image::FilterMode f)
  447. {
  448. defaultMipmapFilter = f;
  449. }
  450. Image::FilterMode Image::getDefaultMipmapFilter()
  451. {
  452. return defaultMipmapFilter;
  453. }
  454. bool Image::isCompressed() const
  455. {
  456. return compressed;
  457. }
  458. GLenum Image::getCompressedFormat(image::CompressedData::TextureType type) const
  459. {
  460. switch (type)
  461. {
  462. case image::CompressedData::TYPE_DXT1:
  463. return GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
  464. case image::CompressedData::TYPE_DXT3:
  465. return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
  466. case image::CompressedData::TYPE_DXT5:
  467. return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
  468. case image::CompressedData::TYPE_BC5:
  469. return GL_COMPRESSED_RG_RGTC2;
  470. case image::CompressedData::TYPE_BC5s:
  471. return GL_COMPRESSED_SIGNED_RG_RGTC2;
  472. case image::CompressedData::TYPE_BC7:
  473. return GL_COMPRESSED_RGBA_BPTC_UNORM_ARB;
  474. case image::CompressedData::TYPE_BC7srgb:
  475. return GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB;
  476. default:
  477. return GL_RGBA8;
  478. }
  479. }
  480. bool Image::hasNpot()
  481. {
  482. return GLEE_VERSION_2_0 || GLEE_ARB_texture_non_power_of_two;
  483. }
  484. bool Image::hasAnisotropicFilteringSupport()
  485. {
  486. return GLEE_EXT_texture_filter_anisotropic;
  487. }
  488. bool Image::hasMipmapSupport()
  489. {
  490. return GLEE_VERSION_1_4 || GLEE_SGIS_generate_mipmap;
  491. }
  492. bool Image::hasMipmapSharpnessSupport()
  493. {
  494. return GLEE_VERSION_1_4 || GLEE_EXT_texture_lod_bias;
  495. }
  496. bool Image::hasCompressedTextureSupport()
  497. {
  498. return GLEE_VERSION_1_3 || GLEE_ARB_texture_compression;
  499. }
  500. bool Image::hasCompressedTextureSupport(image::CompressedData::TextureType type)
  501. {
  502. if (!hasCompressedTextureSupport())
  503. return false;
  504. switch (type)
  505. {
  506. case image::CompressedData::TYPE_DXT1:
  507. case image::CompressedData::TYPE_DXT3:
  508. case image::CompressedData::TYPE_DXT5:
  509. return GLEE_EXT_texture_compression_s3tc;
  510. case image::CompressedData::TYPE_BC5:
  511. case image::CompressedData::TYPE_BC5s:
  512. return (GLEE_VERSION_3_0 || GLEE_ARB_texture_compression_rgtc || GLEE_EXT_texture_compression_rgtc);
  513. case image::CompressedData::TYPE_BC7:
  514. case image::CompressedData::TYPE_BC7srgb:
  515. return (GLEE_VERSION_4_2 || GLEE_ARB_texture_compression_bptc);
  516. default:
  517. return false;
  518. }
  519. }
  520. } // opengl
  521. } // graphics
  522. } // love