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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user