From 4ad0ac9b3dee9d96bf82fc79585a9ddfb8884e89 Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Tue, 14 Oct 2025 21:10:45 +0100 Subject: [PATCH 1/5] Add the rest of the frames logic Adds ability to either get the frame as a full texture, or get the frame that describes it allows setting a number of uniform cells for an image. --- Engine/source/T3D/assets/ImageAsset.cpp | 87 +++++++++++++++++++++++-- Engine/source/T3D/assets/ImageAsset.h | 24 ++++++- 2 files changed, 106 insertions(+), 5 deletions(-) diff --git a/Engine/source/T3D/assets/ImageAsset.cpp b/Engine/source/T3D/assets/ImageAsset.cpp index dd50934d5d..5c6b757a1f 100644 --- a/Engine/source/T3D/assets/ImageAsset.cpp +++ b/Engine/source/T3D/assets/ImageAsset.cpp @@ -153,9 +153,12 @@ ImageAsset::ImageAsset() : mImageWidth(-1), mImageHeight(-1), mImageDepth(-1), - mImageChannels(-1) + mImageChannels(-1), + mCellCountX(1), + mCellCountY(1) { mLoadedState = AssetErrCode::NotLoaded; + VECTOR_SET_ASSOCIATION(mFrames); } //----------------------------------------------------------------------------- @@ -169,6 +172,16 @@ ImageAsset::~ImageAsset() } mResourceMap.clear(); + + FrameTextureMap::iterator frameIter = mFrameTextureMap.begin(); + for (; frameIter != mFrameTextureMap.end(); ++frameIter) + { + frameIter->value.free(); + } + + mFrameTextureMap.clear(); + + mFrames.clear(); } @@ -201,6 +214,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() { @@ -456,9 +472,9 @@ U32 ImageAsset::load() return mLoadedState; } -GFXTexHandle ImageAsset::getTexture(GFXTextureProfile* requestedProfile) +GFXTexHandle ImageAsset::getTexture(GFXTextureProfile* requestedProfile, U32 frame) { - if (mLoadedState == Ok && mResourceMap.contains(requestedProfile)) + if (mLoadedState == Ok && frame == 0 && mResourceMap.contains(requestedProfile)) { return mResourceMap.find(requestedProfile)->value; } @@ -491,6 +507,8 @@ GFXTexHandle ImageAsset::getTexture(GFXTextureProfile* requestedProfile) if (mLoadedState == Ok) { + if (frame == 0 || mCellCountX <= 1 || mCellCountY <= 1) + { //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 +517,35 @@ GFXTexHandle ImageAsset::getTexture(GFXTextureProfile* requestedProfile) mResourceMap.insert(requestedProfile, newTex); return newTex; } + } + + if (frame > mFrames.size()) + { + Con::warnf("ImageAsset::getTexture - Frame %d out of range (max %d). Returning full texture.", frame, mFrames.size()); + return getTexture(requestedProfile, 0); + } + + const FrameKey frameKey(frame, requestedProfile); + if (mFrameTextureMap.contains(frameKey)) + return mFrameTextureMap.find(frameKey)->value; + + GBitmap* source = GBitmap::load(mImageFile); + if (!source) + { + Con::errorf("ImageAsset::getTexture - Failed to load bitmap for %s", mImageFile); + return nullptr; + } + + const Frame& frameData = mFrames[frame]; + + GBitmap* cropped = new GBitmap(frameData.pixelSize.x, frameData.pixelSize.y, false, source->getFormat()); + source->copyRect(cropped, RectI(frameData.pixelOffset, frameData.pixelSize), Point2I(0, 0)); + delete source; + + // Create texture and cache + GFXTexHandle tex(cropped, requestedProfile, true, avar("%s %s() - frame %d", mImageFile, __FUNCTION__, frame)); + mFrameTextureMap.insert(frameKey, tex); + return tex; } return nullptr; @@ -685,7 +732,9 @@ void ImageAsset::populateImage(void) } // we only support 2d textures..... for now ;) - mImageDepth = 1; + mImageDepth = 1; + + generateFrames(); } } @@ -713,6 +762,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..a39941d91e 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,6 +86,16 @@ 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, @@ -143,6 +156,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(); @@ -176,7 +195,7 @@ class ImageAsset : public AssetBase void setTextureHDR(const bool pIsHDR); inline bool getTextureHDR(void) const { return mIsHDRImage; }; - GFXTexHandle getTexture(GFXTextureProfile* requestedProfile); + GFXTexHandle getTexture(GFXTextureProfile* requestedProfile, U32 frame = 0); static StringTableEntry getImageTypeNameFromType(ImageTypes type); static ImageTypes getImageTypeFromName(StringTableEntry name); @@ -191,6 +210,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 +219,7 @@ class ImageAsset : public AssetBase void populateImage(void); const char* getImageInfo(); + void generateFrames(void); protected: // Asset Base callback From 7929ecd65e4f04af31f3cd457ec4a82554a25b20 Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Thu, 16 Oct 2025 12:42:17 +0100 Subject: [PATCH 2/5] Allow image asset to cache the image Remove ability to create a full texture for the frame (should use it as an atlas) Add ability to output the image as a cached gbitmap --- Engine/source/T3D/assets/ImageAsset.cpp | 180 ++++++++++++++++-------- Engine/source/T3D/assets/ImageAsset.h | 5 +- Engine/source/assets/assetBase.cpp | 2 + Engine/source/assets/assetBase.h | 2 + Engine/source/assets/assetManager.cpp | 2 + 5 files changed, 130 insertions(+), 61 deletions(-) diff --git a/Engine/source/T3D/assets/ImageAsset.cpp b/Engine/source/T3D/assets/ImageAsset.cpp index 5c6b757a1f..0551242f6d 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,6 +151,7 @@ ImageAsset::ImageAsset() : mIsHDRImage(false), mImageType(Albedo), mIsNamedTarget(false), + mIsDDS(false), mImageWidth(-1), mImageHeight(-1), mImageDepth(-1), @@ -157,6 +159,7 @@ ImageAsset::ImageAsset() : mCellCountX(1), mCellCountY(1) { + mEmbeddedBitmap = NULL; mLoadedState = AssetErrCode::NotLoaded; VECTOR_SET_ASSOCIATION(mFrames); } @@ -182,6 +185,9 @@ ImageAsset::~ImageAsset() mFrameTextureMap.clear(); mFrames.clear(); + + if (mEmbeddedBitmap) + delete mEmbeddedBitmap; } @@ -451,6 +457,12 @@ U32 ImageAsset::load() if (mLoadedState == Ok) return mLoadedState; + if (mEmbeddedBitmap) + { + mLoadedState = Ok; + return mLoadedState; + } + if (!Torque::FS::IsFile(mImageFile)) { if (isNamedTarget()) @@ -472,9 +484,9 @@ U32 ImageAsset::load() return mLoadedState; } -GFXTexHandle ImageAsset::getTexture(GFXTextureProfile* requestedProfile, U32 frame) +GFXTexHandle ImageAsset::getTexture(GFXTextureProfile* requestedProfile) { - if (mLoadedState == Ok && frame == 0 && mResourceMap.contains(requestedProfile)) + if (mLoadedState == Ok && mResourceMap.contains(requestedProfile)) { return mResourceMap.find(requestedProfile)->value; } @@ -507,45 +519,27 @@ GFXTexHandle ImageAsset::getTexture(GFXTextureProfile* requestedProfile, U32 fra if (mLoadedState == Ok) { - if (frame == 0 || mCellCountX <= 1 || mCellCountY <= 1) + if (mEmbeddedBitmap) { - //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__)); + newTex.set(mEmbeddedBitmap, requestedProfile, false, avar("%s %s() - mTextureObject (line %d)", mImageFile, __FUNCTION__, __LINE__)); if (newTex) { mResourceMap.insert(requestedProfile, newTex); return newTex; } } - - if (frame > mFrames.size()) - { - Con::warnf("ImageAsset::getTexture - Frame %d out of range (max %d). Returning full texture.", frame, mFrames.size()); - return getTexture(requestedProfile, 0); - } - - const FrameKey frameKey(frame, requestedProfile); - if (mFrameTextureMap.contains(frameKey)) - return mFrameTextureMap.find(frameKey)->value; - - GBitmap* source = GBitmap::load(mImageFile); - if (!source) + else { - Con::errorf("ImageAsset::getTexture - Failed to load bitmap for %s", mImageFile); - return nullptr; + //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__)); + if (newTex) + { + mResourceMap.insert(requestedProfile, newTex); + return newTex; + } } - - const Frame& frameData = mFrames[frame]; - - GBitmap* cropped = new GBitmap(frameData.pixelSize.x, frameData.pixelSize.y, false, source->getFormat()); - source->copyRect(cropped, RectI(frameData.pixelOffset, frameData.pixelSize), Point2I(0, 0)); - delete source; - - // Create texture and cache - GFXTexHandle tex(cropped, requestedProfile, true, avar("%s %s() - frame %d", mImageFile, __FUNCTION__, frame)); - mFrameTextureMap.insert(frameKey, tex); - return tex; } return nullptr; @@ -647,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) @@ -683,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. @@ -695,45 +749,53 @@ 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::setImageFile 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::setImageFile 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; - } - - 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::setImageFile 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(); } } diff --git a/Engine/source/T3D/assets/ImageAsset.h b/Engine/source/T3D/assets/ImageAsset.h index a39941d91e..b010b973c1 100644 --- a/Engine/source/T3D/assets/ImageAsset.h +++ b/Engine/source/T3D/assets/ImageAsset.h @@ -145,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; @@ -195,7 +196,7 @@ class ImageAsset : public AssetBase void setTextureHDR(const bool pIsHDR); inline bool getTextureHDR(void) const { return mIsHDRImage; }; - GFXTexHandle getTexture(GFXTextureProfile* requestedProfile, U32 frame = 0); + GFXTexHandle getTexture(GFXTextureProfile* requestedProfile); static StringTableEntry getImageTypeNameFromType(ImageTypes type); static ImageTypes getImageTypeFromName(StringTableEntry name); 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) From b528a4b4417f6bbe2d2a062170059be26015fae4 Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Thu, 16 Oct 2025 12:44:57 +0100 Subject: [PATCH 3/5] dds and gbitmap need to handle more formats DDS files can be uncompressed, need it to handle more formats being converted to gbitmap refactor gbitmap to handle more formats remove the macos #ifdefs for arm and apple chipsets, instead use bigendian --- Engine/source/gfx/bitmap/ddsFile.cpp | 31 +- Engine/source/gfx/bitmap/ddsFile.h | 2 + Engine/source/gfx/bitmap/gBitmap.cpp | 426 ++++++++++++++++++++------- Engine/source/gfx/bitmap/gBitmap.h | 1 + 4 files changed, 346 insertions(+), 114 deletions(-) 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..55c386ea1c 100644 --- a/Engine/source/gfx/bitmap/gBitmap.cpp +++ b/Engine/source/gfx/bitmap/gBitmap.cpp @@ -40,6 +40,101 @@ const U32 GBitmap::csFileVersion = 3; Vector GBitmap::sRegistrations( __FILE__, __LINE__ ); +//----------------------------------------------------------------------------- +// 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)); + } +} + GBitmap::GBitmap() : mInternalFormat(GFXFormatR8G8B8), @@ -126,6 +221,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 +439,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 +496,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; @@ -688,40 +799,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 +884,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; } @@ -1133,23 +1351,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); 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; From 99bc64df0d0f7a115c1a3bc3f08bc43d8cf840a3 Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Fri, 17 Oct 2025 12:46:05 +0100 Subject: [PATCH 4/5] further refactoring gbitmap now has extrude mips for more formats the 16bit float format type doesnt extrude mips correctly with this function, as to where the issue is im not entirely sure. AssetImporter now recognizes hdr and bmp format in its auto import function. IES now becomes an image asset, could probably change the lights to accept assets instead of raw images now. --- Engine/source/T3D/assets/ImageAsset.cpp | 11 +- Engine/source/T3D/assets/ImageAsset.h | 2 +- Engine/source/T3D/assets/assetImporter.cpp | 2 +- Engine/source/T3D/assets/assetImporter.h | 12 +- Engine/source/gfx/bitmap/bitmapUtils.cpp | 203 ++++++---------- Engine/source/gfx/bitmap/bitmapUtils.h | 101 +++++++- Engine/source/gfx/bitmap/gBitmap.cpp | 114 ++------- .../source/gfx/bitmap/loaders/bitmapSTB.cpp | 219 ++++++++---------- Engine/source/platformWin32/winAsmBlit.cpp | 1 - .../assetBrowser/scripts/assetImport.tscript | 20 +- 10 files changed, 310 insertions(+), 375 deletions(-) diff --git a/Engine/source/T3D/assets/ImageAsset.cpp b/Engine/source/T3D/assets/ImageAsset.cpp index 0551242f6d..ebe713cd1b 100644 --- a/Engine/source/T3D/assets/ImageAsset.cpp +++ b/Engine/source/T3D/assets/ImageAsset.cpp @@ -759,12 +759,12 @@ void ImageAsset::populateImage(void) FileStream* ddsFs; if ((ddsFs = FileStream::createAndOpen(mImageFile, Torque::FS::File::Read)) == NULL) { - Con::errorf("ImageAsset::setImageFile Failed to open ddsfile: %s", mImageFile); + 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); + Con::errorf("ImageAsset::populateImage Failed to read header of ddsfile: %s", mImageFile); } else { @@ -778,13 +778,18 @@ void ImageAsset::populateImage(void) } else { + if (dStrEndsWith(mImageFile, ".ies")) + { + return; + } + 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); } } diff --git a/Engine/source/T3D/assets/ImageAsset.h b/Engine/source/T3D/assets/ImageAsset.h index b010b973c1..b378c200e8 100644 --- a/Engine/source/T3D/assets/ImageAsset.h +++ b/Engine/source/T3D/assets/ImageAsset.h @@ -102,7 +102,7 @@ class ImageAsset : public AssetBase StringTableEntry inRegionName = StringTable->EmptyString()) : regionName(inRegionName) { - pixelOffset.set(pixelOffsetY, pixelOffsetY); + pixelOffset.set(pixelOffsetX, pixelOffsetY); pixelSize.set(pixelWidth, pixelHeight); texelLower.set(pixelOffsetX * texelWidthScale, pixelOffsetY * texelHeightScale); 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/gfx/bitmap/bitmapUtils.cpp b/Engine/source/gfx/bitmap/bitmapUtils.cpp index 3dd6388529..5221057321 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,82 @@ 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 srcRowStride = 0, // in elements, 0 = tightly packed + U32 dstRowStride = 0) // in elements, 0 = tightly packed { - 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; + if (srcRowStride == 0) srcHeight != 1 ? srcRowStride = srcWidth * channels : srcRowStride = 0; + U32 dstWidth = srcWidth > 1 ? srcWidth / 2 : 1; + U32 dstHeight = srcHeight > 1 ? srcHeight / 2 : 1; + if (dstRowStride == 0) dstRowStride = dstWidth * channels; - 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) { + bitmapExtrudeGeneric((const U8*)src, (U8*)dst, w, h, 4); +}; + +// 16-bit RGBA (U16 / F32 stored as U16) +auto bitmapExtrudeU16_RGBA = [](const void* src, void* dst, U32 h, U32 w) { + bitmapExtrudeGeneric((const U16*)src, (U16*)dst, w, h, 4); +}; + +// 32-bit float RGBA +auto bitmapExtrudeF32_RGBA = [](const void* src, void* dst, U32 h, U32 w) { + bitmapExtrudeGeneric((const F32*)src, (F32*)dst, w, h, 4); +}; + +// RGB U8 +auto bitmapExtrudeU8_RGB = [](const void* src, void* dst, U32 h, U32 w) { + bitmapExtrudeGeneric((const U8*)src, (U8*)dst, w, h, 3); +}; + +void (*bitmapExtrude5551)(const void* srcMip, void* mip, U32 height, U32 width) = bitmapExtrude5551_c; +void (*bitmapExtrudeRGB)(const void* srcMip, void* mip, U32 srcHeight, U32 srcWidth) = bitmapExtrudeU8_RGB; +void (*bitmapExtrudeRGBA)(const void* srcMip, void* mip, U32 srcHeight, U32 srcWidth) = bitmapExtrudeU8_RGBA; +void (*bitmapExtrude16BitRGBA)(const void* srcMip, void* mip, U32 srcHeight, U32 srcWidth) = bitmapExtrudeU16_RGBA; +void (*bitmapExtrudeFPRGBA)(const void* srcMip, void* mip, U32 srcHeight, U32 srcWidth) = bitmapExtrudeU16_RGBA; +void (*bitmapExtrudeF32RGBA)(const void* srcMip, void* mip, U32 srcHeight, U32 srcWidth) = bitmapExtrudeF32_RGBA; //-------------------------------------------------------------------------- @@ -238,7 +169,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 +191,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..1a52246195 100644 --- a/Engine/source/gfx/bitmap/bitmapUtils.h +++ b/Engine/source/gfx/bitmap/bitmapUtils.h @@ -22,7 +22,9 @@ #ifndef _BITMAPUTILS_H_ #define _BITMAPUTILS_H_ - +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif #ifndef _TORQUE_TYPES_H_ #include "platform/types.h" #endif @@ -30,13 +32,108 @@ 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 (*bitmapExtrude16BitRGBA)(const void *srcMip, void *mip, U32 height, U32 width); extern void(*bitmapExtrudeFPRGBA)(const void *srcMip, void *mip, U32 height, U32 width); +extern void(*bitmapExtrudeF32RGBA)(const void *srcMip, void *mip, U32 height, U32 width); 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/gBitmap.cpp b/Engine/source/gfx/bitmap/gBitmap.cpp index 55c386ea1c..5a813f5c21 100644 --- a/Engine/source/gfx/bitmap/gBitmap.cpp +++ b/Engine/source/gfx/bitmap/gBitmap.cpp @@ -40,101 +40,6 @@ const U32 GBitmap::csFileVersion = 3; Vector GBitmap::sRegistrations( __FILE__, __LINE__ ); -//----------------------------------------------------------------------------- -// 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)); - } -} - GBitmap::GBitmap() : mInternalFormat(GFXFormatR8G8B8), @@ -561,20 +466,37 @@ void GBitmap::extrudeMipLevels(bool clearBorders) 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)); break; } + case GFXFormatR16G16B16A16: + { + for (U32 i = 1; i < mNumMipLevels; i++) + bitmapExtrude16BitRGBA(getBits(i - 1), getWritableBits(i), getHeight(i - 1), getWidth(i - 1)); + break; + } + case GFXFormatR16G16B16A16F: { for (U32 i = 1; i < mNumMipLevels; i++) bitmapExtrudeFPRGBA(getBits(i - 1), getWritableBits(i), getHeight(i - 1), getWidth(i - 1)); break; } - + + case GFXFormatR32G32B32A32F: + { + for (U32 i = 1; i < mNumMipLevels; i++) + bitmapExtrudeF32RGBA(getBits(i - 1), getWritableBits(i), getHeight(i - 1), getWidth(i - 1)); + break; + } + default: + Con::warnf("GBitmap::extrudeMipLevels() - Unsupported format %d", getFormat()); break; } if (clearBorders) 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() { From 225cda363783100a8c0f2f4749f2caf68ce8808e Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Fri, 17 Oct 2025 19:33:38 +0100 Subject: [PATCH 5/5] bitmap utils use bytes per pixel for the row stride --- Engine/source/gfx/bitmap/bitmapUtils.cpp | 34 +++++++++++------------- Engine/source/gfx/bitmap/bitmapUtils.h | 10 +++---- Engine/source/gfx/bitmap/gBitmap.cpp | 14 +++++----- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/Engine/source/gfx/bitmap/bitmapUtils.cpp b/Engine/source/gfx/bitmap/bitmapUtils.cpp index 5221057321..a9206d7c0e 100644 --- a/Engine/source/gfx/bitmap/bitmapUtils.cpp +++ b/Engine/source/gfx/bitmap/bitmapUtils.cpp @@ -87,14 +87,12 @@ template void bitmapExtrudeGeneric( const T* src, T* dst, U32 srcWidth, U32 srcHeight, - U32 channels, - U32 srcRowStride = 0, // in elements, 0 = tightly packed - U32 dstRowStride = 0) // in elements, 0 = tightly packed + U32 channels, U32 bpp) { - if (srcRowStride == 0) srcHeight != 1 ? srcRowStride = srcWidth * channels : srcRowStride = 0; + U32 srcRowStride = srcHeight != 1 ? (srcWidth * bpp) / sizeof(T) : 0; U32 dstWidth = srcWidth > 1 ? srcWidth / 2 : 1; U32 dstHeight = srcHeight > 1 ? srcHeight / 2 : 1; - if (dstRowStride == 0) dstRowStride = dstWidth * channels; + U32 dstRowStride = dstHeight != 1 ? (dstWidth * bpp) / sizeof(T) : 0; for (U32 y = 0; y < dstHeight; ++y) { @@ -132,31 +130,31 @@ void bitmapExtrudeGeneric( } // 8-bit RGBA -auto bitmapExtrudeU8_RGBA = [](const void* src, void* dst, U32 h, U32 w) { - bitmapExtrudeGeneric((const U8*)src, (U8*)dst, w, h, 4); +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) { - bitmapExtrudeGeneric((const U16*)src, (U16*)dst, w, h, 4); +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) { - bitmapExtrudeGeneric((const F32*)src, (F32*)dst, w, h, 4); +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) { - bitmapExtrudeGeneric((const U8*)src, (U8*)dst, w, h, 3); +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) = bitmapExtrudeU8_RGB; -void (*bitmapExtrudeRGBA)(const void* srcMip, void* mip, U32 srcHeight, U32 srcWidth) = bitmapExtrudeU8_RGBA; -void (*bitmapExtrude16BitRGBA)(const void* srcMip, void* mip, U32 srcHeight, U32 srcWidth) = bitmapExtrudeU16_RGBA; -void (*bitmapExtrudeFPRGBA)(const void* srcMip, void* mip, U32 srcHeight, U32 srcWidth) = bitmapExtrudeU16_RGBA; -void (*bitmapExtrudeF32RGBA)(const void* srcMip, void* mip, U32 srcHeight, U32 srcWidth) = bitmapExtrudeF32_RGBA; +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; //-------------------------------------------------------------------------- diff --git a/Engine/source/gfx/bitmap/bitmapUtils.h b/Engine/source/gfx/bitmap/bitmapUtils.h index 1a52246195..ce860b39af 100644 --- a/Engine/source/gfx/bitmap/bitmapUtils.h +++ b/Engine/source/gfx/bitmap/bitmapUtils.h @@ -30,11 +30,11 @@ #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 (*bitmapExtrude16BitRGBA)(const void *srcMip, void *mip, U32 height, U32 width); -extern void(*bitmapExtrudeFPRGBA)(const void *srcMip, void *mip, U32 height, U32 width); -extern void(*bitmapExtrudeF32RGBA)(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 ); diff --git a/Engine/source/gfx/bitmap/gBitmap.cpp b/Engine/source/gfx/bitmap/gBitmap.cpp index 5a813f5c21..ed51b86b46 100644 --- a/Engine/source/gfx/bitmap/gBitmap.cpp +++ b/Engine/source/gfx/bitmap/gBitmap.cpp @@ -460,7 +460,7 @@ 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; } @@ -470,28 +470,28 @@ void GBitmap::extrudeMipLevels(bool clearBorders) 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)); + 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)); + bitmapExtrudeF32RGBA(getBits(i - 1), getWritableBits(i), getHeight(i - 1), getWidth(i - 1), mBytesPerPixel); break; } @@ -571,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 @@ -1264,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); @@ -1294,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);