Merge pull request #79 from enduro2d/feature/image_loader

Feature/image loader
This commit is contained in:
2019-06-28 13:03:28 +07:00
committed by GitHub
18 changed files with 438 additions and 6 deletions

2
.gitattributes vendored
View File

@@ -9,3 +9,5 @@
*.dll filter=lfs diff=lfs merge=lfs -text
*.lib filter=lfs diff=lfs merge=lfs -text
*.dylib filter=lfs diff=lfs merge=lfs -text
*.pvr filter=lfs diff=lfs merge=lfs -text
*.dds filter=lfs diff=lfs merge=lfs -text

View File

@@ -6,11 +6,151 @@
#include "image_impl.hpp"
namespace
{
// DDS format specification:
// https://docs.microsoft.com/en-us/windows/desktop/direct3ddds/dx-graphics-dds-pguide
using namespace e2d;
// pixel format flags
const u32 ddsf_alphapixels = 0x00000001;
const u32 ddsf_fourcc = 0x00000004;
const u32 ddsf_rgb = 0x00000040;
const u32 ddsf_rgba = 0x00000041;
// dwCaps2 flags
const u32 ddsf_cubemap = 0x00000200;
const u32 ddsf_volume = 0x00200000;
// compressed texture types
const u32 fourcc_dxt1 = 0x31545844; // 'DXT1'
const u32 fourcc_dxt3 = 0x33545844; // 'DXT3'
const u32 fourcc_dxt5 = 0x35545844; // 'DXT5'
struct dds_pixel_format {
u32 dwSize;
u32 dwFlags;
u32 dwFourCC;
u32 dwRGBBitCount;
u32 dwRBitMask;
u32 dwGBitMask;
u32 dwBBitMask;
u32 dwABitMask;
};
struct dds_header {
u32 dwMagic;
u32 dwSize;
u32 dwFlags;
u32 dwHeight;
u32 dwWidth;
u32 dwPitchOrLinearSize;
u32 dwDepth;
u32 dwMipMapCount;
u32 dwReserved1[11];
dds_pixel_format ddspf;
u32 dwCaps1;
u32 dwCaps2;
u32 dwReserved2[3];
};
static_assert(sizeof(dds_pixel_format) == 32, "invalid dds_pixel_format structure size");
static_assert(sizeof(dds_header) == 128, "invalid dds_header structure size");
bool is_dds(const void* data, std::size_t byte_size) noexcept {
if ( byte_size > sizeof(dds_header) ) {
const auto* hdr = reinterpret_cast<const dds_header*>(data);
return hdr->dwMagic == 0x20534444; // 'DDS '
}
return false;
}
bool get_dds_format(
const dds_header& hdr,
image_data_format& out_format,
u32& out_bytes_per_block) noexcept
{
if ( math::check_all_flags(hdr.ddspf.dwFlags, ddsf_fourcc) ) {
switch ( hdr.ddspf.dwFourCC ) {
case fourcc_dxt1:
out_format = math::check_all_flags(hdr.ddspf.dwFlags, ddsf_alphapixels)
? image_data_format::rgba_dxt1
: image_data_format::rgb_dxt1;
out_bytes_per_block = 8;
break;
case fourcc_dxt3:
out_format = image_data_format::rgba_dxt3;
out_bytes_per_block = 16;
break;
case fourcc_dxt5:
out_format = image_data_format::rgba_dxt5;
out_bytes_per_block = 16;
break;
default:
return false;
}
} else if ( math::check_all_flags(hdr.ddspf.dwFlags, ddsf_rgba) && (hdr.ddspf.dwRGBBitCount == 32) ) {
out_format = image_data_format::rgba8;
out_bytes_per_block = 4;
if ( hdr.ddspf.dwRBitMask == 0x00FF0000 ) {
return false; // BGRA format is not supported
}
} else if ( math::check_all_flags(hdr.ddspf.dwFlags, ddsf_rgb) && (hdr.ddspf.dwRGBBitCount == 24) ) {
out_format = image_data_format::rgb8;
out_bytes_per_block = 3;
if (hdr.ddspf.dwRBitMask == 0x00FF0000) {
return false; // BGRA format is not supported
}
} else if ( hdr.ddspf.dwRGBBitCount == 16 ) {
return false; // 16-bit format is not supported by engine
} else if ( hdr.ddspf.dwRGBBitCount == 8 ) {
out_format = image_data_format::g8;
out_bytes_per_block = 1;
} else {
return false;
}
return true;
}
}
namespace e2d::images::impl
{
bool try_load_image_dds(image& dst, const buffer& src) noexcept {
//TODO(BlackMat): implme
E2D_UNUSED(dst, src);
return false;
if ( !is_dds(src.data(), src.size()) ) {
return false;
}
const dds_header& hdr = *reinterpret_cast<const dds_header*>(src.data());
const u8* content = src.data() + sizeof(dds_header);
if ( math::check_all_flags(hdr.dwCaps2, ddsf_cubemap) ||
math::check_all_flags(hdr.dwCaps2, ddsf_volume) ||
(hdr.dwDepth > 0) )
{
return false;
}
image_data_format format = image_data_format(-1);
u32 bytes_per_block = 0;
if ( !get_dds_format(hdr, format, bytes_per_block) ) {
return false;
}
const v2u dimension = v2u(hdr.dwWidth, hdr.dwHeight);
std::size_t size;
switch ( format ) {
case image_data_format::rgb_dxt1:
case image_data_format::rgba_dxt3:
case image_data_format::rgba_dxt5:
size = ((dimension.x+3)/4) * ((dimension.y+3)/4) * bytes_per_block;
break;
default:
size = dimension.x * dimension.y * bytes_per_block;
break;
}
dst = image(dimension, format, buffer(content, size));
return true;
}
}

