diff --git a/Engine/source/T3D/assets/ImageAsset.cpp b/Engine/source/T3D/assets/ImageAsset.cpp index dd50934d5d..ebe713cd1b 100644 --- a/Engine/source/T3D/assets/ImageAsset.cpp +++ b/Engine/source/T3D/assets/ImageAsset.cpp @@ -43,6 +43,7 @@ #include "gfx/gfxStringEnumTranslate.h" #include "ImageAssetInspectors.h" +#include "core/stream/memStream.h" // Debug Profiling. #include "platform/profiler.h" @@ -150,12 +151,17 @@ ImageAsset::ImageAsset() : mIsHDRImage(false), mImageType(Albedo), mIsNamedTarget(false), + mIsDDS(false), mImageWidth(-1), mImageHeight(-1), mImageDepth(-1), - mImageChannels(-1) + mImageChannels(-1), + mCellCountX(1), + mCellCountY(1) { + mEmbeddedBitmap = NULL; mLoadedState = AssetErrCode::NotLoaded; + VECTOR_SET_ASSOCIATION(mFrames); } //----------------------------------------------------------------------------- @@ -169,6 +175,19 @@ ImageAsset::~ImageAsset() } mResourceMap.clear(); + + FrameTextureMap::iterator frameIter = mFrameTextureMap.begin(); + for (; frameIter != mFrameTextureMap.end(); ++frameIter) + { + frameIter->value.free(); + } + + mFrameTextureMap.clear(); + + mFrames.clear(); + + if (mEmbeddedBitmap) + delete mEmbeddedBitmap; } @@ -201,6 +220,9 @@ void ImageAsset::initPersistFields() addProtectedField("isHDRImage", TypeBool, Offset(mIsHDRImage, ImageAsset), &setTextureHDR, &defaultProtectedGetFn, &writeTextureHDR, "HDR Image?"); addField("imageType", TypeImageAssetType, Offset(mImageType, ImageAsset), "What the main use-case for the image is for."); + + addField("cellCountX", TypeS32, Offset(mCellCountX, ImageAsset), "Number of cells along the X axis."); + addField("cellCountY", TypeS32, Offset(mCellCountY, ImageAsset), "Number of cells along the Y axis."); } bool ImageAsset::onAdd() { @@ -435,6 +457,12 @@ U32 ImageAsset::load() if (mLoadedState == Ok) return mLoadedState; + if (mEmbeddedBitmap) + { + mLoadedState = Ok; + return mLoadedState; + } + if (!Torque::FS::IsFile(mImageFile)) { if (isNamedTarget()) @@ -491,6 +519,18 @@ GFXTexHandle ImageAsset::getTexture(GFXTextureProfile* requestedProfile) if (mLoadedState == Ok) { + if (mEmbeddedBitmap) + { + GFXTexHandle newTex; + newTex.set(mEmbeddedBitmap, requestedProfile, false, avar("%s %s() - mTextureObject (line %d)", mImageFile, __FUNCTION__, __LINE__)); + if (newTex) + { + mResourceMap.insert(requestedProfile, newTex); + return newTex; + } + } + else + { //If we don't have an existing map case to the requested format, we'll just create it and insert it in GFXTexHandle newTex; newTex.set(mImageFile, requestedProfile, avar("%s %s() - mTextureObject (line %d)", mImageFile, __FUNCTION__, __LINE__)); @@ -499,6 +539,7 @@ GFXTexHandle ImageAsset::getTexture(GFXTextureProfile* requestedProfile) mResourceMap.insert(requestedProfile, newTex); return newTex; } + } } return nullptr; @@ -600,6 +641,52 @@ void ImageAsset::onTamlCustomWrite(TamlCustomNodes& customNodes) pImageInfoNode->addField(StringTable->insert("ImageHeight"), mImageHeight); pImageInfoNode->addField(StringTable->insert("ImageDepth"), mImageDepth); + if (gEmbedAssetData) + { + GBitmap* image = NULL; + Torque::Path path = expandAssetFilePath(mImageFile); + if (mIsDDS) + { + Resource dds = DDSFile::load(path, 0); + if (dds != NULL) + { + image = new GBitmap(); + if (!dds->decompressToGBitmap(image)) + { + delete image; + image = NULL; + } + } + } + else + { + Resource resImage = GBitmap::load(path); + if(resImage) + image = new GBitmap(*resImage); + } + + if (!image) + return; + + path.setExtension("dbm"); + + FileStream stream; + if (!stream.open(path, Torque::FS::File::Write)) + { + Con::errorf("ImageAsset::onTamlCustomWrite failed to open cache path %s", path.getFullFileName().c_str()); + stream.close(); + delete image; + return; + } + + image->write(stream); + stream.close(); + + delete image; + + pImageInfoNode->addField(StringTable->insert("BitmapCache"), path.getFullPath().c_str()); + } + } void ImageAsset::onTamlCustomRead(const TamlCustomNodes& customNodes) @@ -636,6 +723,20 @@ void ImageAsset::onTamlCustomRead(const TamlCustomNodes& customNodes) { pField->getFieldValue(mImageDepth); } + else if (fieldName == StringTable->insert("BitmapCache")) + { + Torque::Path path = pField->getFieldValue(); + + if (Torque::FS::IsFile(path)) + { + FileStream stream; + if (stream.open(path, Torque::FS::File::Read)) + { + mEmbeddedBitmap = new GBitmap; + mEmbeddedBitmap->read(stream); + } + } + } else { // Unknown name so warn. @@ -648,44 +749,59 @@ void ImageAsset::onTamlCustomRead(const TamlCustomNodes& customNodes) void ImageAsset::populateImage(void) { - if (Torque::FS::IsFile(mImageFile)) + if (mEmbeddedBitmap == NULL) { - if (dStrEndsWith(mImageFile, ".dds")) + if (Torque::FS::IsFile(mImageFile)) { - DDSFile* tempFile = new DDSFile(); - FileStream* ddsFs; - if ((ddsFs = FileStream::createAndOpen(mImageFile, Torque::FS::File::Read)) == NULL) + if (dStrEndsWith(mImageFile, ".dds")) { - Con::errorf("ImageAsset::setImageFile Failed to open ddsfile: %s", mImageFile); - } + DDSFile* tempFile = new DDSFile(); + FileStream* ddsFs; + if ((ddsFs = FileStream::createAndOpen(mImageFile, Torque::FS::File::Read)) == NULL) + { + Con::errorf("ImageAsset::populateImage Failed to open ddsfile: %s", mImageFile); + } - if (!tempFile->readHeader(*ddsFs)) - { - Con::errorf("ImageAsset::setImageFile Failed to read header of ddsfile: %s", mImageFile); + if (!tempFile->readHeader(*ddsFs)) + { + Con::errorf("ImageAsset::populateImage Failed to read header of ddsfile: %s", mImageFile); + } + else + { + mImageWidth = tempFile->mWidth; + mImageHeight = tempFile->mHeight; + } + + ddsFs->close(); + delete tempFile; + mIsDDS = true; } else { - mImageWidth = tempFile->mWidth; - mImageHeight = tempFile->mHeight; - } + if (dStrEndsWith(mImageFile, ".ies")) + { + return; + } - ddsFs->close(); - delete tempFile; - } - else - { - if (!stbi_info(mImageFile, &mImageWidth, &mImageHeight, &mImageChannels)) - { - StringTableEntry stbErr = stbi_failure_reason(); - if (stbErr == StringTable->EmptyString()) - stbErr = "ImageAsset::Unkown Error!"; + if (!stbi_info(mImageFile, &mImageWidth, &mImageHeight, &mImageChannels)) + { + StringTableEntry stbErr = stbi_failure_reason(); + if (stbErr == StringTable->EmptyString()) + stbErr = "ImageAsset::Unkown Error!"; - Con::errorf("ImageAsset::setImageFile STB Get file info failed: %s", stbErr); + Con::errorf("ImageAsset::populateImage STB Get file info failed: %s", stbErr); + } } - } - // we only support 2d textures..... for now ;) - mImageDepth = 1; + // we only support 2d textures..... for now ;) + mImageDepth = 1; + + generateFrames(); + } + } + else // we have an embedded bitmap, just generate frames. + { + generateFrames(); } } @@ -713,6 +829,36 @@ const char* ImageAsset::getImageInfo() return ""; } +void ImageAsset::generateFrames(void) +{ + mFrames.clear(); + + const F32 texelWidthScale = 1.0f / (F32)mImageWidth; + const F32 texelHeightScale = 1.0f / (F32)mImageHeight; + + // mFrames zero is always the full image. + mFrames.push_back(Frame(0, 0, + mImageWidth, mImageHeight, + texelWidthScale, texelHeightScale)); + + if (mCellCountX <= 1 && mCellCountY <= 1) + return; + + const U32 cellWidth = mImageWidth / mCellCountX; + const U32 cellHeight = mImageHeight / mCellCountY; + + for (U32 y = 0; y < mCellCountY; y++) + { + for (U32 x = 0; x < mCellCountX; x++) + { + mFrames.push_back(Frame( + x * cellWidth, y * cellHeight, + cellWidth, cellHeight, + texelWidthScale, texelHeightScale)); + } + } +} + DefineEngineMethod(ImageAsset, getImagePath, const char*, (), , "Gets the image filepath of this asset.\n" "@return File path of the image file.") diff --git a/Engine/source/T3D/assets/ImageAsset.h b/Engine/source/T3D/assets/ImageAsset.h index 53f6189cd2..b378c200e8 100644 --- a/Engine/source/T3D/assets/ImageAsset.h +++ b/Engine/source/T3D/assets/ImageAsset.h @@ -63,6 +63,9 @@ class ImageAsset : public AssetBase public: typedef HashMap ImageTextureMap; + + typedef CompoundKey FrameKey; + typedef HashMap FrameTextureMap; /// The different types of image use cases enum ImageTypes { @@ -83,13 +86,23 @@ class ImageAsset : public AssetBase class Frame { public: + Frame() + : regionName(StringTable->EmptyString()) + { + pixelOffset.set(0, 0); + pixelSize.set(0, 0); + texelLower.set(0.0f, 0.0f); + texelUpper.set(0.0f, 0.0f); + texelSize.set(0.0f, 0.0f); + } + Frame(const S32 pixelOffsetX, const S32 pixelOffsetY, const U32 pixelWidth, const U32 pixelHeight, const F32 texelWidthScale, const F32 texelHeightScale, StringTableEntry inRegionName = StringTable->EmptyString()) : regionName(inRegionName) { - pixelOffset.set(pixelOffsetY, pixelOffsetY); + pixelOffset.set(pixelOffsetX, pixelOffsetY); pixelSize.set(pixelWidth, pixelHeight); texelLower.set(pixelOffsetX * texelWidthScale, pixelOffsetY * texelHeightScale); @@ -132,10 +145,11 @@ class ImageAsset : public AssetBase return mErrCodeStrings[errCode - Parent::Extended]; }; private: - + GBitmap* mEmbeddedBitmap; StringTableEntry mImageFile; bool mUseMips; bool mIsHDRImage; + bool mIsDDS; ImageTypes mImageType; ImageTextureMap mResourceMap; bool mIsNamedTarget; @@ -143,6 +157,12 @@ class ImageAsset : public AssetBase S32 mImageHeight; S32 mImageDepth; S32 mImageChannels; + U32 mCellCountX; + U32 mCellCountY; + Vector mFrames; + FrameTextureMap mFrameTextureMap; + + inline void clampFrame(U32& frame) const { const U32 totalFrames = mFrames.size(); if (frame >= totalFrames) frame = (totalFrames == 0 ? 0 : totalFrames - 1); }; public: ImageAsset(); virtual ~ImageAsset(); @@ -191,6 +211,8 @@ class ImageAsset : public AssetBase bool isNamedTarget(void) const { return mIsNamedTarget; } NamedTexTargetRef getNamedTarget(void) const { return NamedTexTarget::find(mImageFile + 1); } + inline const Frame& getImageFrame(U32 frame) const { clampFrame(frame); return mFrames[frame]; }; + static U32 getAssetByFilename(StringTableEntry fileName, AssetPtr* imageAsset); static StringTableEntry getAssetIdByFilename(StringTableEntry fileName); static U32 getAssetById(StringTableEntry assetId, AssetPtr* imageAsset); @@ -198,6 +220,7 @@ class ImageAsset : public AssetBase void populateImage(void); const char* getImageInfo(); + void generateFrames(void); protected: // Asset Base callback diff --git a/Engine/source/T3D/assets/assetImporter.cpp b/Engine/source/T3D/assets/assetImporter.cpp index 3fb908cb82..b2ec38bf9a 100644 --- a/Engine/source/T3D/assets/assetImporter.cpp +++ b/Engine/source/T3D/assets/assetImporter.cpp @@ -813,7 +813,7 @@ String AssetImporter::getAssetTypeByFile(Torque::Path filePath) if (fileExt == String("dts") && fileName.endsWith("cached")) return ""; - if (fileExt == String("png") || fileExt == String("jpg") || fileExt == String("jpeg") || fileExt == String("dds")) + if (fileExt == String("png") || fileExt == String("jpg") || fileExt == String("jpeg") || fileExt == String("bmp") || fileExt == String("hdr") || fileExt == String("dds") || fileExt == String("ies")) return "ImageAsset"; else if (fileExt == String("dae") || fileExt == String("fbx") || fileExt == String("blend") || fileExt == String("obj") || fileExt == String("dts") || fileExt == String("gltf") || fileExt == String("glb")) return "ShapeAsset"; diff --git a/Engine/source/T3D/assets/assetImporter.h b/Engine/source/T3D/assets/assetImporter.h index c25bf07835..af5c62efd4 100644 --- a/Engine/source/T3D/assets/assetImporter.h +++ b/Engine/source/T3D/assets/assetImporter.h @@ -961,12 +961,20 @@ class AssetImporter : public SimObject String imagePath; if (Platform::isFile(testPath + String(".jpg"))) imagePath = testPath + String(".jpg"); + else if (Platform::isFile(testPath + String(".jpeg"))) + imagePath = testPath + String(".jpeg"); else if (Platform::isFile(testPath + String(".png"))) imagePath = testPath + String(".png"); else if (Platform::isFile(testPath + String(".dds"))) imagePath = testPath + String(".dds"); - else if (Platform::isFile(testPath + String(".tif"))) - imagePath = testPath + String(".tif"); + else if (Platform::isFile(testPath + String(".tga"))) + imagePath = testPath + String(".tga"); + else if (Platform::isFile(testPath + String(".bmp"))) + imagePath = testPath + String(".bmp"); + else if (Platform::isFile(testPath + String(".hdr"))) + imagePath = testPath + String(".hdr"); + else if (Platform::isFile(testPath + String(".ies"))) + imagePath = testPath + String(".ies"); if(imagePath.isNotEmpty()) //This ensures case-correct for the filename diff --git a/Engine/source/assets/assetBase.cpp b/Engine/source/assets/assetBase.cpp index fcb9b50512..2a336046f8 100644 --- a/Engine/source/assets/assetBase.cpp +++ b/Engine/source/assets/assetBase.cpp @@ -42,6 +42,8 @@ IMPLEMENT_CONOBJECT(AssetBase); +bool gEmbedAssetData = false; + //----------------------------------------------------------------------------- StringTableEntry assetNameField = StringTable->insert("AssetName"); diff --git a/Engine/source/assets/assetBase.h b/Engine/source/assets/assetBase.h index fa62065bc7..5e541c5e1a 100644 --- a/Engine/source/assets/assetBase.h +++ b/Engine/source/assets/assetBase.h @@ -38,6 +38,8 @@ class AssetManager; +extern bool gEmbedAssetData; + //----------------------------------------------------------------------------- extern StringTableEntry assetNameField; diff --git a/Engine/source/assets/assetManager.cpp b/Engine/source/assets/assetManager.cpp index 5d471d03f2..006e85aed3 100644 --- a/Engine/source/assets/assetManager.cpp +++ b/Engine/source/assets/assetManager.cpp @@ -1002,6 +1002,7 @@ bool AssetManager::compileAllAssets(const bool compressed, const bool includeUnl bool oldCompressed = mTaml.getBinaryCompression(); mTaml.setBinaryCompression(compressed); bool success = false; + gEmbedAssetData = true; // Refresh the current loaded assets. // NOTE: This will result in some assets being refreshed more than once due to asset dependencies. for (typeDeclaredAssetsHash::iterator assetItr = mDeclaredAssets.begin(); assetItr != mDeclaredAssets.end(); ++assetItr) @@ -1019,6 +1020,7 @@ bool AssetManager::compileAllAssets(const bool compressed, const bool includeUnl } mTaml.setBinaryCompression(oldCompressed); + gEmbedAssetData = false; // Are we including unloaded assets? if (includeUnloaded) diff --git a/Engine/source/gfx/bitmap/bitmapUtils.cpp b/Engine/source/gfx/bitmap/bitmapUtils.cpp index 3dd6388529..a9206d7c0e 100644 --- a/Engine/source/gfx/bitmap/bitmapUtils.cpp +++ b/Engine/source/gfx/bitmap/bitmapUtils.cpp @@ -67,7 +67,7 @@ void bitmapExtrude5551_c(const void *srcMip, void *mip, U32 srcHeight, U32 srcWi { U32 a = src[0]; U32 c = src[stride]; -#if defined(TORQUE_OS_MAC) +#if defined(TORQUE_BIG_ENDIAN) dst[y] = ((( (a >> 10) + (c >> 10)) >> 1) << 10) | ((( ((a >> 5) & 0x1F) + ((c >> 5) & 0x1f)) >> 1) << 5) | ((( ((a >> 0) & 0x1F) + ((c >> 0) & 0x1f)) >> 1) << 0); @@ -81,151 +81,80 @@ void bitmapExtrude5551_c(const void *srcMip, void *mip, U32 srcHeight, U32 srcWi } } - //-------------------------------------------------------------------------- -void bitmapExtrudeRGB_c(const void *srcMip, void *mip, U32 srcHeight, U32 srcWidth) -{ - const U8 *src = (const U8 *) srcMip; - U8 *dst = (U8 *) mip; - U32 stride = srcHeight != 1 ? (srcWidth) * 3 : 0; - - U32 width = srcWidth >> 1; - U32 height = srcHeight >> 1; - if (width == 0) width = 1; - if (height == 0) height = 1; - - if (srcWidth != 1) - { - for(U32 y = 0; y < height; y++) - { - for(U32 x = 0; x < width; x++) - { - *dst++ = (U32(*src) + U32(src[3]) + U32(src[stride]) + U32(src[stride+3]) + 2) >> 2; - src++; - *dst++ = (U32(*src) + U32(src[3]) + U32(src[stride]) + U32(src[stride+3]) + 2) >> 2; - src++; - *dst++ = (U32(*src) + U32(src[3]) + U32(src[stride]) + U32(src[stride+3]) + 2) >> 2; - src += 4; - } - src += stride; // skip - } - } - else - { - for(U32 y = 0; y < height; y++) - { - *dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1; - src++; - *dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1; - src++; - *dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1; - src += 4; - - src += stride; // skip - } - } -} - -//-------------------------------------------------------------------------- -void bitmapExtrudeRGBA_c(const void *srcMip, void *mip, U32 srcHeight, U32 srcWidth) -{ - const U8 *src = (const U8 *) srcMip; - U8 *dst = (U8 *) mip; - U32 stride = srcHeight != 1 ? (srcWidth) * 4 : 0; - - U32 width = srcWidth >> 1; - U32 height = srcHeight >> 1; - if (width == 0) width = 1; - if (height == 0) height = 1; - if (srcWidth != 1) - { - for(U32 y = 0; y < height; y++) - { - for(U32 x = 0; x < width; x++) - { - *dst++ = (U32(*src) + U32(src[4]) + U32(src[stride]) + U32(src[stride+4]) + 2) >> 2; - src++; - *dst++ = (U32(*src) + U32(src[4]) + U32(src[stride]) + U32(src[stride+4]) + 2) >> 2; - src++; - *dst++ = (U32(*src) + U32(src[4]) + U32(src[stride]) + U32(src[stride+4]) + 2) >> 2; - src++; - *dst++ = (U32(*src) + U32(src[4]) + U32(src[stride]) + U32(src[stride+4]) + 2) >> 2; - src += 5; - } - src += stride; // skip - } - } - else - { - for(U32 y = 0; y < height; y++) - { - *dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1; - src++; - *dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1; - src++; - *dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1; - src++; - *dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1; - src += 5; - - src += stride; // skip - } - } -} - -void bitmapExtrudeFPRGBA_c(const void *srcMip, void *mip, U32 srcHeight, U32 srcWidth) +template +void bitmapExtrudeGeneric( + const T* src, T* dst, + U32 srcWidth, U32 srcHeight, + U32 channels, U32 bpp) { - const U16 *src = (const U16 *)srcMip; - U16 *dst = (U16 *)mip; - U32 stride = srcHeight != 1 ? (srcWidth) * 8 : 0; - - U32 width = srcWidth >> 1; - U32 height = srcHeight >> 1; - if (width == 0) width = 1; - if (height == 0) height = 1; + U32 srcRowStride = srcHeight != 1 ? (srcWidth * bpp) / sizeof(T) : 0; + U32 dstWidth = srcWidth > 1 ? srcWidth / 2 : 1; + U32 dstHeight = srcHeight > 1 ? srcHeight / 2 : 1; + U32 dstRowStride = dstHeight != 1 ? (dstWidth * bpp) / sizeof(T) : 0; - if (srcWidth != 1) + for (U32 y = 0; y < dstHeight; ++y) { - for (U32 y = 0; y < height; y++) + for (U32 x = 0; x < dstWidth; ++x) { - for (U32 x = 0; x < width; x++) + for (U32 c = 0; c < channels; ++c) { - *dst++ = (U32(*src) + U32(src[4]) + U32(src[stride]) + U32(src[stride + 4]) + 2) >> 2; - src++; - *dst++ = (U32(*src) + U32(src[4]) + U32(src[stride]) + U32(src[stride + 4]) + 2) >> 2; - src++; - *dst++ = (U32(*src) + U32(src[4]) + U32(src[stride]) + U32(src[stride + 4]) + 2) >> 2; - src++; - *dst++ = (U32(*src) + U32(src[4]) + U32(src[stride]) + U32(src[stride + 4]) + 2) >> 2; - src += 5; + U32 x0 = x * 2; + U32 y0 = y * 2; + U32 x1 = (x0 + 1 < srcWidth) ? x0 + 1 : x0; + U32 y1 = (y0 + 1 < srcHeight) ? y0 + 1 : y0; + + if constexpr (std::is_floating_point_v) + { + T sum = 0; + sum += src[y0 * srcRowStride + x0 * channels + c]; + sum += src[y0 * srcRowStride + x1 * channels + c]; + sum += src[y1 * srcRowStride + x0 * channels + c]; + sum += src[y1 * srcRowStride + x1 * channels + c]; + + dst[y * dstRowStride + x * channels + c] = sum * 0.25f; + } + else + { + U32 sum = 0; + sum += src[y0 * srcRowStride + x0 * channels + c]; + sum += src[y0 * srcRowStride + x1 * channels + c]; + sum += src[y1 * srcRowStride + x0 * channels + c]; + sum += src[y1 * srcRowStride + x1 * channels + c]; + dst[y * dstRowStride + x * channels + c] = T((sum + 2) >> 2); + } } - src += stride; // skip - } - } - else - { - for (U32 y = 0; y < height; y++) - { - *dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1; - src++; - *dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1; - src++; - *dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1; - src++; - *dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1; - src += 5; - - src += stride; // skip } } } -void (*bitmapExtrude5551)(const void *srcMip, void *mip, U32 height, U32 width) = bitmapExtrude5551_c; -void (*bitmapExtrudeRGB)(const void *srcMip, void *mip, U32 srcHeight, U32 srcWidth) = bitmapExtrudeRGB_c; -void (*bitmapExtrudeRGBA)(const void *srcMip, void *mip, U32 srcHeight, U32 srcWidth) = bitmapExtrudeRGBA_c; -void (*bitmapExtrudeFPRGBA)(const void *srcMip, void *mip, U32 srcHeight, U32 srcWidth) = bitmapExtrudeFPRGBA_c; - +// 8-bit RGBA +auto bitmapExtrudeU8_RGBA = [](const void* src, void* dst, U32 h, U32 w, U32 bpp) { + bitmapExtrudeGeneric((const U8*)src, (U8*)dst, w, h, 4, bpp); +}; + +// 16-bit RGBA (U16 / F32 stored as U16) +auto bitmapExtrudeU16_RGBA = [](const void* src, void* dst, U32 h, U32 w, U32 bpp) { + bitmapExtrudeGeneric((const U16*)src, (U16*)dst, w, h, 4, bpp); +}; + +// 32-bit float RGBA +auto bitmapExtrudeF32_RGBA = [](const void* src, void* dst, U32 h, U32 w, U32 bpp) { + bitmapExtrudeGeneric((const F32*)src, (F32*)dst, w, h, 4, bpp); +}; + +// RGB U8 +auto bitmapExtrudeU8_RGB = [](const void* src, void* dst, U32 h, U32 w, U32 bpp) { + bitmapExtrudeGeneric((const U8*)src, (U8*)dst, w, h, 3, bpp); +}; + +void (*bitmapExtrude5551)(const void* srcMip, void* mip, U32 height, U32 width) = bitmapExtrude5551_c; +void (*bitmapExtrudeRGB)(const void* srcMip, void* mip, U32 srcHeight, U32 srcWidth, U32 bpp) = bitmapExtrudeU8_RGB; +void (*bitmapExtrudeRGBA)(const void* srcMip, void* mip, U32 srcHeight, U32 srcWidth, U32 bpp) = bitmapExtrudeU8_RGBA; +void (*bitmapExtrude16BitRGBA)(const void* srcMip, void* mip, U32 srcHeight, U32 srcWidth, U32 bpp) = bitmapExtrudeU16_RGBA; +void (*bitmapExtrudeFPRGBA)(const void* srcMip, void* mip, U32 srcHeight, U32 srcWidth, U32 bpp) = bitmapExtrudeU16_RGBA; +void (*bitmapExtrudeF32RGBA)(const void* srcMip, void* mip, U32 srcHeight, U32 srcWidth, U32 bpp) = bitmapExtrudeF32_RGBA; //-------------------------------------------------------------------------- @@ -238,7 +167,7 @@ void bitmapConvertRGB_to_1555_c(U8 *src, U32 pixels) U32 g = src[1] >> 3; U32 b = src[2] >> 3; -#if defined(TORQUE_OS_MAC) +#if defined(TORQUE_BIG_ENDIAN) *dst++ = 0x8000 | (b << 10) | (g << 5) | (r << 0); #else *dst++ = b | (g << 5) | (r << 10) | 0x8000; @@ -260,7 +189,7 @@ void bitmapConvertRGB_to_5551_c(U8 *src, U32 pixels) U32 g = src[1] >> 3; U32 b = src[2] >> 3; -#if defined(TORQUE_OS_MAC) +#if defined(TORQUE_BIG_ENDIAN) *dst++ = (1 << 15) | (b << 10) | (g << 5) | (r << 0); #else *dst++ = (b << 1) | (g << 6) | (r << 11) | 1; diff --git a/Engine/source/gfx/bitmap/bitmapUtils.h b/Engine/source/gfx/bitmap/bitmapUtils.h index 489a8f296f..ce860b39af 100644 --- a/Engine/source/gfx/bitmap/bitmapUtils.h +++ b/Engine/source/gfx/bitmap/bitmapUtils.h @@ -22,21 +22,118 @@ #ifndef _BITMAPUTILS_H_ #define _BITMAPUTILS_H_ - +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif #ifndef _TORQUE_TYPES_H_ #include "platform/types.h" #endif extern void (*bitmapExtrude5551)(const void *srcMip, void *mip, U32 height, U32 width); -extern void (*bitmapExtrudeRGB)(const void *srcMip, void *mip, U32 height, U32 width); -extern void (*bitmapExtrudeRGBA)(const void *srcMip, void *mip, U32 height, U32 width); -extern void(*bitmapExtrudeFPRGBA)(const void *srcMip, void *mip, U32 height, U32 width); +extern void (*bitmapExtrudeRGB)(const void *srcMip, void *mip, U32 height, U32 width, U32 bpp); +extern void (*bitmapExtrudeRGBA)(const void *srcMip, void *mip, U32 height, U32 width, U32 bpp); +extern void (*bitmapExtrude16BitRGBA)(const void *srcMip, void *mip, U32 height, U32 width, U32 bpp); +extern void(*bitmapExtrudeFPRGBA)(const void *srcMip, void *mip, U32 height, U32 width, U32 bpp); +extern void(*bitmapExtrudeF32RGBA)(const void *srcMip, void *mip, U32 height, U32 width, U32 bpp); extern void (*bitmapConvertRGB_to_5551)(U8 *src, U32 pixels); extern void (*bitmapConvertRGB_to_1555)(U8 *src, U32 pixels); extern void (*bitmapConvertRGB_to_RGBX)( U8 **src, U32 pixels ); extern void (*bitmapConvertRGBX_to_RGB)( U8 **src, U32 pixels ); extern void (*bitmapConvertA8_to_RGBA)( U8 **src, U32 pixels ); -void bitmapExtrudeRGB_c(const void *srcMip, void *mip, U32 height, U32 width); +//----------------------------------------------------------------------------- +// Half <-> Float Conversion Utilities +//----------------------------------------------------------------------------- + +inline F32 convertHalfToFloat(U16 h) +{ + U32 sign = (h >> 15) & 0x00000001; + U32 exp = (h >> 10) & 0x0000001F; + U32 mant = h & 0x000003FF; + + U32 outSign = sign << 31; + U32 outExp, outMant; + + if (exp == 0) + { + if (mant == 0) + { + // Zero + outExp = 0; + outMant = 0; + } + else + { + // Subnormal number -> normalize + exp = 1; + while ((mant & 0x00000400) == 0) + { + mant <<= 1; + exp -= 1; + } + mant &= 0x000003FF; + outExp = (exp + (127 - 15)) << 23; + outMant = mant << 13; + } + } + else if (exp == 31) + { + // Inf or NaN + outExp = 0xFF << 23; + outMant = mant ? (mant << 13) : 0; + } + else + { + // Normalized + outExp = (exp + (127 - 15)) << 23; + outMant = mant << 13; + } + + U32 out = outSign | outExp | outMant; + F32 result; + dMemcpy(&result, &out, sizeof(F32)); + return result; +} + +inline U16 convertFloatToHalf(F32 f) +{ + U32 bits; + dMemcpy(&bits, &f, sizeof(U32)); + + U32 sign = (bits >> 16) & 0x00008000; + U32 exp = ((bits >> 23) & 0x000000FF) - (127 - 15); + U32 mant = bits & 0x007FFFFF; + + if (exp <= 0) + { + if (exp < -10) + return (U16)sign; // Too small => 0 + mant = (mant | 0x00800000) >> (1 - exp); + return (U16)(sign | (mant >> 13)); + } + else if (exp == 0xFF - (127 - 15)) + { + if (mant == 0) + { + // Inf + return (U16)(sign | 0x7C00); + } + else + { + // NaN + mant >>= 13; + return (U16)(sign | 0x7C00 | mant | (mant == 0)); + } + } + else + { + if (exp > 30) + { + // Overflow => Inf + return (U16)(sign | 0x7C00); + } + return (U16)(sign | (exp << 10) | (mant >> 13)); + } +} #endif //_BITMAPUTILS_H_ diff --git a/Engine/source/gfx/bitmap/ddsFile.cpp b/Engine/source/gfx/bitmap/ddsFile.cpp index f204450587..9b110fc4e8 100644 --- a/Engine/source/gfx/bitmap/ddsFile.cpp +++ b/Engine/source/gfx/bitmap/ddsFile.cpp @@ -652,6 +652,12 @@ Resource DDSFile::load( const Torque::Path &path, U32 dropMipCount ) //------------------------------------------------------------------------------ +bool DDSFile::isCompressedFormat(GFXFormat fmt) +{ + return (fmt >= GFXFormatBC1 && fmt <= GFXFormatBC5) || + (fmt >= GFXFormatBC1_SRGB && fmt <= GFXFormatBC3_SRGB); +} + DDSFile *DDSFile::createDDSFileFromGBitmap( const GBitmap *gbmp ) { if( gbmp == NULL ) @@ -778,8 +784,29 @@ DDSFile *DDSFile::createDDSCubemapFileFromGBitmaps(GBitmap **gbmps) bool DDSFile::decompressToGBitmap(GBitmap *dest) { // TBD: do we support other formats? - if (mFormat != GFXFormatBC1 && mFormat != GFXFormatBC2 && mFormat != GFXFormatBC3) - return false; + if (!isCompressedFormat(mFormat)) + { + dest->allocateBitmapWithMips(getWidth(), getHeight(), getMipLevels(), mFormat); + U32 numMips = getMipLevels(); + + for (U32 i = 0; i < numMips; i++) + { + U8* addr = dest->getAddress(0, 0, i); + + const U8* mipBuffer = mSurfaces[0]->mMips[i]; + const U32 mipWidth = getWidth(i); + const U32 mipHeight = getHeight(i); + + const U32 bpp = dest->getBytesPerPixel(); + const U32 rowBytes = mipWidth * bpp; + + for (U32 y = 0; y < mipHeight; ++y) + { + dMemcpy(addr + y * rowBytes, mipBuffer + y * rowBytes, rowBytes); + } + } + return true; + } dest->allocateBitmapWithMips(getWidth(), getHeight(), getMipLevels(), GFXFormatR8G8B8A8); diff --git a/Engine/source/gfx/bitmap/ddsFile.h b/Engine/source/gfx/bitmap/ddsFile.h index 84b515f86e..5ad0a1baa0 100644 --- a/Engine/source/gfx/bitmap/ddsFile.h +++ b/Engine/source/gfx/bitmap/ddsFile.h @@ -205,6 +205,8 @@ struct DDSFile mSurfaces.clear(); } + bool isCompressedFormat(GFXFormat fmt); + static DDSFile *createDDSFileFromGBitmap( const GBitmap *gbmp ); //Create a single cubemap texture from 6 GBitmap static DDSFile *createDDSCubemapFileFromGBitmaps(GBitmap **gbmps); diff --git a/Engine/source/gfx/bitmap/gBitmap.cpp b/Engine/source/gfx/bitmap/gBitmap.cpp index 4c85537ad0..ed51b86b46 100644 --- a/Engine/source/gfx/bitmap/gBitmap.cpp +++ b/Engine/source/gfx/bitmap/gBitmap.cpp @@ -126,6 +126,65 @@ GBitmap::~GBitmap() //-------------------------------------------------------------------------- +U32 GBitmap::getFormatBytesPerPixel(GFXFormat fmt) +{ + switch (fmt) + { + // 8-bit formats + case GFXFormatA8: + case GFXFormatL8: + case GFXFormatA4L4: + return 1; + + // 16-bit formats + case GFXFormatR5G6B5: + case GFXFormatR5G5B5A1: + case GFXFormatR5G5B5X1: + case GFXFormatA8L8: + case GFXFormatL16: + case GFXFormatR16F: + case GFXFormatR16G16: + case GFXFormatR16G16F: + case GFXFormatD16: + return 2; + + // 24-bit formats + case GFXFormatR8G8B8: + case GFXFormatR8G8B8_SRGB: + return 3; + + // 32-bit formats + case GFXFormatR8G8B8A8: + case GFXFormatR8G8B8X8: + case GFXFormatB8G8R8A8: + case GFXFormatR8G8B8A8_SRGB: + case GFXFormatR32F: + case GFXFormatR10G10B10A2: + case GFXFormatR11G11B10: + case GFXFormatD24X8: + case GFXFormatD24S8: + case GFXFormatD24FS8: + case GFXFormatR8G8B8A8_LINEAR_FORCE: + return 4; + + // 64-bit formats + case GFXFormatR16G16B16A16: + case GFXFormatR16G16B16A16F: + case GFXFormatD32FS8X24: + return 8; + + // 128-bit formats + case GFXFormatR32G32B32A32F: + return 16; + + default: + AssertWarn(false, "getFormatBytesPerPixel() - Unknown or compressed format"); + return 4; + } +} + +//-------------------------------------------------------------------------- + void GBitmap::sRegisterFormat( const GBitmap::Registration ® ) { U32 insert = sRegistrations.size(); @@ -285,29 +344,7 @@ void GBitmap::allocateBitmap(const U32 in_width, const U32 in_height, const bool mWidth = in_width; mHeight = in_height; - mBytesPerPixel = 1; - switch (mInternalFormat) - { - case GFXFormatA8: - case GFXFormatL8: mBytesPerPixel = 1; - break; - case GFXFormatR8G8B8: mBytesPerPixel = 3; - break; - case GFXFormatR8G8B8A8_LINEAR_FORCE: - case GFXFormatR8G8B8X8: - case GFXFormatR8G8B8A8: mBytesPerPixel = 4; - break; - case GFXFormatL16: - case GFXFormatR5G6B5: - case GFXFormatR5G5B5A1: mBytesPerPixel = 2; - break; - case GFXFormatR16G16B16A16F: - case GFXFormatR16G16B16A16: mBytesPerPixel = 8; - break; - default: - AssertFatal(false, "GBitmap::GBitmap: misunderstood format specifier"); - break; - } + mBytesPerPixel = getFormatBytesPerPixel(mInternalFormat); // Set up the mip levels, if necessary... mNumMipLevels = 1; @@ -364,28 +401,7 @@ void GBitmap::allocateBitmapWithMips(const U32 in_width, const U32 in_height, co mWidth = in_width; mHeight = in_height; - mBytesPerPixel = 1; - switch (mInternalFormat) - { - case GFXFormatA8: - case GFXFormatL8: mBytesPerPixel = 1; - break; - case GFXFormatR8G8B8: mBytesPerPixel = 3; - break; - case GFXFormatR8G8B8X8: - case GFXFormatR8G8B8A8: mBytesPerPixel = 4; - break; - case GFXFormatL16: - case GFXFormatR5G6B5: - case GFXFormatR5G5B5A1: mBytesPerPixel = 2; - break; - case GFXFormatR16G16B16A16F: - case GFXFormatR16G16B16A16: mBytesPerPixel = 8; - break; - default: - AssertFatal(false, "GBitmap::GBitmap: misunderstood format specifier"); - break; - } + mBytesPerPixel = getFormatBytesPerPixel(mInternalFormat); // Set up the mip levels, if necessary... mNumMipLevels = 1; @@ -444,26 +460,43 @@ void GBitmap::extrudeMipLevels(bool clearBorders) case GFXFormatR8G8B8: { for(U32 i = 1; i < mNumMipLevels; i++) - bitmapExtrudeRGB(getBits(i - 1), getWritableBits(i), getHeight(i-1), getWidth(i-1)); + bitmapExtrudeRGB(getBits(i - 1), getWritableBits(i), getHeight(i-1), getWidth(i-1), mBytesPerPixel); break; } case GFXFormatR8G8B8A8: case GFXFormatR8G8B8X8: + case GFXFormatB8G8R8A8: + case GFXFormatR8G8B8A8_SRGB: { for(U32 i = 1; i < mNumMipLevels; i++) - bitmapExtrudeRGBA(getBits(i - 1), getWritableBits(i), getHeight(i-1), getWidth(i-1)); + bitmapExtrudeRGBA(getBits(i - 1), getWritableBits(i), getHeight(i-1), getWidth(i-1), mBytesPerPixel); + break; + } + + case GFXFormatR16G16B16A16: + { + for (U32 i = 1; i < mNumMipLevels; i++) + bitmapExtrude16BitRGBA(getBits(i - 1), getWritableBits(i), getHeight(i - 1), getWidth(i - 1), mBytesPerPixel); break; } case GFXFormatR16G16B16A16F: { for (U32 i = 1; i < mNumMipLevels; i++) - bitmapExtrudeFPRGBA(getBits(i - 1), getWritableBits(i), getHeight(i - 1), getWidth(i - 1)); + bitmapExtrudeFPRGBA(getBits(i - 1), getWritableBits(i), getHeight(i - 1), getWidth(i - 1), mBytesPerPixel); break; } - + + case GFXFormatR32G32B32A32F: + { + for (U32 i = 1; i < mNumMipLevels; i++) + bitmapExtrudeF32RGBA(getBits(i - 1), getWritableBits(i), getHeight(i - 1), getWidth(i - 1), mBytesPerPixel); + break; + } + default: + Con::warnf("GBitmap::extrudeMipLevels() - Unsupported format %d", getFormat()); break; } if (clearBorders) @@ -538,7 +571,7 @@ void GBitmap::extrudeMipLevelsDetail() allocateBitmap(getWidth(), getHeight(), true, getFormat()); for (i = 1; i < mNumMipLevels; i++) { - bitmapExtrudeRGB(getBits(i - 1), getWritableBits(i), getHeight(i-1), getWidth(i-1)); + bitmapExtrudeRGB(getBits(i - 1), getWritableBits(i), getHeight(i-1), getWidth(i-1), mBytesPerPixel); } // Ok, now that we have the levels extruded, we need to move the lower miplevels @@ -688,40 +721,43 @@ bool GBitmap::checkForTransparency() { mHasTransparency = false; + if (!mBits || mByteSize == 0) + return false; + ColorI pixel(255, 255, 255, 255); + // Only check formats that can *possibly* have alpha. switch (mInternalFormat) { - // Non-transparent formats - case GFXFormatL8: - case GFXFormatL16: - case GFXFormatR8G8B8: - case GFXFormatR5G6B5: - break; - // Transparent formats - case GFXFormatA8: - case GFXFormatR8G8B8A8: - case GFXFormatR5G5B5A1: - // Let getColor() do the heavy lifting - for (U32 x = 0; x < mWidth; x++) + case GFXFormatA8: + case GFXFormatA4L4: + case GFXFormatA8L8: + case GFXFormatR5G5B5A1: + case GFXFormatR8G8B8A8: + case GFXFormatB8G8R8A8: + case GFXFormatR8G8B8A8_SRGB: + case GFXFormatR10G10B10A2: + case GFXFormatR16G16B16A16: + case GFXFormatR16G16B16A16F: + case GFXFormatR32G32B32A32F: + break; // alpha-capable + default: + return false; // skip formats with no alpha + } + + for (U32 x = 0; x < mWidth; x++) + { + for (U32 y = 0; y < mHeight; y++) + { + if (getColor(x, y, pixel)) { - for (U32 y = 0; y < mHeight; y++) + if (pixel.alpha < 255) { - if (getColor(x, y, pixel)) - { - if (pixel.alpha < 255) - { - mHasTransparency = true; - break; - } - } + mHasTransparency = true; + break; } } - - break; - default: - AssertFatal(false, "GBitmap::checkForTransparency: misunderstood format specifier"); - break; + } } return mHasTransparency; @@ -770,40 +806,144 @@ bool GBitmap::getColor(const U32 x, const U32 y, ColorI& rColor) const if (x >= mWidth || y >= mHeight) return false; - const U8* pLoc = getAddress(x, y); + const U8* p = getAddress(x, y); - switch (mInternalFormat) { - case GFXFormatA8: - case GFXFormatL8: - rColor.set( *pLoc, *pLoc, *pLoc, *pLoc ); + switch (mInternalFormat) + { + // --- 8-bit --- + case GFXFormatA8: + rColor.set(255, 255, 255, p[0]); break; - case GFXFormatL16: - rColor.set(U8(U16((pLoc[0] << 8) + pLoc[1])), 0, 0, 0); - break; - case GFXFormatR8G8B8: - case GFXFormatR8G8B8X8: - rColor.set( pLoc[0], pLoc[1], pLoc[2], 255 ); + + case GFXFormatL8: + rColor.set(p[0], p[0], p[0], 255); break; - case GFXFormatR8G8B8A8: - rColor.set( pLoc[0], pLoc[1], pLoc[2], pLoc[3] ); + case GFXFormatA4L4: + { + U8 v = p[0]; + U8 lum = (v & 0x0F) * 17; + U8 alp = ((v >> 4) & 0x0F) * 17; + rColor.set(lum, lum, lum, alp); break; + } - case GFXFormatR5G5B5A1: -#if defined(TORQUE_OS_MAC) - rColor.set( (*((U16*)pLoc) >> 0) & 0x1F, - (*((U16*)pLoc) >> 5) & 0x1F, - (*((U16*)pLoc) >> 10) & 0x1F, - ((*((U16*)pLoc) >> 15) & 0x01) ? 255 : 0 ); + // --- 16-bit --- + case GFXFormatR5G6B5: + { + U16 c = ((U16*)p)[0]; +#ifdef TORQUE_BIG_ENDIAN + c = convertLEndianToHost(c); +#endif + U8 r = (c >> 11) & 0x1F; + U8 g = (c >> 5) & 0x3F; + U8 b = c & 0x1F; + rColor.set((r << 3) | (r >> 2), + (g << 2) | (g >> 4), + (b << 3) | (b >> 2), + 255); + break; + } + + case GFXFormatR5G5B5A1: + { + U16 c = ((U16*)p)[0]; +#ifdef TORQUE_BIG_ENDIAN + c = convertLEndianToHost(c); +#endif + U8 r = (c >> 11) & 0x1F; + U8 g = (c >> 6) & 0x1F; + U8 b = (c >> 1) & 0x1F; + U8 a = (c & 0x01) ? 255 : 0; + rColor.set((r << 3) | (r >> 2), + (g << 3) | (g >> 2), + (b << 3) | (b >> 2), + a); + break; + } + + case GFXFormatA8L8: + { + U16 c = ((U16*)p)[0]; +#ifdef TORQUE_BIG_ENDIAN + c = convertLEndianToHost(c); +#endif + U8 l = c & 0xFF; + U8 a = (c >> 8) & 0xFF; + rColor.set(l, l, l, a); + break; + } + + case GFXFormatL16: + { + U16 l = ((U16*)p)[0]; +#ifdef TORQUE_BIG_ENDIAN + l = convertLEndianToHost(l); +#endif + U8 lum = l >> 8; + rColor.set(lum, lum, lum, 255); + break; + } + + // --- 24-bit --- + case GFXFormatR8G8B8: + case GFXFormatR8G8B8_SRGB: + rColor.set(p[0], p[1], p[2], 255); + break; + + // --- 32-bit --- + case GFXFormatR8G8B8A8: + case GFXFormatR8G8B8A8_SRGB: + rColor.set(p[0], p[1], p[2], p[3]); + break; + + case GFXFormatB8G8R8A8: + rColor.set(p[2], p[1], p[0], p[3]); + break; + + case GFXFormatR8G8B8X8: + rColor.set(p[0], p[1], p[2], 255); + break; + + // --- 64-bit (16 bits per channel) --- + case GFXFormatR16G16B16A16: + { + const U16* v = (U16*)p; +#ifdef TORQUE_BIG_ENDIAN + rColor.set(v[2] >> 8, v[1] >> 8, v[0] >> 8, v[3] >> 8); // fallback #else - rColor.set( *((U16*)pLoc) >> 11, - (*((U16*)pLoc) >> 6) & 0x1f, - (*((U16*)pLoc) >> 1) & 0x1f, - (*((U16*)pLoc) & 1) ? 255 : 0 ); + rColor.set(v[0] >> 8, v[1] >> 8, v[2] >> 8, v[3] >> 8); #endif break; + } - default: + // --- 64-bit float --- + case GFXFormatR16G16B16A16F: + { + const U16* v = (U16*)p; + F32 r = convertHalfToFloat(v[0]); + F32 g = convertHalfToFloat(v[1]); + F32 b = convertHalfToFloat(v[2]); + F32 a = convertHalfToFloat(v[3]); + rColor.set(mClamp(r * 255.0f, 0.0f, 255.0f), + mClamp(g * 255.0f, 0.0f, 255.0f), + mClamp(b * 255.0f, 0.0f, 255.0f), + mClamp(a * 255.0f, 0.0f, 255.0f)); + break; + } + + // --- 128-bit float --- + case GFXFormatR32G32B32A32F: + { + const F32* v = (F32*)p; + rColor.set(mClamp(v[0] * 255.0f, 0.0f, 255.0f), + mClamp(v[1] * 255.0f, 0.0f, 255.0f), + mClamp(v[2] * 255.0f, 0.0f, 255.0f), + mClamp(v[3] * 255.0f, 0.0f, 255.0f)); + break; + } + + default: AssertFatal(false, "Bad internal format"); return false; } @@ -1124,6 +1264,7 @@ void GBitmap::copyChannel( U32 index, GBitmap *outBitmap ) const bool GBitmap::read(Stream& io_rStream) { + PROFILE_SCOPE(GBitmap_Read); // Handle versioning U32 version; io_rStream.read(&version); @@ -1133,23 +1274,7 @@ bool GBitmap::read(Stream& io_rStream) U32 fmt; io_rStream.read(&fmt); mInternalFormat = GFXFormat(fmt); - mBytesPerPixel = 1; - switch (mInternalFormat) { - case GFXFormatA8: - case GFXFormatL8: mBytesPerPixel = 1; - break; - case GFXFormatR8G8B8: mBytesPerPixel = 3; - break; - case GFXFormatR8G8B8A8: mBytesPerPixel = 4; - break; - case GFXFormatL16: - case GFXFormatR5G6B5: - case GFXFormatR5G5B5A1: mBytesPerPixel = 2; - break; - default: - AssertFatal(false, "GBitmap::read: misunderstood format specifier"); - break; - } + mBytesPerPixel = getFormatBytesPerPixel(mInternalFormat); io_rStream.read(&mByteSize); @@ -1170,6 +1295,7 @@ bool GBitmap::read(Stream& io_rStream) bool GBitmap::write(Stream& io_rStream) const { + PROFILE_SCOPE(GBitmap_Write); // Handle versioning io_rStream.write(csFileVersion); diff --git a/Engine/source/gfx/bitmap/gBitmap.h b/Engine/source/gfx/bitmap/gBitmap.h index 558c5ec102..289b8db54f 100644 --- a/Engine/source/gfx/bitmap/gBitmap.h +++ b/Engine/source/gfx/bitmap/gBitmap.h @@ -201,6 +201,7 @@ class GBitmap U32 getByteSize() const { return mByteSize; } U32 getBytesPerPixel() const { return mBytesPerPixel; } + U32 getFormatBytesPerPixel(GFXFormat fmt); U32 getSurfaceSize(const U32 mipLevel) const; diff --git a/Engine/source/gfx/bitmap/loaders/bitmapSTB.cpp b/Engine/source/gfx/bitmap/loaders/bitmapSTB.cpp index a0e8cf9a8b..4a984539fe 100644 --- a/Engine/source/gfx/bitmap/loaders/bitmapSTB.cpp +++ b/Engine/source/gfx/bitmap/loaders/bitmapSTB.cpp @@ -27,6 +27,7 @@ #include "core/stream/memStream.h" #include "core/strings/stringFunctions.h" #include "gfx/bitmap/gBitmap.h" +#include "gfx/bitmap/bitmapUtils.h" #include "gfx/bitmap/imageUtils.h" #include "gfx/bitmap/loaders/ies/ies_loader.h" #include "platform/profiler.h" @@ -56,6 +57,43 @@ static bool sReadStreamSTB(Stream& stream, GBitmap* bitmap, U32 len); static bool sWriteSTB(const Torque::Path& path, GBitmap* bitmap, U32 compressionLevel); static bool sWriteStreamSTB(const String& bmType, Stream& stream, GBitmap* bitmap, U32 compressionLevel); +static GFXFormat determineFormat(bool isHDR, bool is16Bit, int numChannels) +{ + if (isHDR) + { + switch (numChannels) + { + case 1: return GFXFormatR32F; + case 2: return GFXFormatR16G16F; // approximate, most HDRs are 3 or 4 channel though + case 3: return GFXFormatR16G16B16A16F; // store RGB in RGBA + case 4: return GFXFormatR16G16B16A16F; + } + } + else if (is16Bit) + { + switch (numChannels) + { + case 1: return GFXFormatL16; + case 2: return GFXFormatA8L8; // No native L16A16, but could add one later + case 3: return GFXFormatR16G16B16A16; + case 4: return GFXFormatR16G16B16A16; + } + } + else // 8-bit + { + switch (numChannels) + { + case 1: return GFXFormatA8; + case 2: return GFXFormatA8L8; + case 3: return GFXFormatR8G8B8; + case 4: return GFXFormatR8G8B8A8; + } + } + + // fallback + return GFXFormatR8G8B8A8; +} + // stbi_write callback / rextimmy. static void stbiWriteFunc(void* context, void* data, int size) { @@ -210,119 +248,55 @@ bool sReadSTB(const Torque::Path& path, GBitmap* bitmap) } - if (!stbi_info(path.getFullPath().c_str(), &x, &y, &channels)) - { - const char* stbErr = stbi_failure_reason(); + const char* filePath = path.getFullPath().c_str(); - if (!stbErr) - stbErr = "Unknown Error!"; + // Detect format + bool isHDR = stbi_is_hdr(filePath); + bool is16Bit = stbi_is_16_bit(filePath); - Con::errorf("STB get file info: %s", stbErr); - } - - // do this to map 2 channels to 4, 2 channel not supported by gbitmap yet.. - if (channels == 2) - channels = 4; - if (!ext.equal("png")) - { - if (stbi_is_16_bit(path.getFullPath().c_str())) - { - U16* data = stbi_load_16(path.getFullPath().c_str(), &x, &y, &n, channels); - - // if succesful deal make the bitmap, else try other loaders. - if (data) - { - GFXFormat format; - if (n == 1) - format = GFXFormatL16; - else - format = GFXFormatR16G16B16A16; // not sure if this is correct. - - bitmap->deleteImage(); + void* data = nullptr; - // actually allocate the bitmap space... - bitmap->allocateBitmap(x, y, - false, // don't extrude miplevels... - format); // use determined format... - - U16* pBase = (U16*)bitmap->getBits(); - - U32 rowBytes = bitmap->getByteSize(); - - dMemcpy(pBase, data, rowBytes); - - stbi_image_free(data); - - PROFILE_END(); - return true; - } - } + if (isHDR) { + data = stbi_loadf(filePath, &x, &y, &n, 4); } + else if (is16Bit) + data = stbi_load_16(filePath, &x, &y, &n, 4); + else + data = stbi_load(filePath, &x, &y, &n, 0); - if (ext.equal("hdr")) + if (!data) { - // force load to 4 channel. - float* data = stbi_loadf(path.getFullPath().c_str(), &x, &y, &n, 0); - - unsigned char* dataChar = stbi__hdr_to_ldr(data, x, y, n); - bitmap->deleteImage(); - // actually allocate the bitmap space... - bitmap->allocateBitmap(x, y, - false, - GFXFormatR8G8B8); - - U8* pBase = (U8*)bitmap->getBits(); - - U32 rowBytes = x * y * n; - - dMemcpy(pBase, dataChar, rowBytes); - - //stbi_image_free(data); - stbi_image_free(dataChar); - PROFILE_END(); - return true; + Con::errorf("sReadSTB() - Failed to load %s: %s", filePath, stbi_failure_reason()); + return false; } - unsigned char* data = stbi_load(path.getFullPath().c_str(), &x, &y, &n, channels); + // Determine internal GFX format + GFXFormat format = determineFormat(isHDR, is16Bit, n); + // Allocate bitmap bitmap->deleteImage(); + bitmap->allocateBitmap(x, y, false, format); - GFXFormat format; - - switch (channels) { - case 1: - format = GFXFormatA8; - break; - case 2: - format = GFXFormatA8L8; - break; - case 3: - format = GFXFormatR8G8B8; - break; - case 4: - format = GFXFormatR8G8B8A8; - break; - default: - PROFILE_END(); - return false; + if (isHDR) + { + U16* pBase = (U16*)bitmap->getBits(); + const size_t totalPixels = (size_t)x * (size_t)y; + for (size_t i = 0; i < totalPixels * 4; ++i) + { + pBase[i] = convertFloatToHalf(reinterpret_cast(data)[i]); // convert F32 -> U16 + } + } + else + { + U8* dst = (U8*)bitmap->getBits(); + U32 byteSize = bitmap->getByteSize(); + dMemcpy(dst, data, byteSize); } - - // actually allocate the bitmap space... - bitmap->allocateBitmap(x, y, - false, // don't extrude miplevels... - format); // use determined format... - - U8* pBase = (U8*)bitmap->getBits(); - - U32 rowBytes = bitmap->getByteSize(); - - dMemcpy(pBase, data, rowBytes); stbi_image_free(data); - // Check this bitmap for transparency - if (channels == 4) - bitmap->checkForTransparency(); + + bitmap->checkForTransparency(); PROFILE_END(); return true; @@ -331,45 +305,36 @@ bool sReadSTB(const Torque::Path& path, GBitmap* bitmap) bool sReadStreamSTB(Stream& stream, GBitmap* bitmap, U32 len) { PROFILE_SCOPE(sReadStreamSTB); - // only used for font at the moment. - U8* data = new U8[len]; - stream.read(len, data); + Vector data(len); + stream.read(len, data.address()); - S32 width, height, comp = 0; + int x, y, n; + bool isHDR = stbi_is_hdr_from_memory(data.address(), len); + bool is16Bit = stbi_is_16_bit_from_memory(data.address(), len); - unsigned char* pixelData = stbi_load_from_memory((const U8*)data, (int)len, &width, &height, &comp, 0); + void* pixels = nullptr; + if (isHDR) + pixels = stbi_loadf_from_memory(data.address(), len, &x, &y, &n, 0); + else if (is16Bit) + pixels = stbi_load_16_from_memory(data.address(), len, &x, &y, &n, 0); + else + pixels = stbi_load_from_memory(data.address(), len, &x, &y, &n, 0); - if (!pixelData) + if (!pixels) { - const char* stbErr = stbi_failure_reason(); - - if (!stbErr) - stbErr = "Unknown Error!"; - - Con::errorf("sReadStreamSTB Error: %s", stbErr); + Con::errorf("sReadStreamSTB() - STB load failed: %s", stbi_failure_reason()); return false; } - bitmap->deleteImage(); - - //work out what format we need to use - todo floating point? - GFXFormat fmt = GFXFormat_FIRST; - switch (comp) - { - case 1: fmt = GFXFormatA8; break; - case 2: fmt = GFXFormatA8L8; break; //todo check this - case 3: fmt = GFXFormatR8G8B8; break; - case 4: fmt = GFXFormatR8G8B8A8; break; - } - bitmap->allocateBitmap(width, height, false, fmt); + GFXFormat format = determineFormat(isHDR, is16Bit, n); + bitmap->deleteImage(); + bitmap->allocateBitmap(x, y, false, format); + dMemcpy(bitmap->getWritableBits(0), pixels, bitmap->getByteSize()); - U8* pBase = bitmap->getWritableBits(0); - U32 rowBytes = bitmap->getByteSize(); - dMemcpy(pBase, pixelData, rowBytes); + stbi_image_free(pixels); - dFree(data); - dFree(pixelData); + bitmap->checkForTransparency(); return true; } diff --git a/Engine/source/platformWin32/winAsmBlit.cpp b/Engine/source/platformWin32/winAsmBlit.cpp index 8dd6dc413a..0d9d1b5191 100644 --- a/Engine/source/platformWin32/winAsmBlit.cpp +++ b/Engine/source/platformWin32/winAsmBlit.cpp @@ -195,7 +195,6 @@ void bitmapConvertRGB_to_5551_mmx(U8 *src, U32 pixels) void PlatformBlitInit() { bitmapExtrude5551 = bitmapExtrude5551_asm; - bitmapExtrudeRGB = bitmapExtrudeRGB_c; if (Platform::SystemInfo.processor.properties & CPU_PROP_MMX) { diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetImport.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetImport.tscript index 47f172b686..15024e5c45 100644 --- a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetImport.tscript +++ b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetImport.tscript @@ -94,7 +94,7 @@ function ImportAssetWindow::onWake(%this) // function isImageFormat(%fileExt) { - if( (%fileExt $= ".png") || (%fileExt $= ".jpg") || (%fileExt $= ".bmp") || (%fileExt $= ".dds") || (%fileExt $= ".tif") || (%fileExt $= ".psd") || (%fileExt $= ".gif") || (%fileExt $= ".hdr")) + if( (%fileExt $= ".png") || (%fileExt $= ".jpg") || (%fileExt $= ".jpeg") || (%fileExt $= ".bmp") || (%fileExt $= ".dds") || (%fileExt $= ".tga") || (%fileExt $= ".psd") || (%fileExt $= ".gif") || (%fileExt $= ".hdr") || (%fileExt $= ".ies")) return true; return false; @@ -133,12 +133,20 @@ function findImageFile(%path, %materialName, %type) { if(isFile(%path @ "/" @ %materialName @ ".jpg")) return %path @ "/" @ %materialName @ ".jpg"; + else if(isFile(%path @ "/" @ %materialName @ ".jpeg")) + return %path @ "/" @ %materialName @ ".jpeg"; + else if(isFile(%path @ "/" @ %materialName @ ".bmp")) + return %path @ "/" @ %materialName @ ".bmp"; else if(isFile(%path @ "/" @ %materialName @ ".png")) return %path @ "/" @ %materialName @ ".png"; else if(isFile(%path @ "/" @ %materialName @ ".dds")) return %path @ "/" @ %materialName @ ".dds"; - else if(isFile(%path @ "/" @ %materialName @ ".tif")) - return %path @ "/" @ %materialName @ ".tif"; + else if(isFile(%path @ "/" @ %materialName @ ".tga")) + return %path @ "/" @ %materialName @ ".tga"; + else if(isFile(%path @ "/" @ %materialName @ ".hdr")) + return %path @ "/" @ %materialName @ ".hdr"; + else if(isFile(%path @ "/" @ %materialName @ ".ies")) + return %path @ "/" @ %materialName @ ".ies"; } function getAssetTypeByFilename(%filePath) @@ -270,7 +278,7 @@ function AssetBrowser::onDropFolder(%this, %filePath) %fileExt = ""; //If not modules, it's likely an art pack or other mixed files, so we'll import them as normal - if( (%fileExt $= ".png") || (%fileExt $= ".jpg") || (%fileExt $= ".bmp") || (%fileExt $= ".dds") ) + if( (%fileExt $= ".png") || (%fileExt $= ".jpg") || (%fileExt $= ".jpeg") || (%fileExt $= ".bmp") || (%fileExt $= ".dds") || (%fileExt $= ".tga") || (%fileExt $= ".psd") || (%fileExt $= ".gif") || (%fileExt $= ".hdr") || (%fileExt $= ".ies")) %this.importAssetListArray.add("ImageAsset", %filePath); else if( (%fileExt $= ".dae") || (%fileExt $= ".dts")) %this.importAssetListArray.add("ShapeAsset", %filePath); @@ -746,7 +754,7 @@ function ImportAssetWindow::addNewImportingAsset(%this, %filterType) if(%filterType $= "Sound" || %filterType $= "") %filter = "Sound Files(*.wav, *.ogg)|*.wav;*.ogg|" @ %filter; if(%filterType $= "Image" || %filterType $= "") - %filter = "Images Files(*.jpg,*.png,*.tga,*.bmp,*.dds)|*.jpg;*.png;*.tga;*.bmp;*.dds|" @ %filter; + %filter = "Images Files(*.jpg,*.jpeg,*.png,*.tga,*.bmp,*.dds,*.ies)|*.jpg;*.jpeg;*.png;*.tga;*.bmp;*.dds;*.ies|" @ %filter; if(%filterType $= "Shape" || %filterType $= "") %filter = "Shape Files(*.dae, *.cached.dts)|*.dae;*.cached.dts|" @ %filter; @@ -870,7 +878,7 @@ function ImportAssetWindow::findMissingFile(%this, %assetItem) if(%assetItem.assetType $= "ShapeAsset") %filters = "Shape Files(*.dae, *.cached.dts)|*.dae;*.cached.dts"; else if(%assetItem.assetType $= "ImageAsset") - %filters = "Images Files(*.jpg,*.png,*.tga,*.bmp,*.dds)|*.jpg;*.png;*.tga;*.bmp;*.dds"; + %filters = "Images Files(*.jpg,*.jpeg,*.png,*.tga,*.bmp,*.dds,*.ies)|*.jpg;*.jpeg;*.png;*.tga;*.bmp;*.dds;*.ies"; %dlg = new OpenFileDialog() {