mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2025-06-11 15:28:11 -05:00
refactor: extract image code into single component
This commit is contained in:
112
src/ObjImage/Image/D3DFormat.h
Normal file
112
src/ObjImage/Image/D3DFormat.h
Normal file
@ -0,0 +1,112 @@
|
||||
#pragma once
|
||||
|
||||
#include "Utils/FileUtils.h"
|
||||
|
||||
// This is a redefinition to make this enum available on platforms without DirectX headers
|
||||
namespace oat
|
||||
{
|
||||
enum D3DFORMAT
|
||||
{
|
||||
D3DFMT_UNKNOWN = 0,
|
||||
|
||||
D3DFMT_R8G8B8 = 20,
|
||||
D3DFMT_A8R8G8B8 = 21,
|
||||
D3DFMT_X8R8G8B8 = 22,
|
||||
D3DFMT_R5G6B5 = 23,
|
||||
D3DFMT_X1R5G5B5 = 24,
|
||||
D3DFMT_A1R5G5B5 = 25,
|
||||
D3DFMT_A4R4G4B4 = 26,
|
||||
D3DFMT_R3G3B2 = 27,
|
||||
D3DFMT_A8 = 28,
|
||||
D3DFMT_A8R3G3B2 = 29,
|
||||
D3DFMT_X4R4G4B4 = 30,
|
||||
D3DFMT_A2B10G10R10 = 31,
|
||||
D3DFMT_A8B8G8R8 = 32,
|
||||
D3DFMT_X8B8G8R8 = 33,
|
||||
D3DFMT_G16R16 = 34,
|
||||
D3DFMT_A2R10G10B10 = 35,
|
||||
D3DFMT_A16B16G16R16 = 36,
|
||||
|
||||
D3DFMT_A8P8 = 40,
|
||||
D3DFMT_P8 = 41,
|
||||
|
||||
D3DFMT_L8 = 50,
|
||||
D3DFMT_A8L8 = 51,
|
||||
D3DFMT_A4L4 = 52,
|
||||
|
||||
D3DFMT_V8U8 = 60,
|
||||
D3DFMT_L6V5U5 = 61,
|
||||
D3DFMT_X8L8V8U8 = 62,
|
||||
D3DFMT_Q8W8V8U8 = 63,
|
||||
D3DFMT_V16U16 = 64,
|
||||
D3DFMT_A2W10V10U10 = 67,
|
||||
|
||||
D3DFMT_UYVY = FileUtils::MakeMagic32('U', 'Y', 'V', 'Y'),
|
||||
D3DFMT_R8G8_B8G8 = FileUtils::MakeMagic32('R', 'G', 'B', 'G'),
|
||||
D3DFMT_YUY2 = FileUtils::MakeMagic32('Y', 'U', 'Y', '2'),
|
||||
D3DFMT_G8R8_G8B8 = FileUtils::MakeMagic32('G', 'R', 'G', 'B'),
|
||||
D3DFMT_DXT1 = FileUtils::MakeMagic32('D', 'X', 'T', '1'),
|
||||
D3DFMT_DXT2 = FileUtils::MakeMagic32('D', 'X', 'T', '2'),
|
||||
D3DFMT_DXT3 = FileUtils::MakeMagic32('D', 'X', 'T', '3'),
|
||||
D3DFMT_DXT4 = FileUtils::MakeMagic32('D', 'X', 'T', '4'),
|
||||
D3DFMT_DXT5 = FileUtils::MakeMagic32('D', 'X', 'T', '5'),
|
||||
|
||||
D3DFMT_D16_LOCKABLE = 70,
|
||||
D3DFMT_D32 = 71,
|
||||
D3DFMT_D15S1 = 73,
|
||||
D3DFMT_D24S8 = 75,
|
||||
D3DFMT_D24X8 = 77,
|
||||
D3DFMT_D24X4S4 = 79,
|
||||
D3DFMT_D16 = 80,
|
||||
|
||||
D3DFMT_D32F_LOCKABLE = 82,
|
||||
D3DFMT_D24FS8 = 83,
|
||||
|
||||
/* D3D9Ex only -- */
|
||||
|
||||
/* Z-Stencil formats valid for CPU access */
|
||||
D3DFMT_D32_LOCKABLE = 84,
|
||||
D3DFMT_S8_LOCKABLE = 85,
|
||||
|
||||
/* -- D3D9Ex only */
|
||||
|
||||
D3DFMT_L16 = 81,
|
||||
|
||||
D3DFMT_VERTEXDATA = 100,
|
||||
D3DFMT_INDEX16 = 101,
|
||||
D3DFMT_INDEX32 = 102,
|
||||
|
||||
D3DFMT_Q16W16V16U16 = 110,
|
||||
|
||||
D3DFMT_MULTI2_ARGB8 = FileUtils::MakeMagic32('M', 'E', 'T', '1'),
|
||||
|
||||
// Floating point surface formats
|
||||
|
||||
// s10e5 formats (16-bits per channel)
|
||||
D3DFMT_R16F = 111,
|
||||
D3DFMT_G16R16F = 112,
|
||||
D3DFMT_A16B16G16R16F = 113,
|
||||
|
||||
// IEEE s23e8 formats (32-bits per channel)
|
||||
D3DFMT_R32F = 114,
|
||||
D3DFMT_G32R32F = 115,
|
||||
D3DFMT_A32B32G32R32F = 116,
|
||||
|
||||
D3DFMT_CxV8U8 = 117,
|
||||
|
||||
/* D3D9Ex only -- */
|
||||
|
||||
// Monochrome 1 bit per pixel format
|
||||
D3DFMT_A1 = 118,
|
||||
|
||||
// 2.8 biased fixed point
|
||||
D3DFMT_A2B10G10R10_XR_BIAS = 119,
|
||||
|
||||
// Binary format indicating that the data has no inherent type
|
||||
D3DFMT_BINARYBUFFER = 199,
|
||||
|
||||
/* -- D3D9Ex only */
|
||||
|
||||
D3DFMT_FORCE_DWORD = 0x7fffffff
|
||||
};
|
||||
} // namespace oat
|
271
src/ObjImage/Image/DdsLoader.cpp
Normal file
271
src/ObjImage/Image/DdsLoader.cpp
Normal file
@ -0,0 +1,271 @@
|
||||
#include "DdsLoader.h"
|
||||
|
||||
#include "Image/DdsTypes.h"
|
||||
#include "Utils/ClassUtils.h"
|
||||
#include "Utils/FileUtils.h"
|
||||
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
namespace dds
|
||||
{
|
||||
class DdsLoaderInternal
|
||||
{
|
||||
static constexpr auto DDS_MAGIC = FileUtils::MakeMagic32('D', 'D', 'S', ' ');
|
||||
|
||||
std::istream& m_stream;
|
||||
|
||||
TextureType m_texture_type;
|
||||
bool m_has_mip_maps;
|
||||
size_t m_width;
|
||||
size_t m_height;
|
||||
size_t m_depth;
|
||||
const ImageFormat* m_format;
|
||||
|
||||
_NODISCARD bool ReadMagic() const
|
||||
{
|
||||
uint32_t magic;
|
||||
m_stream.read(reinterpret_cast<char*>(&magic), sizeof(magic));
|
||||
if (m_stream.gcount() != sizeof(magic))
|
||||
{
|
||||
std::cerr << "Failed to read dds data\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (magic != DDS_MAGIC)
|
||||
{
|
||||
std::cerr << "Invalid magic for dds\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_NODISCARD bool ReadDxt10Header()
|
||||
{
|
||||
DDS_HEADER_DXT10 headerDx10{};
|
||||
m_stream.read(reinterpret_cast<char*>(&headerDx10), sizeof(headerDx10));
|
||||
if (m_stream.gcount() != sizeof(headerDx10))
|
||||
{
|
||||
std::cerr << "Failed to read dds data\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (headerDx10.resourceDimension == D3D10_RESOURCE_DIMENSION_TEXTURE3D)
|
||||
{
|
||||
m_texture_type = TextureType::T_3D;
|
||||
}
|
||||
else if (headerDx10.resourceDimension == D3D10_RESOURCE_DIMENSION_TEXTURE2D)
|
||||
{
|
||||
if (headerDx10.miscFlag & DDS_RESOURCE_MISC_TEXTURECUBE || headerDx10.arraySize == 6)
|
||||
{
|
||||
m_texture_type = TextureType::T_CUBE;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_texture_type = TextureType::T_2D;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << std::format("Unsupported dds resourceDimension {}\n", static_cast<unsigned>(headerDx10.resourceDimension));
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto* imageFormat : ImageFormat::ALL_FORMATS)
|
||||
{
|
||||
if (imageFormat->GetDxgiFormat() == headerDx10.dxgiFormat)
|
||||
{
|
||||
m_format = imageFormat;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
std::cerr << std::format("Unsupported dds dxgi format {}\n", static_cast<unsigned>(headerDx10.dxgiFormat));
|
||||
return false;
|
||||
}
|
||||
|
||||
_NODISCARD bool ReadPixelFormatFourCc(DDS_PIXELFORMAT& pf)
|
||||
{
|
||||
switch (pf.dwFourCC)
|
||||
{
|
||||
case FileUtils::MakeMagic32('D', 'X', 'T', '1'):
|
||||
m_format = &ImageFormat::FORMAT_BC1;
|
||||
return true;
|
||||
|
||||
case FileUtils::MakeMagic32('D', 'X', 'T', '3'):
|
||||
m_format = &ImageFormat::FORMAT_BC2;
|
||||
return true;
|
||||
|
||||
case FileUtils::MakeMagic32('D', 'X', 'T', '5'):
|
||||
m_format = &ImageFormat::FORMAT_BC3;
|
||||
return true;
|
||||
|
||||
case FileUtils::MakeMagic32('D', 'X', '1', '0'):
|
||||
return ReadDxt10Header();
|
||||
|
||||
default:
|
||||
std::cerr << std::format("Unknown dds FourCC {}\n", pf.dwFourCC);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void ExtractSizeAndOffsetFromMask(uint32_t mask, unsigned& offset, unsigned& size)
|
||||
{
|
||||
offset = 0;
|
||||
size = 0;
|
||||
|
||||
if (mask == 0)
|
||||
return;
|
||||
|
||||
while ((mask & 1) == 0)
|
||||
{
|
||||
offset++;
|
||||
mask >>= 1;
|
||||
}
|
||||
|
||||
while ((mask & 1) == 1)
|
||||
{
|
||||
size++;
|
||||
mask >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
_NODISCARD bool ReadPixelFormatUnsigned(DDS_PIXELFORMAT& pf)
|
||||
{
|
||||
unsigned rOffset, rSize, gOffset, gSize, bOffset, bSize, aOffset, aSize;
|
||||
|
||||
ExtractSizeAndOffsetFromMask(pf.dwRBitMask, rOffset, rSize);
|
||||
ExtractSizeAndOffsetFromMask(pf.dwGBitMask, gOffset, gSize);
|
||||
ExtractSizeAndOffsetFromMask(pf.dwBBitMask, bOffset, bSize);
|
||||
ExtractSizeAndOffsetFromMask(pf.dwABitMask, aOffset, aSize);
|
||||
|
||||
for (const auto* imageFormat : ImageFormat::ALL_FORMATS)
|
||||
{
|
||||
if (imageFormat->GetType() != ImageFormatType::UNSIGNED)
|
||||
continue;
|
||||
|
||||
const auto* unsignedImageFormat = dynamic_cast<const ImageFormatUnsigned*>(imageFormat);
|
||||
|
||||
if (unsignedImageFormat->m_r_offset == rOffset && unsignedImageFormat->m_r_size == rSize && unsignedImageFormat->m_g_offset == gOffset
|
||||
&& unsignedImageFormat->m_g_size == gSize && unsignedImageFormat->m_b_offset == bOffset && unsignedImageFormat->m_b_size == bSize
|
||||
&& unsignedImageFormat->m_a_offset == aOffset && unsignedImageFormat->m_a_size == aSize)
|
||||
{
|
||||
m_format = imageFormat;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
std::cerr << std::format(
|
||||
"Failed to find dds pixel format: R={:#x} G={:#x} B={:#x} A={:#x}\n", pf.dwRBitMask, pf.dwGBitMask, pf.dwBBitMask, pf.dwABitMask);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
_NODISCARD bool ReadPixelFormat(DDS_PIXELFORMAT& pf)
|
||||
{
|
||||
if (pf.dwFlags & DDPF_FOURCC)
|
||||
return ReadPixelFormatFourCc(pf);
|
||||
|
||||
return ReadPixelFormatUnsigned(pf);
|
||||
}
|
||||
|
||||
_NODISCARD bool ReadHeader()
|
||||
{
|
||||
DDS_HEADER header{};
|
||||
m_stream.read(reinterpret_cast<char*>(&header), sizeof(header));
|
||||
if (m_stream.gcount() != sizeof(header))
|
||||
{
|
||||
std::cerr << "Failed to read dds data\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_width = header.dwWidth;
|
||||
m_height = header.dwHeight;
|
||||
m_depth = header.dwDepth;
|
||||
m_has_mip_maps = (header.dwCaps & DDSCAPS_MIPMAP) != 0 || header.dwMipMapCount > 1;
|
||||
|
||||
if (header.dwCaps2 & DDSCAPS2_CUBEMAP)
|
||||
m_texture_type = TextureType::T_CUBE;
|
||||
else if (header.dwDepth > 1)
|
||||
m_texture_type = TextureType::T_3D;
|
||||
else
|
||||
m_texture_type = TextureType::T_2D;
|
||||
|
||||
return ReadPixelFormat(header.ddspf);
|
||||
}
|
||||
|
||||
_NODISCARD std::unique_ptr<Texture> ReadTextureData() const
|
||||
{
|
||||
std::unique_ptr<Texture> result;
|
||||
|
||||
switch (m_texture_type)
|
||||
{
|
||||
case TextureType::T_2D:
|
||||
result = std::make_unique<Texture2D>(m_format, m_width, m_height, m_has_mip_maps);
|
||||
break;
|
||||
|
||||
case TextureType::T_3D:
|
||||
result = std::make_unique<Texture3D>(m_format, m_width, m_height, m_depth, m_has_mip_maps);
|
||||
break;
|
||||
|
||||
case TextureType::T_CUBE:
|
||||
result = std::make_unique<TextureCube>(m_format, m_width, m_height, m_has_mip_maps);
|
||||
break;
|
||||
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto mipMapCount = m_has_mip_maps ? result->GetMipMapCount() : 1;
|
||||
const auto faceCount = m_texture_type == TextureType::T_CUBE ? 6 : 1;
|
||||
|
||||
result->Allocate();
|
||||
|
||||
for (auto mipLevel = 0; mipLevel < mipMapCount; mipLevel++)
|
||||
{
|
||||
const auto mipSize = result->GetSizeOfMipLevel(mipLevel);
|
||||
|
||||
for (auto face = 0; face < faceCount; face++)
|
||||
{
|
||||
m_stream.read(reinterpret_cast<char*>(result->GetBufferForMipLevel(mipLevel, face)), mipSize);
|
||||
|
||||
if (m_stream.gcount() != mipSize)
|
||||
{
|
||||
std::cerr << "Failed to read texture data from dds\n";
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public:
|
||||
explicit DdsLoaderInternal(std::istream& stream)
|
||||
: m_stream(stream),
|
||||
m_texture_type(TextureType::T_2D),
|
||||
m_has_mip_maps(false),
|
||||
m_width(0u),
|
||||
m_height(0u),
|
||||
m_depth(0u),
|
||||
m_format(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<Texture> LoadDds()
|
||||
{
|
||||
if (!ReadMagic() || !ReadHeader())
|
||||
return nullptr;
|
||||
|
||||
return ReadTextureData();
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<Texture> LoadDds(std::istream& stream)
|
||||
{
|
||||
DdsLoaderInternal internal(stream);
|
||||
return internal.LoadDds();
|
||||
}
|
||||
} // namespace dds
|
10
src/ObjImage/Image/DdsLoader.h
Normal file
10
src/ObjImage/Image/DdsLoader.h
Normal file
@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "Image/Texture.h"
|
||||
|
||||
#include <istream>
|
||||
|
||||
namespace dds
|
||||
{
|
||||
std::unique_ptr<Texture> LoadDds(std::istream& stream);
|
||||
}
|
111
src/ObjImage/Image/DdsTypes.h
Normal file
111
src/ObjImage/Image/DdsTypes.h
Normal file
@ -0,0 +1,111 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
constexpr uint32_t MakeFourCc(const char ch0, const char ch1, const char ch2, const char ch3)
|
||||
{
|
||||
return static_cast<uint32_t>(ch0) | static_cast<uint32_t>(ch1) << 8 | static_cast<uint32_t>(ch2) << 16 | static_cast<uint32_t>(ch3) << 24;
|
||||
}
|
||||
|
||||
enum DDP_FLAGS
|
||||
{
|
||||
DDPF_ALPHAPIXELS = 0x1,
|
||||
DDPF_ALPHA = 0x2,
|
||||
DDPF_FOURCC = 0x4,
|
||||
DDPF_RGB = 0x40,
|
||||
DDPF_YUV = 0x200,
|
||||
DDPF_LUMINANCE = 0x20000
|
||||
};
|
||||
|
||||
enum DDS_HEADER_FLAGS
|
||||
{
|
||||
DDSD_CAPS = 0x1,
|
||||
DDSD_HEIGHT = 0x2,
|
||||
DDSD_WIDTH = 0x4,
|
||||
DDSD_PITCH = 0x8,
|
||||
DDSD_PIXELFORMAT = 0x1000,
|
||||
DDSD_MIPMAPCOUNT = 0x20000,
|
||||
DDSD_LINEARSIZE = 0x80000,
|
||||
DDSD_DEPTH = 0x800000,
|
||||
};
|
||||
|
||||
enum DDS_HEADER_CAPS
|
||||
{
|
||||
DDSCAPS_COMPLEX = 0x8,
|
||||
DDSCAPS_TEXTURE = 0x1000,
|
||||
DDSCAPS_MIPMAP = 0x400000,
|
||||
};
|
||||
|
||||
enum DDS_HEADER_CAPS2
|
||||
{
|
||||
DDSCAPS2_CUBEMAP = 0x200,
|
||||
DDSCAPS2_CUBEMAP_POSITIVEX = 0x400,
|
||||
DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800,
|
||||
DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000,
|
||||
DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000,
|
||||
DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000,
|
||||
DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000,
|
||||
DDSCAPS2_VOLUME = 0x200000,
|
||||
};
|
||||
|
||||
struct DDS_PIXELFORMAT
|
||||
{
|
||||
uint32_t dwSize;
|
||||
uint32_t dwFlags;
|
||||
uint32_t dwFourCC;
|
||||
uint32_t dwRGBBitCount;
|
||||
uint32_t dwRBitMask;
|
||||
uint32_t dwGBitMask;
|
||||
uint32_t dwBBitMask;
|
||||
uint32_t dwABitMask;
|
||||
};
|
||||
|
||||
struct DDS_HEADER
|
||||
{
|
||||
uint32_t dwSize;
|
||||
uint32_t dwFlags;
|
||||
uint32_t dwHeight;
|
||||
uint32_t dwWidth;
|
||||
uint32_t dwPitchOrLinearSize;
|
||||
uint32_t dwDepth;
|
||||
uint32_t dwMipMapCount;
|
||||
uint32_t dwReserved1[11];
|
||||
DDS_PIXELFORMAT ddspf;
|
||||
uint32_t dwCaps;
|
||||
uint32_t dwCaps2;
|
||||
uint32_t dwCaps3;
|
||||
uint32_t dwCaps4;
|
||||
uint32_t dwReserved2;
|
||||
};
|
||||
|
||||
enum D3D10_RESOURCE_DIMENSION
|
||||
{
|
||||
D3D10_RESOURCE_DIMENSION_UNKNOWN,
|
||||
D3D10_RESOURCE_DIMENSION_BUFFER,
|
||||
D3D10_RESOURCE_DIMENSION_TEXTURE1D,
|
||||
D3D10_RESOURCE_DIMENSION_TEXTURE2D,
|
||||
D3D10_RESOURCE_DIMENSION_TEXTURE3D
|
||||
};
|
||||
|
||||
enum DDS_HEADER_DXT10_MISC
|
||||
{
|
||||
DDS_RESOURCE_MISC_TEXTURECUBE = 0x4
|
||||
};
|
||||
|
||||
enum DDS_HEADER_DXT10_MISC2
|
||||
{
|
||||
DDS_ALPHA_MODE_UNKNOWN = 0x0,
|
||||
DDS_ALPHA_MODE_STRAIGHT = 0x1,
|
||||
DDS_ALPHA_MODE_PREMULTIPLIED = 0x2,
|
||||
DDS_ALPHA_MODE_OPAQUE = 0x3,
|
||||
DDS_ALPHA_MODE_CUSTOM = 0x4,
|
||||
};
|
||||
|
||||
struct DDS_HEADER_DXT10
|
||||
{
|
||||
oat::DXGI_FORMAT dxgiFormat;
|
||||
D3D10_RESOURCE_DIMENSION resourceDimension;
|
||||
uint32_t miscFlag;
|
||||
uint32_t arraySize;
|
||||
uint32_t miscFlags2;
|
||||
};
|
240
src/ObjImage/Image/DdsWriter.cpp
Normal file
240
src/ObjImage/Image/DdsWriter.cpp
Normal file
@ -0,0 +1,240 @@
|
||||
#include "DdsWriter.h"
|
||||
|
||||
#include "Image/DdsTypes.h"
|
||||
#include "Image/TextureConverter.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
const std::map<ImageFormatId, ImageFormatId> DDS_CONVERSION_TABLE{
|
||||
{ImageFormatId::R8_G8_B8, ImageFormatId::B8_G8_R8_X8},
|
||||
};
|
||||
|
||||
class DdsWriterInternal
|
||||
{
|
||||
public:
|
||||
static bool SupportsImageFormat(const ImageFormat* imageFormat)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string GetFileExtension()
|
||||
{
|
||||
return ".dds";
|
||||
}
|
||||
|
||||
DdsWriterInternal(std::ostream& stream, const Texture* texture)
|
||||
: m_stream(stream),
|
||||
m_texture(texture),
|
||||
m_use_dx10_extension(false)
|
||||
{
|
||||
}
|
||||
|
||||
void DumpImage()
|
||||
{
|
||||
ConvertTextureIfNecessary();
|
||||
|
||||
DDS_HEADER header{};
|
||||
PopulateDdsHeader(header);
|
||||
|
||||
constexpr auto magic = MakeFourCc('D', 'D', 'S', ' ');
|
||||
|
||||
m_stream.write(reinterpret_cast<const char*>(&magic), sizeof(magic));
|
||||
m_stream.write(reinterpret_cast<const char*>(&header), sizeof(header));
|
||||
|
||||
if (m_use_dx10_extension)
|
||||
{
|
||||
DDS_HEADER_DXT10 dxt10{};
|
||||
PopulateDxt10Header(dxt10);
|
||||
m_stream.write(reinterpret_cast<const char*>(&dxt10), sizeof(dxt10));
|
||||
}
|
||||
|
||||
const auto mipCount = m_texture->HasMipMaps() ? m_texture->GetMipMapCount() : 1;
|
||||
for (auto mipLevel = 0; mipLevel < mipCount; mipLevel++)
|
||||
{
|
||||
const auto* buffer = m_texture->GetBufferForMipLevel(mipLevel);
|
||||
const auto mipLevelSize = m_texture->GetSizeOfMipLevel(mipLevel) * m_texture->GetFaceCount();
|
||||
m_stream.write(reinterpret_cast<const char*>(buffer), mipLevelSize);
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr unsigned Mask1(const unsigned length)
|
||||
{
|
||||
if (length >= sizeof(unsigned) * 8)
|
||||
return UINT32_MAX;
|
||||
|
||||
return UINT32_MAX >> (sizeof(unsigned) * 8 - length);
|
||||
}
|
||||
|
||||
void PopulatePixelFormatBlockCompressed(DDS_PIXELFORMAT& pf, const ImageFormatBlockCompressed* format)
|
||||
{
|
||||
pf.dwSize = sizeof(DDS_PIXELFORMAT);
|
||||
pf.dwFlags = DDPF_FOURCC;
|
||||
pf.dwRGBBitCount = 0;
|
||||
pf.dwRBitMask = 0;
|
||||
pf.dwGBitMask = 0;
|
||||
pf.dwBBitMask = 0;
|
||||
pf.dwABitMask = 0;
|
||||
|
||||
// Use standard pixel format for DXT1-5 for maximum compatibility and only otherwise use DX10 extension
|
||||
switch (format->GetDxgiFormat())
|
||||
{
|
||||
case oat::DXGI_FORMAT_BC1_UNORM:
|
||||
pf.dwFourCC = MakeFourCc('D', 'X', 'T', '1');
|
||||
break;
|
||||
case oat::DXGI_FORMAT_BC2_UNORM:
|
||||
pf.dwFourCC = MakeFourCc('D', 'X', 'T', '3');
|
||||
break;
|
||||
case oat::DXGI_FORMAT_BC3_UNORM:
|
||||
pf.dwFourCC = MakeFourCc('D', 'X', 'T', '5');
|
||||
break;
|
||||
default:
|
||||
m_use_dx10_extension = true;
|
||||
pf.dwFourCC = MakeFourCc('D', 'X', '1', '0');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void PopulatePixelFormatUnsigned(DDS_PIXELFORMAT& pf, const ImageFormatUnsigned* format)
|
||||
{
|
||||
pf.dwSize = sizeof(DDS_PIXELFORMAT);
|
||||
pf.dwFourCC = 0;
|
||||
pf.dwRGBBitCount = format->m_bits_per_pixel;
|
||||
pf.dwRBitMask = format->HasR() ? Mask1(format->m_r_size) << format->m_r_offset : 0;
|
||||
pf.dwGBitMask = format->HasG() ? Mask1(format->m_g_size) << format->m_g_offset : 0;
|
||||
pf.dwBBitMask = format->HasB() ? Mask1(format->m_b_size) << format->m_b_offset : 0;
|
||||
pf.dwABitMask = format->HasA() ? Mask1(format->m_a_size) << format->m_a_offset : 0;
|
||||
|
||||
pf.dwFlags = 0;
|
||||
if (format->HasA())
|
||||
pf.dwFlags |= DDPF_ALPHAPIXELS;
|
||||
|
||||
if (format->HasR() && !format->HasG() && !format->HasB())
|
||||
pf.dwFlags |= DDPF_LUMINANCE;
|
||||
else
|
||||
pf.dwFlags |= DDPF_RGB;
|
||||
}
|
||||
|
||||
void PopulatePixelFormat(DDS_PIXELFORMAT& pf)
|
||||
{
|
||||
const auto* format = m_texture->GetFormat();
|
||||
|
||||
switch (format->GetType())
|
||||
{
|
||||
case ImageFormatType::BLOCK_COMPRESSED:
|
||||
PopulatePixelFormatBlockCompressed(pf, dynamic_cast<const ImageFormatBlockCompressed*>(format));
|
||||
break;
|
||||
case ImageFormatType::UNSIGNED:
|
||||
PopulatePixelFormatUnsigned(pf, dynamic_cast<const ImageFormatUnsigned*>(format));
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void PopulateDdsHeader(DDS_HEADER& header)
|
||||
{
|
||||
header.dwSize = sizeof(header);
|
||||
header.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
|
||||
|
||||
if (m_texture->HasMipMaps())
|
||||
header.dwFlags |= DDSD_MIPMAPCOUNT;
|
||||
|
||||
if (m_texture->GetFormat()->GetType() == ImageFormatType::BLOCK_COMPRESSED)
|
||||
header.dwFlags |= DDSD_LINEARSIZE;
|
||||
else
|
||||
header.dwFlags |= DDSD_PITCH;
|
||||
|
||||
if (m_texture->GetDepth() > 1)
|
||||
header.dwFlags |= DDSD_DEPTH;
|
||||
|
||||
header.dwHeight = m_texture->GetHeight();
|
||||
header.dwWidth = m_texture->GetWidth();
|
||||
header.dwDepth = m_texture->GetDepth();
|
||||
header.dwPitchOrLinearSize = m_texture->GetFormat()->GetPitch(0, m_texture->GetWidth());
|
||||
header.dwMipMapCount = m_texture->HasMipMaps() ? m_texture->GetMipMapCount() : 1;
|
||||
|
||||
PopulatePixelFormat(header.ddspf);
|
||||
|
||||
header.dwCaps = DDSCAPS_TEXTURE;
|
||||
|
||||
if (m_texture->HasMipMaps())
|
||||
header.dwCaps |= DDSCAPS_COMPLEX | DDSCAPS_MIPMAP;
|
||||
|
||||
if (m_texture->GetTextureType() == TextureType::T_CUBE)
|
||||
header.dwCaps |= DDSCAPS_COMPLEX;
|
||||
|
||||
header.dwCaps2 = 0;
|
||||
|
||||
if (m_texture->GetTextureType() == TextureType::T_CUBE)
|
||||
{
|
||||
header.dwCaps2 |= DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEX | DDSCAPS2_CUBEMAP_NEGATIVEX | DDSCAPS2_CUBEMAP_POSITIVEY
|
||||
| DDSCAPS2_CUBEMAP_NEGATIVEY | DDSCAPS2_CUBEMAP_POSITIVEZ | DDSCAPS2_CUBEMAP_NEGATIVEZ;
|
||||
}
|
||||
|
||||
header.dwCaps3 = 0;
|
||||
header.dwCaps4 = 0;
|
||||
header.dwReserved2 = 0;
|
||||
}
|
||||
|
||||
void PopulateDxt10Header(DDS_HEADER_DXT10& header) const
|
||||
{
|
||||
header.dxgiFormat = m_texture->GetFormat()->GetDxgiFormat();
|
||||
header.miscFlag = 0;
|
||||
header.miscFlags2 = 0;
|
||||
|
||||
switch (m_texture->GetTextureType())
|
||||
{
|
||||
case TextureType::T_2D:
|
||||
header.resourceDimension = D3D10_RESOURCE_DIMENSION_TEXTURE2D;
|
||||
header.arraySize = 1;
|
||||
break;
|
||||
case TextureType::T_CUBE:
|
||||
header.resourceDimension = D3D10_RESOURCE_DIMENSION_TEXTURE2D;
|
||||
header.arraySize = 6;
|
||||
header.miscFlag |= DDS_RESOURCE_MISC_TEXTURECUBE;
|
||||
break;
|
||||
case TextureType::T_3D:
|
||||
header.resourceDimension = D3D10_RESOURCE_DIMENSION_TEXTURE3D;
|
||||
header.arraySize = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ConvertTextureIfNecessary()
|
||||
{
|
||||
const auto entry = DDS_CONVERSION_TABLE.find(m_texture->GetFormat()->GetId());
|
||||
|
||||
if (entry != DDS_CONVERSION_TABLE.end())
|
||||
{
|
||||
TextureConverter converter(m_texture, ImageFormat::ALL_FORMATS[static_cast<unsigned>(entry->second)]);
|
||||
m_converted_texture = converter.Convert();
|
||||
m_texture = m_converted_texture.get();
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream& m_stream;
|
||||
const Texture* m_texture;
|
||||
std::unique_ptr<Texture> m_converted_texture;
|
||||
bool m_use_dx10_extension;
|
||||
};
|
||||
|
||||
DdsWriter::~DdsWriter() = default;
|
||||
|
||||
bool DdsWriter::SupportsImageFormat(const ImageFormat* imageFormat)
|
||||
{
|
||||
return DdsWriterInternal::SupportsImageFormat(imageFormat);
|
||||
}
|
||||
|
||||
std::string DdsWriter::GetFileExtension()
|
||||
{
|
||||
return DdsWriterInternal::GetFileExtension();
|
||||
}
|
||||
|
||||
void DdsWriter::DumpImage(std::ostream& stream, const Texture* texture)
|
||||
{
|
||||
DdsWriterInternal internal(stream, texture);
|
||||
internal.DumpImage();
|
||||
}
|
12
src/ObjImage/Image/DdsWriter.h
Normal file
12
src/ObjImage/Image/DdsWriter.h
Normal file
@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include "IImageWriter.h"
|
||||
|
||||
class DdsWriter final : public IImageWriter
|
||||
{
|
||||
public:
|
||||
~DdsWriter() override;
|
||||
|
||||
bool SupportsImageFormat(const ImageFormat* imageFormat) override;
|
||||
std::string GetFileExtension() override;
|
||||
void DumpImage(std::ostream& stream, const Texture* texture) override;
|
||||
};
|
104
src/ObjImage/Image/Dx12TextureLoader.cpp
Normal file
104
src/ObjImage/Image/Dx12TextureLoader.cpp
Normal file
@ -0,0 +1,104 @@
|
||||
#include "Dx12TextureLoader.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
Dx12TextureLoader::Dx12TextureLoader()
|
||||
: m_format(oat::DXGI_FORMAT_UNKNOWN),
|
||||
m_type(TextureType::T_2D),
|
||||
m_has_mip_maps(false),
|
||||
m_width(1u),
|
||||
m_height(1u),
|
||||
m_depth(1u)
|
||||
{
|
||||
}
|
||||
|
||||
const ImageFormat* Dx12TextureLoader::GetFormatForDx12Format() const
|
||||
{
|
||||
for (auto i : ImageFormat::ALL_FORMATS)
|
||||
{
|
||||
if (i->GetDxgiFormat() == m_format)
|
||||
return i;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Dx12TextureLoader& Dx12TextureLoader::Format(const oat::DXGI_FORMAT format)
|
||||
{
|
||||
m_format = format;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Dx12TextureLoader& Dx12TextureLoader::Type(const TextureType textureType)
|
||||
{
|
||||
m_type = textureType;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Dx12TextureLoader& Dx12TextureLoader::HasMipMaps(const bool hasMipMaps)
|
||||
{
|
||||
m_has_mip_maps = hasMipMaps;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Dx12TextureLoader& Dx12TextureLoader::Width(const size_t width)
|
||||
{
|
||||
m_width = width;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Dx12TextureLoader& Dx12TextureLoader::Height(const size_t height)
|
||||
{
|
||||
m_height = height;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Dx12TextureLoader& Dx12TextureLoader::Depth(const size_t depth)
|
||||
{
|
||||
m_depth = depth;
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::unique_ptr<Texture> Dx12TextureLoader::LoadTexture(const void* data)
|
||||
{
|
||||
const auto* format = GetFormatForDx12Format();
|
||||
|
||||
if (format == nullptr)
|
||||
return nullptr;
|
||||
|
||||
std::unique_ptr<Texture> texture;
|
||||
switch (m_type)
|
||||
{
|
||||
case TextureType::T_2D:
|
||||
texture = std::make_unique<Texture2D>(format, m_width, m_height, m_has_mip_maps);
|
||||
break;
|
||||
|
||||
case TextureType::T_3D:
|
||||
texture = std::make_unique<Texture3D>(format, m_width, m_height, m_depth, m_has_mip_maps);
|
||||
break;
|
||||
|
||||
case TextureType::T_CUBE:
|
||||
texture = std::make_unique<TextureCube>(format, m_width, m_width, m_has_mip_maps);
|
||||
break;
|
||||
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
texture->Allocate();
|
||||
const auto mipMapCount = m_has_mip_maps ? texture->GetMipMapCount() : 1;
|
||||
const auto faceCount = m_type == TextureType::T_CUBE ? 6 : 1;
|
||||
const void* currentDataOffset = data;
|
||||
|
||||
for (auto currentMipLevel = 0; currentMipLevel < mipMapCount; currentMipLevel++)
|
||||
{
|
||||
for (auto currentFace = 0; currentFace < faceCount; currentFace++)
|
||||
{
|
||||
const auto mipSize = texture->GetSizeOfMipLevel(currentMipLevel);
|
||||
memcpy(texture->GetBufferForMipLevel(currentMipLevel, currentFace), currentDataOffset, mipSize);
|
||||
currentDataOffset = reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(currentDataOffset) + mipSize);
|
||||
}
|
||||
}
|
||||
|
||||
return texture;
|
||||
}
|
36
src/ObjImage/Image/Dx12TextureLoader.h
Normal file
36
src/ObjImage/Image/Dx12TextureLoader.h
Normal file
@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include "Image/DxgiFormat.h"
|
||||
#include "Image/Texture.h"
|
||||
#include "Utils/ClassUtils.h"
|
||||
#include "Utils/MemoryManager.h"
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
class Dx12TextureLoader
|
||||
{
|
||||
public:
|
||||
Dx12TextureLoader();
|
||||
|
||||
Dx12TextureLoader& Format(oat::DXGI_FORMAT format);
|
||||
Dx12TextureLoader& Type(TextureType textureType);
|
||||
Dx12TextureLoader& HasMipMaps(bool hasMipMaps);
|
||||
Dx12TextureLoader& Width(size_t width);
|
||||
Dx12TextureLoader& Height(size_t height);
|
||||
Dx12TextureLoader& Depth(size_t depth);
|
||||
|
||||
std::unique_ptr<Texture> LoadTexture(const void* data);
|
||||
|
||||
private:
|
||||
_NODISCARD const ImageFormat* GetFormatForDx12Format() const;
|
||||
|
||||
static std::unordered_map<ImageFormatId, ImageFormatId> m_conversion_table;
|
||||
|
||||
oat::DXGI_FORMAT m_format;
|
||||
TextureType m_type;
|
||||
bool m_has_mip_maps;
|
||||
size_t m_width;
|
||||
size_t m_height;
|
||||
size_t m_depth;
|
||||
};
|
104
src/ObjImage/Image/Dx9TextureLoader.cpp
Normal file
104
src/ObjImage/Image/Dx9TextureLoader.cpp
Normal file
@ -0,0 +1,104 @@
|
||||
#include "Dx9TextureLoader.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
Dx9TextureLoader::Dx9TextureLoader()
|
||||
: m_format(oat::D3DFMT_UNKNOWN),
|
||||
m_type(TextureType::T_2D),
|
||||
m_has_mip_maps(false),
|
||||
m_width(1u),
|
||||
m_height(1u),
|
||||
m_depth(1u)
|
||||
{
|
||||
}
|
||||
|
||||
const ImageFormat* Dx9TextureLoader::GetFormatForDx9Format() const
|
||||
{
|
||||
for (const auto* i : ImageFormat::ALL_FORMATS)
|
||||
{
|
||||
if (i->GetD3DFormat() == m_format)
|
||||
return i;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Dx9TextureLoader& Dx9TextureLoader::Format(const oat::D3DFORMAT format)
|
||||
{
|
||||
m_format = format;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Dx9TextureLoader& Dx9TextureLoader::Type(const TextureType textureType)
|
||||
{
|
||||
m_type = textureType;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Dx9TextureLoader& Dx9TextureLoader::HasMipMaps(const bool hasMipMaps)
|
||||
{
|
||||
m_has_mip_maps = hasMipMaps;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Dx9TextureLoader& Dx9TextureLoader::Width(const size_t width)
|
||||
{
|
||||
m_width = width;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Dx9TextureLoader& Dx9TextureLoader::Height(const size_t height)
|
||||
{
|
||||
m_height = height;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Dx9TextureLoader& Dx9TextureLoader::Depth(const size_t depth)
|
||||
{
|
||||
m_depth = depth;
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::unique_ptr<Texture> Dx9TextureLoader::LoadTexture(const void* data)
|
||||
{
|
||||
const auto* format = GetFormatForDx9Format();
|
||||
|
||||
if (format == nullptr)
|
||||
return nullptr;
|
||||
|
||||
std::unique_ptr<Texture> texture;
|
||||
switch (m_type)
|
||||
{
|
||||
case TextureType::T_2D:
|
||||
texture = std::make_unique<Texture2D>(format, m_width, m_height, m_has_mip_maps);
|
||||
break;
|
||||
|
||||
case TextureType::T_3D:
|
||||
texture = std::make_unique<Texture3D>(format, m_width, m_height, m_depth, m_has_mip_maps);
|
||||
break;
|
||||
|
||||
case TextureType::T_CUBE:
|
||||
texture = std::make_unique<TextureCube>(format, m_width, m_width, m_has_mip_maps);
|
||||
break;
|
||||
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
texture->Allocate();
|
||||
const auto mipMapCount = m_has_mip_maps ? texture->GetMipMapCount() : 1;
|
||||
const auto faceCount = m_type == TextureType::T_CUBE ? 6 : 1;
|
||||
const void* currentDataOffset = data;
|
||||
|
||||
for (auto currentMipLevel = 0; currentMipLevel < mipMapCount; currentMipLevel++)
|
||||
{
|
||||
for (auto currentFace = 0; currentFace < faceCount; currentFace++)
|
||||
{
|
||||
const auto mipSize = texture->GetSizeOfMipLevel(currentMipLevel);
|
||||
memcpy(texture->GetBufferForMipLevel(currentMipLevel, currentFace), currentDataOffset, mipSize);
|
||||
currentDataOffset = reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(currentDataOffset) + mipSize);
|
||||
}
|
||||
}
|
||||
|
||||
return texture;
|
||||
}
|
34
src/ObjImage/Image/Dx9TextureLoader.h
Normal file
34
src/ObjImage/Image/Dx9TextureLoader.h
Normal file
@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include "Image/D3DFormat.h"
|
||||
#include "Image/Texture.h"
|
||||
#include "Utils/ClassUtils.h"
|
||||
#include "Utils/MemoryManager.h"
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
class Dx9TextureLoader
|
||||
{
|
||||
public:
|
||||
Dx9TextureLoader();
|
||||
|
||||
Dx9TextureLoader& Format(oat::D3DFORMAT format);
|
||||
Dx9TextureLoader& Type(TextureType textureType);
|
||||
Dx9TextureLoader& HasMipMaps(bool hasMipMaps);
|
||||
Dx9TextureLoader& Width(size_t width);
|
||||
Dx9TextureLoader& Height(size_t height);
|
||||
Dx9TextureLoader& Depth(size_t depth);
|
||||
|
||||
std::unique_ptr<Texture> LoadTexture(const void* data);
|
||||
|
||||
private:
|
||||
_NODISCARD const ImageFormat* GetFormatForDx9Format() const;
|
||||
|
||||
oat::D3DFORMAT m_format;
|
||||
TextureType m_type;
|
||||
bool m_has_mip_maps;
|
||||
size_t m_width;
|
||||
size_t m_height;
|
||||
size_t m_depth;
|
||||
};
|
152
src/ObjImage/Image/DxgiFormat.h
Normal file
152
src/ObjImage/Image/DxgiFormat.h
Normal file
@ -0,0 +1,152 @@
|
||||
#pragma once
|
||||
|
||||
// Modified for OAT
|
||||
/*
|
||||
* Copyright 2016 Józef Kucia for CodeWeavers
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
namespace oat
|
||||
{
|
||||
const unsigned int DXGI_FORMAT_DEFINED = 1;
|
||||
|
||||
// This is a redefinition to make this enum available on platforms without DirectX headers
|
||||
enum DXGI_FORMAT
|
||||
{
|
||||
DXGI_FORMAT_UNKNOWN = 0x00,
|
||||
DXGI_FORMAT_R32G32B32A32_TYPELESS = 0x01,
|
||||
DXGI_FORMAT_R32G32B32A32_FLOAT = 0x02,
|
||||
DXGI_FORMAT_R32G32B32A32_UINT = 0x03,
|
||||
DXGI_FORMAT_R32G32B32A32_SINT = 0x04,
|
||||
DXGI_FORMAT_R32G32B32_TYPELESS = 0x05,
|
||||
DXGI_FORMAT_R32G32B32_FLOAT = 0x06,
|
||||
DXGI_FORMAT_R32G32B32_UINT = 0x07,
|
||||
DXGI_FORMAT_R32G32B32_SINT = 0x08,
|
||||
DXGI_FORMAT_R16G16B16A16_TYPELESS = 0x09,
|
||||
DXGI_FORMAT_R16G16B16A16_FLOAT = 0x0a,
|
||||
DXGI_FORMAT_R16G16B16A16_UNORM = 0x0b,
|
||||
DXGI_FORMAT_R16G16B16A16_UINT = 0x0c,
|
||||
DXGI_FORMAT_R16G16B16A16_SNORM = 0x0d,
|
||||
DXGI_FORMAT_R16G16B16A16_SINT = 0x0e,
|
||||
DXGI_FORMAT_R32G32_TYPELESS = 0x0f,
|
||||
DXGI_FORMAT_R32G32_FLOAT = 0x10,
|
||||
DXGI_FORMAT_R32G32_UINT = 0x11,
|
||||
DXGI_FORMAT_R32G32_SINT = 0x12,
|
||||
DXGI_FORMAT_R32G8X24_TYPELESS = 0x13,
|
||||
DXGI_FORMAT_D32_FLOAT_S8X24_UINT = 0x14,
|
||||
DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS = 0x15,
|
||||
DXGI_FORMAT_X32_TYPELESS_G8X24_UINT = 0x16,
|
||||
DXGI_FORMAT_R10G10B10A2_TYPELESS = 0x17,
|
||||
DXGI_FORMAT_R10G10B10A2_UNORM = 0x18,
|
||||
DXGI_FORMAT_R10G10B10A2_UINT = 0x19,
|
||||
DXGI_FORMAT_R11G11B10_FLOAT = 0x1a,
|
||||
DXGI_FORMAT_R8G8B8A8_TYPELESS = 0x1b,
|
||||
DXGI_FORMAT_R8G8B8A8_UNORM = 0x1c,
|
||||
DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = 0x1d,
|
||||
DXGI_FORMAT_R8G8B8A8_UINT = 0x1e,
|
||||
DXGI_FORMAT_R8G8B8A8_SNORM = 0x1f,
|
||||
DXGI_FORMAT_R8G8B8A8_SINT = 0x20,
|
||||
DXGI_FORMAT_R16G16_TYPELESS = 0x21,
|
||||
DXGI_FORMAT_R16G16_FLOAT = 0x22,
|
||||
DXGI_FORMAT_R16G16_UNORM = 0x23,
|
||||
DXGI_FORMAT_R16G16_UINT = 0x24,
|
||||
DXGI_FORMAT_R16G16_SNORM = 0x25,
|
||||
DXGI_FORMAT_R16G16_SINT = 0x26,
|
||||
DXGI_FORMAT_R32_TYPELESS = 0x27,
|
||||
DXGI_FORMAT_D32_FLOAT = 0x28,
|
||||
DXGI_FORMAT_R32_FLOAT = 0x29,
|
||||
DXGI_FORMAT_R32_UINT = 0x2a,
|
||||
DXGI_FORMAT_R32_SINT = 0x2b,
|
||||
DXGI_FORMAT_R24G8_TYPELESS = 0x2c,
|
||||
DXGI_FORMAT_D24_UNORM_S8_UINT = 0x2d,
|
||||
DXGI_FORMAT_R24_UNORM_X8_TYPELESS = 0x2e,
|
||||
DXGI_FORMAT_X24_TYPELESS_G8_UINT = 0x2f,
|
||||
DXGI_FORMAT_R8G8_TYPELESS = 0x30,
|
||||
DXGI_FORMAT_R8G8_UNORM = 0x31,
|
||||
DXGI_FORMAT_R8G8_UINT = 0x32,
|
||||
DXGI_FORMAT_R8G8_SNORM = 0x33,
|
||||
DXGI_FORMAT_R8G8_SINT = 0x34,
|
||||
DXGI_FORMAT_R16_TYPELESS = 0x35,
|
||||
DXGI_FORMAT_R16_FLOAT = 0x36,
|
||||
DXGI_FORMAT_D16_UNORM = 0x37,
|
||||
DXGI_FORMAT_R16_UNORM = 0x38,
|
||||
DXGI_FORMAT_R16_UINT = 0x39,
|
||||
DXGI_FORMAT_R16_SNORM = 0x3a,
|
||||
DXGI_FORMAT_R16_SINT = 0x3b,
|
||||
DXGI_FORMAT_R8_TYPELESS = 0x3c,
|
||||
DXGI_FORMAT_R8_UNORM = 0x3d,
|
||||
DXGI_FORMAT_R8_UINT = 0x3e,
|
||||
DXGI_FORMAT_R8_SNORM = 0x3f,
|
||||
DXGI_FORMAT_R8_SINT = 0x40,
|
||||
DXGI_FORMAT_A8_UNORM = 0x41,
|
||||
DXGI_FORMAT_R1_UNORM = 0x42,
|
||||
DXGI_FORMAT_R9G9B9E5_SHAREDEXP = 0x43,
|
||||
DXGI_FORMAT_R8G8_B8G8_UNORM = 0x44,
|
||||
DXGI_FORMAT_G8R8_G8B8_UNORM = 0x45,
|
||||
DXGI_FORMAT_BC1_TYPELESS = 0x46,
|
||||
DXGI_FORMAT_BC1_UNORM = 0x47,
|
||||
DXGI_FORMAT_BC1_UNORM_SRGB = 0x48,
|
||||
DXGI_FORMAT_BC2_TYPELESS = 0x49,
|
||||
DXGI_FORMAT_BC2_UNORM = 0x4a,
|
||||
DXGI_FORMAT_BC2_UNORM_SRGB = 0x4b,
|
||||
DXGI_FORMAT_BC3_TYPELESS = 0x4c,
|
||||
DXGI_FORMAT_BC3_UNORM = 0x4d,
|
||||
DXGI_FORMAT_BC3_UNORM_SRGB = 0x4e,
|
||||
DXGI_FORMAT_BC4_TYPELESS = 0x4f,
|
||||
DXGI_FORMAT_BC4_UNORM = 0x50,
|
||||
DXGI_FORMAT_BC4_SNORM = 0x51,
|
||||
DXGI_FORMAT_BC5_TYPELESS = 0x52,
|
||||
DXGI_FORMAT_BC5_UNORM = 0x53,
|
||||
DXGI_FORMAT_BC5_SNORM = 0x54,
|
||||
DXGI_FORMAT_B5G6R5_UNORM = 0x55,
|
||||
DXGI_FORMAT_B5G5R5A1_UNORM = 0x56,
|
||||
DXGI_FORMAT_B8G8R8A8_UNORM = 0x57,
|
||||
DXGI_FORMAT_B8G8R8X8_UNORM = 0x58,
|
||||
DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM = 0x59,
|
||||
DXGI_FORMAT_B8G8R8A8_TYPELESS = 0x5a,
|
||||
DXGI_FORMAT_B8G8R8A8_UNORM_SRGB = 0x5b,
|
||||
DXGI_FORMAT_B8G8R8X8_TYPELESS = 0x5c,
|
||||
DXGI_FORMAT_B8G8R8X8_UNORM_SRGB = 0x5d,
|
||||
DXGI_FORMAT_BC6H_TYPELESS = 0x5e,
|
||||
DXGI_FORMAT_BC6H_UF16 = 0x5f,
|
||||
DXGI_FORMAT_BC6H_SF16 = 0x60,
|
||||
DXGI_FORMAT_BC7_TYPELESS = 0x61,
|
||||
DXGI_FORMAT_BC7_UNORM = 0x62,
|
||||
DXGI_FORMAT_BC7_UNORM_SRGB = 0x63,
|
||||
DXGI_FORMAT_AYUV = 0x64,
|
||||
DXGI_FORMAT_Y410 = 0x65,
|
||||
DXGI_FORMAT_Y416 = 0x66,
|
||||
DXGI_FORMAT_NV12 = 0x67,
|
||||
DXGI_FORMAT_P010 = 0x68,
|
||||
DXGI_FORMAT_P016 = 0x69,
|
||||
DXGI_FORMAT_420_OPAQUE = 0x6a,
|
||||
DXGI_FORMAT_YUY2 = 0x6b,
|
||||
DXGI_FORMAT_Y210 = 0x6c,
|
||||
DXGI_FORMAT_Y216 = 0x6d,
|
||||
DXGI_FORMAT_NV11 = 0x6e,
|
||||
DXGI_FORMAT_AI44 = 0x6f,
|
||||
DXGI_FORMAT_IA44 = 0x70,
|
||||
DXGI_FORMAT_P8 = 0x71,
|
||||
DXGI_FORMAT_A8P8 = 0x72,
|
||||
DXGI_FORMAT_B4G4R4A4_UNORM = 0x73,
|
||||
|
||||
DXGI_FORMAT_P208 = 0x82,
|
||||
DXGI_FORMAT_V208 = 0x83,
|
||||
DXGI_FORMAT_V408 = 0x84,
|
||||
|
||||
DXGI_FORMAT_FORCE_UINT = 0xffffffff,
|
||||
};
|
||||
} // namespace oat
|
21
src/ObjImage/Image/IImageWriter.h
Normal file
21
src/ObjImage/Image/IImageWriter.h
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "Image/Texture.h"
|
||||
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
|
||||
class IImageWriter
|
||||
{
|
||||
public:
|
||||
IImageWriter() = default;
|
||||
virtual ~IImageWriter() = default;
|
||||
IImageWriter(const IImageWriter& other) = default;
|
||||
IImageWriter(IImageWriter&& other) noexcept = default;
|
||||
IImageWriter& operator=(const IImageWriter& other) = default;
|
||||
IImageWriter& operator=(IImageWriter&& other) noexcept = default;
|
||||
|
||||
virtual bool SupportsImageFormat(const ImageFormat* imageFormat) = 0;
|
||||
virtual std::string GetFileExtension() = 0;
|
||||
virtual void DumpImage(std::ostream& stream, const Texture* texture) = 0;
|
||||
};
|
175
src/ObjImage/Image/ImageFormat.cpp
Normal file
175
src/ObjImage/Image/ImageFormat.cpp
Normal file
@ -0,0 +1,175 @@
|
||||
#include "ImageFormat.h"
|
||||
|
||||
ImageFormat::ImageFormat(const ImageFormatId id, const oat::D3DFORMAT d3dFormat, const oat::DXGI_FORMAT dxgiFormat)
|
||||
: m_id(id),
|
||||
m_d3d_format(d3dFormat),
|
||||
m_dxgi_format(dxgiFormat)
|
||||
{
|
||||
}
|
||||
|
||||
ImageFormatId ImageFormat::GetId() const
|
||||
{
|
||||
return m_id;
|
||||
}
|
||||
|
||||
oat::D3DFORMAT ImageFormat::GetD3DFormat() const
|
||||
{
|
||||
return m_d3d_format;
|
||||
}
|
||||
|
||||
oat::DXGI_FORMAT ImageFormat::GetDxgiFormat() const
|
||||
{
|
||||
return m_dxgi_format;
|
||||
}
|
||||
|
||||
ImageFormatUnsigned::ImageFormatUnsigned(const ImageFormatId id,
|
||||
const oat::D3DFORMAT d3dFormat,
|
||||
const oat::DXGI_FORMAT dxgiFormat,
|
||||
const unsigned bitsPerPixel,
|
||||
const unsigned rOffset,
|
||||
const unsigned rSize,
|
||||
const unsigned gOffset,
|
||||
const unsigned gSize,
|
||||
const unsigned bOffset,
|
||||
const unsigned bSize,
|
||||
const unsigned aOffset,
|
||||
const unsigned aSize)
|
||||
: ImageFormat(id, d3dFormat, dxgiFormat),
|
||||
m_bits_per_pixel(bitsPerPixel),
|
||||
m_r_offset(rOffset),
|
||||
m_r_size(rSize),
|
||||
m_g_offset(gOffset),
|
||||
m_g_size(gSize),
|
||||
m_b_offset(bOffset),
|
||||
m_b_size(bSize),
|
||||
m_a_offset(aOffset),
|
||||
m_a_size(aSize)
|
||||
{
|
||||
}
|
||||
|
||||
ImageFormatType ImageFormatUnsigned::GetType() const
|
||||
{
|
||||
return ImageFormatType::UNSIGNED;
|
||||
}
|
||||
|
||||
size_t ImageFormatUnsigned::GetPitch(const unsigned mipLevel, const unsigned width) const
|
||||
{
|
||||
unsigned mipWidth = width >> mipLevel;
|
||||
if (mipWidth == 0)
|
||||
mipWidth = 1;
|
||||
|
||||
return mipWidth * m_bits_per_pixel / 8;
|
||||
}
|
||||
|
||||
size_t ImageFormatUnsigned::GetSizeOfMipLevel(const unsigned mipLevel, const unsigned width, const unsigned height, const unsigned depth) const
|
||||
{
|
||||
unsigned mipWidth = width >> mipLevel;
|
||||
unsigned mipHeight = height >> mipLevel;
|
||||
unsigned mipDepth = depth >> mipLevel;
|
||||
|
||||
if (mipWidth == 0)
|
||||
mipWidth = 1;
|
||||
if (mipHeight == 0)
|
||||
mipHeight = 1;
|
||||
if (mipDepth == 0)
|
||||
mipDepth = 1;
|
||||
|
||||
return mipWidth * mipHeight * mipDepth * m_bits_per_pixel / 8;
|
||||
}
|
||||
|
||||
ImageFormatBlockCompressed::ImageFormatBlockCompressed(
|
||||
const ImageFormatId id, const oat::D3DFORMAT d3dFormat, const oat::DXGI_FORMAT dxgiFormat, const unsigned blockSize, const unsigned bitsPerBlock)
|
||||
: ImageFormat(id, d3dFormat, dxgiFormat),
|
||||
m_block_size(blockSize),
|
||||
m_bits_per_block(bitsPerBlock)
|
||||
{
|
||||
}
|
||||
|
||||
ImageFormatType ImageFormatBlockCompressed::GetType() const
|
||||
{
|
||||
return ImageFormatType::BLOCK_COMPRESSED;
|
||||
}
|
||||
|
||||
size_t ImageFormatBlockCompressed::GetPitch(const unsigned mipLevel, const unsigned width) const
|
||||
{
|
||||
unsigned mipWidth = width >> mipLevel;
|
||||
|
||||
if (mipWidth == 0)
|
||||
mipWidth = 1;
|
||||
|
||||
const unsigned blockCount = (mipWidth + m_block_size - 1) / m_block_size;
|
||||
|
||||
return blockCount * m_bits_per_block / 8;
|
||||
}
|
||||
|
||||
size_t ImageFormatBlockCompressed::GetSizeOfMipLevel(const unsigned mipLevel, const unsigned width, const unsigned height, const unsigned depth) const
|
||||
{
|
||||
unsigned mipWidth = width >> mipLevel;
|
||||
unsigned mipHeight = height >> mipLevel;
|
||||
unsigned mipDepth = depth >> mipLevel;
|
||||
|
||||
if (mipWidth == 0)
|
||||
mipWidth = 1;
|
||||
if (mipHeight == 0)
|
||||
mipHeight = 1;
|
||||
if (mipDepth == 0)
|
||||
mipDepth = 1;
|
||||
|
||||
const unsigned blockCount = ((mipWidth + m_block_size - 1) / m_block_size) * ((mipHeight + m_block_size - 1) / m_block_size) * mipDepth;
|
||||
|
||||
return blockCount * m_bits_per_block / 8;
|
||||
}
|
||||
|
||||
bool ImageFormatUnsigned::HasR() const
|
||||
{
|
||||
return m_r_size > 0;
|
||||
}
|
||||
|
||||
bool ImageFormatUnsigned::HasG() const
|
||||
{
|
||||
return m_g_size > 0;
|
||||
}
|
||||
|
||||
bool ImageFormatUnsigned::HasB() const
|
||||
{
|
||||
return m_b_size > 0;
|
||||
}
|
||||
|
||||
bool ImageFormatUnsigned::HasA() const
|
||||
{
|
||||
return m_a_size > 0;
|
||||
}
|
||||
|
||||
const ImageFormatUnsigned ImageFormat::FORMAT_R8_G8_B8(ImageFormatId::R8_G8_B8, oat::D3DFMT_R8G8B8, oat::DXGI_FORMAT_UNKNOWN, 24, 0, 8, 8, 8, 16, 8, 0, 0);
|
||||
const ImageFormatUnsigned
|
||||
ImageFormat::FORMAT_B8_G8_R8_X8(ImageFormatId::B8_G8_R8_X8, oat::D3DFMT_X8R8G8B8, oat::DXGI_FORMAT_B8G8R8X8_UNORM, 32, 16, 8, 8, 8, 0, 8, 0, 0);
|
||||
const ImageFormatUnsigned
|
||||
ImageFormat::FORMAT_R8_G8_B8_A8(ImageFormatId::R8_G8_B8_A8, oat::D3DFMT_A8B8G8R8, oat::DXGI_FORMAT_R8G8B8A8_UNORM, 32, 0, 8, 8, 8, 16, 8, 24, 8);
|
||||
const ImageFormatUnsigned
|
||||
ImageFormat::FORMAT_B8_G8_R8_A8(ImageFormatId::B8_G8_R8_A8, oat::D3DFMT_A8R8G8B8, oat::DXGI_FORMAT_B8G8R8A8_UNORM, 32, 16, 8, 8, 8, 0, 8, 24, 8);
|
||||
const ImageFormatUnsigned ImageFormat::FORMAT_A8(ImageFormatId::A8, oat::D3DFMT_A8, oat::DXGI_FORMAT_A8_UNORM, 8, 0, 0, 0, 0, 0, 0, 0, 8);
|
||||
const ImageFormatUnsigned ImageFormat::FORMAT_R16_G16_B16_A16_FLOAT(
|
||||
ImageFormatId::R16_G16_B16_A16_FLOAT, oat::D3DFMT_A16B16G16R16F, oat::DXGI_FORMAT_R16G16B16A16_FLOAT, 128, 0, 0, 0, 0, 0, 0, 0, 8);
|
||||
const ImageFormatUnsigned ImageFormat::FORMAT_R8(ImageFormatId::R8, oat::D3DFMT_L8, oat::DXGI_FORMAT_R8_UNORM, 8, 0, 8, 0, 0, 0, 0, 0, 0);
|
||||
const ImageFormatUnsigned ImageFormat::FORMAT_R8_A8(ImageFormatId::R8_A8, oat::D3DFMT_A8L8, oat::DXGI_FORMAT_UNKNOWN, 16, 0, 8, 0, 0, 0, 0, 8, 8);
|
||||
const ImageFormatBlockCompressed ImageFormat::FORMAT_BC1(ImageFormatId::BC1, oat::D3DFMT_DXT1, oat::DXGI_FORMAT_BC1_UNORM, 4, 64);
|
||||
const ImageFormatBlockCompressed ImageFormat::FORMAT_BC2(ImageFormatId::BC2, oat::D3DFMT_DXT3, oat::DXGI_FORMAT_BC2_UNORM, 4, 128);
|
||||
const ImageFormatBlockCompressed ImageFormat::FORMAT_BC3(ImageFormatId::BC3, oat::D3DFMT_DXT5, oat::DXGI_FORMAT_BC3_UNORM, 4, 128);
|
||||
const ImageFormatBlockCompressed ImageFormat::FORMAT_BC4(ImageFormatId::BC4, oat::D3DFMT_UNKNOWN, oat::DXGI_FORMAT_BC4_UNORM, 4, 64);
|
||||
const ImageFormatBlockCompressed ImageFormat::FORMAT_BC5(ImageFormatId::BC5, oat::D3DFMT_UNKNOWN, oat::DXGI_FORMAT_BC5_UNORM, 4, 128);
|
||||
|
||||
const ImageFormat* const ImageFormat::ALL_FORMATS[static_cast<unsigned>(ImageFormatId::MAX)]{
|
||||
&FORMAT_R8_G8_B8,
|
||||
&FORMAT_B8_G8_R8_X8,
|
||||
&FORMAT_R8_G8_B8_A8,
|
||||
&FORMAT_B8_G8_R8_A8,
|
||||
&FORMAT_A8,
|
||||
&FORMAT_R16_G16_B16_A16_FLOAT,
|
||||
&FORMAT_R8,
|
||||
&FORMAT_R8_A8,
|
||||
&FORMAT_BC1,
|
||||
&FORMAT_BC2,
|
||||
&FORMAT_BC3,
|
||||
&FORMAT_BC4,
|
||||
&FORMAT_BC5,
|
||||
};
|
122
src/ObjImage/Image/ImageFormat.h
Normal file
122
src/ObjImage/Image/ImageFormat.h
Normal file
@ -0,0 +1,122 @@
|
||||
#pragma once
|
||||
|
||||
#include "D3DFormat.h"
|
||||
#include "DxgiFormat.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
enum class ImageFormatId
|
||||
{
|
||||
UNKNOWN = -1,
|
||||
R8_G8_B8,
|
||||
B8_G8_R8_X8,
|
||||
R8_G8_B8_A8,
|
||||
B8_G8_R8_A8,
|
||||
A8,
|
||||
R16_G16_B16_A16_FLOAT,
|
||||
R8,
|
||||
R8_A8,
|
||||
BC1,
|
||||
BC2,
|
||||
BC3,
|
||||
BC4,
|
||||
BC5,
|
||||
|
||||
MAX
|
||||
};
|
||||
|
||||
enum class ImageFormatType
|
||||
{
|
||||
UNKNOWN,
|
||||
UNSIGNED,
|
||||
BLOCK_COMPRESSED
|
||||
};
|
||||
|
||||
class ImageFormatUnsigned;
|
||||
class ImageFormatBlockCompressed;
|
||||
|
||||
class ImageFormat
|
||||
{
|
||||
ImageFormatId m_id;
|
||||
oat::D3DFORMAT m_d3d_format;
|
||||
oat::DXGI_FORMAT m_dxgi_format;
|
||||
|
||||
protected:
|
||||
ImageFormat(ImageFormatId id, oat::D3DFORMAT d3dFormat, oat::DXGI_FORMAT dxgiFormat);
|
||||
|
||||
public:
|
||||
virtual ~ImageFormat() = default;
|
||||
|
||||
ImageFormatId GetId() const;
|
||||
oat::D3DFORMAT GetD3DFormat() const;
|
||||
oat::DXGI_FORMAT GetDxgiFormat() const;
|
||||
|
||||
virtual ImageFormatType GetType() const = 0;
|
||||
virtual size_t GetPitch(unsigned mipLevel, unsigned width) const = 0;
|
||||
virtual size_t GetSizeOfMipLevel(unsigned mipLevel, unsigned width, unsigned height, unsigned depth) const = 0;
|
||||
|
||||
static const ImageFormatUnsigned FORMAT_R8_G8_B8;
|
||||
static const ImageFormatUnsigned FORMAT_B8_G8_R8_X8;
|
||||
static const ImageFormatUnsigned FORMAT_R8_G8_B8_A8;
|
||||
static const ImageFormatUnsigned FORMAT_B8_G8_R8_A8;
|
||||
static const ImageFormatUnsigned FORMAT_A8;
|
||||
static const ImageFormatUnsigned FORMAT_R16_G16_B16_A16_FLOAT; // TODO: Float not unsigned
|
||||
static const ImageFormatUnsigned FORMAT_R8;
|
||||
static const ImageFormatUnsigned FORMAT_R8_A8;
|
||||
static const ImageFormatBlockCompressed FORMAT_BC1;
|
||||
static const ImageFormatBlockCompressed FORMAT_BC2;
|
||||
static const ImageFormatBlockCompressed FORMAT_BC3;
|
||||
static const ImageFormatBlockCompressed FORMAT_BC4;
|
||||
static const ImageFormatBlockCompressed FORMAT_BC5;
|
||||
static const ImageFormat* const ALL_FORMATS[static_cast<unsigned>(ImageFormatId::MAX)];
|
||||
};
|
||||
|
||||
class ImageFormatUnsigned final : public ImageFormat
|
||||
{
|
||||
public:
|
||||
unsigned m_bits_per_pixel;
|
||||
unsigned m_r_offset;
|
||||
unsigned m_r_size;
|
||||
unsigned m_g_offset;
|
||||
unsigned m_g_size;
|
||||
unsigned m_b_offset;
|
||||
unsigned m_b_size;
|
||||
unsigned m_a_offset;
|
||||
unsigned m_a_size;
|
||||
|
||||
ImageFormatUnsigned(ImageFormatId id,
|
||||
oat::D3DFORMAT d3dFormat,
|
||||
oat::DXGI_FORMAT dxgiFormat,
|
||||
unsigned bitsPerPixel,
|
||||
unsigned rOffset,
|
||||
unsigned rSize,
|
||||
unsigned gOffset,
|
||||
unsigned gSize,
|
||||
unsigned bOffset,
|
||||
unsigned bSize,
|
||||
unsigned aOffset,
|
||||
unsigned aSize);
|
||||
|
||||
ImageFormatType GetType() const override;
|
||||
size_t GetPitch(unsigned mipLevel, unsigned width) const override;
|
||||
size_t GetSizeOfMipLevel(unsigned mipLevel, unsigned width, unsigned height, unsigned depth) const override;
|
||||
|
||||
bool HasR() const;
|
||||
bool HasG() const;
|
||||
bool HasB() const;
|
||||
bool HasA() const;
|
||||
};
|
||||
|
||||
class ImageFormatBlockCompressed final : public ImageFormat
|
||||
{
|
||||
public:
|
||||
unsigned m_block_size;
|
||||
unsigned m_bits_per_block;
|
||||
|
||||
ImageFormatBlockCompressed(ImageFormatId id, oat::D3DFORMAT d3dFormat, oat::DXGI_FORMAT dxgiFormat, unsigned blockSize, unsigned bitsPerBlock);
|
||||
|
||||
ImageFormatType GetType() const override;
|
||||
size_t GetPitch(unsigned mipLevel, unsigned width) const override;
|
||||
size_t GetSizeOfMipLevel(unsigned mipLevel, unsigned width, unsigned height, unsigned depth) const override;
|
||||
};
|
443
src/ObjImage/Image/IwiLoader.cpp
Normal file
443
src/ObjImage/Image/IwiLoader.cpp
Normal file
@ -0,0 +1,443 @@
|
||||
#include "IwiLoader.h"
|
||||
|
||||
#include "Image/IwiTypes.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
#include <type_traits>
|
||||
|
||||
namespace iwi
|
||||
{
|
||||
const ImageFormat* GetFormat6(int8_t format)
|
||||
{
|
||||
switch (static_cast<iwi6::IwiFormat>(format))
|
||||
{
|
||||
case iwi6::IwiFormat::IMG_FORMAT_BITMAP_RGBA:
|
||||
return &ImageFormat::FORMAT_R8_G8_B8_A8;
|
||||
case iwi6::IwiFormat::IMG_FORMAT_BITMAP_RGB:
|
||||
return &ImageFormat::FORMAT_R8_G8_B8;
|
||||
case iwi6::IwiFormat::IMG_FORMAT_BITMAP_ALPHA:
|
||||
return &ImageFormat::FORMAT_A8;
|
||||
case iwi6::IwiFormat::IMG_FORMAT_DXT1:
|
||||
return &ImageFormat::FORMAT_BC1;
|
||||
case iwi6::IwiFormat::IMG_FORMAT_DXT3:
|
||||
return &ImageFormat::FORMAT_BC2;
|
||||
case iwi6::IwiFormat::IMG_FORMAT_DXT5:
|
||||
return &ImageFormat::FORMAT_BC3;
|
||||
case iwi6::IwiFormat::IMG_FORMAT_DXN:
|
||||
return &ImageFormat::FORMAT_BC5;
|
||||
case iwi6::IwiFormat::IMG_FORMAT_BITMAP_LUMINANCE_ALPHA:
|
||||
return &ImageFormat::FORMAT_R8_A8;
|
||||
case iwi6::IwiFormat::IMG_FORMAT_BITMAP_LUMINANCE:
|
||||
return &ImageFormat::FORMAT_R8;
|
||||
case iwi6::IwiFormat::IMG_FORMAT_WAVELET_RGBA: // used
|
||||
case iwi6::IwiFormat::IMG_FORMAT_WAVELET_RGB: // used
|
||||
case iwi6::IwiFormat::IMG_FORMAT_WAVELET_LUMINANCE_ALPHA:
|
||||
case iwi6::IwiFormat::IMG_FORMAT_WAVELET_LUMINANCE:
|
||||
case iwi6::IwiFormat::IMG_FORMAT_WAVELET_ALPHA:
|
||||
std::cerr << std::format("Unsupported IWI format: {}\n", format);
|
||||
break;
|
||||
default:
|
||||
std::cerr << std::format("Unknown IWI format: {}\n", format);
|
||||
break;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<Texture> LoadIwi6(std::istream& stream)
|
||||
{
|
||||
iwi6::IwiHeader header{};
|
||||
|
||||
stream.read(reinterpret_cast<char*>(&header), sizeof(header));
|
||||
if (stream.gcount() != sizeof(header))
|
||||
return nullptr;
|
||||
|
||||
const auto* format = GetFormat6(header.format);
|
||||
if (format == nullptr)
|
||||
return nullptr;
|
||||
|
||||
auto width = header.dimensions[0];
|
||||
auto height = header.dimensions[1];
|
||||
auto depth = header.dimensions[2];
|
||||
auto hasMipMaps = !(header.flags & iwi6::IwiFlags::IMG_FLAG_NOMIPMAPS);
|
||||
|
||||
std::unique_ptr<Texture> texture;
|
||||
if (header.flags & iwi6::IwiFlags::IMG_FLAG_CUBEMAP)
|
||||
texture = std::make_unique<TextureCube>(format, width, height, hasMipMaps);
|
||||
else if (header.flags & iwi6::IwiFlags::IMG_FLAG_VOLMAP)
|
||||
texture = std::make_unique<Texture3D>(format, width, height, depth, hasMipMaps);
|
||||
else
|
||||
texture = std::make_unique<Texture2D>(format, width, height, hasMipMaps);
|
||||
|
||||
texture->Allocate();
|
||||
|
||||
auto currentFileSize = sizeof(iwi6::IwiHeader) + sizeof(IwiVersion);
|
||||
const auto mipMapCount = hasMipMaps ? texture->GetMipMapCount() : 1;
|
||||
|
||||
for (auto currentMipLevel = mipMapCount - 1; currentMipLevel >= 0; currentMipLevel--)
|
||||
{
|
||||
const auto sizeOfMipLevel = texture->GetSizeOfMipLevel(currentMipLevel) * texture->GetFaceCount();
|
||||
currentFileSize += sizeOfMipLevel;
|
||||
|
||||
if (currentMipLevel < static_cast<int>(std::extent_v<decltype(iwi6::IwiHeader::fileSizeForPicmip)>)
|
||||
&& currentFileSize != header.fileSizeForPicmip[currentMipLevel])
|
||||
{
|
||||
std::cerr << std::format("Iwi has invalid file size for picmip {}\n", currentMipLevel);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
stream.read(reinterpret_cast<char*>(texture->GetBufferForMipLevel(currentMipLevel)), sizeOfMipLevel);
|
||||
if (stream.gcount() != sizeOfMipLevel)
|
||||
{
|
||||
std::cerr << std::format("Unexpected eof of iwi in mip level {}\n", currentMipLevel);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
const ImageFormat* GetFormat8(int8_t format)
|
||||
{
|
||||
switch (static_cast<iwi8::IwiFormat>(format))
|
||||
{
|
||||
case iwi8::IwiFormat::IMG_FORMAT_BITMAP_RGBA:
|
||||
return &ImageFormat::FORMAT_R8_G8_B8_A8;
|
||||
case iwi8::IwiFormat::IMG_FORMAT_BITMAP_RGB:
|
||||
return &ImageFormat::FORMAT_R8_G8_B8;
|
||||
case iwi8::IwiFormat::IMG_FORMAT_BITMAP_ALPHA:
|
||||
return &ImageFormat::FORMAT_A8;
|
||||
case iwi8::IwiFormat::IMG_FORMAT_DXT1:
|
||||
return &ImageFormat::FORMAT_BC1;
|
||||
case iwi8::IwiFormat::IMG_FORMAT_DXT3:
|
||||
return &ImageFormat::FORMAT_BC2;
|
||||
case iwi8::IwiFormat::IMG_FORMAT_DXT5:
|
||||
return &ImageFormat::FORMAT_BC3;
|
||||
case iwi8::IwiFormat::IMG_FORMAT_DXN:
|
||||
return &ImageFormat::FORMAT_BC5;
|
||||
case iwi8::IwiFormat::IMG_FORMAT_BITMAP_LUMINANCE_ALPHA:
|
||||
return &ImageFormat::FORMAT_R8_A8;
|
||||
case iwi8::IwiFormat::IMG_FORMAT_BITMAP_LUMINANCE:
|
||||
return &ImageFormat::FORMAT_R8;
|
||||
case iwi8::IwiFormat::IMG_FORMAT_WAVELET_RGBA: // used
|
||||
case iwi8::IwiFormat::IMG_FORMAT_WAVELET_RGB: // used
|
||||
case iwi8::IwiFormat::IMG_FORMAT_WAVELET_LUMINANCE_ALPHA:
|
||||
case iwi8::IwiFormat::IMG_FORMAT_WAVELET_LUMINANCE:
|
||||
case iwi8::IwiFormat::IMG_FORMAT_WAVELET_ALPHA:
|
||||
case iwi8::IwiFormat::IMG_FORMAT_DXT3A_AS_LUMINANCE:
|
||||
case iwi8::IwiFormat::IMG_FORMAT_DXT5A_AS_LUMINANCE:
|
||||
case iwi8::IwiFormat::IMG_FORMAT_DXT3A_AS_ALPHA:
|
||||
case iwi8::IwiFormat::IMG_FORMAT_DXT5A_AS_ALPHA:
|
||||
case iwi8::IwiFormat::IMG_FORMAT_DXT1_AS_LUMINANCE_ALPHA:
|
||||
case iwi8::IwiFormat::IMG_FORMAT_DXN_AS_LUMINANCE_ALPHA:
|
||||
case iwi8::IwiFormat::IMG_FORMAT_DXT1_AS_LUMINANCE:
|
||||
case iwi8::IwiFormat::IMG_FORMAT_DXT1_AS_ALPHA:
|
||||
std::cerr << std::format("Unsupported IWI format: {}\n", format);
|
||||
break;
|
||||
default:
|
||||
std::cerr << std::format("Unknown IWI format: {}\n", format);
|
||||
break;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<Texture> LoadIwi8(std::istream& stream)
|
||||
{
|
||||
iwi8::IwiHeader header{};
|
||||
|
||||
stream.read(reinterpret_cast<char*>(&header), sizeof(header));
|
||||
if (stream.gcount() != sizeof(header))
|
||||
return nullptr;
|
||||
|
||||
const auto* format = GetFormat8(header.format);
|
||||
if (format == nullptr)
|
||||
return nullptr;
|
||||
|
||||
auto width = header.dimensions[0];
|
||||
auto height = header.dimensions[1];
|
||||
auto depth = header.dimensions[2];
|
||||
auto hasMipMaps = !(header.flags & iwi8::IwiFlags::IMG_FLAG_NOMIPMAPS);
|
||||
|
||||
std::unique_ptr<Texture> texture;
|
||||
if ((header.flags & iwi8::IwiFlags::IMG_FLAG_MAPTYPE_MASK) == iwi8::IwiFlags::IMG_FLAG_MAPTYPE_CUBE)
|
||||
{
|
||||
texture = std::make_unique<TextureCube>(format, width, height, hasMipMaps);
|
||||
}
|
||||
else if ((header.flags & iwi8::IwiFlags::IMG_FLAG_MAPTYPE_MASK) == iwi8::IwiFlags::IMG_FLAG_MAPTYPE_3D)
|
||||
{
|
||||
texture = std::make_unique<Texture3D>(format, width, height, depth, hasMipMaps);
|
||||
}
|
||||
else if ((header.flags & iwi8::IwiFlags::IMG_FLAG_MAPTYPE_MASK) == iwi8::IwiFlags::IMG_FLAG_MAPTYPE_2D)
|
||||
{
|
||||
texture = std::make_unique<Texture2D>(format, width, height, hasMipMaps);
|
||||
}
|
||||
else if ((header.flags & iwi8::IwiFlags::IMG_FLAG_MAPTYPE_MASK) == iwi8::IwiFlags::IMG_FLAG_MAPTYPE_1D)
|
||||
{
|
||||
std::cerr << "Iwi has unsupported map type 1D\n";
|
||||
return nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "Iwi has unsupported map type\n";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
texture->Allocate();
|
||||
|
||||
auto currentFileSize = sizeof(iwi8::IwiHeader) + sizeof(IwiVersion);
|
||||
const auto mipMapCount = hasMipMaps ? texture->GetMipMapCount() : 1;
|
||||
|
||||
for (auto currentMipLevel = mipMapCount - 1; currentMipLevel >= 0; currentMipLevel--)
|
||||
{
|
||||
const auto sizeOfMipLevel = texture->GetSizeOfMipLevel(currentMipLevel) * texture->GetFaceCount();
|
||||
currentFileSize += sizeOfMipLevel;
|
||||
|
||||
if (currentMipLevel < static_cast<int>(std::extent_v<decltype(iwi8::IwiHeader::fileSizeForPicmip)>)
|
||||
&& currentFileSize != header.fileSizeForPicmip[currentMipLevel])
|
||||
{
|
||||
std::cerr << std::format("Iwi has invalid file size for picmip {}\n", currentMipLevel);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
stream.read(reinterpret_cast<char*>(texture->GetBufferForMipLevel(currentMipLevel)), sizeOfMipLevel);
|
||||
if (stream.gcount() != sizeOfMipLevel)
|
||||
{
|
||||
std::cerr << std::format("Unexpected eof of iwi in mip level {}\n", currentMipLevel);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
const ImageFormat* GetFormat13(int8_t format)
|
||||
{
|
||||
switch (static_cast<iwi13::IwiFormat>(format))
|
||||
{
|
||||
case iwi13::IwiFormat::IMG_FORMAT_BITMAP_RGBA:
|
||||
return &ImageFormat::FORMAT_R8_G8_B8_A8;
|
||||
case iwi13::IwiFormat::IMG_FORMAT_BITMAP_RGB:
|
||||
return &ImageFormat::FORMAT_R8_G8_B8;
|
||||
case iwi13::IwiFormat::IMG_FORMAT_BITMAP_ALPHA:
|
||||
return &ImageFormat::FORMAT_A8;
|
||||
case iwi13::IwiFormat::IMG_FORMAT_DXT1:
|
||||
return &ImageFormat::FORMAT_BC1;
|
||||
case iwi13::IwiFormat::IMG_FORMAT_DXT3:
|
||||
return &ImageFormat::FORMAT_BC2;
|
||||
case iwi13::IwiFormat::IMG_FORMAT_DXT5:
|
||||
return &ImageFormat::FORMAT_BC3;
|
||||
case iwi13::IwiFormat::IMG_FORMAT_DXN:
|
||||
return &ImageFormat::FORMAT_BC5;
|
||||
case iwi13::IwiFormat::IMG_FORMAT_BITMAP_LUMINANCE_ALPHA:
|
||||
return &ImageFormat::FORMAT_R8_A8;
|
||||
case iwi13::IwiFormat::IMG_FORMAT_BITMAP_LUMINANCE:
|
||||
return &ImageFormat::FORMAT_R8;
|
||||
case iwi13::IwiFormat::IMG_FORMAT_WAVELET_RGBA: // used
|
||||
case iwi13::IwiFormat::IMG_FORMAT_WAVELET_RGB: // used
|
||||
case iwi13::IwiFormat::IMG_FORMAT_WAVELET_LUMINANCE_ALPHA:
|
||||
case iwi13::IwiFormat::IMG_FORMAT_WAVELET_LUMINANCE:
|
||||
case iwi13::IwiFormat::IMG_FORMAT_WAVELET_ALPHA:
|
||||
case iwi13::IwiFormat::IMG_FORMAT_BITMAP_RGB565:
|
||||
case iwi13::IwiFormat::IMG_FORMAT_BITMAP_RGB5A3:
|
||||
case iwi13::IwiFormat::IMG_FORMAT_BITMAP_C8:
|
||||
case iwi13::IwiFormat::IMG_FORMAT_BITMAP_RGBA8:
|
||||
case iwi13::IwiFormat::IMG_FORMAT_A16B16G16R16F:
|
||||
std::cerr << std::format("Unsupported IWI format: {}\n", format);
|
||||
break;
|
||||
default:
|
||||
std::cerr << std::format("Unknown IWI format: {}\n", format);
|
||||
break;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<Texture> LoadIwi13(std::istream& stream)
|
||||
{
|
||||
iwi13::IwiHeader header{};
|
||||
|
||||
stream.read(reinterpret_cast<char*>(&header), sizeof(header));
|
||||
if (stream.gcount() != sizeof(header))
|
||||
return nullptr;
|
||||
|
||||
const auto* format = GetFormat6(header.format);
|
||||
if (format == nullptr)
|
||||
return nullptr;
|
||||
|
||||
auto width = header.dimensions[0];
|
||||
auto height = header.dimensions[1];
|
||||
auto depth = header.dimensions[2];
|
||||
auto hasMipMaps = !(header.flags & iwi13::IwiFlags::IMG_FLAG_NOMIPMAPS);
|
||||
|
||||
std::unique_ptr<Texture> texture;
|
||||
if (header.flags & iwi13::IwiFlags::IMG_FLAG_CUBEMAP)
|
||||
texture = std::make_unique<TextureCube>(format, width, height, hasMipMaps);
|
||||
else if (header.flags & iwi13::IwiFlags::IMG_FLAG_VOLMAP)
|
||||
texture = std::make_unique<Texture3D>(format, width, height, depth, hasMipMaps);
|
||||
else
|
||||
texture = std::make_unique<Texture2D>(format, width, height, hasMipMaps);
|
||||
|
||||
texture->Allocate();
|
||||
|
||||
auto currentFileSize = sizeof(iwi13::IwiHeader) + sizeof(IwiVersion);
|
||||
const auto mipMapCount = hasMipMaps ? texture->GetMipMapCount() : 1;
|
||||
|
||||
for (auto currentMipLevel = mipMapCount - 1; currentMipLevel >= 0; currentMipLevel--)
|
||||
{
|
||||
const auto sizeOfMipLevel = texture->GetSizeOfMipLevel(currentMipLevel) * texture->GetFaceCount();
|
||||
currentFileSize += sizeOfMipLevel;
|
||||
|
||||
if (currentMipLevel < static_cast<int>(std::extent_v<decltype(iwi13::IwiHeader::fileSizeForPicmip)>)
|
||||
&& currentFileSize != header.fileSizeForPicmip[currentMipLevel])
|
||||
{
|
||||
std::cerr << std::format("Iwi has invalid file size for picmip {}\n", currentMipLevel);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
stream.read(reinterpret_cast<char*>(texture->GetBufferForMipLevel(currentMipLevel)), sizeOfMipLevel);
|
||||
if (stream.gcount() != sizeOfMipLevel)
|
||||
{
|
||||
std::cerr << std::format("Unexpected eof of iwi in mip level {}\n", currentMipLevel);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
const ImageFormat* GetFormat27(int8_t format)
|
||||
{
|
||||
switch (static_cast<iwi27::IwiFormat>(format))
|
||||
{
|
||||
case iwi27::IwiFormat::IMG_FORMAT_BITMAP_RGBA:
|
||||
return &ImageFormat::FORMAT_R8_G8_B8_A8;
|
||||
case iwi27::IwiFormat::IMG_FORMAT_BITMAP_ALPHA:
|
||||
return &ImageFormat::FORMAT_A8;
|
||||
case iwi27::IwiFormat::IMG_FORMAT_DXT1:
|
||||
return &ImageFormat::FORMAT_BC1;
|
||||
case iwi27::IwiFormat::IMG_FORMAT_DXT3:
|
||||
return &ImageFormat::FORMAT_BC2;
|
||||
case iwi27::IwiFormat::IMG_FORMAT_DXT5:
|
||||
return &ImageFormat::FORMAT_BC3;
|
||||
case iwi27::IwiFormat::IMG_FORMAT_DXN:
|
||||
return &ImageFormat::FORMAT_BC5;
|
||||
case iwi27::IwiFormat::IMG_FORMAT_A16B16G16R16F:
|
||||
assert(false); // Unsupported yet
|
||||
return &ImageFormat::FORMAT_R16_G16_B16_A16_FLOAT;
|
||||
case iwi27::IwiFormat::IMG_FORMAT_BITMAP_RGB:
|
||||
return &ImageFormat::FORMAT_R8_G8_B8;
|
||||
case iwi27::IwiFormat::IMG_FORMAT_BITMAP_LUMINANCE_ALPHA:
|
||||
return &ImageFormat::FORMAT_R8_A8;
|
||||
case iwi27::IwiFormat::IMG_FORMAT_BITMAP_LUMINANCE:
|
||||
return &ImageFormat::FORMAT_R8;
|
||||
case iwi27::IwiFormat::IMG_FORMAT_WAVELET_RGBA:
|
||||
case iwi27::IwiFormat::IMG_FORMAT_WAVELET_RGB:
|
||||
case iwi27::IwiFormat::IMG_FORMAT_WAVELET_LUMINANCE_ALPHA:
|
||||
case iwi27::IwiFormat::IMG_FORMAT_WAVELET_LUMINANCE:
|
||||
case iwi27::IwiFormat::IMG_FORMAT_WAVELET_ALPHA:
|
||||
case iwi27::IwiFormat::IMG_FORMAT_BITMAP_RGB565:
|
||||
case iwi27::IwiFormat::IMG_FORMAT_BITMAP_RGB5A3:
|
||||
case iwi27::IwiFormat::IMG_FORMAT_BITMAP_C8:
|
||||
case iwi27::IwiFormat::IMG_FORMAT_BITMAP_RGBA8:
|
||||
std::cerr << std::format("Unsupported IWI format: {}\n", format);
|
||||
break;
|
||||
default:
|
||||
std::cerr << std::format("Unknown IWI format: {}\n", format);
|
||||
break;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<Texture> LoadIwi27(std::istream& stream)
|
||||
{
|
||||
iwi27::IwiHeader header{};
|
||||
|
||||
stream.read(reinterpret_cast<char*>(&header), sizeof(header));
|
||||
if (stream.gcount() != sizeof(header))
|
||||
return nullptr;
|
||||
|
||||
const auto* format = GetFormat27(header.format);
|
||||
if (format == nullptr)
|
||||
return nullptr;
|
||||
|
||||
auto width = header.dimensions[0];
|
||||
auto height = header.dimensions[1];
|
||||
auto depth = header.dimensions[2];
|
||||
auto hasMipMaps = !(header.flags & iwi27::IwiFlags::IMG_FLAG_NOMIPMAPS);
|
||||
|
||||
std::unique_ptr<Texture> texture;
|
||||
if (header.flags & iwi27::IwiFlags::IMG_FLAG_CUBEMAP)
|
||||
texture = std::make_unique<TextureCube>(format, width, height, hasMipMaps);
|
||||
else if (header.flags & iwi27::IwiFlags::IMG_FLAG_VOLMAP)
|
||||
texture = std::make_unique<Texture3D>(format, width, height, depth, hasMipMaps);
|
||||
else
|
||||
texture = std::make_unique<Texture2D>(format, width, height, hasMipMaps);
|
||||
|
||||
texture->Allocate();
|
||||
|
||||
auto currentFileSize = sizeof(iwi27::IwiHeader) + sizeof(IwiVersion);
|
||||
const auto mipMapCount = hasMipMaps ? texture->GetMipMapCount() : 1;
|
||||
|
||||
for (auto currentMipLevel = mipMapCount - 1; currentMipLevel >= 0; currentMipLevel--)
|
||||
{
|
||||
const auto sizeOfMipLevel = texture->GetSizeOfMipLevel(currentMipLevel) * texture->GetFaceCount();
|
||||
currentFileSize += sizeOfMipLevel;
|
||||
|
||||
if (currentMipLevel < static_cast<int>(std::extent_v<decltype(iwi27::IwiHeader::fileSizeForPicmip)>)
|
||||
&& currentFileSize != header.fileSizeForPicmip[currentMipLevel])
|
||||
{
|
||||
std::cerr << std::format("Iwi has invalid file size for picmip {}\n", currentMipLevel);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
stream.read(reinterpret_cast<char*>(texture->GetBufferForMipLevel(currentMipLevel)), sizeOfMipLevel);
|
||||
if (stream.gcount() != sizeOfMipLevel)
|
||||
{
|
||||
std::cerr << std::format("Unexpected eof of iwi in mip level {}\n", currentMipLevel);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
std::unique_ptr<Texture> LoadIwi(std::istream& stream)
|
||||
{
|
||||
IwiVersion iwiVersion{};
|
||||
|
||||
stream.read(reinterpret_cast<char*>(&iwiVersion), sizeof(iwiVersion));
|
||||
if (stream.gcount() != sizeof(iwiVersion))
|
||||
return nullptr;
|
||||
|
||||
if (iwiVersion.tag[0] != 'I' || iwiVersion.tag[1] != 'W' || iwiVersion.tag[2] != 'i')
|
||||
{
|
||||
std::cerr << "Invalid IWI magic\n";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
switch (iwiVersion.version)
|
||||
{
|
||||
case 6:
|
||||
return LoadIwi6(stream);
|
||||
|
||||
case 8:
|
||||
return LoadIwi8(stream);
|
||||
|
||||
case 13:
|
||||
return LoadIwi13(stream);
|
||||
|
||||
case 27:
|
||||
return LoadIwi27(stream);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
std::cerr << std::format("Unknown IWI version {}\n", iwiVersion.version);
|
||||
return nullptr;
|
||||
}
|
||||
} // namespace iwi
|
11
src/ObjImage/Image/IwiLoader.h
Normal file
11
src/ObjImage/Image/IwiLoader.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "Image/Texture.h"
|
||||
|
||||
#include <istream>
|
||||
#include <memory>
|
||||
|
||||
namespace iwi
|
||||
{
|
||||
std::unique_ptr<Texture> LoadIwi(std::istream& stream);
|
||||
}; // namespace iwi
|
235
src/ObjImage/Image/IwiTypes.h
Normal file
235
src/ObjImage/Image/IwiTypes.h
Normal file
@ -0,0 +1,235 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
struct IwiVersion
|
||||
{
|
||||
char tag[3];
|
||||
char version;
|
||||
};
|
||||
|
||||
// IW3
|
||||
namespace iwi6
|
||||
{
|
||||
struct IwiHeader
|
||||
{
|
||||
int8_t format;
|
||||
int8_t flags;
|
||||
uint16_t dimensions[3];
|
||||
uint32_t fileSizeForPicmip[4];
|
||||
};
|
||||
|
||||
enum class IwiFormat
|
||||
{
|
||||
IMG_FORMAT_INVALID = 0x0,
|
||||
IMG_FORMAT_BITMAP_RGBA = 0x1,
|
||||
IMG_FORMAT_BITMAP_RGB = 0x2,
|
||||
IMG_FORMAT_BITMAP_LUMINANCE_ALPHA = 0x3,
|
||||
IMG_FORMAT_BITMAP_LUMINANCE = 0x4,
|
||||
IMG_FORMAT_BITMAP_ALPHA = 0x5,
|
||||
IMG_FORMAT_WAVELET_RGBA = 0x6,
|
||||
IMG_FORMAT_WAVELET_RGB = 0x7,
|
||||
IMG_FORMAT_WAVELET_LUMINANCE_ALPHA = 0x8,
|
||||
IMG_FORMAT_WAVELET_LUMINANCE = 0x9,
|
||||
IMG_FORMAT_WAVELET_ALPHA = 0xA,
|
||||
IMG_FORMAT_DXT1 = 0xB,
|
||||
IMG_FORMAT_DXT3 = 0xC,
|
||||
IMG_FORMAT_DXT5 = 0xD,
|
||||
IMG_FORMAT_DXN = 0xE,
|
||||
|
||||
IMG_FORMAT_COUNT
|
||||
};
|
||||
|
||||
enum IwiFlags
|
||||
{
|
||||
IMG_FLAG_NOPICMIP = 1 << 0,
|
||||
IMG_FLAG_NOMIPMAPS = 1 << 1,
|
||||
IMG_FLAG_CUBEMAP = 1 << 2,
|
||||
IMG_FLAG_VOLMAP = 1 << 3,
|
||||
IMG_FLAG_STREAMING = 1 << 4,
|
||||
IMG_FLAG_LEGACY_NORMALS = 1 << 5,
|
||||
IMG_FLAG_CLAMP_U = 1 << 6,
|
||||
IMG_FLAG_CLAMP_V = 1 << 7,
|
||||
IMG_FLAG_DYNAMIC = 1 << 16,
|
||||
IMG_FLAG_RENDER_TARGET = 1 << 17,
|
||||
IMG_FLAG_SYSTEMMEM = 1 << 18
|
||||
};
|
||||
} // namespace iwi6
|
||||
|
||||
// IW4
|
||||
namespace iwi8
|
||||
{
|
||||
struct IwiHeader
|
||||
{
|
||||
uint32_t flags;
|
||||
int8_t format;
|
||||
int8_t unused;
|
||||
uint16_t dimensions[3];
|
||||
uint32_t fileSizeForPicmip[4];
|
||||
};
|
||||
|
||||
enum class IwiFormat
|
||||
{
|
||||
IMG_FORMAT_INVALID = 0x0,
|
||||
IMG_FORMAT_BITMAP_RGBA = 0x1,
|
||||
IMG_FORMAT_BITMAP_RGB = 0x2,
|
||||
IMG_FORMAT_BITMAP_LUMINANCE_ALPHA = 0x3,
|
||||
IMG_FORMAT_BITMAP_LUMINANCE = 0x4,
|
||||
IMG_FORMAT_BITMAP_ALPHA = 0x5,
|
||||
IMG_FORMAT_WAVELET_RGBA = 0x6,
|
||||
IMG_FORMAT_WAVELET_RGB = 0x7,
|
||||
IMG_FORMAT_WAVELET_LUMINANCE_ALPHA = 0x8,
|
||||
IMG_FORMAT_WAVELET_LUMINANCE = 0x9,
|
||||
IMG_FORMAT_WAVELET_ALPHA = 0xA,
|
||||
IMG_FORMAT_DXT1 = 0xB,
|
||||
IMG_FORMAT_DXT3 = 0xC,
|
||||
IMG_FORMAT_DXT5 = 0xD,
|
||||
IMG_FORMAT_DXN = 0xE,
|
||||
IMG_FORMAT_DXT3A_AS_LUMINANCE = 0xF,
|
||||
IMG_FORMAT_DXT5A_AS_LUMINANCE = 0x10,
|
||||
IMG_FORMAT_DXT3A_AS_ALPHA = 0x11,
|
||||
IMG_FORMAT_DXT5A_AS_ALPHA = 0x12,
|
||||
IMG_FORMAT_DXT1_AS_LUMINANCE_ALPHA = 0x13,
|
||||
IMG_FORMAT_DXN_AS_LUMINANCE_ALPHA = 0x14,
|
||||
IMG_FORMAT_DXT1_AS_LUMINANCE = 0x15,
|
||||
IMG_FORMAT_DXT1_AS_ALPHA = 0x16,
|
||||
|
||||
IMG_FORMAT_COUNT
|
||||
};
|
||||
|
||||
enum IwiFlags
|
||||
{
|
||||
IMG_FLAG_NOPICMIP = 1 << 0,
|
||||
IMG_FLAG_NOMIPMAPS = 1 << 1,
|
||||
IMG_FLAG_STREAMING = 1 << 2,
|
||||
IMG_FLAG_LEGACY_NORMALS = 1 << 3,
|
||||
IMG_FLAG_CLAMP_U = 1 << 4,
|
||||
IMG_FLAG_CLAMP_V = 1 << 5,
|
||||
IMG_FLAG_ALPHA_WEIGHTED_COLORS = 1 << 6,
|
||||
IMG_FLAG_DXTC_APPROX_WEIGHTS = 1 << 7,
|
||||
IMG_FLAG_GAMMA_NONE = 0,
|
||||
IMG_FLAG_GAMMA_SRGB = 1 << 8,
|
||||
IMG_FLAG_GAMMA_PWL = 1 << 9,
|
||||
IMG_FLAG_GAMMA_2 = IMG_FLAG_GAMMA_SRGB | IMG_FLAG_GAMMA_PWL,
|
||||
IMG_FLAG_MAPTYPE_2D = 0,
|
||||
IMG_FLAG_MAPTYPE_CUBE = 1 << 16,
|
||||
IMG_FLAG_MAPTYPE_3D = 1 << 17,
|
||||
IMG_FLAG_MAPTYPE_1D = IMG_FLAG_MAPTYPE_CUBE | IMG_FLAG_MAPTYPE_3D,
|
||||
IMG_FLAG_MAPTYPE_MASK = IMG_FLAG_MAPTYPE_2D | IMG_FLAG_MAPTYPE_CUBE | IMG_FLAG_MAPTYPE_3D | IMG_FLAG_MAPTYPE_1D,
|
||||
IMG_FLAG_NORMALMAP = 1 << 18,
|
||||
IMG_FLAG_INTENSITY_TO_ALPHA = 1 << 19,
|
||||
IMG_FLAG_DYNAMIC = 1 << 24,
|
||||
IMG_FLAG_RENDER_TARGET = 1 << 25,
|
||||
IMG_FLAG_SYSTEMMEM = 1 << 26
|
||||
};
|
||||
} // namespace iwi8
|
||||
|
||||
// T5
|
||||
namespace iwi13
|
||||
{
|
||||
struct IwiHeader
|
||||
{
|
||||
int8_t format;
|
||||
int8_t flags;
|
||||
uint16_t dimensions[3];
|
||||
float gamma;
|
||||
uint32_t fileSizeForPicmip[8];
|
||||
};
|
||||
|
||||
enum class IwiFormat
|
||||
{
|
||||
IMG_FORMAT_INVALID = 0x0,
|
||||
IMG_FORMAT_BITMAP_RGBA = 0x1,
|
||||
IMG_FORMAT_BITMAP_RGB = 0x2,
|
||||
IMG_FORMAT_BITMAP_LUMINANCE_ALPHA = 0x3,
|
||||
IMG_FORMAT_BITMAP_LUMINANCE = 0x4,
|
||||
IMG_FORMAT_BITMAP_ALPHA = 0x5,
|
||||
IMG_FORMAT_WAVELET_RGBA = 0x6,
|
||||
IMG_FORMAT_WAVELET_RGB = 0x7,
|
||||
IMG_FORMAT_WAVELET_LUMINANCE_ALPHA = 0x8,
|
||||
IMG_FORMAT_WAVELET_LUMINANCE = 0x9,
|
||||
IMG_FORMAT_WAVELET_ALPHA = 0xA,
|
||||
IMG_FORMAT_DXT1 = 0xB,
|
||||
IMG_FORMAT_DXT3 = 0xC,
|
||||
IMG_FORMAT_DXT5 = 0xD,
|
||||
IMG_FORMAT_DXN = 0xE,
|
||||
IMG_FORMAT_BITMAP_RGB565 = 0xF,
|
||||
IMG_FORMAT_BITMAP_RGB5A3 = 0x10,
|
||||
IMG_FORMAT_BITMAP_C8 = 0x11,
|
||||
IMG_FORMAT_BITMAP_RGBA8 = 0x12,
|
||||
IMG_FORMAT_A16B16G16R16F = 0x13,
|
||||
IMG_FORMAT_COUNT = 0x14,
|
||||
};
|
||||
|
||||
enum IwiFlags
|
||||
{
|
||||
IMG_FLAG_NOPICMIP = 1 << 0,
|
||||
IMG_FLAG_NOMIPMAPS = 1 << 1,
|
||||
IMG_FLAG_CUBEMAP = 1 << 2,
|
||||
IMG_FLAG_VOLMAP = 1 << 3,
|
||||
IMG_FLAG_STREAMING = 1 << 4,
|
||||
IMG_FLAG_LEGACY_NORMALS = 1 << 5,
|
||||
IMG_FLAG_CLAMP_U = 1 << 6,
|
||||
IMG_FLAG_CLAMP_V = 1 << 7,
|
||||
IMG_FLAG_FORCE_SYSTEM = 1 << 8,
|
||||
IMG_FLAG_DYNAMIC = 1 << 16,
|
||||
IMG_FLAG_RENDER_TARGET = 1 << 17,
|
||||
IMG_FLAG_SYSTEMMEM = 1 << 18,
|
||||
};
|
||||
|
||||
} // namespace iwi13
|
||||
|
||||
// T6
|
||||
namespace iwi27
|
||||
{
|
||||
struct IwiHeader
|
||||
{
|
||||
int8_t format;
|
||||
int8_t flags;
|
||||
uint16_t dimensions[3];
|
||||
float gamma;
|
||||
int8_t maxGlossForMip[16];
|
||||
uint32_t fileSizeForPicmip[8];
|
||||
};
|
||||
|
||||
enum class IwiFormat
|
||||
{
|
||||
IMG_FORMAT_INVALID = 0x0,
|
||||
IMG_FORMAT_BITMAP_RGBA = 0x1,
|
||||
IMG_FORMAT_BITMAP_RGB = 0x2,
|
||||
IMG_FORMAT_BITMAP_LUMINANCE_ALPHA = 0x3,
|
||||
IMG_FORMAT_BITMAP_LUMINANCE = 0x4,
|
||||
IMG_FORMAT_BITMAP_ALPHA = 0x5,
|
||||
IMG_FORMAT_WAVELET_RGBA = 0x6,
|
||||
IMG_FORMAT_WAVELET_RGB = 0x7,
|
||||
IMG_FORMAT_WAVELET_LUMINANCE_ALPHA = 0x8,
|
||||
IMG_FORMAT_WAVELET_LUMINANCE = 0x9,
|
||||
IMG_FORMAT_WAVELET_ALPHA = 0xA,
|
||||
IMG_FORMAT_DXT1 = 0xB,
|
||||
IMG_FORMAT_DXT3 = 0xC,
|
||||
IMG_FORMAT_DXT5 = 0xD,
|
||||
IMG_FORMAT_DXN = 0xE,
|
||||
IMG_FORMAT_BITMAP_RGB565 = 0xF,
|
||||
IMG_FORMAT_BITMAP_RGB5A3 = 0x10,
|
||||
IMG_FORMAT_BITMAP_C8 = 0x11,
|
||||
IMG_FORMAT_BITMAP_RGBA8 = 0x12,
|
||||
IMG_FORMAT_A16B16G16R16F = 0x13,
|
||||
IMG_FORMAT_COUNT,
|
||||
};
|
||||
|
||||
enum IwiFlags
|
||||
{
|
||||
IMG_FLAG_NOPICMIP = 1 << 0,
|
||||
IMG_FLAG_NOMIPMAPS = 1 << 1,
|
||||
IMG_FLAG_CUBEMAP = 1 << 2,
|
||||
IMG_FLAG_VOLMAP = 1 << 3,
|
||||
IMG_FLAG_STREAMING = 1 << 4,
|
||||
IMG_FLAG_CLAMP_U = 1 << 6,
|
||||
IMG_FLAG_CLAMP_V = 1 << 7,
|
||||
IMG_FLAG_FORCE_SYSTEM = 1 << 8,
|
||||
IMG_FLAG_DYNAMIC = 1 << 16,
|
||||
IMG_FLAG_RENDER_TARGET = 1 << 17,
|
||||
IMG_FLAG_MULTISAMPLE = 1 << 18,
|
||||
};
|
||||
|
||||
} // namespace iwi27
|
141
src/ObjImage/Image/IwiWriter13.cpp
Normal file
141
src/ObjImage/Image/IwiWriter13.cpp
Normal file
@ -0,0 +1,141 @@
|
||||
#include "IwiWriter13.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <ostream>
|
||||
|
||||
using namespace iwi13;
|
||||
|
||||
IwiWriter::IwiWriter() = default;
|
||||
|
||||
IwiWriter::~IwiWriter() = default;
|
||||
|
||||
IwiFormat IwiWriter::GetIwiFormatForImageFormat(const ImageFormat* imageFormat)
|
||||
{
|
||||
switch (imageFormat->GetId())
|
||||
{
|
||||
case ImageFormatId::R8_G8_B8:
|
||||
return IwiFormat::IMG_FORMAT_BITMAP_RGB;
|
||||
|
||||
case ImageFormatId::R8_G8_B8_A8:
|
||||
return IwiFormat::IMG_FORMAT_BITMAP_RGBA;
|
||||
|
||||
case ImageFormatId::A8:
|
||||
return IwiFormat::IMG_FORMAT_BITMAP_ALPHA;
|
||||
|
||||
case ImageFormatId::R16_G16_B16_A16_FLOAT:
|
||||
return IwiFormat::IMG_FORMAT_A16B16G16R16F;
|
||||
|
||||
case ImageFormatId::BC1:
|
||||
return IwiFormat::IMG_FORMAT_DXT1;
|
||||
|
||||
case ImageFormatId::BC2:
|
||||
return IwiFormat::IMG_FORMAT_DXT3;
|
||||
|
||||
case ImageFormatId::BC3:
|
||||
return IwiFormat::IMG_FORMAT_DXT5;
|
||||
|
||||
case ImageFormatId::BC5:
|
||||
return IwiFormat::IMG_FORMAT_DXN;
|
||||
|
||||
default:
|
||||
return IwiFormat::IMG_FORMAT_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
bool IwiWriter::SupportsImageFormat(const ImageFormat* imageFormat)
|
||||
{
|
||||
return GetIwiFormatForImageFormat(imageFormat) != IwiFormat::IMG_FORMAT_INVALID;
|
||||
}
|
||||
|
||||
std::string IwiWriter::GetFileExtension()
|
||||
{
|
||||
return ".iwi";
|
||||
}
|
||||
|
||||
void IwiWriter::WriteVersion(std::ostream& stream)
|
||||
{
|
||||
IwiVersion version{};
|
||||
version.tag[0] = 'I';
|
||||
version.tag[1] = 'W';
|
||||
version.tag[2] = 'i';
|
||||
version.version = 13;
|
||||
|
||||
stream.write(reinterpret_cast<char*>(&version), sizeof(IwiVersion));
|
||||
}
|
||||
|
||||
void IwiWriter::FillHeader2D(IwiHeader& header, const Texture2D& texture)
|
||||
{
|
||||
header.dimensions[0] = static_cast<uint16_t>(texture.GetWidth());
|
||||
header.dimensions[1] = static_cast<uint16_t>(texture.GetHeight());
|
||||
header.dimensions[2] = 1;
|
||||
}
|
||||
|
||||
void IwiWriter::FillHeaderCube(IwiHeader& header, const TextureCube& texture)
|
||||
{
|
||||
header.dimensions[0] = static_cast<uint16_t>(texture.GetWidth());
|
||||
header.dimensions[1] = static_cast<uint16_t>(texture.GetHeight());
|
||||
header.dimensions[2] = 1;
|
||||
header.flags |= IMG_FLAG_CUBEMAP;
|
||||
}
|
||||
|
||||
void IwiWriter::FillHeader3D(IwiHeader& header, const Texture3D& texture)
|
||||
{
|
||||
header.dimensions[0] = static_cast<uint16_t>(texture.GetWidth());
|
||||
header.dimensions[1] = static_cast<uint16_t>(texture.GetHeight());
|
||||
header.dimensions[2] = static_cast<uint16_t>(texture.GetDepth());
|
||||
header.flags |= IMG_FLAG_VOLMAP;
|
||||
}
|
||||
|
||||
void IwiWriter::DumpImage(std::ostream& stream, const Texture* texture)
|
||||
{
|
||||
assert(texture != nullptr);
|
||||
|
||||
WriteVersion(stream);
|
||||
|
||||
IwiHeader header{};
|
||||
header.flags = 0;
|
||||
header.gamma = 0.0f;
|
||||
|
||||
header.format = static_cast<int8_t>(GetIwiFormatForImageFormat(texture->GetFormat()));
|
||||
|
||||
if (!texture->HasMipMaps())
|
||||
header.flags |= IMG_FLAG_NOMIPMAPS;
|
||||
|
||||
auto currentFileSize = sizeof(IwiVersion) + sizeof(IwiHeader);
|
||||
|
||||
const auto textureMipCount = texture->HasMipMaps() ? texture->GetMipMapCount() : 1;
|
||||
for (auto currentMipLevel = textureMipCount - 1; currentMipLevel >= 0; currentMipLevel--)
|
||||
{
|
||||
const auto mipLevelSize = texture->GetSizeOfMipLevel(currentMipLevel) * texture->GetFaceCount();
|
||||
currentFileSize += mipLevelSize;
|
||||
|
||||
if (currentMipLevel < static_cast<int>(std::extent_v<decltype(IwiHeader::fileSizeForPicmip)>))
|
||||
header.fileSizeForPicmip[currentMipLevel] = currentFileSize;
|
||||
}
|
||||
|
||||
if (const auto* texture2D = dynamic_cast<const Texture2D*>(texture))
|
||||
{
|
||||
FillHeader2D(header, *texture2D);
|
||||
}
|
||||
else if (const auto* textureCube = dynamic_cast<const TextureCube*>(texture))
|
||||
{
|
||||
FillHeaderCube(header, *textureCube);
|
||||
}
|
||||
else if (const auto* texture3D = dynamic_cast<const Texture3D*>(texture))
|
||||
{
|
||||
FillHeader3D(header, *texture3D);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
stream.write(reinterpret_cast<char*>(&header), sizeof(IwiHeader));
|
||||
|
||||
for (auto currentMipLevel = textureMipCount - 1; currentMipLevel >= 0; currentMipLevel--)
|
||||
{
|
||||
const auto mipLevelSize = texture->GetSizeOfMipLevel(currentMipLevel) * texture->GetFaceCount();
|
||||
stream.write(reinterpret_cast<const char*>(texture->GetBufferForMipLevel(currentMipLevel)), mipLevelSize);
|
||||
}
|
||||
}
|
30
src/ObjImage/Image/IwiWriter13.h
Normal file
30
src/ObjImage/Image/IwiWriter13.h
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "IImageWriter.h"
|
||||
#include "Image/IwiTypes.h"
|
||||
|
||||
namespace iwi13
|
||||
{
|
||||
class IwiWriter final : public IImageWriter
|
||||
{
|
||||
static IwiFormat GetIwiFormatForImageFormat(const ImageFormat* imageFormat);
|
||||
|
||||
static void WriteVersion(std::ostream& stream);
|
||||
static void FillHeader2D(IwiHeader& header, const Texture2D& texture);
|
||||
static void FillHeaderCube(IwiHeader& header, const TextureCube& texture);
|
||||
static void FillHeader3D(IwiHeader& header, const Texture3D& texture);
|
||||
|
||||
public:
|
||||
IwiWriter();
|
||||
IwiWriter(const IwiWriter& other) = delete;
|
||||
IwiWriter(IwiWriter&& other) noexcept = delete;
|
||||
~IwiWriter() override;
|
||||
|
||||
IwiWriter& operator=(const IwiWriter& other) = delete;
|
||||
IwiWriter& operator=(IwiWriter&& other) noexcept = delete;
|
||||
|
||||
bool SupportsImageFormat(const ImageFormat* imageFormat) override;
|
||||
std::string GetFileExtension() override;
|
||||
void DumpImage(std::ostream& stream, const Texture* texture) override;
|
||||
};
|
||||
} // namespace iwi13
|
144
src/ObjImage/Image/IwiWriter27.cpp
Normal file
144
src/ObjImage/Image/IwiWriter27.cpp
Normal file
@ -0,0 +1,144 @@
|
||||
#include "IwiWriter27.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <ostream>
|
||||
|
||||
using namespace iwi27;
|
||||
|
||||
IwiWriter::IwiWriter() = default;
|
||||
|
||||
IwiWriter::~IwiWriter() = default;
|
||||
|
||||
IwiFormat IwiWriter::GetIwiFormatForImageFormat(const ImageFormat* imageFormat)
|
||||
{
|
||||
switch (imageFormat->GetId())
|
||||
{
|
||||
case ImageFormatId::R8_G8_B8:
|
||||
return IwiFormat::IMG_FORMAT_BITMAP_RGB;
|
||||
|
||||
case ImageFormatId::R8_G8_B8_A8:
|
||||
return IwiFormat::IMG_FORMAT_BITMAP_RGBA;
|
||||
|
||||
case ImageFormatId::A8:
|
||||
return IwiFormat::IMG_FORMAT_BITMAP_ALPHA;
|
||||
|
||||
case ImageFormatId::R16_G16_B16_A16_FLOAT:
|
||||
return IwiFormat::IMG_FORMAT_A16B16G16R16F;
|
||||
|
||||
case ImageFormatId::BC1:
|
||||
return IwiFormat::IMG_FORMAT_DXT1;
|
||||
|
||||
case ImageFormatId::BC2:
|
||||
return IwiFormat::IMG_FORMAT_DXT3;
|
||||
|
||||
case ImageFormatId::BC3:
|
||||
return IwiFormat::IMG_FORMAT_DXT5;
|
||||
|
||||
case ImageFormatId::BC5:
|
||||
return IwiFormat::IMG_FORMAT_DXN;
|
||||
|
||||
default:
|
||||
return IwiFormat::IMG_FORMAT_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
bool IwiWriter::SupportsImageFormat(const ImageFormat* imageFormat)
|
||||
{
|
||||
return GetIwiFormatForImageFormat(imageFormat) != IwiFormat::IMG_FORMAT_INVALID;
|
||||
}
|
||||
|
||||
std::string IwiWriter::GetFileExtension()
|
||||
{
|
||||
return ".iwi";
|
||||
}
|
||||
|
||||
void IwiWriter::WriteVersion(std::ostream& stream)
|
||||
{
|
||||
IwiVersion version{};
|
||||
version.tag[0] = 'I';
|
||||
version.tag[1] = 'W';
|
||||
version.tag[2] = 'i';
|
||||
version.version = 27;
|
||||
|
||||
stream.write(reinterpret_cast<char*>(&version), sizeof(IwiVersion));
|
||||
}
|
||||
|
||||
void IwiWriter::FillHeader2D(IwiHeader& header, const Texture2D& texture)
|
||||
{
|
||||
header.dimensions[0] = static_cast<uint16_t>(texture.GetWidth());
|
||||
header.dimensions[1] = static_cast<uint16_t>(texture.GetHeight());
|
||||
header.dimensions[2] = 1;
|
||||
}
|
||||
|
||||
void IwiWriter::FillHeaderCube(IwiHeader& header, const TextureCube& texture)
|
||||
{
|
||||
header.dimensions[0] = static_cast<uint16_t>(texture.GetWidth());
|
||||
header.dimensions[1] = static_cast<uint16_t>(texture.GetHeight());
|
||||
header.dimensions[2] = 1;
|
||||
header.flags |= IMG_FLAG_CUBEMAP;
|
||||
}
|
||||
|
||||
void IwiWriter::FillHeader3D(IwiHeader& header, const Texture3D& texture)
|
||||
{
|
||||
header.dimensions[0] = static_cast<uint16_t>(texture.GetWidth());
|
||||
header.dimensions[1] = static_cast<uint16_t>(texture.GetHeight());
|
||||
header.dimensions[2] = static_cast<uint16_t>(texture.GetDepth());
|
||||
header.flags |= IMG_FLAG_VOLMAP;
|
||||
}
|
||||
|
||||
void IwiWriter::DumpImage(std::ostream& stream, const Texture* texture)
|
||||
{
|
||||
assert(texture != nullptr);
|
||||
|
||||
WriteVersion(stream);
|
||||
|
||||
IwiHeader header{};
|
||||
header.flags = 0;
|
||||
header.gamma = 0.0f;
|
||||
|
||||
header.format = static_cast<int8_t>(GetIwiFormatForImageFormat(texture->GetFormat()));
|
||||
|
||||
if (!texture->HasMipMaps())
|
||||
header.flags |= IMG_FLAG_NOMIPMAPS;
|
||||
|
||||
for (auto& i : header.maxGlossForMip)
|
||||
i = 0;
|
||||
|
||||
auto currentFileSize = sizeof(IwiVersion) + sizeof(IwiHeader);
|
||||
|
||||
const auto textureMipCount = texture->HasMipMaps() ? texture->GetMipMapCount() : 1;
|
||||
for (auto currentMipLevel = textureMipCount - 1; currentMipLevel >= 0; currentMipLevel--)
|
||||
{
|
||||
const auto mipLevelSize = texture->GetSizeOfMipLevel(currentMipLevel) * texture->GetFaceCount();
|
||||
currentFileSize += mipLevelSize;
|
||||
|
||||
if (currentMipLevel < static_cast<int>(std::extent_v<decltype(iwi27::IwiHeader::fileSizeForPicmip)>))
|
||||
header.fileSizeForPicmip[currentMipLevel] = currentFileSize;
|
||||
}
|
||||
|
||||
if (const auto* texture2D = dynamic_cast<const Texture2D*>(texture))
|
||||
{
|
||||
FillHeader2D(header, *texture2D);
|
||||
}
|
||||
else if (const auto* textureCube = dynamic_cast<const TextureCube*>(texture))
|
||||
{
|
||||
FillHeaderCube(header, *textureCube);
|
||||
}
|
||||
else if (const auto* texture3D = dynamic_cast<const Texture3D*>(texture))
|
||||
{
|
||||
FillHeader3D(header, *texture3D);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
stream.write(reinterpret_cast<char*>(&header), sizeof(IwiHeader));
|
||||
|
||||
for (auto currentMipLevel = textureMipCount - 1; currentMipLevel >= 0; currentMipLevel--)
|
||||
{
|
||||
const auto mipLevelSize = texture->GetSizeOfMipLevel(currentMipLevel) * texture->GetFaceCount();
|
||||
stream.write(reinterpret_cast<const char*>(texture->GetBufferForMipLevel(currentMipLevel)), mipLevelSize);
|
||||
}
|
||||
}
|
30
src/ObjImage/Image/IwiWriter27.h
Normal file
30
src/ObjImage/Image/IwiWriter27.h
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "IImageWriter.h"
|
||||
#include "Image/IwiTypes.h"
|
||||
|
||||
namespace iwi27
|
||||
{
|
||||
class IwiWriter final : public IImageWriter
|
||||
{
|
||||
static IwiFormat GetIwiFormatForImageFormat(const ImageFormat* imageFormat);
|
||||
|
||||
static void WriteVersion(std::ostream& stream);
|
||||
static void FillHeader2D(IwiHeader& header, const Texture2D& texture);
|
||||
static void FillHeaderCube(IwiHeader& header, const TextureCube& texture);
|
||||
static void FillHeader3D(IwiHeader& header, const Texture3D& texture);
|
||||
|
||||
public:
|
||||
IwiWriter();
|
||||
IwiWriter(const IwiWriter& other) = delete;
|
||||
IwiWriter(IwiWriter&& other) noexcept = delete;
|
||||
~IwiWriter() override;
|
||||
|
||||
IwiWriter& operator=(const IwiWriter& other) = delete;
|
||||
IwiWriter& operator=(IwiWriter&& other) noexcept = delete;
|
||||
|
||||
bool SupportsImageFormat(const ImageFormat* imageFormat) override;
|
||||
std::string GetFileExtension() override;
|
||||
void DumpImage(std::ostream& stream, const Texture* texture) override;
|
||||
};
|
||||
} // namespace iwi27
|
136
src/ObjImage/Image/IwiWriter6.cpp
Normal file
136
src/ObjImage/Image/IwiWriter6.cpp
Normal file
@ -0,0 +1,136 @@
|
||||
#include "IwiWriter6.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
using namespace iwi6;
|
||||
|
||||
IwiWriter::IwiWriter() = default;
|
||||
|
||||
IwiWriter::~IwiWriter() = default;
|
||||
|
||||
IwiFormat IwiWriter::GetIwiFormatForImageFormat(const ImageFormat* imageFormat)
|
||||
{
|
||||
switch (imageFormat->GetId())
|
||||
{
|
||||
case ImageFormatId::R8_G8_B8:
|
||||
return IwiFormat::IMG_FORMAT_BITMAP_RGB;
|
||||
|
||||
case ImageFormatId::R8_G8_B8_A8:
|
||||
return IwiFormat::IMG_FORMAT_BITMAP_RGBA;
|
||||
|
||||
case ImageFormatId::A8:
|
||||
return IwiFormat::IMG_FORMAT_BITMAP_ALPHA;
|
||||
|
||||
case ImageFormatId::BC1:
|
||||
return IwiFormat::IMG_FORMAT_DXT1;
|
||||
|
||||
case ImageFormatId::BC2:
|
||||
return IwiFormat::IMG_FORMAT_DXT3;
|
||||
|
||||
case ImageFormatId::BC3:
|
||||
return IwiFormat::IMG_FORMAT_DXT5;
|
||||
|
||||
case ImageFormatId::BC5:
|
||||
return IwiFormat::IMG_FORMAT_DXN;
|
||||
|
||||
default:
|
||||
return IwiFormat::IMG_FORMAT_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
void IwiWriter::WriteVersion(std::ostream& stream)
|
||||
{
|
||||
IwiVersion version{};
|
||||
version.tag[0] = 'I';
|
||||
version.tag[1] = 'W';
|
||||
version.tag[2] = 'i';
|
||||
version.version = 6;
|
||||
|
||||
stream.write(reinterpret_cast<char*>(&version), sizeof(IwiVersion));
|
||||
}
|
||||
|
||||
void IwiWriter::FillHeader2D(IwiHeader& header, const Texture2D& texture)
|
||||
{
|
||||
header.dimensions[0] = static_cast<uint16_t>(texture.GetWidth());
|
||||
header.dimensions[1] = static_cast<uint16_t>(texture.GetHeight());
|
||||
header.dimensions[2] = 1u;
|
||||
}
|
||||
|
||||
void IwiWriter::FillHeaderCube(IwiHeader& header, const TextureCube& texture)
|
||||
{
|
||||
header.dimensions[0] = static_cast<uint16_t>(texture.GetWidth());
|
||||
header.dimensions[1] = static_cast<uint16_t>(texture.GetHeight());
|
||||
header.dimensions[2] = 1u;
|
||||
header.flags |= IMG_FLAG_CUBEMAP;
|
||||
}
|
||||
|
||||
void IwiWriter::FillHeader3D(IwiHeader& header, const Texture3D& texture)
|
||||
{
|
||||
header.dimensions[0] = static_cast<uint16_t>(texture.GetWidth());
|
||||
header.dimensions[1] = static_cast<uint16_t>(texture.GetHeight());
|
||||
header.dimensions[2] = static_cast<uint16_t>(texture.GetDepth());
|
||||
header.flags |= IMG_FLAG_VOLMAP;
|
||||
}
|
||||
|
||||
bool IwiWriter::SupportsImageFormat(const ImageFormat* imageFormat)
|
||||
{
|
||||
return GetIwiFormatForImageFormat(imageFormat) != IwiFormat::IMG_FORMAT_INVALID;
|
||||
}
|
||||
|
||||
std::string IwiWriter::GetFileExtension()
|
||||
{
|
||||
return ".iwi";
|
||||
}
|
||||
|
||||
void IwiWriter::DumpImage(std::ostream& stream, const Texture* texture)
|
||||
{
|
||||
assert(texture != nullptr);
|
||||
|
||||
WriteVersion(stream);
|
||||
|
||||
IwiHeader header{};
|
||||
header.flags = 0;
|
||||
|
||||
header.format = static_cast<int8_t>(GetIwiFormatForImageFormat(texture->GetFormat()));
|
||||
|
||||
if (!texture->HasMipMaps())
|
||||
header.flags |= IMG_FLAG_NOMIPMAPS;
|
||||
|
||||
auto currentFileSize = sizeof(IwiVersion) + sizeof(IwiHeader);
|
||||
|
||||
const auto textureMipCount = texture->HasMipMaps() ? texture->GetMipMapCount() : 1;
|
||||
for (auto currentMipLevel = textureMipCount - 1; currentMipLevel >= 0; currentMipLevel--)
|
||||
{
|
||||
const auto mipLevelSize = texture->GetSizeOfMipLevel(currentMipLevel) * texture->GetFaceCount();
|
||||
currentFileSize += mipLevelSize;
|
||||
|
||||
if (currentMipLevel < static_cast<int>(std::extent_v<decltype(IwiHeader::fileSizeForPicmip)>))
|
||||
header.fileSizeForPicmip[currentMipLevel] = currentFileSize;
|
||||
}
|
||||
|
||||
if (const auto* texture2D = dynamic_cast<const Texture2D*>(texture))
|
||||
{
|
||||
FillHeader2D(header, *texture2D);
|
||||
}
|
||||
else if (const auto* textureCube = dynamic_cast<const TextureCube*>(texture))
|
||||
{
|
||||
FillHeaderCube(header, *textureCube);
|
||||
}
|
||||
else if (const auto* texture3D = dynamic_cast<const Texture3D*>(texture))
|
||||
{
|
||||
FillHeader3D(header, *texture3D);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
stream.write(reinterpret_cast<char*>(&header), sizeof(IwiHeader));
|
||||
|
||||
for (auto currentMipLevel = textureMipCount - 1; currentMipLevel >= 0; currentMipLevel--)
|
||||
{
|
||||
const auto mipLevelSize = texture->GetSizeOfMipLevel(currentMipLevel) * texture->GetFaceCount();
|
||||
stream.write(reinterpret_cast<const char*>(texture->GetBufferForMipLevel(currentMipLevel)), mipLevelSize);
|
||||
}
|
||||
}
|
30
src/ObjImage/Image/IwiWriter6.h
Normal file
30
src/ObjImage/Image/IwiWriter6.h
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "IImageWriter.h"
|
||||
#include "Image/IwiTypes.h"
|
||||
|
||||
namespace iwi6
|
||||
{
|
||||
class IwiWriter final : public IImageWriter
|
||||
{
|
||||
static IwiFormat GetIwiFormatForImageFormat(const ImageFormat* imageFormat);
|
||||
|
||||
static void WriteVersion(std::ostream& stream);
|
||||
static void FillHeader2D(IwiHeader& header, const Texture2D& texture);
|
||||
static void FillHeaderCube(IwiHeader& header, const TextureCube& texture);
|
||||
static void FillHeader3D(IwiHeader& header, const Texture3D& texture);
|
||||
|
||||
public:
|
||||
IwiWriter();
|
||||
IwiWriter(const IwiWriter& other) = delete;
|
||||
IwiWriter(IwiWriter&& other) noexcept = delete;
|
||||
~IwiWriter() override;
|
||||
|
||||
IwiWriter& operator=(const IwiWriter& other) = delete;
|
||||
IwiWriter& operator=(IwiWriter&& other) noexcept = delete;
|
||||
|
||||
bool SupportsImageFormat(const ImageFormat* imageFormat) override;
|
||||
std::string GetFileExtension() override;
|
||||
void DumpImage(std::ostream& stream, const Texture* texture) override;
|
||||
};
|
||||
} // namespace iwi6
|
136
src/ObjImage/Image/IwiWriter8.cpp
Normal file
136
src/ObjImage/Image/IwiWriter8.cpp
Normal file
@ -0,0 +1,136 @@
|
||||
#include "IwiWriter8.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
using namespace iwi8;
|
||||
|
||||
IwiWriter::IwiWriter() = default;
|
||||
|
||||
IwiWriter::~IwiWriter() = default;
|
||||
|
||||
IwiFormat IwiWriter::GetIwiFormatForImageFormat(const ImageFormat* imageFormat)
|
||||
{
|
||||
switch (imageFormat->GetId())
|
||||
{
|
||||
case ImageFormatId::R8_G8_B8:
|
||||
return IwiFormat::IMG_FORMAT_BITMAP_RGB;
|
||||
|
||||
case ImageFormatId::R8_G8_B8_A8:
|
||||
return IwiFormat::IMG_FORMAT_BITMAP_RGBA;
|
||||
|
||||
case ImageFormatId::A8:
|
||||
return IwiFormat::IMG_FORMAT_BITMAP_ALPHA;
|
||||
|
||||
case ImageFormatId::BC1:
|
||||
return IwiFormat::IMG_FORMAT_DXT1;
|
||||
|
||||
case ImageFormatId::BC2:
|
||||
return IwiFormat::IMG_FORMAT_DXT3;
|
||||
|
||||
case ImageFormatId::BC3:
|
||||
return IwiFormat::IMG_FORMAT_DXT5;
|
||||
|
||||
case ImageFormatId::BC5:
|
||||
return IwiFormat::IMG_FORMAT_DXN;
|
||||
|
||||
default:
|
||||
return IwiFormat::IMG_FORMAT_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
void IwiWriter::WriteVersion(std::ostream& stream)
|
||||
{
|
||||
IwiVersion version{};
|
||||
version.tag[0] = 'I';
|
||||
version.tag[1] = 'W';
|
||||
version.tag[2] = 'i';
|
||||
version.version = 8;
|
||||
|
||||
stream.write(reinterpret_cast<char*>(&version), sizeof(IwiVersion));
|
||||
}
|
||||
|
||||
void IwiWriter::FillHeader2D(IwiHeader& header, const Texture2D& texture)
|
||||
{
|
||||
header.dimensions[0] = static_cast<uint16_t>(texture.GetWidth());
|
||||
header.dimensions[1] = static_cast<uint16_t>(texture.GetHeight());
|
||||
header.dimensions[2] = 1u;
|
||||
}
|
||||
|
||||
void IwiWriter::FillHeaderCube(IwiHeader& header, const TextureCube& texture)
|
||||
{
|
||||
header.dimensions[0] = static_cast<uint16_t>(texture.GetWidth());
|
||||
header.dimensions[1] = static_cast<uint16_t>(texture.GetHeight());
|
||||
header.dimensions[2] = 1u;
|
||||
header.flags |= IMG_FLAG_MAPTYPE_CUBE;
|
||||
}
|
||||
|
||||
void IwiWriter::FillHeader3D(IwiHeader& header, const Texture3D& texture)
|
||||
{
|
||||
header.dimensions[0] = static_cast<uint16_t>(texture.GetWidth());
|
||||
header.dimensions[1] = static_cast<uint16_t>(texture.GetHeight());
|
||||
header.dimensions[2] = static_cast<uint16_t>(texture.GetDepth());
|
||||
header.flags |= IMG_FLAG_MAPTYPE_3D;
|
||||
}
|
||||
|
||||
bool IwiWriter::SupportsImageFormat(const ImageFormat* imageFormat)
|
||||
{
|
||||
return GetIwiFormatForImageFormat(imageFormat) != IwiFormat::IMG_FORMAT_INVALID;
|
||||
}
|
||||
|
||||
std::string IwiWriter::GetFileExtension()
|
||||
{
|
||||
return ".iwi";
|
||||
}
|
||||
|
||||
void IwiWriter::DumpImage(std::ostream& stream, const Texture* texture)
|
||||
{
|
||||
assert(texture != nullptr);
|
||||
|
||||
WriteVersion(stream);
|
||||
|
||||
IwiHeader header{};
|
||||
header.flags = 0;
|
||||
|
||||
header.format = static_cast<int8_t>(GetIwiFormatForImageFormat(texture->GetFormat()));
|
||||
|
||||
if (!texture->HasMipMaps())
|
||||
header.flags |= IMG_FLAG_NOMIPMAPS;
|
||||
|
||||
auto currentFileSize = sizeof(IwiVersion) + sizeof(IwiHeader);
|
||||
|
||||
const auto textureMipCount = texture->HasMipMaps() ? texture->GetMipMapCount() : 1;
|
||||
for (auto currentMipLevel = textureMipCount - 1; currentMipLevel >= 0; currentMipLevel--)
|
||||
{
|
||||
const auto mipLevelSize = texture->GetSizeOfMipLevel(currentMipLevel) * texture->GetFaceCount();
|
||||
currentFileSize += mipLevelSize;
|
||||
|
||||
if (currentMipLevel < static_cast<int>(std::extent_v<decltype(IwiHeader::fileSizeForPicmip)>))
|
||||
header.fileSizeForPicmip[currentMipLevel] = currentFileSize;
|
||||
}
|
||||
|
||||
if (const auto* texture2D = dynamic_cast<const Texture2D*>(texture))
|
||||
{
|
||||
FillHeader2D(header, *texture2D);
|
||||
}
|
||||
else if (const auto* textureCube = dynamic_cast<const TextureCube*>(texture))
|
||||
{
|
||||
FillHeaderCube(header, *textureCube);
|
||||
}
|
||||
else if (const auto* texture3D = dynamic_cast<const Texture3D*>(texture))
|
||||
{
|
||||
FillHeader3D(header, *texture3D);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
stream.write(reinterpret_cast<char*>(&header), sizeof(IwiHeader));
|
||||
|
||||
for (auto currentMipLevel = textureMipCount - 1; currentMipLevel >= 0; currentMipLevel--)
|
||||
{
|
||||
const auto mipLevelSize = texture->GetSizeOfMipLevel(currentMipLevel) * texture->GetFaceCount();
|
||||
stream.write(reinterpret_cast<const char*>(texture->GetBufferForMipLevel(currentMipLevel)), mipLevelSize);
|
||||
}
|
||||
}
|
30
src/ObjImage/Image/IwiWriter8.h
Normal file
30
src/ObjImage/Image/IwiWriter8.h
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "IImageWriter.h"
|
||||
#include "Image/IwiTypes.h"
|
||||
|
||||
namespace iwi8
|
||||
{
|
||||
class IwiWriter final : public IImageWriter
|
||||
{
|
||||
static IwiFormat GetIwiFormatForImageFormat(const ImageFormat* imageFormat);
|
||||
|
||||
static void WriteVersion(std::ostream& stream);
|
||||
static void FillHeader2D(IwiHeader& header, const Texture2D& texture);
|
||||
static void FillHeaderCube(IwiHeader& header, const TextureCube& texture);
|
||||
static void FillHeader3D(IwiHeader& header, const Texture3D& texture);
|
||||
|
||||
public:
|
||||
IwiWriter();
|
||||
IwiWriter(const IwiWriter& other) = delete;
|
||||
IwiWriter(IwiWriter&& other) noexcept = delete;
|
||||
~IwiWriter() override;
|
||||
|
||||
IwiWriter& operator=(const IwiWriter& other) = delete;
|
||||
IwiWriter& operator=(IwiWriter&& other) noexcept = delete;
|
||||
|
||||
bool SupportsImageFormat(const ImageFormat* imageFormat) override;
|
||||
std::string GetFileExtension() override;
|
||||
void DumpImage(std::ostream& stream, const Texture* texture) override;
|
||||
};
|
||||
} // namespace iwi8
|
443
src/ObjImage/Image/Texture.cpp
Normal file
443
src/ObjImage/Image/Texture.cpp
Normal file
@ -0,0 +1,443 @@
|
||||
#include "Texture.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
// ==============================================
|
||||
// ================= Texture ====================
|
||||
// ==============================================
|
||||
Texture::Texture(const ImageFormat* format, const bool mipMaps)
|
||||
{
|
||||
m_format = format;
|
||||
m_has_mip_maps = mipMaps;
|
||||
m_data = nullptr;
|
||||
}
|
||||
|
||||
Texture::Texture(Texture&& other) noexcept
|
||||
{
|
||||
m_format = other.m_format;
|
||||
m_has_mip_maps = other.m_has_mip_maps;
|
||||
m_data = other.m_data;
|
||||
|
||||
other.m_data = nullptr;
|
||||
}
|
||||
|
||||
Texture& Texture::operator=(Texture&& other) noexcept
|
||||
{
|
||||
m_format = other.m_format;
|
||||
m_has_mip_maps = other.m_has_mip_maps;
|
||||
m_data = other.m_data;
|
||||
|
||||
other.m_data = nullptr;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Texture::~Texture()
|
||||
{
|
||||
delete[] m_data;
|
||||
m_data = nullptr;
|
||||
}
|
||||
|
||||
const ImageFormat* Texture::GetFormat() const
|
||||
{
|
||||
return m_format;
|
||||
}
|
||||
|
||||
void Texture::Allocate()
|
||||
{
|
||||
size_t storageRequirement = 0;
|
||||
const int mipLevelCount = m_has_mip_maps ? GetMipMapCount() : 1;
|
||||
|
||||
for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++)
|
||||
{
|
||||
storageRequirement += GetSizeOfMipLevel(currentMipLevel) * GetFaceCount();
|
||||
}
|
||||
|
||||
if (storageRequirement > 0)
|
||||
{
|
||||
m_data = new uint8_t[storageRequirement];
|
||||
memset(m_data, 0, storageRequirement);
|
||||
}
|
||||
}
|
||||
|
||||
bool Texture::Empty() const
|
||||
{
|
||||
return m_data == nullptr;
|
||||
}
|
||||
|
||||
bool Texture::HasMipMaps() const
|
||||
{
|
||||
return m_has_mip_maps;
|
||||
}
|
||||
|
||||
uint8_t* Texture::GetBufferForMipLevel(const int mipLevel)
|
||||
{
|
||||
return GetBufferForMipLevel(mipLevel, 0);
|
||||
}
|
||||
|
||||
const uint8_t* Texture::GetBufferForMipLevel(const int mipLevel) const
|
||||
{
|
||||
return GetBufferForMipLevel(mipLevel, 0);
|
||||
}
|
||||
|
||||
// ==============================================
|
||||
// ================ Texture2D ===================
|
||||
// ==============================================
|
||||
Texture2D::Texture2D(const ImageFormat* format, const unsigned width, const unsigned height)
|
||||
: Texture2D(format, width, height, false)
|
||||
{
|
||||
}
|
||||
|
||||
Texture2D::Texture2D(const ImageFormat* format, const unsigned width, const unsigned height, const bool mipMaps)
|
||||
: Texture(format, mipMaps)
|
||||
{
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
}
|
||||
|
||||
Texture2D::Texture2D(Texture2D&& other) noexcept
|
||||
: Texture(std::move(other)),
|
||||
m_width(other.m_width),
|
||||
m_height(other.m_height)
|
||||
{
|
||||
}
|
||||
|
||||
Texture2D::~Texture2D() = default;
|
||||
|
||||
Texture2D& Texture2D::operator=(Texture2D&& other) noexcept
|
||||
{
|
||||
if (this == &other)
|
||||
return *this;
|
||||
|
||||
Texture::operator=(std::move(other));
|
||||
m_width = other.m_width;
|
||||
m_height = other.m_height;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
TextureType Texture2D::GetTextureType() const
|
||||
{
|
||||
return TextureType::T_2D;
|
||||
}
|
||||
|
||||
unsigned Texture2D::GetWidth() const
|
||||
{
|
||||
return m_width;
|
||||
}
|
||||
|
||||
unsigned Texture2D::GetHeight() const
|
||||
{
|
||||
return m_height;
|
||||
}
|
||||
|
||||
unsigned Texture2D::GetDepth() const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Texture2D::GetFaceCount() const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t Texture2D::GetSizeOfMipLevel(const int mipLevel) const
|
||||
{
|
||||
return m_format->GetSizeOfMipLevel(mipLevel, m_width, m_height, 1);
|
||||
}
|
||||
|
||||
int Texture2D::GetMipMapCount() const
|
||||
{
|
||||
unsigned maxDimension = std::max(m_width, m_height);
|
||||
|
||||
int mipMapCount = 0;
|
||||
|
||||
while (maxDimension != 0)
|
||||
{
|
||||
maxDimension >>= 1;
|
||||
mipMapCount++;
|
||||
}
|
||||
|
||||
return mipMapCount;
|
||||
}
|
||||
|
||||
uint8_t* Texture2D::GetBufferForMipLevel(const int mipLevel, const int face)
|
||||
{
|
||||
assert(mipLevel >= 0);
|
||||
assert(mipLevel < (m_has_mip_maps ? GetMipMapCount() : 1));
|
||||
assert(face == 0);
|
||||
assert(m_data);
|
||||
|
||||
if (mipLevel < 0 || mipLevel >= (m_has_mip_maps ? GetMipMapCount() : 1))
|
||||
return nullptr;
|
||||
|
||||
if (face != 0)
|
||||
return nullptr;
|
||||
|
||||
if (!m_data)
|
||||
return nullptr;
|
||||
|
||||
size_t bufferOffset = 0;
|
||||
for (int previousMipLevel = 0; previousMipLevel < mipLevel; previousMipLevel++)
|
||||
{
|
||||
bufferOffset += GetSizeOfMipLevel(previousMipLevel);
|
||||
}
|
||||
|
||||
return &m_data[bufferOffset];
|
||||
}
|
||||
|
||||
const uint8_t* Texture2D::GetBufferForMipLevel(const int mipLevel, const int face) const
|
||||
{
|
||||
assert(mipLevel >= 0);
|
||||
assert(mipLevel < (m_has_mip_maps ? GetMipMapCount() : 1));
|
||||
assert(face == 0);
|
||||
assert(m_data);
|
||||
|
||||
if (mipLevel < 0 || mipLevel >= (m_has_mip_maps ? GetMipMapCount() : 1))
|
||||
return nullptr;
|
||||
|
||||
if (face != 0)
|
||||
return nullptr;
|
||||
|
||||
if (!m_data)
|
||||
return nullptr;
|
||||
|
||||
size_t bufferOffset = 0;
|
||||
for (int previousMipLevel = 0; previousMipLevel < mipLevel; previousMipLevel++)
|
||||
{
|
||||
bufferOffset += GetSizeOfMipLevel(previousMipLevel);
|
||||
}
|
||||
|
||||
return &m_data[bufferOffset];
|
||||
}
|
||||
|
||||
// ==============================================
|
||||
// =============== TextureCube ==================
|
||||
// ==============================================
|
||||
const int TextureCube::FACE_COUNT = 6;
|
||||
|
||||
TextureCube::TextureCube(const ImageFormat* format, const unsigned width, const unsigned height)
|
||||
: Texture2D(format, width, height)
|
||||
{
|
||||
}
|
||||
|
||||
TextureCube::TextureCube(const ImageFormat* format, const unsigned width, const unsigned height, const bool mipMaps)
|
||||
: Texture2D(format, width, height, mipMaps)
|
||||
{
|
||||
}
|
||||
|
||||
TextureCube::TextureCube(TextureCube&& other) noexcept
|
||||
: Texture2D(std::move(other))
|
||||
{
|
||||
}
|
||||
|
||||
TextureCube::~TextureCube() = default;
|
||||
|
||||
TextureCube& TextureCube::operator=(TextureCube&& other) noexcept
|
||||
{
|
||||
if (this == &other)
|
||||
return *this;
|
||||
|
||||
Texture2D::operator=(std::move(other));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
TextureType TextureCube::GetTextureType() const
|
||||
{
|
||||
return TextureType::T_CUBE;
|
||||
}
|
||||
|
||||
int TextureCube::GetFaceCount() const
|
||||
{
|
||||
return FACE_COUNT;
|
||||
}
|
||||
|
||||
uint8_t* TextureCube::GetBufferForMipLevel(const int mipLevel, const int face)
|
||||
{
|
||||
assert(mipLevel >= 0);
|
||||
assert(mipLevel < (m_has_mip_maps ? GetMipMapCount() : 1));
|
||||
assert(face >= 0);
|
||||
assert(face < FACE_COUNT);
|
||||
assert(m_data);
|
||||
|
||||
if (mipLevel < 0 || mipLevel >= (m_has_mip_maps ? GetMipMapCount() : 1))
|
||||
return nullptr;
|
||||
|
||||
if (face < 0 || face >= FACE_COUNT)
|
||||
return nullptr;
|
||||
|
||||
if (!m_data)
|
||||
return nullptr;
|
||||
|
||||
size_t bufferOffset = 0;
|
||||
for (int previousMipLevel = 0; previousMipLevel < mipLevel; previousMipLevel++)
|
||||
{
|
||||
bufferOffset += GetSizeOfMipLevel(previousMipLevel) * FACE_COUNT;
|
||||
}
|
||||
|
||||
return &m_data[bufferOffset + GetSizeOfMipLevel(mipLevel) * face];
|
||||
}
|
||||
|
||||
const uint8_t* TextureCube::GetBufferForMipLevel(const int mipLevel, const int face) const
|
||||
{
|
||||
assert(mipLevel >= 0);
|
||||
assert(mipLevel < (m_has_mip_maps ? GetMipMapCount() : 1));
|
||||
assert(face >= 0);
|
||||
assert(face < FACE_COUNT);
|
||||
assert(m_data);
|
||||
|
||||
if (mipLevel < 0 || mipLevel >= (m_has_mip_maps ? GetMipMapCount() : 1))
|
||||
return nullptr;
|
||||
|
||||
if (face < 0 || face >= FACE_COUNT)
|
||||
return nullptr;
|
||||
|
||||
if (!m_data)
|
||||
return nullptr;
|
||||
|
||||
size_t bufferOffset = 0;
|
||||
for (int previousMipLevel = 0; previousMipLevel < mipLevel; previousMipLevel++)
|
||||
{
|
||||
bufferOffset += GetSizeOfMipLevel(previousMipLevel) * FACE_COUNT;
|
||||
}
|
||||
|
||||
return &m_data[bufferOffset + GetSizeOfMipLevel(mipLevel) * face];
|
||||
}
|
||||
|
||||
// ==============================================
|
||||
// ================ Texture3D ===================
|
||||
// ==============================================
|
||||
|
||||
Texture3D::Texture3D(const ImageFormat* format, const unsigned width, const unsigned height, const unsigned depth)
|
||||
: Texture3D(format, width, height, depth, false)
|
||||
{
|
||||
}
|
||||
|
||||
Texture3D::Texture3D(const ImageFormat* format, const unsigned width, const unsigned height, const unsigned depth, const bool mipMaps)
|
||||
: Texture(format, mipMaps)
|
||||
{
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
m_depth = depth;
|
||||
}
|
||||
|
||||
Texture3D::Texture3D(Texture3D&& other) noexcept
|
||||
: Texture(std::move(other)),
|
||||
m_width(other.m_width),
|
||||
m_height(other.m_height),
|
||||
m_depth(other.m_depth)
|
||||
{
|
||||
}
|
||||
|
||||
Texture3D::~Texture3D() = default;
|
||||
|
||||
Texture3D& Texture3D::operator=(Texture3D&& other) noexcept
|
||||
{
|
||||
if (this == &other)
|
||||
return *this;
|
||||
|
||||
Texture::operator=(std::move(other));
|
||||
m_width = other.m_width;
|
||||
m_height = other.m_height;
|
||||
m_depth = other.m_depth;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
TextureType Texture3D::GetTextureType() const
|
||||
{
|
||||
return TextureType::T_3D;
|
||||
}
|
||||
|
||||
unsigned Texture3D::GetWidth() const
|
||||
{
|
||||
return m_width;
|
||||
}
|
||||
|
||||
unsigned Texture3D::GetHeight() const
|
||||
{
|
||||
return m_height;
|
||||
}
|
||||
|
||||
unsigned Texture3D::GetDepth() const
|
||||
{
|
||||
return m_depth;
|
||||
}
|
||||
|
||||
int Texture3D::GetFaceCount() const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t Texture3D::GetSizeOfMipLevel(const int mipLevel) const
|
||||
{
|
||||
return m_format->GetSizeOfMipLevel(mipLevel, m_width, m_height, m_depth);
|
||||
}
|
||||
|
||||
int Texture3D::GetMipMapCount() const
|
||||
{
|
||||
unsigned maxDimension = std::max(std::max(m_width, m_height), m_depth);
|
||||
|
||||
int mipMapCount = 0;
|
||||
|
||||
while (maxDimension != 0)
|
||||
{
|
||||
maxDimension >>= 1;
|
||||
mipMapCount++;
|
||||
}
|
||||
|
||||
return mipMapCount;
|
||||
}
|
||||
|
||||
uint8_t* Texture3D::GetBufferForMipLevel(const int mipLevel, const int face)
|
||||
{
|
||||
assert(mipLevel >= 0);
|
||||
assert(mipLevel < (m_has_mip_maps ? GetMipMapCount() : 1));
|
||||
assert(face == 0);
|
||||
assert(m_data);
|
||||
|
||||
if (mipLevel < 0 || mipLevel >= (m_has_mip_maps ? GetMipMapCount() : 1))
|
||||
return nullptr;
|
||||
|
||||
if (face != 0)
|
||||
return nullptr;
|
||||
|
||||
if (!m_data)
|
||||
return nullptr;
|
||||
|
||||
size_t bufferOffset = 0;
|
||||
for (int previousMipLevel = 0; previousMipLevel < mipLevel; previousMipLevel++)
|
||||
{
|
||||
bufferOffset += GetSizeOfMipLevel(previousMipLevel);
|
||||
}
|
||||
|
||||
return &m_data[bufferOffset];
|
||||
}
|
||||
|
||||
const uint8_t* Texture3D::GetBufferForMipLevel(const int mipLevel, const int face) const
|
||||
{
|
||||
assert(mipLevel >= 0);
|
||||
assert(mipLevel < (m_has_mip_maps ? GetMipMapCount() : 1));
|
||||
assert(face == 0);
|
||||
assert(m_data);
|
||||
|
||||
if (mipLevel < 0 || mipLevel >= (m_has_mip_maps ? GetMipMapCount() : 1))
|
||||
return nullptr;
|
||||
|
||||
if (face != 0)
|
||||
return nullptr;
|
||||
|
||||
if (!m_data)
|
||||
return nullptr;
|
||||
|
||||
size_t bufferOffset = 0;
|
||||
for (int previousMipLevel = 0; previousMipLevel < mipLevel; previousMipLevel++)
|
||||
{
|
||||
bufferOffset += GetSizeOfMipLevel(previousMipLevel);
|
||||
}
|
||||
|
||||
return &m_data[bufferOffset];
|
||||
}
|
132
src/ObjImage/Image/Texture.h
Normal file
132
src/ObjImage/Image/Texture.h
Normal file
@ -0,0 +1,132 @@
|
||||
#pragma once
|
||||
#include "ImageFormat.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
enum class TextureType : std::uint8_t
|
||||
{
|
||||
T_2D,
|
||||
T_CUBE,
|
||||
T_3D
|
||||
};
|
||||
|
||||
class Texture
|
||||
{
|
||||
protected:
|
||||
const ImageFormat* m_format;
|
||||
bool m_has_mip_maps;
|
||||
uint8_t* m_data;
|
||||
|
||||
Texture(const ImageFormat* format, bool mipMaps);
|
||||
Texture(Texture&& other) noexcept;
|
||||
|
||||
Texture& operator=(Texture&& other) noexcept;
|
||||
|
||||
public:
|
||||
Texture(const Texture& other) = delete;
|
||||
virtual ~Texture();
|
||||
|
||||
Texture& operator=(const Texture& other) = delete;
|
||||
|
||||
[[nodiscard]] virtual TextureType GetTextureType() const = 0;
|
||||
[[nodiscard]] const ImageFormat* GetFormat() const;
|
||||
|
||||
[[nodiscard]] virtual unsigned GetWidth() const = 0;
|
||||
[[nodiscard]] virtual unsigned GetHeight() const = 0;
|
||||
[[nodiscard]] virtual unsigned GetDepth() const = 0;
|
||||
[[nodiscard]] virtual int GetFaceCount() const = 0;
|
||||
|
||||
void Allocate();
|
||||
[[nodiscard]] bool Empty() const;
|
||||
|
||||
[[nodiscard]] virtual size_t GetSizeOfMipLevel(int mipLevel) const = 0;
|
||||
[[nodiscard]] virtual uint8_t* GetBufferForMipLevel(int mipLevel, int face) = 0;
|
||||
[[nodiscard]] virtual const uint8_t* GetBufferForMipLevel(int mipLevel, int face) const = 0;
|
||||
[[nodiscard]] uint8_t* GetBufferForMipLevel(int mipLevel);
|
||||
[[nodiscard]] const uint8_t* GetBufferForMipLevel(int mipLevel) const;
|
||||
|
||||
[[nodiscard]] bool HasMipMaps() const;
|
||||
[[nodiscard]] virtual int GetMipMapCount() const = 0;
|
||||
};
|
||||
|
||||
class Texture2D : public Texture
|
||||
{
|
||||
protected:
|
||||
unsigned m_width;
|
||||
unsigned m_height;
|
||||
|
||||
public:
|
||||
Texture2D(const ImageFormat* format, unsigned width, unsigned height);
|
||||
Texture2D(const ImageFormat* format, unsigned width, unsigned height, bool mipMaps);
|
||||
Texture2D(const Texture2D& other) = delete;
|
||||
Texture2D(Texture2D&& other) noexcept;
|
||||
~Texture2D() override;
|
||||
|
||||
Texture2D& operator=(const Texture2D& other) = delete;
|
||||
Texture2D& operator=(Texture2D&& other) noexcept;
|
||||
|
||||
[[nodiscard]] TextureType GetTextureType() const override;
|
||||
|
||||
[[nodiscard]] unsigned GetWidth() const override;
|
||||
[[nodiscard]] unsigned GetHeight() const override;
|
||||
[[nodiscard]] unsigned GetDepth() const override;
|
||||
[[nodiscard]] int GetFaceCount() const override;
|
||||
|
||||
[[nodiscard]] size_t GetSizeOfMipLevel(int mipLevel) const override;
|
||||
[[nodiscard]] uint8_t* GetBufferForMipLevel(int mipLevel, int face) override;
|
||||
[[nodiscard]] const uint8_t* GetBufferForMipLevel(int mipLevel, int face) const override;
|
||||
|
||||
[[nodiscard]] int GetMipMapCount() const override;
|
||||
};
|
||||
|
||||
class TextureCube final : public Texture2D
|
||||
{
|
||||
static const int FACE_COUNT;
|
||||
|
||||
public:
|
||||
TextureCube(const ImageFormat* format, unsigned width, unsigned height);
|
||||
TextureCube(const ImageFormat* format, unsigned width, unsigned height, bool mipMaps);
|
||||
TextureCube(const TextureCube& other) = delete;
|
||||
TextureCube(TextureCube&& other) noexcept;
|
||||
~TextureCube() override;
|
||||
|
||||
TextureCube& operator=(const TextureCube& other) = delete;
|
||||
TextureCube& operator=(TextureCube&& other) noexcept;
|
||||
|
||||
[[nodiscard]] TextureType GetTextureType() const override;
|
||||
|
||||
[[nodiscard]] int GetFaceCount() const override;
|
||||
|
||||
[[nodiscard]] uint8_t* GetBufferForMipLevel(int mipLevel, int face) override;
|
||||
[[nodiscard]] const uint8_t* GetBufferForMipLevel(int mipLevel, int face) const override;
|
||||
};
|
||||
|
||||
class Texture3D final : public Texture
|
||||
{
|
||||
unsigned m_width;
|
||||
unsigned m_height;
|
||||
unsigned m_depth;
|
||||
|
||||
public:
|
||||
Texture3D(const ImageFormat* format, unsigned width, unsigned height, unsigned depth);
|
||||
Texture3D(const ImageFormat* format, unsigned width, unsigned height, unsigned depth, bool mipMaps);
|
||||
Texture3D(const Texture3D& other) = delete;
|
||||
Texture3D(Texture3D&& other) noexcept;
|
||||
~Texture3D() override;
|
||||
|
||||
Texture3D& operator=(const Texture3D& other) = delete;
|
||||
Texture3D& operator=(Texture3D&& other) noexcept;
|
||||
|
||||
[[nodiscard]] TextureType GetTextureType() const override;
|
||||
|
||||
[[nodiscard]] unsigned GetWidth() const override;
|
||||
[[nodiscard]] unsigned GetHeight() const override;
|
||||
[[nodiscard]] unsigned GetDepth() const override;
|
||||
[[nodiscard]] int GetFaceCount() const override;
|
||||
|
||||
[[nodiscard]] size_t GetSizeOfMipLevel(int mipLevel) const override;
|
||||
[[nodiscard]] uint8_t* GetBufferForMipLevel(int mipLevel, int face) override;
|
||||
[[nodiscard]] const uint8_t* GetBufferForMipLevel(int mipLevel, int face) const override;
|
||||
|
||||
[[nodiscard]] int GetMipMapCount() const override;
|
||||
};
|
222
src/ObjImage/Image/TextureConverter.cpp
Normal file
222
src/ObjImage/Image/TextureConverter.cpp
Normal file
@ -0,0 +1,222 @@
|
||||
#include "TextureConverter.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
constexpr uint64_t TextureConverter::Mask1(const unsigned length)
|
||||
{
|
||||
if (length >= sizeof(uint64_t) * 8)
|
||||
return UINT64_MAX;
|
||||
|
||||
return UINT64_MAX >> (sizeof(uint64_t) * 8 - length);
|
||||
}
|
||||
|
||||
void TextureConverter::SetPixelFunctions(const unsigned inBitCount, const unsigned outBitCount)
|
||||
{
|
||||
switch (inBitCount)
|
||||
{
|
||||
case 16:
|
||||
m_read_pixel_func = [](const void* offset, unsigned bitCount)
|
||||
{
|
||||
return static_cast<uint64_t>(*static_cast<const uint16_t*>(offset));
|
||||
};
|
||||
break;
|
||||
|
||||
case 32:
|
||||
m_read_pixel_func = [](const void* offset, unsigned bitCount)
|
||||
{
|
||||
return static_cast<uint64_t>(*static_cast<const uint32_t*>(offset));
|
||||
};
|
||||
break;
|
||||
|
||||
case 64:
|
||||
m_read_pixel_func = [](const void* offset, unsigned bitCount)
|
||||
{
|
||||
return *static_cast<const uint64_t*>(offset);
|
||||
};
|
||||
break;
|
||||
|
||||
default:
|
||||
if (inBitCount <= 64)
|
||||
{
|
||||
m_read_pixel_func = [](const void* offset, const unsigned bitCount)
|
||||
{
|
||||
uint64_t result = 0;
|
||||
|
||||
for (auto pixelOffset = 0u; pixelOffset < bitCount; pixelOffset += 8)
|
||||
{
|
||||
result |= (static_cast<uint64_t>(*(static_cast<const uint8_t*>(offset) + (pixelOffset / 8))) << pixelOffset);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false);
|
||||
m_read_pixel_func = [](const void* offset, unsigned bitCount)
|
||||
{
|
||||
return 0ull;
|
||||
};
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
switch (outBitCount)
|
||||
{
|
||||
case 16:
|
||||
m_write_pixel_func = [](void* offset, const uint64_t pixel, unsigned bitCount)
|
||||
{
|
||||
*static_cast<uint16_t*>(offset) = static_cast<uint16_t>(pixel);
|
||||
};
|
||||
break;
|
||||
|
||||
case 32:
|
||||
m_write_pixel_func = [](void* offset, const uint64_t pixel, unsigned bitCount)
|
||||
{
|
||||
*static_cast<uint32_t*>(offset) = static_cast<uint32_t>(pixel);
|
||||
};
|
||||
break;
|
||||
|
||||
case 64:
|
||||
m_write_pixel_func = [](void* offset, const uint64_t pixel, unsigned bitCount)
|
||||
{
|
||||
*static_cast<uint64_t*>(offset) = pixel;
|
||||
};
|
||||
break;
|
||||
|
||||
default:
|
||||
if (inBitCount <= 64)
|
||||
{
|
||||
m_write_pixel_func = [](void* offset, const uint64_t pixel, const unsigned bitCount)
|
||||
{
|
||||
for (auto pixelOffset = 0u; pixelOffset < bitCount; pixelOffset += 8)
|
||||
{
|
||||
*(static_cast<uint8_t*>(offset) + (pixelOffset / 8)) = static_cast<uint8_t>(pixel >> pixelOffset);
|
||||
}
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false);
|
||||
m_write_pixel_func = [](void* offset, uint64_t pixel, unsigned bitCount) {};
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
TextureConverter::TextureConverter(const Texture* inputTexture, const ImageFormat* targetFormat)
|
||||
: m_input_texture(inputTexture),
|
||||
m_output_texture(nullptr),
|
||||
m_input_format(inputTexture->GetFormat()),
|
||||
m_output_format(targetFormat)
|
||||
{
|
||||
}
|
||||
|
||||
void TextureConverter::CreateOutputTexture()
|
||||
{
|
||||
switch (m_input_texture->GetTextureType())
|
||||
{
|
||||
case TextureType::T_2D:
|
||||
m_output_texture =
|
||||
std::make_unique<Texture2D>(m_output_format, m_input_texture->GetWidth(), m_input_texture->GetHeight(), m_input_texture->HasMipMaps());
|
||||
break;
|
||||
|
||||
case TextureType::T_CUBE:
|
||||
m_output_texture =
|
||||
std::make_unique<TextureCube>(m_output_format, m_input_texture->GetWidth(), m_input_texture->GetHeight(), m_input_texture->HasMipMaps());
|
||||
break;
|
||||
|
||||
case TextureType::T_3D:
|
||||
m_output_texture = std::make_unique<Texture3D>(
|
||||
m_output_format, m_input_texture->GetWidth(), m_input_texture->GetHeight(), m_input_texture->GetDepth(), m_input_texture->HasMipMaps());
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
m_output_texture->Allocate();
|
||||
}
|
||||
|
||||
void TextureConverter::ReorderUnsignedToUnsigned() const
|
||||
{
|
||||
const auto* inputFormat = dynamic_cast<const ImageFormatUnsigned*>(m_input_format);
|
||||
const auto* outputFormat = dynamic_cast<const ImageFormatUnsigned*>(m_output_format);
|
||||
const auto mipCount = m_input_texture->HasMipMaps() ? m_input_texture->GetMipMapCount() : 1;
|
||||
|
||||
const auto rInputMask = inputFormat->HasR() ? Mask1(inputFormat->m_r_size) << inputFormat->m_r_offset : 0;
|
||||
const auto gInputMask = inputFormat->HasG() ? Mask1(inputFormat->m_g_size) << inputFormat->m_g_offset : 0;
|
||||
const auto bInputMask = inputFormat->HasB() ? Mask1(inputFormat->m_b_size) << inputFormat->m_b_offset : 0;
|
||||
const auto aInputMask = inputFormat->HasA() ? Mask1(inputFormat->m_a_size) << inputFormat->m_a_offset : 0;
|
||||
const bool rConvert = rInputMask != 0 && outputFormat->m_r_size > 0;
|
||||
const bool gConvert = gInputMask != 0 && outputFormat->m_g_size > 0;
|
||||
const bool bConvert = bInputMask != 0 && outputFormat->m_b_size > 0;
|
||||
const bool aConvert = aInputMask != 0 && outputFormat->m_a_size > 0;
|
||||
|
||||
for (auto mipLevel = 0; mipLevel < mipCount; mipLevel++)
|
||||
{
|
||||
const auto mipLevelSize = m_input_texture->GetSizeOfMipLevel(mipLevel) * m_input_texture->GetFaceCount();
|
||||
const auto* inputBuffer = m_input_texture->GetBufferForMipLevel(mipLevel);
|
||||
auto* outputBuffer = m_output_texture->GetBufferForMipLevel(mipLevel);
|
||||
|
||||
const auto inputBytePerPixel = inputFormat->m_bits_per_pixel / 8;
|
||||
const auto outputBytePerPixel = outputFormat->m_bits_per_pixel / 8;
|
||||
|
||||
auto outputOffset = 0u;
|
||||
for (auto inputOffset = 0u; inputOffset < mipLevelSize; inputOffset += inputBytePerPixel, outputOffset += outputBytePerPixel)
|
||||
{
|
||||
uint64_t outPixel = 0;
|
||||
const auto inPixel = m_read_pixel_func(&inputBuffer[inputOffset], inputFormat->m_bits_per_pixel);
|
||||
|
||||
if (rConvert)
|
||||
outPixel |= (inPixel & rInputMask) >> inputFormat->m_r_offset << outputFormat->m_r_offset;
|
||||
if (gConvert)
|
||||
outPixel |= (inPixel & gInputMask) >> inputFormat->m_g_offset << outputFormat->m_g_offset;
|
||||
if (bConvert)
|
||||
outPixel |= (inPixel & bInputMask) >> inputFormat->m_b_offset << outputFormat->m_b_offset;
|
||||
if (aConvert)
|
||||
outPixel |= (inPixel & aInputMask) >> inputFormat->m_a_offset << outputFormat->m_a_offset;
|
||||
|
||||
m_write_pixel_func(&outputBuffer[outputOffset], outPixel, outputFormat->m_bits_per_pixel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TextureConverter::ConvertUnsignedToUnsigned()
|
||||
{
|
||||
const auto* inputFormat = dynamic_cast<const ImageFormatUnsigned*>(m_input_format);
|
||||
const auto* outputFormat = dynamic_cast<const ImageFormatUnsigned*>(m_output_format);
|
||||
|
||||
assert(inputFormat->m_bits_per_pixel <= 64);
|
||||
assert(outputFormat->m_bits_per_pixel <= 64);
|
||||
|
||||
SetPixelFunctions(inputFormat->m_bits_per_pixel, outputFormat->m_bits_per_pixel);
|
||||
|
||||
if (inputFormat->m_r_size == outputFormat->m_r_size && inputFormat->m_g_size == outputFormat->m_g_size && inputFormat->m_b_size == outputFormat->m_b_size
|
||||
&& inputFormat->m_a_size == outputFormat->m_a_size)
|
||||
{
|
||||
ReorderUnsignedToUnsigned();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unsupported as of now
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Texture> TextureConverter::Convert()
|
||||
{
|
||||
CreateOutputTexture();
|
||||
|
||||
if (m_input_format->GetType() == ImageFormatType::UNSIGNED && m_output_format->GetType() == ImageFormatType::UNSIGNED)
|
||||
{
|
||||
ConvertUnsignedToUnsigned();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unsupported as of now
|
||||
assert(false);
|
||||
}
|
||||
|
||||
return std::move(m_output_texture);
|
||||
}
|
31
src/ObjImage/Image/TextureConverter.h
Normal file
31
src/ObjImage/Image/TextureConverter.h
Normal file
@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include "Texture.h"
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
class TextureConverter
|
||||
{
|
||||
public:
|
||||
TextureConverter(const Texture* inputTexture, const ImageFormat* targetFormat);
|
||||
|
||||
std::unique_ptr<Texture> Convert();
|
||||
|
||||
private:
|
||||
static constexpr uint64_t Mask1(unsigned length);
|
||||
void SetPixelFunctions(unsigned inBitCount, unsigned outBitCount);
|
||||
|
||||
void CreateOutputTexture();
|
||||
|
||||
void ReorderUnsignedToUnsigned() const;
|
||||
void ConvertUnsignedToUnsigned();
|
||||
|
||||
std::function<uint64_t(const void* offset, unsigned bitCount)> m_read_pixel_func;
|
||||
std::function<void(void* offset, uint64_t pixel, unsigned bitCount)> m_write_pixel_func;
|
||||
|
||||
const Texture* m_input_texture;
|
||||
std::unique_ptr<Texture> m_output_texture;
|
||||
const ImageFormat* m_input_format;
|
||||
const ImageFormat* m_output_format;
|
||||
};
|
Reference in New Issue
Block a user