// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "MuR/ImageTypes.h" #include "MuR/MutableMath.h" #include "MuR/MutableTrace.h" #include "MuR/Ptr.h" #include "MuR/RefCounted.h" #include "MuR/Serialisation.h" #include "Containers/Array.h" #include "HAL/LowLevelMemTracker.h" #include "HAL/Platform.h" #include "Misc/AssertionMacros.h" #include "MuR/ImageDataStorage.h" #define UE_API MUTABLERUNTIME_API namespace mu { /** 2D image resource with mipmaps. */ class FImage : public FResource { public: //----------------------------------------------------------------------------------------- // Life cycle //----------------------------------------------------------------------------------------- UE_API FImage(); //! Constructor from the size and format. //! It will allocate the memory for the image, but its content will be undefined. //! \param sizeX Width of the image. //! \param sizeY Height of the image. //! \param lods Number of levels of detail (mipmaps) to include in the image, inclduing the //! base level. It must be a number between 1 and the maximum possible levels, which //! depends on the image size. //! \param format Pixel format. UE_API FImage(uint32 SizeX, uint32 SizeY, uint32 Lods, EImageFormat Format, EInitializationType InitType); /** Create a new empty image that repreents an external resource image. */ static UE_API TSharedPtr CreateAsReference(uint32 ID, const FImageDesc& Desc, bool bForceLoad); //! Serialisation static UE_API void Serialise( const FImage*, FOutputArchive& ); static UE_API TSharedPtr StaticUnserialise( FInputArchive& ); // Resource interface UE_API int32 GetDataSize() const override; //----------------------------------------------------------------------------------------- // Own interface //----------------------------------------------------------------------------------------- /** */ UE_API void Init(uint32 SizeX, uint32 SizeY, uint32 Lods, EImageFormat Format, EInitializationType InitType); /** Clear the image to black colour. */ UE_API void InitToBlack(); /** Return true if this is a reference to an engine image. */ UE_API bool IsReference() const; /** If true, this is a reference that must be resolved at compile time. */ UE_API bool IsForceLoad() const; /** Return the id of the engine referenced texture. Only valid if IsReference. */ UE_API uint32 GetReferencedTexture() const; //! Return the width of the image. UE_API uint16 GetSizeX() const; //! Return the height of the image. UE_API uint16 GetSizeY() const; UE_API const FImageSize& GetSize() const; //! Return the pixel format of the image. UE_API EImageFormat GetFormat() const; //! Return the number of levels of detail (mipmaps) in the texture. The base lavel is also //! counted, so the minimum is 1. UE_API int32 GetLODCount() const; //! Return a pointer to a instance-owned buffer where the image pixels are. UE_API const uint8* GetLODData(int32 LODIndex) const; UE_API uint8* GetLODData(int32 LODIndex); //! Return the size in bytes of a specific LOD of the image. UE_API int32 GetLODDataSize(int32 LODIndex) const; public: // This used to be the data in the private implementation of the image interface //----------------------------------------------------------------------------------------- /** These set of flags are used to cache information for images at runtime. */ typedef enum { // Set if the next flag has been calculated and its value is valid. IF_IS_PLAIN_COLOUR_VALID = 1 << 0, // If the previous flag is set and this one too, the image is single colour. IF_IS_PLAIN_COLOUR = 1 << 1, // If this is set, the image shouldn't be scaled: it's contents is resoultion-dependent. IF_CANNOT_BE_SCALED = 1 << 2, // If this is set, the image has an updated relevancy map. This flag is not persisent. IF_HAS_RELEVANCY_MAP = 1 << 3, /** If this is set, this is a reference to an external image, and the ReferenceID is valid. */ IF_IS_REFERENCE = 1 << 4, /** For reference images, this indicates that they should be loaded into full images as soon as they are generated. */ IF_IS_FORCELOAD = 1 << 5 } EImageFlags; /** Persistent flags with some image properties. The meaning will depend of every context. */ mutable uint8 Flags = 0; /** Non-persistent relevancy map. */ uint16 RelevancyMinY = 0; uint16 RelevancyMaxY = 0; /** Only valid if the right flags are set, this identifies a referenced image. */ uint32 ReferenceID = 0; /** Pixel data for all lods. */ FImageDataStorage DataStorage; // This used to be the methods in the private implementation of the image interface //----------------------------------------------------------------------------------------- //! Deep clone. TSharedPtr Clone() const { LLM_SCOPE_BYNAME(TEXT("MutableRuntime")); MUTABLE_CPUPROFILER_SCOPE(ImageClone) TSharedPtr Result = MakeShared(); Result->Flags = Flags; Result->RelevancyMinY = RelevancyMinY; Result->RelevancyMaxY = RelevancyMaxY; Result->DataStorage = DataStorage; Result->ReferenceID = ReferenceID; return Result; } //! Copy another image. void Copy(const FImage* Other) { LLM_SCOPE_BYNAME(TEXT("MutableRuntime")); MUTABLE_CPUPROFILER_SCOPE(Copy); if (Other == this) { return; } Flags = Other->Flags; RelevancyMinY = Other->RelevancyMinY; RelevancyMaxY = Other->RelevancyMaxY; DataStorage = Other->DataStorage; ReferenceID = Other->ReferenceID; } void CopyMove(FImage* Other) { LLM_SCOPE_BYNAME(TEXT("MutableRuntime")); MUTABLE_CPUPROFILER_SCOPE(CopyMove); if (Other == this) { return; } Flags = Other->Flags; RelevancyMinY = Other->RelevancyMinY; RelevancyMaxY = Other->RelevancyMaxY; ReferenceID = Other->ReferenceID; DataStorage = MoveTemp(Other->DataStorage); } //! UE_API void Serialise(FOutputArchive&) const; //! UE_API void Unserialise(FInputArchive&); //! inline bool operator==(const FImage& Other) const { return (DataStorage == Other.DataStorage) && (ReferenceID == Other.ReferenceID); } //----------------------------------------------------------------------------------------- //! Sample the image and return an RGB colour. UE_API FVector4f Sample(FVector2f Coords) const; //! Calculate the size of the image data in bytes, regardless of what is allocated in //! m_data, only using the image descriptions. For non-block-compressed images, it returns //! 0. static UE_API int32 CalculateDataSize(int32 SizeX, int32 SizeY, int32 LodCount, EImageFormat Format); //! Calculate the size in pixels of a particular mipmap of this image. The size doesn't //! include pixels necessary for completing blocks in block-compressed formats. UE_API FIntVector2 CalculateMipSize(int32 LOD) const; //! Return a pointer to the beginning of the data for a particular mip. UE_API uint8* GetMipData(int32 Mip); UE_API const uint8* GetMipData(int32 Mip) const; //! Return the size of the resident mips. for block-compressed images the result is the same as in CalculateDataSize() //! for non-block-compressed images, the sizes encoded in the image data is used to compute the final size. UE_API int32 GetMipsDataSize() const; //! See if all the pixels in the image are equal and return the colour. UE_API bool IsPlainColour(FVector4f& Colour) const; //! See if all the pixels in the alpha channel are the max value of the pixel format //! (white). UE_API bool IsFullAlpha() const; //! Reduce the LODs of the image to the given amount. Don't do anything if there are already //! less LODs than specified. UE_API void ReduceLODsTo(int32 NewLODCount); UE_API void ReduceLODs(int32 LODsToSkip); //! Calculate the number of mipmaps for a particular image size. static UE_API int32 GetMipmapCount(int32 SizeX, int32 SizeY); //! Get the rect inside the image bounding the non-black content of the image. UE_API void GetNonBlackRect(FImageRect& OutRect) const; UE_API void GetNonBlackRect_Reference(FImageRect& OutRect) const; }; /** This struct contains image operations that may need to allocate and free temporary images to work. * It's purpose is to override the creation, release and clone functions depending on the context. */ struct FImageOperator { /** Common callback used for functions that can create temporary images. */ typedef TFunction(int32, int32, int32, EImageFormat, EInitializationType)> FImageCreateFunc; typedef TFunction&)> FImageReleaseFunc; typedef TFunction(const FImage*)> FImageCloneFunc; FImageCreateFunc CreateImage; FImageReleaseFunc ReleaseImage; FImageCloneFunc CloneImage; /** Interface to override the internal mutable image pixel format conversion functions. * Arguments match the FImageOperator::ImagePixelFormat function. */ typedef TFunction FImagePixelFormatFunc; FImagePixelFormatFunc FormatImageOverride; FImageOperator(FImageCreateFunc Create, FImageReleaseFunc Release, FImageCloneFunc Clone, FImagePixelFormatFunc FormatOverride) : CreateImage(Create), ReleaseImage(Release), CloneImage(Clone), FormatImageOverride(FormatOverride) {}; /** Create an default version for untracked resources. */ static FImageOperator GetDefault( const FImagePixelFormatFunc& InFormatOverride ) { return FImageOperator( [](int32 x, int32 y, int32 m, EImageFormat f, EInitializationType i) { return MakeShared(x, y, m, f, i); }, [](TSharedPtr& In) {In = nullptr; }, [](const FImage* In) { return In->Clone(); }, InFormatOverride ); } /** Convert an image to another pixel format. * Allocates the destination image. * \warning Not all format conversions are implemented. * \param onlyLOD If different than -1, only the specified lod level will be converted in the returned image. * \return nullptr if the conversion failed, usually because not enough memory was allocated in the result. This is only checked for RLE compression. */ MUTABLERUNTIME_API TSharedPtr ImagePixelFormat(int32 Quality, const FImage* Base, EImageFormat TargetFormat, int32 OnlyLOD = -1); /** Convert an image to another pixel format. * \warning Not all format conversions are implemented. * \param onlyLOD If different than -1, only the specified lod level will be converted in the returned image. * \return false if the conversion failed, usually because not enough memory was allocated in the result. This is only checked for RLE compression. */ MUTABLERUNTIME_API void ImagePixelFormat(bool& bOutSuccess, int32 Quality, FImage* Result, const FImage* Base, int32 OnlyLOD = -1); MUTABLERUNTIME_API void ImagePixelFormat(bool& bOutSuccess, int32 Quality, FImage* Result, const FImage* Base, int32 BeginResultLOD, int32 BeginBaseLOD, int32 NumLODs); MUTABLERUNTIME_API TSharedPtr ImageSwizzle(EImageFormat Format, const TSharedPtr Sources[], const uint8 Channels[]); /** */ MUTABLERUNTIME_API TSharedPtr ExtractMip(const FImage* From, int32 Mip); /** Bilinear filter image resize. */ MUTABLERUNTIME_API void ImageResizeLinear(FImage* Dest, int32 ImageCompressionQuality, const FImage* Base); /** Fill the image with a plain colour. */ void FillColor(FImage* Image, FVector4f Color); /** Support struct to keep preallocated data required for some mipmap operations. */ struct FScratchImageMipmap { TSharedPtr Uncompressed; TSharedPtr UncompressedMips; TSharedPtr CompressedMips; }; /** Generate the mipmaps for images. * if bGenerateOnlyTail is true, generates the mips missing from Base to LevelCount and sets * them in Dest (the full chain is spit in two images). Otherwise generate the mips missing * from Base up to LevelCount and append them in Dest to the already generated Base's mips. */ void ImageMipmap(int32 CompressionQuality, FImage* Dest, const FImage* Base, int32 StartLevel, int32 LevelCount, const FMipmapGenerationSettings&, bool bGenerateOnlyTail = false); /** Mipmap separating the worst case treatment in 3 steps to manage allocations of temp data. */ void ImageMipmap_PrepareScratch(const FImage* DestImage, int32 StartLevel, int32 LevelCount, FScratchImageMipmap&); void ImageMipmap(FImageOperator::FScratchImageMipmap&, int32 CompressionQuality, FImage* Dest, const FImage* Base, int32 LevelStart, int32 LevelCount, const FMipmapGenerationSettings&, bool bGenerateOnlyTail = false); void ImageMipmap_ReleaseScratch(FScratchImageMipmap&); /** */ MUTABLERUNTIME_API bool ImageCrop(TSharedPtr& OutCropped, int32 CompressionQuality, const FImage* Base, const box& Rect); /** */ void ImageCompose(FImage* Base, const FImage* Block, const box& Rect); }; /** Update all the mipmaps in the image from the data in the base one. * Only the mipmaps already existing in the image are updated. */ MUTABLERUNTIME_API void ImageMipmapInPlace(int32 CompressionQuality, FImage* Base, const FMipmapGenerationSettings&); /** */ MUTABLERUNTIME_API void ImageSwizzle(FImage* Result, const TSharedPtr Sources[], const uint8 Channels[]); MUTABLERUNTIME_API inline EImageFormat GetUncompressedFormat(EImageFormat Format) { check(Format < EImageFormat::Count); EImageFormat Result = Format; switch (Result) { case EImageFormat::L_UBitRLE: Result = EImageFormat::L_UByte; break; case EImageFormat::L_UByteRLE: Result = EImageFormat::L_UByte; break; case EImageFormat::RGB_UByteRLE: Result = EImageFormat::RGB_UByte; break; case EImageFormat::RGBA_UByteRLE: Result = EImageFormat::RGBA_UByte; break; case EImageFormat::BC1: Result = EImageFormat::RGBA_UByte; break; case EImageFormat::BC2: Result = EImageFormat::RGBA_UByte; break; case EImageFormat::BC3: Result = EImageFormat::RGBA_UByte; break; case EImageFormat::BC4: Result = EImageFormat::L_UByte; break; case EImageFormat::BC5: Result = EImageFormat::RGB_UByte; break; case EImageFormat::ASTC_4x4_RGB_LDR: Result = EImageFormat::RGB_UByte; break; case EImageFormat::ASTC_4x4_RGBA_LDR: Result = EImageFormat::RGBA_UByte; break; case EImageFormat::ASTC_4x4_RG_LDR: Result = EImageFormat::RGB_UByte; break; case EImageFormat::ASTC_6x6_RGB_LDR: Result = EImageFormat::RGB_UByte; break; case EImageFormat::ASTC_6x6_RGBA_LDR: Result = EImageFormat::RGBA_UByte; break; case EImageFormat::ASTC_6x6_RG_LDR: Result = EImageFormat::RGB_UByte; break; case EImageFormat::ASTC_8x8_RGB_LDR: Result = EImageFormat::RGB_UByte; break; case EImageFormat::ASTC_8x8_RGBA_LDR: Result = EImageFormat::RGBA_UByte; break; case EImageFormat::ASTC_8x8_RG_LDR: Result = EImageFormat::RGB_UByte; break; case EImageFormat::ASTC_10x10_RGB_LDR: Result = EImageFormat::RGB_UByte; break; case EImageFormat::ASTC_10x10_RGBA_LDR: Result = EImageFormat::RGBA_UByte; break; case EImageFormat::ASTC_10x10_RG_LDR: Result = EImageFormat::RGB_UByte; break; case EImageFormat::ASTC_12x12_RGB_LDR: Result = EImageFormat::RGB_UByte; break; case EImageFormat::ASTC_12x12_RGBA_LDR: Result = EImageFormat::RGBA_UByte; break; case EImageFormat::ASTC_12x12_RG_LDR: Result = EImageFormat::RGB_UByte; break; default: break; } return Result; } /** */ template TSharedPtr CloneOrTakeOver(const TSharedPtr& Source) { TSharedPtr Result; if (Source.IsUnique()) { Result = ConstCastSharedPtr(Source); } else { Result = Source->Clone(); } return Result; } /** */ template TSharedPtr CloneOrTakeOver(const TSharedPtr& Source) { TSharedPtr Result; if (Source.IsUnique()) { Result = ConstCastSharedPtr(Source); } else { Result = Source->Clone(); } return Result; } } #undef UE_API