2
0

BsFreeImgImporter.cpp 9.7 KB

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