CmFreeImgImporter.cpp 9.0 KB

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