Image.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. //
  2. // Urho3D Engine
  3. // Copyright (c) 2008-2011 Lasse Öörni
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to deal
  7. // in the Software without restriction, including without limitation the rights
  8. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. // THE SOFTWARE.
  22. //
  23. #include "Precompiled.h"
  24. #include "File.h"
  25. #include "Image.h"
  26. #include "Log.h"
  27. #include "StringUtils.h"
  28. #include <cstring>
  29. #include <ddraw.h>
  30. #include <stb_image.h>
  31. #include <stb_image_write.h>
  32. #include "DebugNew.h"
  33. Image::Image(const std::string& name) :
  34. Resource(name),
  35. mWidth(0),
  36. mHeight(0),
  37. mComponents(0)
  38. {
  39. }
  40. Image::Image(int width, int height, unsigned components, const std::string& name) :
  41. Resource(name),
  42. mWidth(0),
  43. mHeight(0),
  44. mComponents(0)
  45. {
  46. setSize(width, height, components);
  47. }
  48. Image::Image(int width, int height, unsigned components, unsigned char* pixelData, const std::string& name) :
  49. Resource(name),
  50. mWidth(0),
  51. mHeight(0),
  52. mComponents(0)
  53. {
  54. setSize(width, height, components);
  55. setData(pixelData);
  56. }
  57. Image::~Image()
  58. {
  59. }
  60. void Image::load(Deserializer& source, ResourceCache* cache)
  61. {
  62. char header[4];
  63. // Read first 4 bytes
  64. source.read(header, 4);
  65. // First 4 bytes identify DDS compressed format
  66. if (strncmp(header, "DDS ", 4))
  67. {
  68. // Not DDS, use STBImage to load other image formats as uncompressed
  69. source.seek(0);
  70. int width, height;
  71. unsigned components;
  72. unsigned char* pixelData = loadImageFile(source, width, height, components);
  73. setSize(width, height, components);
  74. setData(pixelData);
  75. freeImageFile(pixelData);
  76. }
  77. else
  78. {
  79. // DDS compressed format
  80. DDSURFACEDESC2 ddsd;
  81. source.read(&ddsd, sizeof(ddsd));
  82. switch (ddsd.ddpfPixelFormat.dwFourCC)
  83. {
  84. case FOURCC_DXT1:
  85. mCompressedFormat = CF_DXT1;
  86. mComponents = 3;
  87. break;
  88. case FOURCC_DXT3:
  89. mCompressedFormat = CF_DXT3;
  90. mComponents = 4;
  91. break;
  92. case FOURCC_DXT5:
  93. mCompressedFormat = CF_DXT5;
  94. mComponents = 4;
  95. break;
  96. default:
  97. EXCEPTION("Unsupported DDS format");
  98. }
  99. unsigned dataSize = source.getSize() - source.getPosition();
  100. mData = new unsigned char[dataSize];
  101. mWidth = ddsd.dwWidth;
  102. mHeight = ddsd.dwHeight;
  103. mCompressedLevels = ddsd.dwMipMapCount;
  104. if (!mCompressedLevels)
  105. mCompressedLevels = 1;
  106. setMemoryUse(dataSize);
  107. source.read(mData.getPtr(), dataSize);
  108. }
  109. }
  110. void Image::setSize(unsigned width, unsigned height, unsigned components)
  111. {
  112. if ((width == mWidth) && (height == mHeight) && (components == mComponents))
  113. return;
  114. if ((width <= 0) || (height <= 0))
  115. return;
  116. mData = new unsigned char[width * height * components];
  117. mWidth = width;
  118. mHeight = height;
  119. mComponents = components;
  120. mCompressedFormat = CF_NONE;
  121. mCompressedLevels = 0;
  122. setMemoryUse(width * height * components);
  123. }
  124. void Image::setData(const unsigned char* pixelData)
  125. {
  126. memcpy(mData.getPtr(), pixelData, mWidth * mHeight * mComponents);
  127. }
  128. void Image::saveBMP(const std::string& fileName)
  129. {
  130. if (!checkDirectoryAccess(getPath(fileName)))
  131. SAFE_EXCEPTION("Access denied to " + fileName);
  132. if (isCompressed())
  133. SAFE_EXCEPTION("Can not save compressed image to BMP");
  134. if (mData)
  135. stbi_write_bmp(fileName.c_str(), mWidth, mHeight, mComponents, mData.getPtr());
  136. }
  137. void Image::saveTGA(const std::string& fileName)
  138. {
  139. if (!checkDirectoryAccess(getPath(fileName)))
  140. SAFE_EXCEPTION("Access denied to " + fileName);
  141. if (isCompressed())
  142. SAFE_EXCEPTION("Can not save compressed image to TGA");
  143. if (mData)
  144. stbi_write_tga(fileName.c_str(), mWidth, mHeight, mComponents, mData.getPtr());
  145. }
  146. unsigned char* Image::loadImageFile(Deserializer& source, int& width, int& height, unsigned& components)
  147. {
  148. unsigned dataSize = source.getSize();
  149. SharedArrayPtr<unsigned char> buffer(new unsigned char[dataSize]);
  150. source.read(buffer.getPtr(), dataSize);
  151. unsigned char* pixelData = stbi_load_from_memory(buffer.getPtr(), dataSize, &width, &height, (int *)&components, 0);
  152. if (!pixelData)
  153. EXCEPTION("Could not load image: " + std::string(stbi_failure_reason()));
  154. return pixelData;
  155. }
  156. void Image::freeImageFile(unsigned char* pixelData)
  157. {
  158. if (!pixelData)
  159. return;
  160. stbi_image_free(pixelData);
  161. }
  162. SharedPtr<Image> Image::getNextLevel() const
  163. {
  164. if (isCompressed())
  165. EXCEPTION("Can not generate mip level from compressed data");
  166. if ((mComponents < 1) || (mComponents > 4))
  167. EXCEPTION("Illegal number of image components for mip level generation");
  168. int widthOut = mWidth / 2;
  169. int heightOut = mHeight / 2;
  170. if (widthOut < 1)
  171. widthOut = 1;
  172. if (heightOut < 1)
  173. heightOut = 1;
  174. SharedPtr<Image> mipImage(new Image(widthOut, heightOut, mComponents));
  175. const unsigned char* pixelDataIn = mData.getPtr();
  176. unsigned char* pixelDataOut = mipImage->mData.getPtr();
  177. // 1D case
  178. if ((mHeight == 1) || (mWidth == 1))
  179. {
  180. // Loop using the larger dimension
  181. if (widthOut < heightOut)
  182. widthOut = heightOut;
  183. switch (mComponents)
  184. {
  185. case 1:
  186. for (int x = 0; x < widthOut; ++x)
  187. pixelDataOut[x] = ((unsigned)pixelDataIn[x*2] + pixelDataIn[x*2+1]) >> 1;
  188. break;
  189. case 2:
  190. for (int x = 0; x < widthOut*2; x += 2)
  191. {
  192. pixelDataOut[x] = ((unsigned)pixelDataIn[x*2] + pixelDataIn[x*2+2]) >> 1;
  193. pixelDataOut[x+1] = ((unsigned)pixelDataIn[x*2+1] + pixelDataIn[x*2+3]) >> 1;
  194. }
  195. break;
  196. case 3:
  197. for (int x = 0; x < widthOut*3; x += 3)
  198. {
  199. pixelDataOut[x] = ((unsigned)pixelDataIn[x*2] + pixelDataIn[x*2+3]) >> 1;
  200. pixelDataOut[x+1] = ((unsigned)pixelDataIn[x*2+1] + pixelDataIn[x*2+4]) >> 1;
  201. pixelDataOut[x+2] = ((unsigned)pixelDataIn[x*2+2] + pixelDataIn[x*2+5]) >> 1;
  202. }
  203. break;
  204. case 4:
  205. for (int x = 0; x < widthOut*4; x += 4)
  206. {
  207. pixelDataOut[x] = ((unsigned)pixelDataIn[x*2] + pixelDataIn[x*2+4]) >> 1;
  208. pixelDataOut[x+1] = ((unsigned)pixelDataIn[x*2+1] + pixelDataIn[x*2+5]) >> 1;
  209. pixelDataOut[x+2] = ((unsigned)pixelDataIn[x*2+2] + pixelDataIn[x*2+6]) >> 1;
  210. pixelDataOut[x+3] = ((unsigned)pixelDataIn[x*2+3] + pixelDataIn[x*2+7]) >> 1;
  211. }
  212. break;
  213. }
  214. }
  215. // 2D case
  216. else
  217. {
  218. switch (mComponents)
  219. {
  220. case 1:
  221. for (int y = 0; y < heightOut; ++y)
  222. {
  223. const unsigned char* inUpper = &pixelDataIn[(y*2)*mWidth];
  224. const unsigned char* inLower = &pixelDataIn[(y*2+1)*mWidth];
  225. unsigned char* out = &pixelDataOut[y*widthOut];
  226. for (int x = 0; x < widthOut; ++x)
  227. {
  228. out[x] = ((unsigned)inUpper[x*2] + inUpper[x*2+1] + inLower[x*2] + inLower[x*2+1]) >> 2;
  229. }
  230. }
  231. break;
  232. case 2:
  233. for (int y = 0; y < heightOut; ++y)
  234. {
  235. const unsigned char* inUpper = &pixelDataIn[(y*2)*mWidth*2];
  236. const unsigned char* inLower = &pixelDataIn[(y*2+1)*mWidth*2];
  237. unsigned char* out = &pixelDataOut[y*widthOut*2];
  238. for (int x = 0; x < widthOut*2; x += 2)
  239. {
  240. out[x] = ((unsigned)inUpper[x*2] + inUpper[x*2+2] + inLower[x*2] + inLower[x*2+2]) >> 2;
  241. out[x+1] = ((unsigned)inUpper[x*2+1] + inUpper[x*2+3] + inLower[x*2+1] + inLower[x*2+3]) >> 2;
  242. }
  243. }
  244. break;
  245. case 3:
  246. for (int y = 0; y < heightOut; ++y)
  247. {
  248. const unsigned char* inUpper = &pixelDataIn[(y*2)*mWidth*3];
  249. const unsigned char* inLower = &pixelDataIn[(y*2+1)*mWidth*3];
  250. unsigned char* out = &pixelDataOut[y*widthOut*3];
  251. for (int x = 0; x < widthOut*3; x += 3)
  252. {
  253. out[x] = ((unsigned)inUpper[x*2] + inUpper[x*2+3] + inLower[x*2] + inLower[x*2+3]) >> 2;
  254. out[x+1] = ((unsigned)inUpper[x*2+1] + inUpper[x*2+4] + inLower[x*2+1] + inLower[x*2+4]) >> 2;
  255. out[x+2] = ((unsigned)inUpper[x*2+2] + inUpper[x*2+5] + inLower[x*2+2] + inLower[x*2+5]) >> 2;
  256. }
  257. }
  258. break;
  259. case 4:
  260. for (int y = 0; y < heightOut; ++y)
  261. {
  262. const unsigned char* inUpper = &pixelDataIn[(y*2)*mWidth*4];
  263. const unsigned char* inLower = &pixelDataIn[(y*2+1)*mWidth*4];
  264. unsigned char* out = &pixelDataOut[y*widthOut*4];
  265. for (int x = 0; x < widthOut*4; x += 4)
  266. {
  267. out[x] = ((unsigned)inUpper[x*2] + inUpper[x*2+4] + inLower[x*2] + inLower[x*2+4]) >> 2;
  268. out[x+1] = ((unsigned)inUpper[x*2+1] + inUpper[x*2+5] + inLower[x*2+1] + inLower[x*2+5]) >> 2;
  269. out[x+2] = ((unsigned)inUpper[x*2+2] + inUpper[x*2+6] + inLower[x*2+2] + inLower[x*2+6]) >> 2;
  270. out[x+3] = ((unsigned)inUpper[x*2+3] + inUpper[x*2+7] + inLower[x*2+3] + inLower[x*2+7]) >> 2;
  271. }
  272. }
  273. break;
  274. }
  275. }
  276. return mipImage;
  277. }
  278. CompressedLevel Image::getCompressedLevel(unsigned index) const
  279. {
  280. CompressedLevel level;
  281. if (mCompressedFormat == CF_NONE)
  282. SAFE_EXCEPTION_RET("Image is not compressed", level);
  283. if (index >= mCompressedLevels)
  284. SAFE_EXCEPTION_RET("Illegal compressed image mip level", level);
  285. level.mWidth = mWidth;
  286. level.mHeight = mHeight;
  287. level.mBlockSize = (mCompressedFormat == CF_DXT1) ? 8 : 16;
  288. unsigned i = 0;
  289. unsigned offset = 0;
  290. for (;;)
  291. {
  292. if (!level.mWidth)
  293. level.mWidth = 1;
  294. if (!level.mHeight)
  295. level.mHeight = 1;
  296. level.mRowSize = ((level.mWidth + 3) / 4) * level.mBlockSize;
  297. level.mRows = ((level.mHeight + 3) / 4);
  298. level.mData = mData.getPtr() + offset;
  299. level.mDataSize = level.mRows * level.mRowSize;
  300. if (offset + level.mDataSize > getMemoryUse())
  301. SAFE_EXCEPTION_RET("Compressed level is outside image data. Offset: " + toString(offset) + " Size: " + toString(level.mDataSize) + " Datasize: " + toString(getMemoryUse()), level);
  302. if (i == index)
  303. return level;
  304. offset += level.mDataSize;
  305. level.mWidth /= 2;
  306. level.mHeight /= 2;
  307. ++i;
  308. }
  309. }