View File

@@ -6,11 +6,217 @@
#include "image_impl.hpp"
namespace
{
// PVR format specification:
// https://cdn.imgtec.com/sdk-documentation/PVR+File+Format.Specification.pdf
using namespace e2d;
enum class pvr_flags : u32 {
none = 0,
premultiplied = 0x02,
};
enum class pvr_pixel_format : u64 {
pvrtc_2bpp_rgb = 0,
pvrtc_2bpp_rgba = 1,
pvrtc_4bpp_rgb = 2,
pvrtc_4bpp_rgba = 3,
pvrtc_ii_2bpp = 4,
pvrtc_ii_4bpp = 5,
etc1 = 6,
dxt1 = 7,
dxt2 = 8,
dxt3 = 9,
dxt4 = 10,
dxt5 = 11,
bc1 = 7,
bc2 = 9,
bc3 = 11,
bc4 = 12,
bc5 = 13,
bc6 = 14,
bc7 = 15,
uyvy = 16,
yuy2 = 17,
bw_1bpp = 18,
r9g9b9e5 = 19,
rgbg8888 = 20,
grgb8888 = 21,
etc2_rgb = 22,
etc2_rgba = 23,
etc2_rgb_a1 = 24,
eac_r11 = 25,
eac_rg11 = 26,
astc_4x4 = 27,
astc_5x4 = 28,
astc_5x5 = 29,
astc_6x5 = 30,
astc_6x6 = 31,
astc_8x5 = 32,
astc_8x6 = 33,
astc_8x8 = 34,
astc_10x5 = 35,
astc_10x6 = 36,
astc_10x8 = 37,
astc_10x10 = 38,
astc_12x10 = 39,
astc_12x12 = 40,
rgba8 = 0x08080808'61626772ull,
r8 = 0x00000008'00000072ull,
rg8 = 0x00000808'00006772ull,
rgb8 = 0x00080808'00626772ull,
};
enum class pvr_color_space : u32 {
linear = 0,
srgb = 1,
};
enum class pvr_channel_type : u32 {
byte_unorm = 0,
byte_snorm = 1,
ubyte = 2,
sbyte = 3,
short_unorm = 4,
short_snorm = 5,
ushort = 6,
sshort = 7,
int_unorm = 8,
int_snorm = 9,
uint = 10,
sint = 11,
sfloat = 12,
};
struct pvr_header {
u32 version;
u32 flags;
u32 pixelFormat0;
u32 pixelFormat1;
u32 colorSpace;
u32 channelType;
u32 height;
u32 width;
u32 depth;
u32 numSurfaces;
u32 numFaces;
u32 mipMapCount;
u32 metaDataSize;
};
static_assert(sizeof(pvr_header) == 52, "invalid PVR header size");
bool is_pvr(const void* data, std::size_t byte_size) {
if ( byte_size > sizeof(pvr_header) ) {
const pvr_header* hdr = reinterpret_cast<const pvr_header*>(data);
return hdr->version == 0x03525650;
}
return false;
}
bool get_pvr_format(const pvr_header& hdr, image_data_format& out_format, u32& out_bytes_per_block, v2u& out_block_size) {
if ( pvr_color_space(hdr.colorSpace) != pvr_color_space::linear ) {
return false;
}
const pvr_pixel_format fmt = pvr_pixel_format(hdr.pixelFormat0 | (u64(hdr.pixelFormat1) << 32));
switch (fmt)
{
case pvr_pixel_format::pvrtc_2bpp_rgb:
out_format = image_data_format::rgb_pvrtc2;
out_bytes_per_block = 8;
out_block_size = v2u(8,4);
break;
case pvr_pixel_format::pvrtc_2bpp_rgba:
out_format = image_data_format::rgba_pvrtc2;
out_bytes_per_block = 8;
out_block_size = v2u(8,4);
break;
case pvr_pixel_format::pvrtc_4bpp_rgb:
out_format = image_data_format::rgb_pvrtc4;
out_bytes_per_block = 8;
out_block_size = v2u(4,4);
break;
case pvr_pixel_format::pvrtc_4bpp_rgba:
out_format = image_data_format::rgba_pvrtc4;
out_bytes_per_block = 8;
out_block_size = v2u(4,4);
break;
case pvr_pixel_format::pvrtc_ii_2bpp:
out_format = image_data_format::rgba_pvrtc2_v2;
out_bytes_per_block = 8;
out_block_size = v2u(8,4);
break;
case pvr_pixel_format::pvrtc_ii_4bpp:
out_format = image_data_format::rgba_pvrtc4_v2;
out_bytes_per_block = 8;
out_block_size = v2u(4,4);
break;
case pvr_pixel_format::dxt1:
out_format = image_data_format::rgb_dxt1;
out_bytes_per_block = 8;
out_block_size = v2u(4,4);
break;
case pvr_pixel_format::dxt3:
out_format = image_data_format::rgba_dxt3;
out_bytes_per_block = 16;
out_block_size = v2u(4,4);
break;
case pvr_pixel_format::dxt5:
out_format = image_data_format::rgba_dxt5;
out_bytes_per_block = 16;
out_block_size = v2u(4,4);
break;
case pvr_pixel_format::rgba8:
out_format = image_data_format::rgba8;
out_bytes_per_block = 4;
out_block_size = v2u(1,1);
break;
case pvr_pixel_format::r8:
out_format = image_data_format::g8;
out_bytes_per_block = 1;
out_block_size = v2u(1,1);
break;
case pvr_pixel_format::rg8:
out_format = image_data_format::ga8;
out_bytes_per_block = 2;
out_block_size = v2u(1,1);
break;
case pvr_pixel_format::rgb8:
out_format = image_data_format::rgb8;
out_bytes_per_block = 3;
out_block_size = v2u(1,1);
break;
default:
return false;
}
return true;
}
}
namespace e2d::images::impl
{
bool try_load_image_pvr(image& dst, const buffer& src) noexcept {
//TODO(BlackMat): implme
E2D_UNUSED(dst, src);
return false;
if ( !is_pvr(src.data(), src.size()) ) {
return false;
}
const pvr_header& hdr = *reinterpret_cast<const pvr_header*>(src.data());
const u8* content = src.data() + sizeof(pvr_header) + hdr.metaDataSize;
if ( hdr.numSurfaces != 1 || hdr.numFaces != 1 || hdr.depth > 1 ) {
return false; // cubemap and volume textures are not supported
}
image_data_format format = image_data_format(-1);
u32 bytes_per_block = 0;
v2u block_size = v2u(1,1);
if ( !get_pvr_format(hdr, format, bytes_per_block, block_size) ) {
return false;
}
v2u dimension = v2u(hdr.width, hdr.height);
std::size_t size = bytes_per_block *
(dimension.x + block_size.x - 1) / block_size.x *
(dimension.y + block_size.y - 1) / block_size.y;
dst = image(dimension, format, buffer(content, size));
return true;
}
}

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5fc7e9bc99500be4f25f1d0e6ea372a1b14fb1dec6fc73a7c5a0ddf505ae16a8
size 8259

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6b7f41f18f51ec169a9c521cd1177fd49037b200a850aad05a923240c37d8d87
size 4163

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:bd1c2362dc5b0b75d97ca3d5bbdf2cf647b25a5b2f8834a9005e8c9c0a70e82c
size 8259

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7a62a7760e33b7130539c3b6a8b7c287ccdd11edb9dd273be3c1e6ec7b81b47e
size 2115

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6a9b1a7f97b26ff7df0a7275f786e029dbc45aab796247f8063c820140933685
size 2115

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:69e2bab57471fbde23d432f77270678e0a28cf809eb079fbb39a61e68abe4059
size 4163

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f730702b1075c5a0ef929272106275debf12dbce31a59664038545b5b91d1545
size 4163

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7fcf6cb866364b0a8b31bea7407a40aa2dcda1af4f618604aec2249e2e0cc588
size 2115

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1a928929dc7532d84072e5668e5098b17289fdea1d36bff3ae48fa232294f770
size 4163

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e4f93405f3302067a0a1bfc05be48c20ef2e12b649d722574497f6dbd64c3b61
size 8259

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:958b8490c189abdebde1f75e8eacbcb8e6ec06dac34baed545e383639e3288d3
size 16451

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ba89ed93595405e5159fd245043062d3ed6acc2ee809aab8f7fd787a9bc40208
size 24643

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:07b0e533a85a8eef51e9baf6ebca2f5a451f74e712557ef315fd1952dcaef307
size 8320

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9bae52a491e072cd7f1cd70100ca4dcab6eb66fbadd3a72f5e517a9916b445e8
size 32835

