diff --git a/Source/Editor/Cooker/Platform/Windows/WindowsPlatformTools.cpp b/Source/Editor/Cooker/Platform/Windows/WindowsPlatformTools.cpp index 5259ca2df..da1832d2e 100644 --- a/Source/Editor/Cooker/Platform/Windows/WindowsPlatformTools.cpp +++ b/Source/Editor/Cooker/Platform/Windows/WindowsPlatformTools.cpp @@ -8,6 +8,7 @@ #include "Engine/Core/Math/Color32.h" #include "Engine/Core/Config/GameSettings.h" #include "Editor/Utilities/EditorUtilities.h" +#include "Engine/Graphics/PixelFormatSampler.h" #include "Engine/Graphics/Textures/TextureData.h" #include "Engine/Tools/TextureTool/TextureTool.h" #include "Engine/Content/Content.h" @@ -240,7 +241,7 @@ void UpdateIconData(uint8* iconData, const TextureData* icon) const TextureMipData* srcPixels = icon->GetData(0, srcPixelsMip); const Color32* srcPixelsData = (Color32*)srcPixels->Data.Get(); const Int2 srcPixelsSize(Math::Max(1, icon->Width >> srcPixelsMip), Math::Max(1, icon->Height >> srcPixelsMip)); - const auto sampler = TextureTool::GetSampler(icon->Format); + const auto sampler = PixelFormatSampler::Get(icon->Format); ASSERT_LOW_LAYER(sampler); // Write colors @@ -252,7 +253,7 @@ void UpdateIconData(uint8* iconData, const TextureData* icon) for (uint32 x = 0; x < width; x++) { float u = (float)x / width; - const Color c = TextureTool::SampleLinear(sampler, Float2(u, v), srcPixelsData, srcPixelsSize, srcPixels->RowPitch); + const Color c = sampler->SampleLinear(srcPixelsData, Float2(u, v), srcPixelsSize, srcPixels->RowPitch); colorData[idx++] = Color32(c).GetAsBGRA(); } } @@ -271,7 +272,7 @@ void UpdateIconData(uint8* iconData, const TextureData* icon) { uint32 x = packedX * 8 + pixelIdx; float u = (float)x / width; - const Color c = TextureTool::SampleLinear(sampler, Float2(u, v), srcPixelsData, srcPixelsSize, srcPixels->RowPitch); + const Color c = sampler->SampleLinear(srcPixelsData, Float2(u, v), srcPixelsSize, srcPixels->RowPitch); if (c.A < 0.25f) mask |= 1 << (7 - pixelIdx); } @@ -322,7 +323,7 @@ bool UpdateExeIcon(const String& path, const TextureData& icon) const TextureData* iconRGBA8 = &icon; TextureData tmpData1; //if (icon.Format != PixelFormat::R8G8B8A8_UNorm) - if (TextureTool::GetSampler(icon.Format) == nullptr) + if (PixelFormatSampler::Get(icon.Format) == nullptr) { if (TextureTool::Convert(tmpData1, *iconRGBA8, PixelFormat::R8G8B8A8_UNorm)) { diff --git a/Source/Editor/Tools/Terrain/TerrainTools.cpp b/Source/Editor/Tools/Terrain/TerrainTools.cpp index 3ba11cdf5..111152ad0 100644 --- a/Source/Editor/Tools/Terrain/TerrainTools.cpp +++ b/Source/Editor/Tools/Terrain/TerrainTools.cpp @@ -8,6 +8,7 @@ #include "Engine/Terrain/TerrainPatch.h" #include "Engine/Terrain/Terrain.h" #include "Engine/Profiler/ProfilerCPU.h" +#include "Engine/Graphics/PixelFormatSampler.h" #include "Engine/Graphics/PixelFormatExtensions.h" #include "Engine/Tools/TextureTool/TextureTool.h" #include "Engine/Graphics/Textures/TextureData.h" @@ -103,7 +104,7 @@ bool GetTextureDataForSampling(Texture* texture, TextureDataResult& data, bool h // Decompress or convert data if need to data.Mip0DataPtr = &data.Mip0Data; - if (PixelFormatExtensions::IsCompressed(data.Format) || TextureTool::GetSampler(data.Format) == nullptr) + if (PixelFormatExtensions::IsCompressed(data.Format) || PixelFormatSampler::Get(data.Format) == nullptr) { PROFILE_CPU_NAMED("Decompress"); @@ -136,7 +137,7 @@ bool GetTextureDataForSampling(Texture* texture, TextureDataResult& data, bool h } // Check if can even sample the given format - const auto sampler = TextureTool::GetSampler(data.Format); + const auto sampler = PixelFormatSampler::Get(data.Format); if (sampler == nullptr) { LOG(Warning, "Texture format {0} cannot be sampled.", (int32)data.Format); @@ -188,7 +189,7 @@ bool TerrainTools::GenerateTerrain(Terrain* terrain, const Int2& numberOfPatches TextureDataResult dataHeightmap; if (GetTextureDataForSampling(heightmap, dataHeightmap, true)) return true; - const auto sampler = TextureTool::GetSampler(dataHeightmap.Format); + const auto sampler = PixelFormatSampler::Get(dataHeightmap.Format); // Initialize with sub-range of the input heightmap const Vector2 uvPerPatch = Vector2::One / Vector2(numberOfPatches); @@ -204,7 +205,7 @@ bool TerrainTools::GenerateTerrain(Terrain* terrain, const Int2& numberOfPatches for (int32 x = 0; x < heightmapSize; x++) { const Vector2 uv = uvStart + Vector2(x * heightmapSizeInv, z * heightmapSizeInv) * uvPerPatch; - const Color color = TextureTool::SampleLinear(sampler, uv, dataHeightmap.Mip0DataPtr->Get(), dataHeightmap.Mip0Size, dataHeightmap.RowPitch); + const Color color = sampler->SampleLinear(dataHeightmap.Mip0DataPtr->Get(), uv, dataHeightmap.Mip0Size, dataHeightmap.RowPitch); heightmapData[z * heightmapSize + x] = color.R * heightmapScale; } } @@ -244,7 +245,7 @@ bool TerrainTools::GenerateTerrain(Terrain* terrain, const Int2& numberOfPatches // Get splatmap data if (GetTextureDataForSampling(splatmap, data1)) return true; - const auto sampler = TextureTool::GetSampler(data1.Format); + const auto sampler = PixelFormatSampler::Get(data1.Format); // Modify heightmap splatmaps with sub-range of the input splatmaps for (int32 patchIndex = 0; patchIndex < terrain->GetPatchesCount(); patchIndex++) @@ -260,7 +261,7 @@ bool TerrainTools::GenerateTerrain(Terrain* terrain, const Int2& numberOfPatches { const Vector2 uv = uvStart + Vector2(x * heightmapSizeInv, z * heightmapSizeInv) * uvPerPatch; - const Color color = TextureTool::SampleLinear(sampler, uv, data1.Mip0DataPtr->Get(), data1.Mip0Size, data1.RowPitch); + const Color color = sampler->SampleLinear(data1.Mip0DataPtr->Get(), uv, data1.Mip0Size, data1.RowPitch); Color32 layers; layers.R = (byte)(Math::Min(1.0f, color.R) * 255.0f); diff --git a/Source/Editor/Utilities/EditorUtilities.cpp b/Source/Editor/Utilities/EditorUtilities.cpp index e3f521437..f946f56d2 100644 --- a/Source/Editor/Utilities/EditorUtilities.cpp +++ b/Source/Editor/Utilities/EditorUtilities.cpp @@ -6,6 +6,7 @@ #include "Engine/Platform/FileSystem.h" #include "Engine/Platform/CreateProcessSettings.h" #include "Engine/Graphics/Textures/TextureData.h" +#include "Engine/Graphics/PixelFormatSampler.h" #include "Engine/Graphics/PixelFormatExtensions.h" #include "Engine/Tools/TextureTool/TextureTool.h" #include "Engine/Core/Log.h" @@ -142,7 +143,7 @@ bool EditorUtilities::ExportApplicationImage(const Guid& iconId, int32 width, in bool EditorUtilities::ExportApplicationImage(const TextureData& icon, int32 width, int32 height, PixelFormat format, const String& path) { // Change format if need to - const TextureData* iconData = &icon; + TextureData* iconData = (TextureData*)&icon; TextureData tmpData1, tmpData2; if (icon.Format != format) { @@ -150,7 +151,7 @@ bool EditorUtilities::ExportApplicationImage(const TextureData& icon, int32 widt if (PixelFormatExtensions::HasAlpha(iconData->Format) && !PixelFormatExtensions::HasAlpha(format)) { // Pre-multiply alpha if can - auto sampler = TextureTool::GetSampler(iconData->Format); + auto sampler = PixelFormatSampler::Get(iconData->Format); if (!sampler) { if (TextureTool::Convert(tmpData2, *iconData, PixelFormat::R16G16B16A16_Float)) @@ -159,7 +160,7 @@ bool EditorUtilities::ExportApplicationImage(const TextureData& icon, int32 widt return true; } iconData = &tmpData2; - sampler = TextureTool::GetSampler(iconData->Format); + sampler = PixelFormatSampler::Get(iconData->Format); } if (sampler) { @@ -168,10 +169,10 @@ bool EditorUtilities::ExportApplicationImage(const TextureData& icon, int32 widt { for (int32 x = 0; x < iconData->Width; x++) { - Color color = TextureTool::SamplePoint(sampler, x, y, mipData->Data.Get(), mipData->RowPitch); + Color color = sampler->SamplePoint(mipData->Data.Get(), x, y, mipData->RowPitch); color *= color.A; color.A = 1.0f; - TextureTool::Store(sampler, x, y, mipData->Data.Get(), mipData->RowPitch, color); + sampler->Store(mipData->Data.Get(), x, y, mipData->RowPitch, color); } } } @@ -191,7 +192,7 @@ bool EditorUtilities::ExportApplicationImage(const TextureData& icon, int32 widt if (PixelFormatExtensions::HasAlpha(icon.Format) && !PixelFormatExtensions::HasAlpha(format)) { // Pre-multiply alpha if can - auto sampler = TextureTool::GetSampler(icon.Format); + auto sampler = PixelFormatSampler::Get(icon.Format); if (sampler) { auto mipData = iconData->GetData(0, 0); @@ -199,10 +200,10 @@ bool EditorUtilities::ExportApplicationImage(const TextureData& icon, int32 widt { for (int32 x = 0; x < iconData->Width; x++) { - Color color = TextureTool::SamplePoint(sampler, x, y, mipData->Data.Get(), mipData->RowPitch); + Color color = sampler->SamplePoint(mipData->Data.Get(), x, y, mipData->RowPitch); color *= color.A; color.A = 1.0f; - TextureTool::Store(sampler, x, y, mipData->Data.Get(), mipData->RowPitch, color); + sampler->Store(mipData->Data.Get(), x, y, mipData->RowPitch, color); } } } diff --git a/Source/Engine/Graphics/PixelFormatExtensions.cpp b/Source/Engine/Graphics/PixelFormatExtensions.cpp index 2a341eec4..10a110d62 100644 --- a/Source/Engine/Graphics/PixelFormatExtensions.cpp +++ b/Source/Engine/Graphics/PixelFormatExtensions.cpp @@ -1,7 +1,13 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. #include "PixelFormatExtensions.h" +#include "PixelFormatSampler.h" #include "Engine/Core/Math/Math.h" +#include "Engine/Core/Math/Color.h" +#include "Engine/Core/Math/Color32.h" +#include "Engine/Core/Math/Half.h" +#include "Engine/Core/Math/Packed.h" +#include "Engine/Core/Math/Vector4.h" // ReSharper disable CppClangTidyClangDiagnosticSwitchEnum @@ -1060,3 +1066,439 @@ PixelFormat PixelFormatExtensions::FindUncompressedFormat(PixelFormat format) return format; } } + +static PixelFormatSampler PixelFormatSamplers[] = +{ + { + PixelFormat::R32G32B32A32_Float, + sizeof(Float4), + [](const void* ptr) + { + return *(Float4*)ptr; + }, + [](void* ptr, const Float4& value) + { + *(Float4*)ptr = value; + }, + }, + { + PixelFormat::R32G32B32_Float, + sizeof(Float3), + [](const void* ptr) + { + return Float4(*(Float3*)ptr, 1.0f); + }, + [](void* ptr, const Float4& value) + { + *(Float3*)ptr = Float3(value); + }, + }, + { + PixelFormat::R16G16B16A16_Float, + sizeof(Half4), + [](const void* ptr) + { + return ((Half4*)ptr)->ToFloat4(); + }, + [](void* ptr, const Float4& value) + { + *(Half4*)ptr = Half4(value.X, value.Y, value.Z, value.W); + }, + }, + { + PixelFormat::R16G16B16A16_UNorm, + sizeof(RGBA16UNorm), + [](const void* ptr) + { + return ((RGBA16UNorm*)ptr)->ToFloat4(); + }, + [](void* ptr, const Float4& value) + { + *(RGBA16UNorm*)ptr = RGBA16UNorm(value.X, value.Y, value.Z, value.W); + } + }, + { + PixelFormat::R32G32_Float, + sizeof(Float2), + [](const void* ptr) + { + return Float4(((Float2*)ptr)->X, ((Float2*)ptr)->Y, 0.0f, 0.0f); + }, + [](void* ptr, const Float4& value) + { + *(Float2*)ptr = Float2(value.X, value.Y); + }, + }, + { + PixelFormat::R8G8B8A8_UNorm, + sizeof(Color32), + [](const void* ptr) + { + return Float4(Color(*(Color32*)ptr)); + }, + [](void* ptr, const Float4& value) + { + *(Color32*)ptr = Color32(value); + }, + }, + { + PixelFormat::R8G8B8A8_UNorm_sRGB, + sizeof(Color32), + [](const void* ptr) + { + return Float4(Color::SrgbToLinear(Color(*(Color32*)ptr))); + }, + [](void* ptr, const Float4& value) + { + Color srgb = Color::LinearToSrgb((const Color&)value); + *(Color32*)ptr = Color32(srgb); + }, + }, + { + PixelFormat::R8G8_UNorm, + sizeof(uint16), + [](const void* ptr) + { + const uint8* rg = (const uint8*)ptr; + return Float4((float)rg[0] / MAX_uint8, (float)rg[1] / MAX_uint8, 0, 1); + }, + [](void* ptr, const Float4& value) + { + uint8* rg = (uint8*)ptr; + rg[0] = (uint8)(value.X * MAX_uint8); + rg[1] = (uint8)(value.Y * MAX_uint8); + }, + }, + { + PixelFormat::R16G16_Float, + sizeof(Half2), + [](const void* ptr) + { + const Float2 rg = ((Half2*)ptr)->ToFloat2(); + return Float4(rg.X, rg.Y, 0, 1); + }, + [](void* ptr, const Float4& value) + { + *(Half2*)ptr = Half2(value.X, value.Y); + }, + }, + { + PixelFormat::R16G16_UNorm, + sizeof(RG16UNorm), + [](const void* ptr) + { + const Float2 rg = ((RG16UNorm*)ptr)->ToFloat2(); + return Float4(rg.X, rg.Y, 0, 1); + }, + [](void* ptr, const Float4& value) + { + *(RG16UNorm*)ptr = RG16UNorm(value.X, value.Y); + }, + }, + { + PixelFormat::R32_Float, + sizeof(float), + [](const void* ptr) + { + return Float4(*(float*)ptr, 0, 0, 1); + }, + [](void* ptr, const Float4& value) + { + *(float*)ptr = value.X; + }, + }, + { + PixelFormat::R16_Float, + sizeof(Half), + [](const void* ptr) + { + return Float4(Float16Compressor::Decompress(*(Half*)ptr), 0, 0, 1); + }, + [](void* ptr, const Float4& value) + { + *(Half*)ptr = Float16Compressor::Compress(value.X); + }, + }, + { + PixelFormat::R16_UNorm, + sizeof(uint16), + [](const void* ptr) + { + return Float4((float)*(uint16*)ptr / MAX_uint16, 0, 0, 1); + }, + [](void* ptr, const Float4& value) + { + *(uint16*)ptr = (uint16)(value.X * MAX_uint16); + }, + }, + { + PixelFormat::R8_UNorm, + sizeof(uint8), + [](const void* ptr) + { + return Float4((float)*(byte*)ptr / MAX_uint8, 0, 0, 1); + }, + [](void* ptr, const Float4& value) + { + *(byte*)ptr = (byte)(value.X * MAX_uint8); + }, + }, + { + PixelFormat::A8_UNorm, + sizeof(uint8), + [](const void* ptr) + { + return Float4(0, 0, 0, (float)*(byte*)ptr / MAX_uint8); + }, + [](void* ptr, const Float4& value) + { + *(byte*)ptr = (byte)(value.W * MAX_uint8); + }, + }, + { + PixelFormat::R32_UInt, + sizeof(uint32), + [](const void* ptr) + { + return Float4((float)*(uint32*)ptr, 0, 0, 1); + }, + [](void* ptr, const Float4& value) + { + *(uint32*)ptr = (uint32)value.X; + }, + }, + { + PixelFormat::R32_SInt, + sizeof(int32), + [](const void* ptr) + { + return Float4((float)*(int32*)ptr, 0, 0, 1); + }, + [](void* ptr, const Float4& value) + { + *(int32*)ptr = (int32)value.X; + }, + }, + { + PixelFormat::R16_UInt, + sizeof(uint16), + [](const void* ptr) + { + return Float4((float)*(uint16*)ptr, 0, 0, 1); + }, + [](void* ptr, const Float4& value) + { + *(uint16*)ptr = (uint16)value.X; + }, + }, + { + PixelFormat::R16_SInt, + sizeof(int16), + [](const void* ptr) + { + return Float4((float)*(int16*)ptr, 0, 0, 1); + }, + [](void* ptr, const Float4& value) + { + *(int16*)ptr = (int16)value.X; + }, + }, + { + PixelFormat::R8_UInt, + sizeof(uint8), + [](const void* ptr) + { + return Float4((float)*(uint8*)ptr, 0, 0, 1); + }, + [](void* ptr, const Float4& value) + { + *(uint8*)ptr = (uint8)value.X; + }, + }, + { + PixelFormat::R8_SInt, + sizeof(int8), + [](const void* ptr) + { + return Float4((float)*(int8*)ptr, 0, 0, 1); + }, + [](void* ptr, const Float4& value) + { + *(int8*)ptr = (int8)value.X; + }, + }, + { + PixelFormat::B8G8R8A8_UNorm, + sizeof(Color32), + [](const void* ptr) + { + const Color32 bgra = *(Color32*)ptr; + return Float4(Color(Color32(bgra.B, bgra.G, bgra.R, bgra.A))); + }, + [](void* ptr, const Float4& value) + { + *(Color32*)ptr = Color32(byte(value.Z * MAX_uint8), byte(value.Y * MAX_uint8), byte(value.X * MAX_uint8), byte(value.W * MAX_uint8)); + }, + }, + { + PixelFormat::B8G8R8A8_UNorm_sRGB, + sizeof(Color32), + [](const void* ptr) + { + const Color32 bgra = *(Color32*)ptr; + return Float4(Color::SrgbToLinear(Color(Color32(bgra.B, bgra.G, bgra.R, bgra.A)))); + }, + [](void* ptr, const Float4& value) + { + Color srgb = Color::LinearToSrgb((const Color&)value); + *(Color32*)ptr = Color32(byte(srgb.B * MAX_uint8), byte(srgb.G * MAX_uint8), byte(srgb.R * MAX_uint8), byte(srgb.A * MAX_uint8)); + }, + }, + { + PixelFormat::B8G8R8X8_UNorm, + sizeof(Color32), + [](const void* ptr) + { + const Color32 bgra = *(Color32*)ptr; + return Float4(Color(Color32(bgra.B, bgra.G, bgra.R, MAX_uint8))); + }, + [](void* ptr, const Float4& value) + { + *(Color32*)ptr = Color32(byte(value.Z * MAX_uint8), byte(value.Y * MAX_uint8), byte(value.X * MAX_uint8), MAX_uint8); + }, + }, + { + PixelFormat::B8G8R8X8_UNorm_sRGB, + sizeof(Color32), + [](const void* ptr) + { + const Color32 bgra = *(Color32*)ptr; + return Float4(Color::SrgbToLinear(Color(Color32(bgra.B, bgra.G, bgra.R, MAX_uint8)))); + }, + [](void* ptr, const Float4& value) + { + Color srgb = Color::LinearToSrgb((const Color&)value); + *(Color32*)ptr = Color32(byte(srgb.B * MAX_uint8), byte(srgb.G * MAX_uint8), byte(srgb.R * MAX_uint8), MAX_uint8); + }, + }, + { + PixelFormat::R11G11B10_Float, + sizeof(FloatR11G11B10), + [](const void* ptr) + { + const Float3 rgb = ((FloatR11G11B10*)ptr)->ToFloat3(); + return Float4(rgb.X, rgb.Y, rgb.Z, 0.0f); + }, + [](void* ptr, const Float4& value) + { + *(FloatR11G11B10*)ptr = FloatR11G11B10(value.X, value.Y, value.Z); + }, + }, + { + PixelFormat::R10G10B10A2_UNorm, + sizeof(Float1010102), + [](const void* ptr) + { + return ((Float1010102*)ptr)->ToFloat4(); + }, + [](void* ptr, const Float4& value) + { + *(Float1010102*)ptr = Float1010102(value.X, value.Y, value.Z, value.W); + }, + }, + { + PixelFormat::R8G8B8A8_UInt, + sizeof(Color32), + [](const void* ptr) + { + uint8 data[4]; + Platform::MemoryCopy(data, ptr, sizeof(data)); + return Float4(data[0], data[1], data[2], data[3]); + }, + [](void* ptr, const Float4& value) + { + uint8 data[4] = { (uint8)value.X, (uint8)value.Y, (uint8)value.Z, (uint8)value.W}; + Platform::MemoryCopy(ptr, data, sizeof(data)); + }, + }, + { + PixelFormat::R8G8B8A8_SInt, + sizeof(Color32), + [](const void* ptr) + { + int8 data[4]; + Platform::MemoryCopy(data, ptr, sizeof(data)); + return Float4(data[0], data[1], data[2], data[3]); + }, + [](void* ptr, const Float4& value) + { + int8 data[4] = { (int8)value.X, (int8)value.Y, (int8)value.Z, (int8)value.W}; + Platform::MemoryCopy(ptr, data, sizeof(data)); + }, + }, +}; + +void PixelFormatSampler::Store(void* data, int32 x, int32 y, int32 rowPitch, const Color& color) const +{ + Write((byte*)data + rowPitch * y + PixelSize * x, (Float4&)color); +} + +Float4 PixelFormatSampler::Sample(const void* data, int32 x) const +{ + return Read((const byte*)data + x * PixelSize); +} + +Color PixelFormatSampler::SamplePoint(const void* data, const Float2& uv, const Int2& size, int32 rowPitch) const +{ + const Int2 end = size - 1; + const Int2 uvFloor(Math::Min(Math::FloorToInt(uv.X * size.X), end.X), Math::Min(Math::FloorToInt(uv.Y * size.Y), end.Y)); + Float4 result = Read((const byte*)data + rowPitch * uvFloor.Y + PixelSize * uvFloor.X); + return *(Color*)&result; +} + +Color PixelFormatSampler::SamplePoint(const void* data, int32 x, int32 y, int32 rowPitch) const +{ + Float4 result = Read((const byte*)data + rowPitch * y + PixelSize * x); + return *(Color*)&result; +} + +Color PixelFormatSampler::SampleLinear(const void* data, const Float2& uv, const Int2& size, int32 rowPitch) const +{ + const Int2 end = size - 1; + const Int2 uvFloor(Math::Min(Math::FloorToInt(uv.X * size.X), end.X), Math::Min(Math::FloorToInt(uv.Y * size.Y), end.Y)); + const Int2 uvNext(Math::Min(uvFloor.X + 1, end.X), Math::Min(uvFloor.Y + 1, end.Y)); + const Float2 uvFraction(uv.X * size.Y - uvFloor.X, uv.Y * size.Y - uvFloor.Y); + + const Float4 v00 = Read((const byte*)data + rowPitch * uvFloor.Y + PixelSize * uvFloor.X); + const Float4 v01 = Read((const byte*)data + rowPitch * uvFloor.Y + PixelSize * uvNext.X); + const Float4 v10 = Read((const byte*)data + rowPitch * uvNext.Y + PixelSize * uvFloor.X); + const Float4 v11 = Read((const byte*)data + rowPitch * uvNext.Y + PixelSize * uvNext.X); + + Float4 result = Float4::Lerp(Float4::Lerp(v00, v01, uvFraction.X), Float4::Lerp(v10, v11, uvFraction.X), uvFraction.Y); + return *(Color*)&result; +} + +const PixelFormatSampler* PixelFormatSampler::Get(PixelFormat format) +{ + format = PixelFormatExtensions::MakeTypelessFloat(format); + for (const auto& sampler : PixelFormatSamplers) + { + if (sampler.Format == format) + return &sampler; + } + return nullptr; +} + +#if !COMPILE_WITHOUT_CSHARP + +void PixelFormatExtensions::GetSamplerInternal(PixelFormat format, int32& pixelSize, void** read, void** write) +{ + if (const PixelFormatSampler* sampler = PixelFormatSampler::Get(format)) + { + pixelSize = sampler->PixelSize; + *read = sampler->Read; + *write = sampler->Write; + } +} + +#endif diff --git a/Source/Engine/Graphics/PixelFormatExtensions.h b/Source/Engine/Graphics/PixelFormatExtensions.h index d5296ea65..f3274d9ea 100644 --- a/Source/Engine/Graphics/PixelFormatExtensions.h +++ b/Source/Engine/Graphics/PixelFormatExtensions.h @@ -205,4 +205,10 @@ public: static PixelFormat FindUnorderedAccessFormat(PixelFormat format); static PixelFormat FindDepthStencilFormat(PixelFormat format); static PixelFormat FindUncompressedFormat(PixelFormat format); + +private: + // Internal bindings +#if !COMPILE_WITHOUT_CSHARP + API_FUNCTION(NoProxy) static void GetSamplerInternal(PixelFormat format, API_PARAM(Out) int32& pixelSize, API_PARAM(Out) void** read, API_PARAM(Out) void** write); +#endif }; diff --git a/Source/Engine/Graphics/PixelFormatSampler.cs b/Source/Engine/Graphics/PixelFormatSampler.cs new file mode 100644 index 000000000..6a8127b4a --- /dev/null +++ b/Source/Engine/Graphics/PixelFormatSampler.cs @@ -0,0 +1,46 @@ +// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. + +namespace FlaxEngine +{ + unsafe partial struct PixelFormatSampler + { + /// + /// Element format. + /// + public PixelFormat Format; + + /// + /// Element size in bytes. + /// + public int PixelSize; + + /// + /// Read data function. + /// + public delegate* unmanaged Read; + + /// + /// Write data function. + /// + public delegate* unmanaged Write; + + /// + /// Tries to get a sampler tool for the specified format to read pixels. + /// + /// The format. + /// The sampler object or empty when cannot sample the given format. + /// True if got sampler, otherwise false. + public static bool Get(PixelFormat format, out PixelFormatSampler sampler) + { + PixelFormatExtensions.Internal_GetSamplerInternal(format, out var pixelSize, out var read, out var write); + sampler = new PixelFormatSampler + { + Format = format, + PixelSize = pixelSize, + Read = (delegate* unmanaged)read.ToPointer(), + Write = (delegate* unmanaged)write.ToPointer(), + }; + return pixelSize != 0; + } + } +} diff --git a/Source/Engine/Graphics/PixelFormatSampler.h b/Source/Engine/Graphics/PixelFormatSampler.h new file mode 100644 index 000000000..c0e0cb6d1 --- /dev/null +++ b/Source/Engine/Graphics/PixelFormatSampler.h @@ -0,0 +1,82 @@ +// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. + +#pragma once + +#include "PixelFormat.h" + +/// +/// Utility for writing and reading from different pixels formats within a single code path. +/// +API_STRUCT(NoDefault) struct FLAXENGINE_API PixelFormatSampler +{ + DECLARE_SCRIPTING_TYPE_MINIMAL(PixelFormatSampler); + typedef Float4 (*ReadPixel)(const void* data); + typedef void (*WritePixel)(void* data, const Float4& value); + +public: + // Element format. + PixelFormat Format; + // Element size in bytes. + int32 PixelSize; + // Read data function. + ReadPixel Read; + // Write data function. + WritePixel Write; + +public: + /// + /// Stores the color into the specified texture data (uses no interpolation). + /// + /// The data pointer for the texture slice (1D or 2D image). + /// The X texture coordinates (normalized to range 0-width). + /// The Y texture coordinates (normalized to range 0-height). + /// The row pitch (in bytes). The offset between each image rows. + /// The color to store (linear). + void Store(void* data, int32 x, int32 y, int32 rowPitch, const Color& color) const; + + /// + /// Samples the specified linear data (uses no interpolation). + /// + /// The data pointer for the data slice (linear buffer or 1D image). + /// Index of the element. + /// The sampled value. + Float4 Sample(const void* data, int32 x) const; + + /// + /// Samples the specified texture data (uses no interpolation). + /// + /// The data pointer for the texture slice (1D or 2D image). + /// The texture coordinates (normalized to range 0-1). + /// The size of the input texture (in pixels). + /// The row pitch (in bytes). The offset between each image rows. + /// The sampled color (linear). + Color SamplePoint(const void* data, const Float2& uv, const Int2& size, int32 rowPitch) const; + + /// + /// Samples the specified texture data (uses no interpolation). + /// + /// The data pointer for the texture slice (1D or 2D image). + /// The X texture coordinates (normalized to range 0-width). + /// The Y texture coordinates (normalized to range 0-height). + /// The row pitch (in bytes). The offset between each image rows. + /// The sampled color (linear). + Color SamplePoint(const void* data, int32 x, int32 y, int32 rowPitch) const; + + /// + /// Samples the specified texture data (uses linear interpolation). + /// + /// The data pointer for the texture slice (1D or 2D image). + /// The texture coordinates (normalized to range 0-1). + /// The size of the input texture (in pixels). + /// The row pitch (in bytes). The offset between each image rows. + /// The sampled color (linear). + Color SampleLinear(const void* data, const Float2& uv, const Int2& size, int32 rowPitch) const; + +public: + /// + /// Tries to get a sampler tool for the specified format to read pixels. + /// + /// The format. + /// The pointer to the sampler object or null when cannot sample the given format. + static const PixelFormatSampler* Get(PixelFormat format); +}; diff --git a/Source/Engine/Graphics/Textures/TextureBase.cpp b/Source/Engine/Graphics/Textures/TextureBase.cpp index 61b8945bb..d1a78a922 100644 --- a/Source/Engine/Graphics/Textures/TextureBase.cpp +++ b/Source/Engine/Graphics/Textures/TextureBase.cpp @@ -11,6 +11,7 @@ #include "Engine/Debug/Exceptions/InvalidOperationException.h" #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Content/Factories/BinaryAssetFactory.h" +#include "Engine/Graphics/PixelFormatSampler.h" #include "Engine/Scripting/Enums.h" #include "Engine/Tools/TextureTool/TextureTool.h" #include "Engine/Threading/Threading.h" @@ -100,14 +101,14 @@ bool TextureMipData::GetPixels(Array& pixels, int32 width, int32 height default: { // Try to use texture sampler utility - auto sampler = TextureTool::GetSampler(format); + auto sampler = PixelFormatSampler::Get(format); if (sampler) { for (int32 y = 0; y < height; y++) { for (int32 x = 0; x < width; x++) { - Color c = TextureTool::SamplePoint(sampler, x, y, src, RowPitch); + Color c = sampler->SamplePoint(src, x, y, RowPitch); *(Color32*)(dst + dstRowSize * y + x * sizeof(Color32)) = Color32(c); } } @@ -149,14 +150,14 @@ bool TextureMipData::GetPixels(Array& pixels, int32 width, int32 height, default: { // Try to use texture sampler utility - auto sampler = TextureTool::GetSampler(format); + auto sampler = PixelFormatSampler::Get(format); if (sampler) { for (int32 y = 0; y < height; y++) { for (int32 x = 0; x < width; x++) { - Color c = TextureTool::SamplePoint(sampler, x, y, src, RowPitch); + Color c = sampler->SamplePoint(src, x, y, RowPitch); *(Color*)(dst + dstRowSize * y + x * sizeof(Color)) = c; } } @@ -475,7 +476,7 @@ bool TextureBase::SetPixels(const Span& pixels, int32 mipIndex, int32 a if (error) { // Try to use texture sampler utility - auto sampler = TextureTool::GetSampler(format); + auto sampler = PixelFormatSampler::Get(format); if (sampler) { for (int32 y = 0; y < height; y++) @@ -483,7 +484,7 @@ bool TextureBase::SetPixels(const Span& pixels, int32 mipIndex, int32 a for (int32 x = 0; x < width; x++) { Color c(pixels.Get()[x + y * width]); - TextureTool::Store(sampler, x, y, dst, rowPitch, c); + sampler->Store(dst, x, y, rowPitch, c); } } error = false; @@ -553,7 +554,7 @@ bool TextureBase::SetPixels(const Span& pixels, int32 mipIndex, int32 arr if (error) { // Try to use texture sampler utility - auto sampler = TextureTool::GetSampler(format); + auto sampler = PixelFormatSampler::Get(format); if (sampler) { for (int32 y = 0; y < height; y++) @@ -561,7 +562,7 @@ bool TextureBase::SetPixels(const Span& pixels, int32 mipIndex, int32 arr for (int32 x = 0; x < width; x++) { Color c(pixels.Get()[x + y * width]); - TextureTool::Store(sampler, x, y, dst, rowPitch, c); + sampler->Store(dst, x, y, rowPitch, c); } } error = false; diff --git a/Source/Engine/Tools/TextureTool/TextureTool.cpp b/Source/Engine/Tools/TextureTool/TextureTool.cpp index 550e26699..1818c7246 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.cpp +++ b/Source/Engine/Tools/TextureTool/TextureTool.cpp @@ -6,15 +6,13 @@ #include "Engine/Core/Log.h" #include "Engine/Core/Types/DateTime.h" #include "Engine/Core/Types/TimeSpan.h" -#include "Engine/Core/Math/Packed.h" -#include "Engine/Core/Math/Color32.h" #include "Engine/Core/Math/Vector2.h" #include "Engine/Platform/FileSystem.h" #include "Engine/Serialization/JsonWriter.h" #include "Engine/Serialization/JsonTools.h" #include "Engine/Scripting/Enums.h" +#include "Engine/Graphics/PixelFormatSampler.h" #include "Engine/Graphics/Textures/TextureData.h" -#include "Engine/Graphics/PixelFormatExtensions.h" #include "Engine/Profiler/ProfilerCPU.h" #if USE_EDITOR @@ -372,326 +370,6 @@ bool TextureTool::Resize(TextureData& dst, const TextureData& src, int32 dstWidt #endif } -TextureTool::PixelFormatSampler PixelFormatSamplers[] = -{ - { - PixelFormat::R32G32B32A32_Float, - sizeof(Float4), - [](const void* ptr) - { - return Color(*(Float4*)ptr); - }, - [](const void* ptr, const Color& color) - { - *(Float4*)ptr = color.ToFloat4(); - }, - }, - { - PixelFormat::R32G32B32_Float, - sizeof(Float3), - [](const void* ptr) - { - return Color(*(Float3*)ptr, 1.0f); - }, - [](const void* ptr, const Color& color) - { - *(Float3*)ptr = color.ToFloat3(); - }, - }, - { - PixelFormat::R16G16B16A16_Float, - sizeof(Half4), - [](const void* ptr) - { - return Color(((Half4*)ptr)->ToFloat4()); - }, - [](const void* ptr, const Color& color) - { - *(Half4*)ptr = Half4(color.R, color.G, color.B, color.A); - }, - }, - { - PixelFormat::R16G16B16A16_UNorm, - sizeof(RGBA16UNorm), - [](const void* ptr) - { - return Color(((RGBA16UNorm*)ptr)->ToFloat4()); - }, - [](const void* ptr, const Color& color) - { - *(RGBA16UNorm*)ptr = RGBA16UNorm(color.R, color.G, color.B, color.A); - } - }, - { - PixelFormat::R32G32_Float, - sizeof(Float2), - [](const void* ptr) - { - return Color(((Float2*)ptr)->X, ((Float2*)ptr)->Y, 1.0f); - }, - [](const void* ptr, const Color& color) - { - *(Float2*)ptr = Float2(color.R, color.G); - }, - }, - { - PixelFormat::R8G8B8A8_UNorm, - sizeof(Color32), - [](const void* ptr) - { - return Color(*(Color32*)ptr); - }, - [](const void* ptr, const Color& color) - { - *(Color32*)ptr = Color32(color); - }, - }, - { - PixelFormat::R8G8B8A8_UNorm_sRGB, - sizeof(Color32), - [](const void* ptr) - { - return Color::SrgbToLinear(Color(*(Color32*)ptr)); - }, - [](const void* ptr, const Color& color) - { - Color srgb = Color::LinearToSrgb(color); - *(Color32*)ptr = Color32(srgb); - }, - }, - { - PixelFormat::R8G8_UNorm, - sizeof(uint16), - [](const void* ptr) - { - const uint8* rg = (const uint8*)ptr; - return Color((float)rg[0] / MAX_uint8, (float)rg[1] / MAX_uint8, 0, 1); - }, - [](const void* ptr, const Color& color) - { - uint8* rg = (uint8*)ptr; - rg[0] = (uint8)(color.R * MAX_uint8); - rg[1] = (uint8)(color.G * MAX_uint8); - }, - }, - { - PixelFormat::R16G16_Float, - sizeof(Half2), - [](const void* ptr) - { - const Float2 rg = ((Half2*)ptr)->ToFloat2(); - return Color(rg.X, rg.Y, 0, 1); - }, - [](const void* ptr, const Color& color) - { - *(Half2*)ptr = Half2(color.R, color.G); - }, - }, - { - PixelFormat::R16G16_UNorm, - sizeof(RG16UNorm), - [](const void* ptr) - { - const Float2 rg = ((RG16UNorm*)ptr)->ToFloat2(); - return Color(rg.X, rg.Y, 0, 1); - }, - [](const void* ptr, const Color& color) - { - *(RG16UNorm*)ptr = RG16UNorm(color.R, color.G); - }, - }, - { - PixelFormat::R32_Float, - sizeof(float), - [](const void* ptr) - { - return Color(*(float*)ptr, 0, 0, 1); - }, - [](const void* ptr, const Color& color) - { - *(float*)ptr = color.R; - }, - }, - { - PixelFormat::R16_Float, - sizeof(Half), - [](const void* ptr) - { - return Color(Float16Compressor::Decompress(*(Half*)ptr), 0, 0, 1); - }, - [](const void* ptr, const Color& color) - { - *(Half*)ptr = Float16Compressor::Compress(color.R); - }, - }, - { - PixelFormat::R16_UNorm, - sizeof(uint16), - [](const void* ptr) - { - return Color((float)*(uint16*)ptr / MAX_uint16, 0, 0, 1); - }, - [](const void* ptr, const Color& color) - { - *(uint16*)ptr = (uint16)(color.R * MAX_uint16); - }, - }, - { - PixelFormat::R8_UNorm, - sizeof(uint8), - [](const void* ptr) - { - return Color((float)*(byte*)ptr / MAX_uint8, 0, 0, 1); - }, - [](const void* ptr, const Color& color) - { - *(byte*)ptr = (byte)(color.R * MAX_uint8); - }, - }, - { - PixelFormat::A8_UNorm, - sizeof(uint8), - [](const void* ptr) - { - return Color(0, 0, 0, (float)*(byte*)ptr / MAX_uint8); - }, - [](const void* ptr, const Color& color) - { - *(byte*)ptr = (byte)(color.A * MAX_uint8); - }, - }, - { - PixelFormat::B8G8R8A8_UNorm, - sizeof(Color32), - [](const void* ptr) - { - const Color32 bgra = *(Color32*)ptr; - return Color(Color32(bgra.B, bgra.G, bgra.R, bgra.A)); - }, - [](const void* ptr, const Color& color) - { - *(Color32*)ptr = Color32(byte(color.B * MAX_uint8), byte(color.G * MAX_uint8), byte(color.R * MAX_uint8), byte(color.A * MAX_uint8)); - }, - }, - { - PixelFormat::B8G8R8A8_UNorm_sRGB, - sizeof(Color32), - [](const void* ptr) - { - const Color32 bgra = *(Color32*)ptr; - return Color::SrgbToLinear(Color(Color32(bgra.B, bgra.G, bgra.R, bgra.A))); - }, - [](const void* ptr, const Color& color) - { - Color srgb = Color::LinearToSrgb(color); - *(Color32*)ptr = Color32(byte(srgb.B * MAX_uint8), byte(srgb.G * MAX_uint8), byte(srgb.R * MAX_uint8), byte(srgb.A * MAX_uint8)); - }, - }, - { - PixelFormat::B8G8R8X8_UNorm, - sizeof(Color32), - [](const void* ptr) - { - const Color32 bgra = *(Color32*)ptr; - return Color(Color32(bgra.B, bgra.G, bgra.R, MAX_uint8)); - }, - [](const void* ptr, const Color& color) - { - *(Color32*)ptr = Color32(byte(color.B * MAX_uint8), byte(color.G * MAX_uint8), byte(color.R * MAX_uint8), MAX_uint8); - }, - }, - { - PixelFormat::B8G8R8X8_UNorm_sRGB, - sizeof(Color32), - [](const void* ptr) - { - const Color32 bgra = *(Color32*)ptr; - return Color::SrgbToLinear(Color(Color32(bgra.B, bgra.G, bgra.R, MAX_uint8))); - }, - [](const void* ptr, const Color& color) - { - Color srgb = Color::LinearToSrgb(color); - *(Color32*)ptr = Color32(byte(srgb.B * MAX_uint8), byte(srgb.G * MAX_uint8), byte(srgb.R * MAX_uint8), MAX_uint8); - }, - }, - { - PixelFormat::R11G11B10_Float, - sizeof(FloatR11G11B10), - [](const void* ptr) - { - const Float3 rgb = ((FloatR11G11B10*)ptr)->ToFloat3(); - return Color(rgb.X, rgb.Y, rgb.Z); - }, - [](const void* ptr, const Color& color) - { - *(FloatR11G11B10*)ptr = FloatR11G11B10(color.R, color.G, color.B); - }, - }, - { - PixelFormat::R10G10B10A2_UNorm, - sizeof(Float1010102), - [](const void* ptr) - { - const Float3 rgb = ((Float1010102*)ptr)->ToFloat3(); - return Color(rgb.X, rgb.Y, rgb.Z); - }, - [](const void* ptr, const Color& color) - { - *(Float1010102*)ptr = Float1010102(color.R, color.G, color.B, color.A); - }, - }, -}; - -const TextureTool::PixelFormatSampler* TextureTool::GetSampler(PixelFormat format) -{ - format = PixelFormatExtensions::MakeTypelessFloat(format); - for (auto& sampler : PixelFormatSamplers) - { - if (sampler.Format == format) - return &sampler; - } - return nullptr; -} - -void TextureTool::Store(const PixelFormatSampler* sampler, int32 x, int32 y, const void* data, int32 rowPitch, const Color& color) -{ - ASSERT_LOW_LAYER(sampler); - sampler->Store((byte*)data + rowPitch * y + sampler->PixelSize * x, color); -} - -Color TextureTool::SamplePoint(const PixelFormatSampler* sampler, const Float2& uv, const void* data, const Int2& size, int32 rowPitch) -{ - ASSERT_LOW_LAYER(sampler); - - const Int2 end = size - 1; - const Int2 uvFloor(Math::Min(Math::FloorToInt(uv.X * size.X), end.X), Math::Min(Math::FloorToInt(uv.Y * size.Y), end.Y)); - - return sampler->Sample((byte*)data + rowPitch * uvFloor.Y + sampler->PixelSize * uvFloor.X); -} - -Color TextureTool::SamplePoint(const PixelFormatSampler* sampler, int32 x, int32 y, const void* data, int32 rowPitch) -{ - ASSERT_LOW_LAYER(sampler); - return sampler->Sample((byte*)data + rowPitch * y + sampler->PixelSize * x); -} - -Color TextureTool::SampleLinear(const PixelFormatSampler* sampler, const Float2& uv, const void* data, const Int2& size, int32 rowPitch) -{ - ASSERT_LOW_LAYER(sampler); - - const Int2 end = size - 1; - const Int2 uvFloor(Math::Min(Math::FloorToInt(uv.X * size.X), end.X), Math::Min(Math::FloorToInt(uv.Y * size.Y), end.Y)); - const Int2 uvNext(Math::Min(uvFloor.X + 1, end.X), Math::Min(uvFloor.Y + 1, end.Y)); - const Float2 uvFraction(uv.X * size.Y - uvFloor.X, uv.Y * size.Y - uvFloor.Y); - - const Color v00 = sampler->Sample((byte*)data + rowPitch * uvFloor.Y + sampler->PixelSize * uvFloor.X); - const Color v01 = sampler->Sample((byte*)data + rowPitch * uvFloor.Y + sampler->PixelSize * uvNext.X); - const Color v10 = sampler->Sample((byte*)data + rowPitch * uvNext.Y + sampler->PixelSize * uvFloor.X); - const Color v11 = sampler->Sample((byte*)data + rowPitch * uvNext.Y + sampler->PixelSize * uvNext.X); - - return Color::Lerp(Color::Lerp(v00, v01, uvFraction.X), Color::Lerp(v10, v11, uvFraction.X), uvFraction.Y); -} - PixelFormat TextureTool::ToPixelFormat(TextureFormatType format, int32 width, int32 height, bool canCompress) { const bool canUseBlockCompression = width % 4 == 0 && height % 4 == 0; @@ -795,7 +473,7 @@ bool TextureTool::GetImageType(const StringView& path, ImageType& type) bool TextureTool::Transform(TextureData& texture, const Function& transformation) { PROFILE_CPU(); - auto sampler = TextureTool::GetSampler(texture.Format); + auto sampler = PixelFormatSampler::Get(texture.Format); if (!sampler) return true; for (auto& slice : texture.Items) @@ -809,9 +487,9 @@ bool TextureTool::Transform(TextureData& texture, const Function& { for (int32 x = 0; x < mipWidth; x++) { - Color color = TextureTool::SamplePoint(sampler, x, y, mip.Data.Get(), mip.RowPitch); + Color color = sampler->SamplePoint(mip.Data.Get(), x, y, mip.RowPitch); transformation(color); - TextureTool::Store(sampler, x, y, mip.Data.Get(), mip.RowPitch, color); + sampler->Store(mip.Data.Get(), x, y, mip.RowPitch, color); } } } diff --git a/Source/Engine/Tools/TextureTool/TextureTool.h b/Source/Engine/Tools/TextureTool/TextureTool.h index 13bac0dc5..cdbd9ee8f 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.h +++ b/Source/Engine/Tools/TextureTool/TextureTool.h @@ -176,80 +176,6 @@ public: static bool Resize(TextureData& dst, const TextureData& src, int32 dstWidth, int32 dstHeight); public: - typedef Color (*ReadPixel)(const void*); - typedef void (*WritePixel)(const void*, const Color&); - - struct PixelFormatSampler - { - PixelFormat Format; - int32 PixelSize; - ReadPixel Sample; - WritePixel Store; - }; - - /// - /// Determines whether this tool can sample the specified format to read texture samples and returns the sampler object. - /// - /// The format. - /// The pointer to the sampler object or null if cannot sample the given format. - static const PixelFormatSampler* GetSampler(PixelFormat format); - - /// - /// Stores the color into the specified texture data (uses no interpolation). - /// - /// - /// Use GetSampler for the texture sampler. - /// - /// The texture data sampler. - /// The X texture coordinates (normalized to range 0-width). - /// The Y texture coordinates (normalized to range 0-height). - /// The data pointer for the texture slice (1D or 2D image). - /// The row pitch (in bytes). The offset between each image rows. - /// The color to store (linear). - static void Store(const PixelFormatSampler* sampler, int32 x, int32 y, const void* data, int32 rowPitch, const Color& color); - - /// - /// Samples the specified texture data (uses no interpolation). - /// - /// - /// Use GetSampler for the texture sampler. - /// - /// The texture data sampler. - /// The texture coordinates (normalized to range 0-1). - /// The data pointer for the texture slice (1D or 2D image). - /// The size of the input texture (in pixels). - /// The row pitch (in bytes). The offset between each image rows. - /// The sampled color (linear). - static Color SamplePoint(const PixelFormatSampler* sampler, const Float2& uv, const void* data, const Int2& size, int32 rowPitch); - - /// - /// Samples the specified texture data (uses no interpolation). - /// - /// - /// Use GetSampler for the texture sampler. - /// - /// The texture data sampler. - /// The X texture coordinates (normalized to range 0-width). - /// The Y texture coordinates (normalized to range 0-height). - /// The data pointer for the texture slice (1D or 2D image). - /// The row pitch (in bytes). The offset between each image rows. - /// The sampled color (linear). - static Color SamplePoint(const PixelFormatSampler* sampler, int32 x, int32 y, const void* data, int32 rowPitch); - - /// - /// Samples the specified texture data (uses linear interpolation). - /// - /// - /// Use GetSampler for the texture sampler. - /// - /// The texture data sampler. - /// The texture coordinates (normalized to range 0-1). - /// The data pointer for the texture slice (1D or 2D image). - /// The size of the input texture (in pixels). - /// The row pitch (in bytes). The offset between each image rows. - /// The sampled color (linear). - static Color SampleLinear(const PixelFormatSampler* sampler, const Float2& uv, const void* data, const Int2& size, int32 rowPitch); - static PixelFormat ToPixelFormat(TextureFormatType format, int32 width, int32 height, bool canCompress); private: