BsFreeImgImporter.cpp 9.8 KB

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