Image.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  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 "Context.h"
  25. #include "File.h"
  26. #include "FileSystem.h"
  27. #include "Image.h"
  28. #include "Log.h"
  29. #include <cstring>
  30. #include <ddraw.h>
  31. #include <stb_image.h>
  32. #include <stb_image_write.h>
  33. #include "DebugNew.h"
  34. OBJECTTYPESTATIC(Image);
  35. Image::Image(Context* context) :
  36. Resource(context),
  37. width_(0),
  38. height_(0),
  39. components_(0)
  40. {
  41. }
  42. Image::~Image()
  43. {
  44. }
  45. void Image::RegisterObject(Context* context)
  46. {
  47. context->RegisterFactory<Image>();
  48. }
  49. bool Image::Load(Deserializer& source)
  50. {
  51. // Check for DDS compressed format
  52. if (source.ReadID() != "DDS ")
  53. {
  54. // Not DDS, use STBImage to load other image formats as uncompressed
  55. source.Seek(0);
  56. int width, height;
  57. unsigned components;
  58. unsigned char* pixelData = GetImageData(source, width, height, components);
  59. if (!pixelData)
  60. {
  61. LOGERROR("Could not load image: " + String(stbi_failure_reason()));
  62. return false;
  63. }
  64. SetSize(width, height, components);
  65. SetData(pixelData);
  66. FreeImageData(pixelData);
  67. }
  68. else
  69. {
  70. // DDS compressed format
  71. DDSURFACEDESC2 ddsd;
  72. source.Read(&ddsd, sizeof(ddsd));
  73. switch (ddsd.ddpfPixelFormat.dwFourCC)
  74. {
  75. case FOURCC_DXT1:
  76. compressedFormat_ = CF_DXT1;
  77. components_ = 3;
  78. break;
  79. case FOURCC_DXT3:
  80. compressedFormat_ = CF_DXT3;
  81. components_ = 4;
  82. break;
  83. case FOURCC_DXT5:
  84. compressedFormat_ = CF_DXT5;
  85. components_ = 4;
  86. break;
  87. default:
  88. LOGERROR("Unsupported DDS format");
  89. return false;
  90. }
  91. unsigned dataSize = source.GetSize() - source.GetPosition();
  92. data_ = new unsigned char[dataSize];
  93. width_ = ddsd.dwWidth;
  94. height_ = ddsd.dwHeight;
  95. numCompressedLevels_ = ddsd.dwMipMapCount;
  96. if (!numCompressedLevels_)
  97. numCompressedLevels_ = 1;
  98. SetMemoryUse(dataSize);
  99. source.Read(data_.GetPtr(), dataSize);
  100. }
  101. return true;
  102. }
  103. void Image::SetSize(unsigned width, unsigned height, unsigned components)
  104. {
  105. if ((width == width_) && (height == height_) && (components == components_))
  106. return;
  107. if ((width <= 0) || (height <= 0))
  108. return;
  109. data_ = new unsigned char[width * height * components];
  110. width_ = width;
  111. height_ = height;
  112. components_ = components;
  113. compressedFormat_ = CF_NONE;
  114. numCompressedLevels_ = 0;
  115. SetMemoryUse(width * height * components);
  116. }
  117. void Image::SetData(const unsigned char* pixelData)
  118. {
  119. memcpy(data_.GetPtr(), pixelData, width_ * height_ * components_);
  120. }
  121. bool Image::SaveBMP(const String& fileName)
  122. {
  123. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  124. if ((fileSystem) && (!fileSystem->CheckAccess(GetPath(fileName))))
  125. {
  126. LOGERROR("Access denied to " + fileName);
  127. return false;
  128. }
  129. if (IsCompressed())
  130. {
  131. LOGERROR("Can not save compressed image to BMP");
  132. return false;
  133. }
  134. if (data_)
  135. return stbi_write_bmp(fileName.CString(), width_, height_, components_, data_.GetPtr()) != 0;
  136. else
  137. return false;
  138. }
  139. bool Image::SaveTGA(const String& fileName)
  140. {
  141. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  142. if ((fileSystem) && (!fileSystem->CheckAccess(GetPath(fileName))))
  143. {
  144. LOGERROR("Access denied to " + fileName);
  145. return false;
  146. }
  147. if (IsCompressed())
  148. {
  149. LOGERROR("Can not save compressed image to TGA");
  150. return false;
  151. }
  152. if (data_)
  153. return stbi_write_tga(fileName.CString(), width_, height_, components_, data_.GetPtr()) != 0;
  154. else
  155. return false;
  156. }
  157. unsigned char* Image::GetImageData(Deserializer& source, int& width, int& height, unsigned& components)
  158. {
  159. unsigned dataSize = source.GetSize();
  160. SharedArrayPtr<unsigned char> buffer(new unsigned char[dataSize]);
  161. source.Read(buffer.GetPtr(), dataSize);
  162. return stbi_load_from_memory(buffer.GetPtr(), dataSize, &width, &height, (int *)&components, 0);
  163. }
  164. void Image::FreeImageData(unsigned char* pixelData)
  165. {
  166. if (!pixelData)
  167. return;
  168. stbi_image_free(pixelData);
  169. }
  170. SharedPtr<Image> Image::GetNextLevel() const
  171. {
  172. if (IsCompressed())
  173. {
  174. LOGERROR("Can not generate mip level from compressed data");
  175. return SharedPtr<Image>();
  176. }
  177. if ((components_ < 1) || (components_ > 4))
  178. {
  179. LOGERROR("Illegal number of image components for mip level generation");
  180. return SharedPtr<Image>();
  181. }
  182. int widthOut = width_ / 2;
  183. int heightOut = height_ / 2;
  184. if (widthOut < 1)
  185. widthOut = 1;
  186. if (heightOut < 1)
  187. heightOut = 1;
  188. SharedPtr<Image> mipImage(new Image(context_));
  189. mipImage->SetSize(widthOut, heightOut, components_);
  190. const unsigned char* pixelDataIn = data_.GetPtr();
  191. unsigned char* pixelDataOut = mipImage->data_.GetPtr();
  192. // 1D case
  193. if ((height_ == 1) || (width_ == 1))
  194. {
  195. // Loop using the larger dimension
  196. if (widthOut < heightOut)
  197. widthOut = heightOut;
  198. switch (components_)
  199. {
  200. case 1:
  201. for (int x = 0; x < widthOut; ++x)
  202. pixelDataOut[x] = ((unsigned)pixelDataIn[x*2] + pixelDataIn[x*2+1]) >> 1;
  203. break;
  204. case 2:
  205. for (int x = 0; x < widthOut*2; x += 2)
  206. {
  207. pixelDataOut[x] = ((unsigned)pixelDataIn[x*2] + pixelDataIn[x*2+2]) >> 1;
  208. pixelDataOut[x+1] = ((unsigned)pixelDataIn[x*2+1] + pixelDataIn[x*2+3]) >> 1;
  209. }
  210. break;
  211. case 3:
  212. for (int x = 0; x < widthOut*3; x += 3)
  213. {
  214. pixelDataOut[x] = ((unsigned)pixelDataIn[x*2] + pixelDataIn[x*2+3]) >> 1;
  215. pixelDataOut[x+1] = ((unsigned)pixelDataIn[x*2+1] + pixelDataIn[x*2+4]) >> 1;
  216. pixelDataOut[x+2] = ((unsigned)pixelDataIn[x*2+2] + pixelDataIn[x*2+5]) >> 1;
  217. }
  218. break;
  219. case 4:
  220. for (int x = 0; x < widthOut*4; x += 4)
  221. {
  222. pixelDataOut[x] = ((unsigned)pixelDataIn[x*2] + pixelDataIn[x*2+4]) >> 1;
  223. pixelDataOut[x+1] = ((unsigned)pixelDataIn[x*2+1] + pixelDataIn[x*2+5]) >> 1;
  224. pixelDataOut[x+2] = ((unsigned)pixelDataIn[x*2+2] + pixelDataIn[x*2+6]) >> 1;
  225. pixelDataOut[x+3] = ((unsigned)pixelDataIn[x*2+3] + pixelDataIn[x*2+7]) >> 1;
  226. }
  227. break;
  228. }
  229. }
  230. // 2D case
  231. else
  232. {
  233. switch (components_)
  234. {
  235. case 1:
  236. for (int y = 0; y < heightOut; ++y)
  237. {
  238. const unsigned char* inUpper = &pixelDataIn[(y*2)*width_];
  239. const unsigned char* inLower = &pixelDataIn[(y*2+1)*width_];
  240. unsigned char* out = &pixelDataOut[y*widthOut];
  241. for (int x = 0; x < widthOut; ++x)
  242. {
  243. out[x] = ((unsigned)inUpper[x*2] + inUpper[x*2+1] + inLower[x*2] + inLower[x*2+1]) >> 2;
  244. }
  245. }
  246. break;
  247. case 2:
  248. for (int y = 0; y < heightOut; ++y)
  249. {
  250. const unsigned char* inUpper = &pixelDataIn[(y*2)*width_*2];
  251. const unsigned char* inLower = &pixelDataIn[(y*2+1)*width_*2];
  252. unsigned char* out = &pixelDataOut[y*widthOut*2];
  253. for (int x = 0; x < widthOut*2; x += 2)
  254. {
  255. out[x] = ((unsigned)inUpper[x*2] + inUpper[x*2+2] + inLower[x*2] + inLower[x*2+2]) >> 2;
  256. out[x+1] = ((unsigned)inUpper[x*2+1] + inUpper[x*2+3] + inLower[x*2+1] + inLower[x*2+3]) >> 2;
  257. }
  258. }
  259. break;
  260. case 3:
  261. for (int y = 0; y < heightOut; ++y)
  262. {
  263. const unsigned char* inUpper = &pixelDataIn[(y*2)*width_*3];
  264. const unsigned char* inLower = &pixelDataIn[(y*2+1)*width_*3];
  265. unsigned char* out = &pixelDataOut[y*widthOut*3];
  266. for (int x = 0; x < widthOut*3; x += 3)
  267. {
  268. out[x] = ((unsigned)inUpper[x*2] + inUpper[x*2+3] + inLower[x*2] + inLower[x*2+3]) >> 2;
  269. out[x+1] = ((unsigned)inUpper[x*2+1] + inUpper[x*2+4] + inLower[x*2+1] + inLower[x*2+4]) >> 2;
  270. out[x+2] = ((unsigned)inUpper[x*2+2] + inUpper[x*2+5] + inLower[x*2+2] + inLower[x*2+5]) >> 2;
  271. }
  272. }
  273. break;
  274. case 4:
  275. for (int y = 0; y < heightOut; ++y)
  276. {
  277. const unsigned char* inUpper = &pixelDataIn[(y*2)*width_*4];
  278. const unsigned char* inLower = &pixelDataIn[(y*2+1)*width_*4];
  279. unsigned char* out = &pixelDataOut[y*widthOut*4];
  280. for (int x = 0; x < widthOut*4; x += 4)
  281. {
  282. out[x] = ((unsigned)inUpper[x*2] + inUpper[x*2+4] + inLower[x*2] + inLower[x*2+4]) >> 2;
  283. out[x+1] = ((unsigned)inUpper[x*2+1] + inUpper[x*2+5] + inLower[x*2+1] + inLower[x*2+5]) >> 2;
  284. out[x+2] = ((unsigned)inUpper[x*2+2] + inUpper[x*2+6] + inLower[x*2+2] + inLower[x*2+6]) >> 2;
  285. out[x+3] = ((unsigned)inUpper[x*2+3] + inUpper[x*2+7] + inLower[x*2+3] + inLower[x*2+7]) >> 2;
  286. }
  287. }
  288. break;
  289. }
  290. }
  291. return mipImage;
  292. }
  293. CompressedLevel Image::GetCompressedLevel(unsigned index) const
  294. {
  295. CompressedLevel level;
  296. if (compressedFormat_ == CF_NONE)
  297. {
  298. LOGERROR("Image is not compressed");
  299. return level;
  300. }
  301. if (index >= numCompressedLevels_)
  302. {
  303. LOGERROR("Compressed image mip level out of bounds");
  304. return level;
  305. }
  306. level.width_ = width_;
  307. level.height_ = height_;
  308. level.blockSize_ = (compressedFormat_ == CF_DXT1) ? 8 : 16;
  309. unsigned i = 0;
  310. unsigned offset = 0;
  311. for (;;)
  312. {
  313. if (!level.width_)
  314. level.width_ = 1;
  315. if (!level.height_)
  316. level.height_ = 1;
  317. level.rowSize_ = ((level.width_ + 3) / 4) * level.blockSize_;
  318. level.rows_ = ((level.height_ + 3) / 4);
  319. level.data_ = data_.GetPtr() + offset;
  320. level.dataSize_ = level.rows_ * level.rowSize_;
  321. if (offset + level.dataSize_ > GetMemoryUse())
  322. {
  323. LOGERROR("Compressed level is outside image data. Offset: " + String(offset) + " Size: " + String(level.dataSize_) +
  324. " Datasize: " + String(GetMemoryUse()));
  325. level.data_ = 0;
  326. return level;
  327. }
  328. if (i == index)
  329. return level;
  330. offset += level.dataSize_;
  331. level.width_ /= 2;
  332. level.height_ /= 2;
  333. ++i;
  334. }
  335. }