CmFreeImgImporter.cpp 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. #include "CmFreeImgImporter.h"
  2. #include "CmResource.h"
  3. #include "CmDebug.h"
  4. #include "CmDataStream.h"
  5. #include "CmPath.h"
  6. #include "CmTextureData.h"
  7. #include "CmTextureManager.h"
  8. #include "CmTexture.h"
  9. #include "FreeImage.h"
  10. // freeimage 3.9.1~3.11.0 interoperability fix
  11. #ifndef FREEIMAGE_COLORORDER
  12. // we have freeimage 3.9.1, define these symbols in such way as 3.9.1 really work (do not use 3.11.0 definition, as color order was changed between these two versions on Apple systems)
  13. #define FREEIMAGE_COLORORDER_BGR 0
  14. #define FREEIMAGE_COLORORDER_RGB 1
  15. #if defined(FREEIMAGE_BIGENDIAN)
  16. #define FREEIMAGE_COLORORDER FREEIMAGE_COLORORDER_RGB
  17. #else
  18. #define FREEIMAGE_COLORORDER FREEIMAGE_COLORORDER_BGR
  19. #endif
  20. #endif
  21. namespace CamelotEngine
  22. {
  23. void FreeImageLoadErrorHandler(FREE_IMAGE_FORMAT fif, const char *message)
  24. {
  25. // Callback method as required by FreeImage to report problems
  26. const char* typeName = FreeImage_GetFormatFromFIF(fif);
  27. if (typeName)
  28. {
  29. gDebug().log("FreeImage error: '" + String(message) + "' when loading format " + typeName, "AssetImport");
  30. }
  31. else
  32. {
  33. gDebug().log("FreeImage error: '" + String(message) + "'", "AssetImport");
  34. }
  35. }
  36. FreeImgImporter::FreeImgImporter()
  37. :SpecificImporter()
  38. {
  39. FreeImage_Initialise(false);
  40. // Register codecs
  41. StringUtil::StrStreamType strExt;
  42. strExt << "Supported formats: ";
  43. bool first = true;
  44. for (int i = 0; i < FreeImage_GetFIFCount(); ++i)
  45. {
  46. // Skip DDS codec since FreeImage does not have the option
  47. // to keep DXT data compressed, we'll use our own codec
  48. if ((FREE_IMAGE_FORMAT)i == FIF_DDS)
  49. continue;
  50. String exts(FreeImage_GetFIFExtensionList((FREE_IMAGE_FORMAT)i));
  51. if (!first)
  52. {
  53. strExt << ",";
  54. }
  55. first = false;
  56. strExt << exts;
  57. // Pull off individual formats (separated by comma by FI)
  58. vector<String>::type extsVector = StringUtil::split(exts, ",");
  59. for (auto v = extsVector.begin(); v != extsVector.end(); ++v)
  60. {
  61. auto findIter = std::find(mExtensions.begin(), mExtensions.end(), *v);
  62. if(findIter == mExtensions.end())
  63. {
  64. String ext = *v;
  65. StringUtil::toLowerCase(ext);
  66. mExtensionToFID.insert(std::make_pair(ext, i));
  67. mExtensions.push_back(ext);
  68. }
  69. }
  70. }
  71. // Set error handler
  72. FreeImage_SetOutputMessage(FreeImageLoadErrorHandler);
  73. }
  74. FreeImgImporter::~FreeImgImporter()
  75. {
  76. FreeImage_DeInitialise();
  77. }
  78. bool FreeImgImporter::isExtensionSupported(const String& ext) const
  79. {
  80. String lowerCaseExt = ext;
  81. StringUtil::toLowerCase(lowerCaseExt);
  82. return find(mExtensions.begin(), mExtensions.end(), lowerCaseExt) != mExtensions.end();
  83. }
  84. bool FreeImgImporter::isMagicNumberSupported(const UINT8* magicNumPtr, UINT32 numBytes) const
  85. {
  86. String ext = magicNumToExtension(magicNumPtr, numBytes);
  87. return isExtensionSupported(ext);
  88. }
  89. String FreeImgImporter::magicNumToExtension(const UINT8* magic, UINT32 maxBytes) const
  90. {
  91. // Set error handler
  92. FreeImage_SetOutputMessage(FreeImageLoadErrorHandler);
  93. FIMEMORY* fiMem =
  94. FreeImage_OpenMemory((BYTE*)magic, static_cast<DWORD>(maxBytes));
  95. FREE_IMAGE_FORMAT fif = FreeImage_GetFileTypeFromMemory(fiMem, (int)maxBytes);
  96. FreeImage_CloseMemory(fiMem);
  97. if (fif != FIF_UNKNOWN)
  98. {
  99. String ext(FreeImage_GetFormatFromFIF(fif));
  100. StringUtil::toLowerCase(ext);
  101. return ext;
  102. }
  103. else
  104. {
  105. return StringUtil::BLANK;
  106. }
  107. }
  108. ResourcePtr FreeImgImporter::import(DataStreamPtr fileData)
  109. {
  110. TextureDataPtr imgData = importRawImage(fileData);
  111. if(imgData == nullptr || imgData->getData() == nullptr)
  112. return nullptr;
  113. TexturePtr newTexture = TextureManager::instance().create(TEX_TYPE_2D,
  114. imgData->getWidth(), imgData->getHeight(), imgData->getNumMipmaps(), imgData->getFormat());
  115. newTexture->setTextureData(0, imgData);
  116. return newTexture;
  117. }
  118. TextureDataPtr FreeImgImporter::importRawImage(DataStreamPtr fileData)
  119. {
  120. size_t magicLen = std::min(fileData->size(), (size_t)32);
  121. UINT8 magicBuf[32];
  122. fileData->read(magicBuf, magicLen);
  123. // return to start
  124. fileData->seek(0);
  125. String fileExtension = magicNumToExtension(magicBuf, magicLen);
  126. auto findFormat = mExtensionToFID.find(fileExtension);
  127. if(findFormat == mExtensionToFID.end())
  128. {
  129. CM_EXCEPT(InvalidParametersException, "Type of the file provided is not supported by this importer. File type: " + fileExtension);
  130. }
  131. FREE_IMAGE_FORMAT imageFormat = (FREE_IMAGE_FORMAT)findFormat->second;
  132. // Set error handler
  133. FreeImage_SetOutputMessage(FreeImageLoadErrorHandler);
  134. // Buffer stream into memory (TODO: override IO functions instead?)
  135. MemoryDataStream memStream(fileData, true);
  136. FIMEMORY* fiMem = FreeImage_OpenMemory(memStream.getPtr(), static_cast<DWORD>(memStream.size()));
  137. FIBITMAP* fiBitmap = FreeImage_LoadFromMemory(
  138. (FREE_IMAGE_FORMAT)imageFormat, fiMem);
  139. if (!fiBitmap)
  140. {
  141. CM_EXCEPT(InternalErrorException, "Error decoding image");
  142. }
  143. UINT32 width = FreeImage_GetWidth(fiBitmap);
  144. UINT32 height = FreeImage_GetHeight(fiBitmap);
  145. PixelFormat format = PF_UNKNOWN;
  146. // Must derive format first, this may perform conversions
  147. FREE_IMAGE_TYPE imageType = FreeImage_GetImageType(fiBitmap);
  148. FREE_IMAGE_COLOR_TYPE colourType = FreeImage_GetColorType(fiBitmap);
  149. unsigned bpp = FreeImage_GetBPP(fiBitmap);
  150. switch(imageType)
  151. {
  152. case FIT_UNKNOWN:
  153. case FIT_COMPLEX:
  154. case FIT_UINT32:
  155. case FIT_INT32:
  156. case FIT_DOUBLE:
  157. default:
  158. CM_EXCEPT(ItemIdentityException, "Unknown or unsupported image format");
  159. break;
  160. case FIT_BITMAP:
  161. // Standard image type
  162. // Perform any colour conversions for greyscale
  163. if (colourType == FIC_MINISWHITE || colourType == FIC_MINISBLACK)
  164. {
  165. FIBITMAP* newBitmap = FreeImage_ConvertToGreyscale(fiBitmap);
  166. // free old bitmap and replace
  167. FreeImage_Unload(fiBitmap);
  168. fiBitmap = newBitmap;
  169. // get new formats
  170. bpp = FreeImage_GetBPP(fiBitmap);
  171. colourType = FreeImage_GetColorType(fiBitmap);
  172. }
  173. // Perform any colour conversions for RGB
  174. else if (bpp < 8 || colourType == FIC_PALETTE || colourType == FIC_CMYK)
  175. {
  176. FIBITMAP* newBitmap = FreeImage_ConvertTo24Bits(fiBitmap);
  177. // free old bitmap and replace
  178. FreeImage_Unload(fiBitmap);
  179. fiBitmap = newBitmap;
  180. // get new formats
  181. bpp = FreeImage_GetBPP(fiBitmap);
  182. colourType = FreeImage_GetColorType(fiBitmap);
  183. }
  184. // by this stage, 8-bit is greyscale, 16/24/32 bit are RGB[A]
  185. switch(bpp)
  186. {
  187. case 8:
  188. format = PF_L8;
  189. break;
  190. case 16:
  191. // Determine 555 or 565 from green mask
  192. // cannot be 16-bit greyscale since that's FIT_UINT16
  193. if(FreeImage_GetGreenMask(fiBitmap) == FI16_565_GREEN_MASK)
  194. {
  195. format = PF_R5G6B5;
  196. }
  197. else
  198. {
  199. // FreeImage doesn't support 4444 format so must be 1555
  200. format = PF_A1R5G5B5;
  201. }
  202. break;
  203. case 24:
  204. // FreeImage differs per platform
  205. // PF_BYTE_BGR[A] for little endian (== PF_ARGB native)
  206. // PF_BYTE_RGB[A] for big endian (== PF_RGBA native)
  207. #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
  208. format = PF_BYTE_RGB;
  209. #else
  210. format = PF_BYTE_BGR;
  211. #endif
  212. break;
  213. case 32:
  214. #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
  215. format = PF_BYTE_RGBA;
  216. #else
  217. format = PF_BYTE_BGRA;
  218. #endif
  219. break;
  220. };
  221. break;
  222. case FIT_UINT16:
  223. case FIT_INT16:
  224. // 16-bit greyscale
  225. format = PF_L16;
  226. break;
  227. case FIT_FLOAT:
  228. // Single-component floating point data
  229. format = PF_FLOAT32_R;
  230. break;
  231. case FIT_RGB16:
  232. format = PF_SHORT_RGB;
  233. break;
  234. case FIT_RGBA16:
  235. format = PF_SHORT_RGBA;
  236. break;
  237. case FIT_RGBF:
  238. format = PF_FLOAT32_RGB;
  239. break;
  240. case FIT_RGBAF:
  241. format = PF_FLOAT32_RGBA;
  242. break;
  243. };
  244. unsigned char* srcData = FreeImage_GetBits(fiBitmap);
  245. unsigned srcPitch = FreeImage_GetPitch(fiBitmap);
  246. // Final data - invert image and trim pitch at the same time
  247. size_t dstPitch = width * PixelUtil::getNumElemBytes(format);
  248. size_t size = dstPitch * height;
  249. // Bind output buffer
  250. UINT8* output = new UINT8[size]; // TextureData frees this when its released
  251. UINT8* pSrc;
  252. UINT8* pDst = output;
  253. for (size_t y = 0; y < height; ++y)
  254. {
  255. pSrc = srcData + (height - y - 1) * srcPitch;
  256. memcpy(pDst, pSrc, dstPitch);
  257. pDst += dstPitch;
  258. }
  259. FreeImage_Unload(fiBitmap);
  260. FreeImage_CloseMemory(fiMem);
  261. TextureDataPtr texData(new TextureData(width, height, size, format, output, 1, 0, 0));
  262. return texData;
  263. }
  264. }