View File

@@ -129,4 +129,46 @@ TEST_CASE("images") {
REQUIRE(math::approximately(img.pixel32(1,0), color32::green(), 0));
REQUIRE(math::approximately(img.pixel32(2,0), color32::blue(), 0));
}
{
struct img_info {
const char* name;
bool can_load;
image_data_format format;
};
const img_info test_images[] = {
{"bin/images/ship_pvrtc_2bpp_rgba.pvr", true, image_data_format::rgba_pvrtc2},
{"bin/images/ship_pvrtc_2bpp_rgb.pvr", true, image_data_format::rgb_pvrtc2},
{"bin/images/ship_pvrtc_4bpp_rgba.pvr", true, image_data_format::rgba_pvrtc4},
{"bin/images/ship_pvrtc_4bpp_rgb.pvr", true, image_data_format::rgb_pvrtc4},
{"bin/images/ship_pvrtc_ii_2bpp.pvr", true, image_data_format::rgba_pvrtc2_v2},
{"bin/images/ship_pvrtc_ii_4bpp.pvr", true, image_data_format::rgba_pvrtc4_v2},
{"bin/images/ship_etc1.pvr", false, image_data_format(-1)},
{"bin/images/ship_etc2.pvr", false, image_data_format(-1)},
{"bin/images/ship_eac_rg11.pvr", false, image_data_format(-1)},
{"bin/images/ship_rgba8.pvr", true, image_data_format::rgba8},
{"bin/images/ship_rgba.dds", true, image_data_format::rgba_dxt5},
{"bin/images/ship_r8.pvr", true, image_data_format::g8},
{"bin/images/ship_rg8.pvr", true, image_data_format::ga8},
{"bin/images/ship_rgb8.pvr", true, image_data_format::rgb8}
};
str resources;
REQUIRE(filesystem::extract_predef_path(
resources,
filesystem::predef_path::resources));
for ( const auto& info : test_images ) {
input_stream_uptr stream = make_read_file(path::combine(resources, info.name));
REQUIRE(stream);
image img;
REQUIRE(images::try_load_image(img, stream) == info.can_load);
if ( info.can_load ) {
REQUIRE(img.format() == info.format);
REQUIRE(img.size().x == 64);
REQUIRE(img.size().y == 128);
REQUIRE(img.data().size() > 0);
}
}
}
}