CmFreeImgImporter.cpp 9.0 KB

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