imageUtils.cpp 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2016 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "platform/platform.h"
  23. #include "gfx/bitmap/imageUtils.h"
  24. #include "gfx/bitmap/ddsFile.h"
  25. #include "platform/threads/threadPool.h"
  26. #include "squish/squish.h"
  27. namespace ImageUtil
  28. {
  29. // get squish quality flag
  30. S32 _getSquishQuality(const CompressQuality quality)
  31. {
  32. switch (quality)
  33. {
  34. case LowQuality:
  35. return squish::kColourRangeFit;
  36. case MediumQuality:
  37. return squish::kColourClusterFit;
  38. case HighQuality:
  39. return squish::kColourIterativeClusterFit;
  40. default:
  41. return squish::kColourRangeFit;//default is low quality
  42. }
  43. }
  44. // get squish compression flag
  45. S32 _getSquishFormat(const GFXFormat compressFormat)
  46. {
  47. switch (compressFormat)
  48. {
  49. case GFXFormatBC1:
  50. return squish::kDxt1;
  51. case GFXFormatBC2:
  52. return squish::kDxt3;
  53. case GFXFormatBC3:
  54. return squish::kDxt5;
  55. case GFXFormatBC4:
  56. return squish::kBc4;
  57. case GFXFormatBC5:
  58. return squish::kBc5;
  59. default:
  60. return squish::kDxt1;
  61. }
  62. }
  63. //Thread work job for compression
  64. struct CompressJob : public ThreadPool::WorkItem
  65. {
  66. S32 width;
  67. S32 height;
  68. const U8 *pSrc;
  69. U8 *pDst;
  70. GFXFormat format;
  71. CompressQuality quality;
  72. CompressJob(const U8 *srcRGBA, U8 *dst, const S32 w, const S32 h, const GFXFormat compressFormat, const CompressQuality compressQuality)
  73. : pSrc(srcRGBA),pDst(dst), width(w), height(h), format(compressFormat),quality(compressQuality) {}
  74. protected:
  75. virtual void execute()
  76. {
  77. rawCompress(pSrc,pDst, width, height, format,quality);
  78. }
  79. };
  80. // compress raw pixel data, expects rgba format
  81. bool rawCompress(const U8 *srcRGBA, U8 *dst, const S32 width, const S32 height, const GFXFormat compressFormat, const CompressQuality compressQuality)
  82. {
  83. if (!isCompressedFormat(compressFormat))
  84. return false;
  85. S32 squishFlags = _getSquishQuality(compressQuality);
  86. S32 squishFormat = _getSquishFormat(compressFormat);
  87. squishFlags |= squishFormat;
  88. squish::CompressImage(srcRGBA, width,height,dst,squishFlags);
  89. return true;
  90. }
  91. // compress DDSFile
  92. bool ddsCompress(DDSFile *srcDDS, const GFXFormat compressFormat,const CompressQuality compressQuality)
  93. {
  94. if (srcDDS->mBytesPerPixel != 4)
  95. {
  96. Con::errorf("ImageCompress::ddsCompress: data must be 32bit");
  97. return false;
  98. }
  99. //can't compress DDSFile if it is already compressed
  100. if (ImageUtil::isCompressedFormat(srcDDS->mFormat))
  101. {
  102. Con::errorf("ImageCompress::ddsCompress: file is already compressed");
  103. return false;
  104. }
  105. const bool cubemap = srcDDS->mFlags.test(DDSFile::CubeMapFlag);
  106. const U32 mipCount = srcDDS->mMipMapCount;
  107. // We got this far, so assume we can finish (gosh I hope so)
  108. srcDDS->mFormat = compressFormat;
  109. srcDDS->mFlags.set(DDSFile::CompressedData);
  110. //grab global thread pool
  111. ThreadPool* pThreadPool = &ThreadPool::GLOBAL();
  112. if (cubemap)
  113. {
  114. static U32 nCubeFaces = 6;
  115. Vector<U8*> dstDataStore;
  116. dstDataStore.setSize(nCubeFaces * mipCount);
  117. for (S32 cubeFace = 0; cubeFace < nCubeFaces; cubeFace++)
  118. {
  119. DDSFile::SurfaceData *srcSurface = srcDDS->mSurfaces[cubeFace];
  120. for (U32 currentMip = 0; currentMip < mipCount; currentMip++)
  121. {
  122. const U32 dataIndex = cubeFace * mipCount + currentMip;
  123. const U8 *srcBits = srcSurface->mMips[currentMip];
  124. const U32 mipSz = srcDDS->getSurfaceSize(currentMip);
  125. U8 *dstBits = new U8[mipSz];
  126. dstDataStore[dataIndex] = dstBits;
  127. ThreadSafeRef<CompressJob> item(new CompressJob(srcBits, dstBits, srcDDS->getWidth(currentMip), srcDDS->getHeight(currentMip), compressFormat, compressQuality));
  128. pThreadPool->queueWorkItem(item);
  129. }
  130. }
  131. //wait for work items to finish
  132. pThreadPool->waitForAllItems();
  133. for (S32 cubeFace = 0; cubeFace < nCubeFaces; cubeFace++)
  134. {
  135. DDSFile::SurfaceData *pSrcSurface = srcDDS->mSurfaces[cubeFace];
  136. for (U32 currentMip = 0; currentMip < mipCount; currentMip++)
  137. {
  138. const U32 dataIndex = cubeFace * mipCount + currentMip;
  139. delete[] pSrcSurface->mMips[currentMip];
  140. pSrcSurface->mMips[currentMip] = dstDataStore[dataIndex];
  141. }
  142. }
  143. }
  144. else
  145. {
  146. // The source surface is the original surface of the file
  147. DDSFile::SurfaceData *pSrcSurface = srcDDS->mSurfaces.last();
  148. // Create a new surface, this will be the DXT compressed surface. Once we
  149. // are done, we can discard the old surface, and replace it with this one.
  150. DDSFile::SurfaceData *pNewSurface = new DDSFile::SurfaceData();
  151. //no point using threading if only 1 mip
  152. const bool useThreading = bool(mipCount > 1);
  153. for (U32 currentMip = 0; currentMip < mipCount; currentMip++)
  154. {
  155. const U8 *pSrcBits = pSrcSurface->mMips[currentMip];
  156. const U32 mipSz = srcDDS->getSurfaceSize(currentMip);
  157. U8 *pDstBits = new U8[mipSz];
  158. pNewSurface->mMips.push_back(pDstBits);
  159. if (useThreading)
  160. {
  161. // Create CompressJob item
  162. ThreadSafeRef<CompressJob> item(new CompressJob(pSrcBits, pDstBits, srcDDS->getWidth(currentMip), srcDDS->getHeight(currentMip), compressFormat, compressQuality));
  163. pThreadPool->queueWorkItem(item);
  164. }
  165. else
  166. rawCompress(pSrcBits, pDstBits, srcDDS->getWidth(currentMip), srcDDS->getHeight(currentMip), compressFormat, compressQuality);
  167. }
  168. //block and wait for CompressJobs to finish
  169. if(useThreading)
  170. pThreadPool->waitForAllItems();
  171. // Now delete the source surface and replace with new compressed surface
  172. srcDDS->mSurfaces.pop_back();
  173. delete pSrcSurface;
  174. srcDDS->mSurfaces.push_back(pNewSurface);
  175. }
  176. return true;
  177. }
  178. bool decompress(const U8 *src, U8 *dstRGBA,const S32 width,const S32 height, const GFXFormat srcFormat)
  179. {
  180. if (!isCompressedFormat(srcFormat))
  181. return false;
  182. S32 squishFlag = _getSquishFormat(srcFormat);
  183. squish::DecompressImage(dstRGBA, width, height, src, squishFlag);
  184. return true;
  185. }
  186. void swizzleDDS(DDSFile *srcDDS, const Swizzle<U8, 4> &swizzle)
  187. {
  188. if (srcDDS->mFlags.test(DDSFile::CubeMapFlag))
  189. {
  190. for (S32 cubeFace = 0; cubeFace < DDSFile::Cubemap_Surface_Count; cubeFace++)
  191. {
  192. for (S32 i = 0; i < srcDDS->mMipMapCount; i++)
  193. {
  194. swizzle.InPlace(srcDDS->mSurfaces[cubeFace]->mMips[i], srcDDS->getSurfaceSize(i));
  195. }
  196. }
  197. }
  198. else
  199. {
  200. for (S32 i = 0; i < srcDDS->mMipMapCount; i++)
  201. {
  202. swizzle.InPlace(srcDDS->mSurfaces.last()->mMips[i], srcDDS->getSurfaceSize(i));
  203. }
  204. }
  205. }
  206. bool isCompressedFormat(const GFXFormat format)
  207. {
  208. if (format >= GFXFormatBC1 && format <= GFXFormatBC3_SRGB)
  209. return true;
  210. else
  211. return false;
  212. }
  213. bool isAlphaFormat(const GFXFormat format)
  214. {
  215. switch (format)
  216. {
  217. case GFXFormatA8:
  218. case GFXFormatA4L4:
  219. case GFXFormatA8L8:
  220. case GFXFormatR5G5B5A1:
  221. case GFXFormatR8G8B8A8:
  222. case GFXFormatB8G8R8A8:
  223. case GFXFormatR16G16B16A16F:
  224. case GFXFormatR32G32B32A32F:
  225. case GFXFormatR10G10B10A2:
  226. //case GFXFormatBC1://todo BC1 can store alpha
  227. case GFXFormatBC2:
  228. case GFXFormatBC3:
  229. return true;
  230. default:
  231. return false;
  232. }
  233. }
  234. bool isSRGBFormat(const GFXFormat format)
  235. {
  236. switch (format)
  237. {
  238. case GFXFormatR8G8B8_SRGB:
  239. case GFXFormatR8G8B8A8_SRGB:
  240. case GFXFormatBC1_SRGB:
  241. case GFXFormatBC2_SRGB:
  242. case GFXFormatBC3_SRGB:
  243. return true;
  244. default:
  245. return false;
  246. };
  247. }
  248. GFXFormat toSRGBFormat(const GFXFormat format)
  249. {
  250. switch (format)
  251. {
  252. case GFXFormatR8G8B8:
  253. return GFXFormatR8G8B8_SRGB;
  254. case GFXFormatR8G8B8X8:
  255. case GFXFormatR8G8B8A8:
  256. return GFXFormatR8G8B8A8_SRGB;
  257. case GFXFormatBC1:
  258. return GFXFormatBC1_SRGB;
  259. case GFXFormatBC2:
  260. return GFXFormatBC2_SRGB;
  261. case GFXFormatBC3:
  262. return GFXFormatBC3_SRGB;
  263. default:
  264. return format;
  265. };
  266. }
  267. U32 getMaxMipCount(const U32 width, const U32 height)
  268. {
  269. return mFloor(mLog2(mMax(width, height))) + 1;
  270. }
  271. }