Add PixelFormatSampler utility to quick read/write operations on various data formats
Moved from `TextureTool` to be used in runtime and with more generic use cases (including C# scripting).
This commit is contained in:
@@ -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))
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
46
Source/Engine/Graphics/PixelFormatSampler.cs
Normal file
46
Source/Engine/Graphics/PixelFormatSampler.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
namespace FlaxEngine
|
||||
{
|
||||
unsafe partial struct PixelFormatSampler
|
||||
{
|
||||
/// <summary>
|
||||
/// Element format.
|
||||
/// </summary>
|
||||
public PixelFormat Format;
|
||||
|
||||
/// <summary>
|
||||
/// Element size in bytes.
|
||||
/// </summary>
|
||||
public int PixelSize;
|
||||
|
||||
/// <summary>
|
||||
/// Read data function.
|
||||
/// </summary>
|
||||
public delegate* unmanaged<void*, Float4> Read;
|
||||
|
||||
/// <summary>
|
||||
/// Write data function.
|
||||
/// </summary>
|
||||
public delegate* unmanaged<void*, ref Float4, void> Write;
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get a sampler tool for the specified format to read pixels.
|
||||
/// </summary>
|
||||
/// <param name="format">The format.</param>
|
||||
/// <param name="sampler">The sampler object or empty when cannot sample the given format.</param>
|
||||
/// <returns>True if got sampler, otherwise false.</returns>
|
||||
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<void*, Float4>)read.ToPointer(),
|
||||
Write = (delegate* unmanaged<void*, ref Float4, void>)write.ToPointer(),
|
||||
};
|
||||
return pixelSize != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
82
Source/Engine/Graphics/PixelFormatSampler.h
Normal file
82
Source/Engine/Graphics/PixelFormatSampler.h
Normal file
@@ -0,0 +1,82 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "PixelFormat.h"
|
||||
|
||||
/// <summary>
|
||||
/// Utility for writing and reading from different pixels formats within a single code path.
|
||||
/// </summary>
|
||||
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:
|
||||
/// <summary>
|
||||
/// Stores the color into the specified texture data (uses no interpolation).
|
||||
/// </summary>
|
||||
/// <param name="data">The data pointer for the texture slice (1D or 2D image).</param>
|
||||
/// <param name="x">The X texture coordinates (normalized to range 0-width).</param>
|
||||
/// <param name="y">The Y texture coordinates (normalized to range 0-height).</param>
|
||||
/// <param name="rowPitch">The row pitch (in bytes). The offset between each image rows.</param>
|
||||
/// <param name="color">The color to store (linear).</param>
|
||||
void Store(void* data, int32 x, int32 y, int32 rowPitch, const Color& color) const;
|
||||
|
||||
/// <summary>
|
||||
/// Samples the specified linear data (uses no interpolation).
|
||||
/// </summary>
|
||||
/// <param name="data">The data pointer for the data slice (linear buffer or 1D image).</param>
|
||||
/// <param name="x">Index of the element.</param>
|
||||
/// <returns>The sampled value.</returns>
|
||||
Float4 Sample(const void* data, int32 x) const;
|
||||
|
||||
/// <summary>
|
||||
/// Samples the specified texture data (uses no interpolation).
|
||||
/// </summary>
|
||||
/// <param name="data">The data pointer for the texture slice (1D or 2D image).</param>
|
||||
/// <param name="uv">The texture coordinates (normalized to range 0-1).</param>
|
||||
/// <param name="size">The size of the input texture (in pixels).</param>
|
||||
/// <param name="rowPitch">The row pitch (in bytes). The offset between each image rows.</param>
|
||||
/// <returns>The sampled color (linear).</returns>
|
||||
Color SamplePoint(const void* data, const Float2& uv, const Int2& size, int32 rowPitch) const;
|
||||
|
||||
/// <summary>
|
||||
/// Samples the specified texture data (uses no interpolation).
|
||||
/// </summary>
|
||||
/// <param name="data">The data pointer for the texture slice (1D or 2D image).</param>
|
||||
/// <param name="x">The X texture coordinates (normalized to range 0-width).</param>
|
||||
/// <param name="y">The Y texture coordinates (normalized to range 0-height).</param>
|
||||
/// <param name="rowPitch">The row pitch (in bytes). The offset between each image rows.</param>
|
||||
/// <returns>The sampled color (linear).</returns>
|
||||
Color SamplePoint(const void* data, int32 x, int32 y, int32 rowPitch) const;
|
||||
|
||||
/// <summary>
|
||||
/// Samples the specified texture data (uses linear interpolation).
|
||||
/// </summary>
|
||||
/// <param name="data">The data pointer for the texture slice (1D or 2D image).</param>
|
||||
/// <param name="uv">The texture coordinates (normalized to range 0-1).</param>
|
||||
/// <param name="size">The size of the input texture (in pixels).</param>
|
||||
/// <param name="rowPitch">The row pitch (in bytes). The offset between each image rows.</param>
|
||||
/// <returns>The sampled color (linear).</returns>
|
||||
Color SampleLinear(const void* data, const Float2& uv, const Int2& size, int32 rowPitch) const;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Tries to get a sampler tool for the specified format to read pixels.
|
||||
/// </summary>
|
||||
/// <param name="format">The format.</param>
|
||||
/// <returns>The pointer to the sampler object or null when cannot sample the given format.</returns>
|
||||
static const PixelFormatSampler* Get(PixelFormat format);
|
||||
};
|
||||
@@ -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<Color32>& 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<Color>& 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<Color32>& 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<Color32>& 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<Color>& 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<Color>& 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;
|
||||
|
||||
@@ -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<void(Color&)>& 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<void(Color&)>&
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this tool can sample the specified format to read texture samples and returns the sampler object.
|
||||
/// </summary>
|
||||
/// <param name="format">The format.</param>
|
||||
/// <returns>The pointer to the sampler object or null if cannot sample the given format.</returns>
|
||||
static const PixelFormatSampler* GetSampler(PixelFormat format);
|
||||
|
||||
/// <summary>
|
||||
/// Stores the color into the specified texture data (uses no interpolation).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Use GetSampler for the texture sampler.
|
||||
/// </remarks>
|
||||
/// <param name="sampler">The texture data sampler.</param>
|
||||
/// <param name="x">The X texture coordinates (normalized to range 0-width).</param>
|
||||
/// <param name="y">The Y texture coordinates (normalized to range 0-height).</param>
|
||||
/// <param name="data">The data pointer for the texture slice (1D or 2D image).</param>
|
||||
/// <param name="rowPitch">The row pitch (in bytes). The offset between each image rows.</param>
|
||||
/// <param name="color">The color to store (linear).</param>
|
||||
static void Store(const PixelFormatSampler* sampler, int32 x, int32 y, const void* data, int32 rowPitch, const Color& color);
|
||||
|
||||
/// <summary>
|
||||
/// Samples the specified texture data (uses no interpolation).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Use GetSampler for the texture sampler.
|
||||
/// </remarks>
|
||||
/// <param name="sampler">The texture data sampler.</param>
|
||||
/// <param name="uv">The texture coordinates (normalized to range 0-1).</param>
|
||||
/// <param name="data">The data pointer for the texture slice (1D or 2D image).</param>
|
||||
/// <param name="size">The size of the input texture (in pixels).</param>
|
||||
/// <param name="rowPitch">The row pitch (in bytes). The offset between each image rows.</param>
|
||||
/// <returns>The sampled color (linear).</returns>
|
||||
static Color SamplePoint(const PixelFormatSampler* sampler, const Float2& uv, const void* data, const Int2& size, int32 rowPitch);
|
||||
|
||||
/// <summary>
|
||||
/// Samples the specified texture data (uses no interpolation).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Use GetSampler for the texture sampler.
|
||||
/// </remarks>
|
||||
/// <param name="sampler">The texture data sampler.</param>
|
||||
/// <param name="x">The X texture coordinates (normalized to range 0-width).</param>
|
||||
/// <param name="y">The Y texture coordinates (normalized to range 0-height).</param>
|
||||
/// <param name="data">The data pointer for the texture slice (1D or 2D image).</param>
|
||||
/// <param name="rowPitch">The row pitch (in bytes). The offset between each image rows.</param>
|
||||
/// <returns>The sampled color (linear).</returns>
|
||||
static Color SamplePoint(const PixelFormatSampler* sampler, int32 x, int32 y, const void* data, int32 rowPitch);
|
||||
|
||||
/// <summary>
|
||||
/// Samples the specified texture data (uses linear interpolation).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Use GetSampler for the texture sampler.
|
||||
/// </remarks>
|
||||
/// <param name="sampler">The texture data sampler.</param>
|
||||
/// <param name="uv">The texture coordinates (normalized to range 0-1).</param>
|
||||
/// <param name="data">The data pointer for the texture slice (1D or 2D image).</param>
|
||||
/// <param name="size">The size of the input texture (in pixels).</param>
|
||||
/// <param name="rowPitch">The row pitch (in bytes). The offset between each image rows.</param>
|
||||
/// <returns>The sampled color (linear).</returns>
|
||||
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:
|
||||
|
||||
Reference in New Issue
Block a user