save dds and pvr

This commit is contained in:
Pinchuk Aleksei
2019-08-05 11:52:24 +03:00
parent 8b72688390
commit 31c1dd4146
6 changed files with 393 additions and 146 deletions

View File

@@ -0,0 +1,174 @@
/*******************************************************************************
* This file is part of the "Enduro2D"
* For conditions of distribution and use, see copyright notice in LICENSE.md
* Copyright (C) 2018-2019, by Matvey Cherevko (blackmatov@gmail.com)
******************************************************************************/
#pragma once
#include <enduro2d/utils/image.hpp>
#include <enduro2d/utils/buffer.hpp>
namespace e2d::images::impl::dds
{
// 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 dwCaps;
u32 dwCaps2;
u32 dwCaps3;
u32 dwCaps4;
u32 dwReserved2;
};
// pixel format flags
const u32 ddsd_caps = 0x00000001;
const u32 ddsd_height = 0x00000002;
const u32 ddsd_width = 0x00000004;
const u32 ddsd_pitch = 0x00000008;
const u32 ddsd_pixelformat = 0x00001000;
const u32 ddsd_mipmapcount = 0x00020000;
const u32 ddsd_linearsize = 0x00080000;
const u32 ddsd_dept = 0x00800000;
static_assert(sizeof(dds_pixel_format) == 32, "invalid dds_pixel_format structure size");
static_assert(sizeof(dds_header) == 128, "invalid dds_header structure size");
}
namespace e2d::images::impl::pvr
{
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;
};
struct pvr_meta_data {
u32 four_cc{0};
u32 key{0};
u32 data_size{0};
};
static_assert(sizeof(pvr_header) == 52, "invalid PVR header size");
}

View File

@@ -5,6 +5,7 @@
******************************************************************************/
#include "image_impl.hpp"
#include "image_impl_structures.hpp"
namespace
{
@@ -12,51 +13,7 @@ namespace
// 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");
using namespace e2d::images::impl::dds;
bool is_dds(const void* data, std::size_t byte_size) noexcept {
if ( byte_size > sizeof(dds_header) ) {

View File

@@ -5,6 +5,7 @@
******************************************************************************/
#include "image_impl.hpp"
#include "image_impl_structures.hpp"
namespace
{
@@ -12,101 +13,7 @@ namespace
// 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");
using namespace e2d::images::impl::pvr;
bool is_pvr(const void* data, std::size_t byte_size) noexcept {
if ( byte_size > sizeof(pvr_header) ) {

View File

@@ -5,15 +5,17 @@
******************************************************************************/
#include "image_impl.hpp"
#include "image_impl_structures.hpp"
namespace
{
using namespace e2d;
using namespace e2d::images::impl::dds;
bool is_save_image_dds_supported(image_data_format data_format) noexcept {
switch ( data_format ) {
case image_data_format::g8:
case image_data_format::ga8:
case image_data_format::rgb8:
case image_data_format::rgba8:
case image_data_format::rgb_dxt1:
@@ -25,6 +27,30 @@ namespace
return false;
}
}
u32 get_pitch_or_linear_size(image_data_format data_format) noexcept {
switch ( data_format ) {
case image_data_format::g8:
break;
case image_data_format::rgb8:
break;
case image_data_format::rgba8:
break;
case image_data_format::rgb_dxt1:
break;
case image_data_format::rgba_dxt1:
break;
case image_data_format::rgba_dxt3:
break;
case image_data_format::rgba_dxt5:
break;
default:
E2D_ASSERT_MSG(false, "unused pixel format");
break;
}
return 0;
}
}
namespace e2d::images::impl
@@ -32,8 +58,65 @@ namespace e2d::images::impl
bool save_image_dds(const image& src, buffer& dst) {
E2D_UNUSED(src, dst);
if ( is_save_image_dds_supported(src.format()) ) {
//TODO(BlackMat): implme
E2D_ASSERT_MSG(false, "implme");
dds_header hdr;
hdr.dwMagic = 0x20534444;
hdr.dwSize = 124;
hdr.dwFlags = ddsd_caps | ddsd_height | ddsd_width | ddsd_pixelformat;
hdr.dwHeight = src.size().y;
hdr.dwWidth = src.size().x;
hdr.dwPitchOrLinearSize = get_pitch_or_linear_size(src.format());
hdr.dwDepth = 0;
hdr.dwMipMapCount = 0;
hdr.ddspf.dwSize = 32;
hdr.ddspf.dwFlags = 0;
hdr.ddspf.dwFourCC = 0;
switch ( src.format() ) {
case image_data_format::g8:
hdr.ddspf.dwRGBBitCount = 8;
break;
case image_data_format::rgb8:
hdr.ddspf.dwFlags |= ddsf_rgb;
hdr.ddspf.dwRGBBitCount = 24;
hdr.ddspf.dwRBitMask = 0x00FF0000;
hdr.ddspf.dwGBitMask = 0x0000FF00;
hdr.ddspf.dwBBitMask = 0x000000FF;
break;
case image_data_format::rgba8:
hdr.ddspf.dwFlags |= ddsf_rgba;
hdr.ddspf.dwRGBBitCount = 32;
break;
case image_data_format::rgb_dxt1:
hdr.ddspf.dwFlags |= ddsf_fourcc;
hdr.ddspf.dwFourCC = fourcc_dxt1;
break;
case image_data_format::rgba_dxt1:
hdr.ddspf.dwFlags |= ddsf_fourcc | ddsf_alphapixels;
hdr.ddspf.dwFourCC = fourcc_dxt1;
break;
case image_data_format::rgba_dxt3:
hdr.ddspf.dwFlags |= ddsf_fourcc;
hdr.ddspf.dwFourCC |= fourcc_dxt3;
break;
case image_data_format::rgba_dxt5:
hdr.ddspf.dwFlags |= ddsf_fourcc;
hdr.ddspf.dwFourCC = fourcc_dxt5;
break;
default:
E2D_ASSERT_MSG(false, "unused pixel format");
break;;
}
hdr.dwCaps = 0x1000;//DDSCAPS_TEXTURE
hdr.dwCaps2 = 0;
hdr.dwCaps3 = 0;
hdr.dwCaps4 = 0;
hdr.dwReserved2 = 0;
size_t size = sizeof(hdr) + src.data().size();
u8 content[size];
std::memcpy(content, &hdr, sizeof(hdr));
std::memcpy(content + sizeof(hdr), src.data().data(), src.data().size());
dst.assign(content, size);
return true;
}
return false;
}

View File

@@ -5,10 +5,12 @@
******************************************************************************/
#include "image_impl.hpp"
#include "image_impl_structures.hpp"
namespace
{
using namespace e2d;
using namespace e2d::images::impl::pvr;
bool is_save_image_pvr_supported(image_data_format data_format) noexcept {
switch ( data_format ) {
@@ -17,7 +19,6 @@ namespace
case image_data_format::rgb8:
case image_data_format::rgba8:
case image_data_format::rgb_dxt1:
case image_data_format::rgba_dxt1:
case image_data_format::rgba_dxt3:
case image_data_format::rgba_dxt5:
case image_data_format::rgb_pvrtc2:
@@ -31,6 +32,56 @@ namespace
return false;
}
}
u64 get_pvr_pixel_format(image_data_format data_format) noexcept {
pvr_pixel_format res;
switch ( data_format ) {
case image_data_format::g8:
res = pvr_pixel_format::r8;
break;
case image_data_format::ga8:
res = pvr_pixel_format::rg8;
break;
case image_data_format::rgb8:
res = pvr_pixel_format::rgb8;
break;
case image_data_format::rgba8:
res = pvr_pixel_format::rgba8;
break;
case image_data_format::rgb_dxt1:
res = pvr_pixel_format::dxt1;
break;
case image_data_format::rgba_dxt3:
res = pvr_pixel_format::dxt3;
break;
case image_data_format::rgba_dxt5:
res = pvr_pixel_format::dxt5;
break;
case image_data_format::rgb_pvrtc2:
res = pvr_pixel_format::pvrtc_2bpp_rgb;
break;
case image_data_format::rgb_pvrtc4:
res = pvr_pixel_format::pvrtc_4bpp_rgb;
break;
case image_data_format::rgba_pvrtc2:
res = pvr_pixel_format::pvrtc_2bpp_rgba;
break;
case image_data_format::rgba_pvrtc4:
res = pvr_pixel_format::pvrtc_4bpp_rgba;
break;
case image_data_format::rgba_pvrtc2_v2:
res = pvr_pixel_format::pvrtc_ii_2bpp;
break;
case image_data_format::rgba_pvrtc4_v2:
res = pvr_pixel_format::pvrtc_ii_4bpp;
break;
default:
E2D_ASSERT_MSG(false, "unused pixel format");
break;
}
return static_cast<u64>(res);
}
}
namespace e2d::images::impl
@@ -38,8 +89,31 @@ namespace e2d::images::impl
bool save_image_pvr(const image& src, buffer& dst) {
E2D_UNUSED(src, dst);
if ( is_save_image_pvr_supported(src.format()) ) {
//TODO(BlackMat): implme
E2D_ASSERT_MSG(false, "implme");
u64 pixel_format = get_pvr_pixel_format(src.format());
pvr_header hdr;
hdr.version = 0x03525650;
hdr.flags = 0;
hdr.pixelFormat0 = pixel_format & 0x00000000'FFFFFFFFull;
hdr.pixelFormat1 = pixel_format >> 32;
hdr.colorSpace = static_cast<u32>(pvr_color_space::linear);
hdr.channelType = 2;//unsigned byte
hdr.height = src.size().y;
hdr.width = src.size().x;
hdr.depth = 0;
hdr.numSurfaces = 1;
hdr.numFaces = 1;
hdr.mipMapCount = 0;
hdr.metaDataSize = sizeof(pvr_meta_data);
pvr_meta_data md;
size_t size = sizeof(hdr) + sizeof(md) + src.data().size();
u8 content[size];
std::memcpy(content, &hdr, sizeof(hdr));
std::memcpy(content + sizeof(hdr), &md, sizeof(md));
std::memcpy(content + sizeof(hdr) + sizeof(md), src.data().data(), src.data().size());
dst.assign(content, size);
return true;
}
return false;
}

View File

@@ -53,6 +53,16 @@ TEST_CASE("images") {
image_file_format::tga,
make_write_file("image_save_test.tga", false)));
REQUIRE(filesystem::file_exists("image_save_test.tga"));
REQUIRE(images::try_save_image(
img,
image_file_format::pvr,
make_write_file("image_save_test.pvr", false)));
REQUIRE(filesystem::file_exists("image_save_test.pvr"));
REQUIRE(images::try_save_image(
img,
image_file_format::dds,
make_write_file("image_save_test.dds", false)));
REQUIRE(filesystem::file_exists("image_save_test.dds"));
}
{
image img;
@@ -77,11 +87,27 @@ TEST_CASE("images") {
REQUIRE(math::approximately(img.pixel32(0,0), color32::red(), 0u));
REQUIRE(math::approximately(img.pixel32(1,0), color32::green(), 0u));
REQUIRE(math::approximately(img.pixel32(2,0), color32::blue(), 0u));
REQUIRE(images::try_load_image(img, make_read_file("image_save_test.pvr")));
REQUIRE(img.size() == v2u(3,1));
REQUIRE(img.format() == image_data_format::rgb8);
REQUIRE(math::approximately(img.pixel32(0,0), color32::red(), 0u));
REQUIRE(math::approximately(img.pixel32(1,0), color32::green(), 0u));
REQUIRE(math::approximately(img.pixel32(2,0), color32::blue(), 0u));
REQUIRE(images::try_load_image(img, make_read_file("image_save_test.dds")));
REQUIRE(img.size() == v2u(3,1));
REQUIRE(img.format() == image_data_format::rgb8);
REQUIRE(math::approximately(img.pixel32(0,0), color32::red(), 0u));
REQUIRE(math::approximately(img.pixel32(1,0), color32::green(), 0u));
REQUIRE(math::approximately(img.pixel32(2,0), color32::blue(), 0u));
}
{
REQUIRE(filesystem::remove_file("image_save_test.jpg"));
REQUIRE(filesystem::remove_file("image_save_test.png"));
REQUIRE(filesystem::remove_file("image_save_test.tga"));
REQUIRE(filesystem::remove_file("image_save_test.pvr"));
REQUIRE(filesystem::remove_file("image_save_test.dds"));
const u8 img_data[] = {255,0,0, 0,255,0, 0,0,255};
image img(v2u(3,1), image_data_format::rgb8, buffer(img_data, sizeof(img_data)));
buffer buf;
@@ -100,6 +126,16 @@ TEST_CASE("images") {
image_file_format::tga,
buf));
REQUIRE(filesystem::try_write_all(buf, "image_save_test.tga", false));
REQUIRE(images::try_save_image(
img,
image_file_format::pvr,
buf));
REQUIRE(filesystem::try_write_all(buf, "image_save_test.pvr", false));
REQUIRE(images::try_save_image(
img,
image_file_format::dds,
buf));
REQUIRE(filesystem::try_write_all(buf, "image_save_test.dds", false));
}
{
image img;
@@ -128,6 +164,22 @@ TEST_CASE("images") {
REQUIRE(math::approximately(img.pixel32(0,0), color32::red(), 0));
REQUIRE(math::approximately(img.pixel32(1,0), color32::green(), 0));
REQUIRE(math::approximately(img.pixel32(2,0), color32::blue(), 0));
REQUIRE(filesystem::try_read_all(buf, "image_save_test.pvr"));
REQUIRE(images::try_load_image(img, buf));
REQUIRE(img.size() == v2u(3,1));
REQUIRE(img.format() == image_data_format::rgb8);
REQUIRE(math::approximately(img.pixel32(0,0), color32::red(), 0));
REQUIRE(math::approximately(img.pixel32(1,0), color32::green(), 0));
REQUIRE(math::approximately(img.pixel32(2,0), color32::blue(), 0));
REQUIRE(filesystem::try_read_all(buf, "image_save_test.dds"));
REQUIRE(images::try_load_image(img, buf));
REQUIRE(img.size() == v2u(3,1));
REQUIRE(img.format() == image_data_format::rgb8);
REQUIRE(math::approximately(img.pixel32(0,0), color32::red(), 0));
REQUIRE(math::approximately(img.pixel32(1,0), color32::green(), 0));
REQUIRE(math::approximately(img.pixel32(2,0), color32::blue(), 0));
}
{
struct img_info {