Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend flip() to texture_cube and texture_cube_array and allow flipping S3TC-compressed images #94

Merged
merged 1 commit into from
Sep 11, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions gli/core/flip.hpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
#pragma once

#include <array>

#include "../texture2d.hpp"
#include "../texture2d_array.hpp"
#include "../texture_cube.hpp"
#include "../texture_cube_array.hpp"

namespace gli
{
Expand Down
291 changes: 282 additions & 9 deletions gli/core/flip.inl
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,195 @@ namespace detail
LineSize);
}
}

struct dxt1_block
{
uint16_t Color0;
uint16_t Color1;
uint8_t Row0;
uint8_t Row1;
uint8_t Row2;
uint8_t Row3;
};

struct dxt3_block
{
uint16_t AlphaRow0;
uint16_t AlphaRow1;
uint16_t AlphaRow2;
uint16_t AlphaRow3;
uint16_t Color0;
uint16_t Color1;
uint8_t Row0;
uint8_t Row1;
uint8_t Row2;
uint8_t Row3;
};

struct dxt5_block
{
uint8_t Alpha0;
uint8_t Alpha1;
uint8_t AlphaR0;
uint8_t AlphaR1;
uint8_t AlphaR2;
uint8_t AlphaR3;
uint8_t AlphaR4;
uint8_t AlphaR5;
uint16_t Color0;
uint16_t Color1;
uint8_t Row0;
uint8_t Row1;
uint8_t Row2;
uint8_t Row3;
};

inline void flip_block_s3tc(uint8_t* BlockDst, uint8_t* BlockSrc, format Format, bool HeightTwo)
{
// There is no distinction between RGB and RGBA in DXT-compressed textures,
// it is used only to tell OpenGL how to interpret the data.
// Moreover, in DXT1 (which does not contain an alpha channel), transparency can be emulated
// using Color0 and Color1 on a per-compression-block basis.
// There is no difference in how textures with and without transparency are laid out in the file,
// so they can be flipped using the same method.
if(Format == FORMAT_RGB_DXT1_UNORM_BLOCK8 || Format == FORMAT_RGB_DXT1_SRGB_BLOCK8
|| Format == FORMAT_RGBA_DXT1_UNORM_BLOCK8 || Format == FORMAT_RGBA_DXT1_SRGB_BLOCK8)
{
dxt1_block* Src = reinterpret_cast<dxt1_block*>(BlockSrc);
dxt1_block* Dst = reinterpret_cast<dxt1_block*>(BlockDst);

if(HeightTwo)
{
Dst->Color0 = Src->Color0;
Dst->Color1 = Src->Color1;
Dst->Row0 = Src->Row1;
Dst->Row1 = Src->Row0;
Dst->Row2 = Src->Row2;
Dst->Row3 = Src->Row3;

return;
}

Dst->Color0 = Src->Color0;
Dst->Color1 = Src->Color1;
Dst->Row0 = Src->Row3;
Dst->Row1 = Src->Row2;
Dst->Row2 = Src->Row1;
Dst->Row3 = Src->Row0;

return;
}

// DXT3
if(Format == FORMAT_RGBA_DXT3_UNORM_BLOCK16 || Format == FORMAT_RGBA_DXT3_SRGB_BLOCK16)
{
dxt3_block* Src = reinterpret_cast<dxt3_block*>(BlockSrc);
dxt3_block* Dst = reinterpret_cast<dxt3_block*>(BlockDst);

if(HeightTwo)
{
Dst->AlphaRow0 = Src->AlphaRow1;
Dst->AlphaRow1 = Src->AlphaRow0;
Dst->AlphaRow2 = Src->AlphaRow2;
Dst->AlphaRow3 = Src->AlphaRow3;
Dst->Color0 = Src->Color0;
Dst->Color1 = Src->Color1;
Dst->Row0 = Src->Row1;
Dst->Row1 = Src->Row0;
Dst->Row2 = Src->Row2;
Dst->Row3 = Src->Row3;

return;
}

Dst->AlphaRow0 = Src->AlphaRow3;
Dst->AlphaRow1 = Src->AlphaRow2;
Dst->AlphaRow2 = Src->AlphaRow1;
Dst->AlphaRow3 = Src->AlphaRow0;
Dst->Color0 = Src->Color0;
Dst->Color1 = Src->Color1;
Dst->Row0 = Src->Row3;
Dst->Row1 = Src->Row2;
Dst->Row2 = Src->Row1;
Dst->Row3 = Src->Row0;

return;
}

// DXT5
if(Format == FORMAT_RGBA_DXT5_UNORM_BLOCK16 || Format == FORMAT_RGBA_DXT5_SRGB_BLOCK16)
{
dxt5_block* Src = reinterpret_cast<dxt5_block*>(BlockSrc);
dxt5_block* Dst = reinterpret_cast<dxt5_block*>(BlockDst);

if(HeightTwo)
{
Dst->Alpha0 = Src->Alpha0;
Dst->Alpha1 = Src->Alpha1;
// operator+ has precedence over operator>> and operator<<, hence the parentheses. very important!
Dst->AlphaR0 = ((Src->AlphaR1 & 0b11110000) >> 4) + ((Src->AlphaR2 & 0b1111) << 4);
Dst->AlphaR1 = ((Src->AlphaR2 & 0b11110000) >> 4) + ((Src->AlphaR0 & 0b1111) << 4);
Dst->AlphaR2 = ((Src->AlphaR0 & 0b11110000) >> 4) + ((Src->AlphaR1 & 0b1111) << 4);
Dst->AlphaR3 = Src->AlphaR3;
Dst->AlphaR4 = Src->AlphaR4;
Dst->AlphaR5 = Src->AlphaR5;
Dst->Color0 = Src->Color0;
Dst->Color1 = Src->Color1;
Dst->Row0 = Src->Row1;
Dst->Row1 = Src->Row0;
Dst->Row2 = Src->Row2;
Dst->Row3 = Src->Row3;

return;
}

Dst->Alpha0 = Src->Alpha0;
Dst->Alpha1 = Src->Alpha1;
// operator+ has precedence over operator>> and operator<<, hence the parentheses. very important!
Dst->AlphaR0 = ((Src->AlphaR4 & 0b11110000) >> 4) + ((Src->AlphaR5 & 0b1111) << 4);
Dst->AlphaR1 = ((Src->AlphaR5 & 0b11110000) >> 4) + ((Src->AlphaR3 & 0b1111) << 4);
Dst->AlphaR2 = ((Src->AlphaR3 & 0b11110000) >> 4) + ((Src->AlphaR4 & 0b1111) << 4);
Dst->AlphaR3 = ((Src->AlphaR1 & 0b11110000) >> 4) + ((Src->AlphaR2 & 0b1111) << 4);
Dst->AlphaR4 = ((Src->AlphaR2 & 0b11110000) >> 4) + ((Src->AlphaR0 & 0b1111) << 4);
Dst->AlphaR5 = ((Src->AlphaR0 & 0b11110000) >> 4) + ((Src->AlphaR1 & 0b1111) << 4);
Dst->Color0 = Src->Color0;
Dst->Color1 = Src->Color1;
Dst->Row0 = Src->Row3;
Dst->Row1 = Src->Row2;
Dst->Row2 = Src->Row1;
Dst->Row3 = Src->Row0;

return;
}

// invalid format specified (unknown S3TC format?)
assert(false);
}

inline void flip_s3tc(image ImageDst, image ImageSrc, format Format)
{
if(ImageSrc.extent().y == 1)
{
memcpy(ImageDst.data(),
ImageSrc.data(),
ImageSrc.size());
return;
}

std::size_t const XBlocks = ImageSrc.extent().x <= 4 ? 1 : ImageSrc.extent().x / 4;
if(ImageSrc.extent().y == 2)
{
for(std::size_t i_block = 0; i_block < XBlocks; ++i_block)
flip_block_s3tc(ImageDst.data<uint8_t>() + i_block * block_size(Format), ImageSrc.data<uint8_t>() + i_block * block_size(Format), Format, true);

return;
}

std::size_t const MaxYBlock = ImageSrc.extent().y / 4 - 1;
for(std::size_t i_row = 0; i_row <= MaxYBlock; ++i_row)
for(std::size_t i_block = 0; i_block < XBlocks; ++i_block)
flip_block_s3tc(ImageDst.data<uint8_t>() + (MaxYBlock - i_row) * block_size(Format) * XBlocks + i_block * block_size(Format), ImageSrc.data<uint8_t>() + i_row * block_size(Format) * XBlocks + i_block * block_size(Format), Format, false);
}

}//namespace detail

Expand All @@ -30,32 +219,116 @@ inline image flip(image const & Image)
template <>
inline texture2d flip(texture2d const& Texture)
{
GLI_ASSERT(!gli::is_compressed(Texture.format()));
GLI_ASSERT(!gli::is_compressed(Texture.format()) || gli::is_s3tc_compressed(Texture.format()));

texture2d Flip(Texture.format(), Texture.extent(), Texture.levels());

texture2d::size_type const BlockSize = block_size(Texture.format());
if(!is_compressed(Texture.format()))
{
texture2d::size_type const BlockSize = block_size(Texture.format());

for(texture2d::size_type Level = 0; Level < Flip.levels(); ++Level)
detail::flip(Flip[Level], Texture[Level], BlockSize);
for(texture2d::size_type Level = 0; Level < Flip.levels(); ++Level)
detail::flip(Flip[Level], Texture[Level], BlockSize);
}
else
for(texture2d::size_type Level = 0; Level < Flip.levels(); ++Level)
detail::flip_s3tc(Flip[Level], Texture[Level], Texture.format());

return Flip;
}

template <>
inline texture2d_array flip(texture2d_array const& Texture)
{
GLI_ASSERT(!gli::is_compressed(Texture.format()));
GLI_ASSERT(!gli::is_compressed(Texture.format()) || gli::is_s3tc_compressed(Texture.format()));

texture2d_array Flip(Texture.format(), Texture.extent(), Texture.layers(), Texture.levels());

texture2d_array::size_type const BlockSize = block_size(Texture.format());
if(!gli::is_compressed(Texture.format()))
{
texture2d_array::size_type const BlockSize = block_size(Texture.format());

for(texture2d_array::size_type Layer = 0; Layer < Flip.layers(); ++Layer)
for(texture2d_array::size_type Level = 0; Level < Flip.levels(); ++Level)
detail::flip(Flip[Layer][Level], Texture[Layer][Level], BlockSize);
}
else
for(texture2d_array::size_type Layer = 0; Layer < Flip.layers(); ++Layer)
for(texture2d_array::size_type Level = 0; Level < Flip.levels(); ++Level)
detail::flip_s3tc(Flip[Layer][Level], Texture[Layer][Level], Texture.format());

for(texture2d_array::size_type Layer = 0; Layer < Flip.layers(); ++Layer)
for(texture2d_array::size_type Level = 0; Level < Flip.levels(); ++Level)
detail::flip(Flip[Layer][Level], Texture[Layer][Level], BlockSize);
return Flip;
}

template <>
inline texture_cube flip(texture_cube const & Texture)
{
GLI_ASSERT(!gli::is_compressed(Texture.format()) || gli::is_s3tc_compressed(Texture.format()));

texture_cube Flip(Texture.format(), Texture.extent(), Texture.levels());

if(!gli::is_compressed(Texture.format()))
{
texture_cube::size_type const BlockSize = block_size(Texture.format());

for(texture_cube::size_type Face = 0; Face < Flip.faces(); ++Face)
for(texture_cube::size_type Level = 0; Level < Flip.levels(); ++Level)
detail::flip(Flip[Face][Level], Texture[Face][Level], BlockSize);
}
else
for(texture_cube::size_type Face = 0; Face < Flip.faces(); ++Face)
for(texture_cube::size_type Level = 0; Level < Flip.levels(); ++Level)
detail::flip_s3tc(Flip[Face][Level], Texture[Face][Level], Texture.format());

return Flip;
}

template <>
inline texture_cube_array flip(texture_cube_array const & Texture)
{
assert(!is_compressed(Texture.format()) || is_s3tc_compressed(Texture.format()));

texture_cube_array Flip(Texture.format(), Texture.extent(), Texture.layers(), Texture.levels());

if(!is_compressed(Texture.format()))
{
gli::size_t const BlockSize = block_size(Texture.format());

for(std::size_t Layer = 0; Layer < Flip.layers(); ++Layer)
for(std::size_t Face = 0; Face < Flip.faces(); ++Face)
for(std::size_t Level = 0; Level < Flip.levels(); ++Level)
detail::flip(Flip[Layer][Face][Level], Texture[Layer][Face][Level], BlockSize);
}
else
for(std::size_t Layer = 0; Layer < Flip.layers(); ++Layer)
for(std::size_t Face = 0; Face < Flip.faces(); ++Face)
for(std::size_t Level = 0; Level < Flip.levels(); ++Level)
detail::flip_s3tc(Flip[Layer][Face][Level], Texture[Layer][Face][Level], Texture.format());

return Flip;
}

template <>
inline texture flip(texture const & Texture)
{
switch(Texture.target())
{
case TARGET_2D:
return flip(texture2d(Texture));

case TARGET_2D_ARRAY:
return flip(texture2d_array(Texture));

case TARGET_CUBE:
return flip(texture_cube(Texture));

case TARGET_CUBE_ARRAY:
return flip(texture_cube_array(Texture));

default:
assert(false && "Texture target does not support flipping.");
return Texture;
}
}

}//namespace gli
5 changes: 5 additions & 0 deletions gli/core/format.inl
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,11 @@ namespace detail
return detail::get_format_info(Format).Flags & detail::CAP_COMPRESSED_BIT ? true : false;
}

inline bool is_s3tc_compressed(format Format)
{
return Format >= FORMAT_RGB_DXT1_UNORM_BLOCK8 && Format <= FORMAT_RGBA_DXT5_SRGB_BLOCK16;
}

inline bool is_srgb(format Format)
{
return detail::get_format_info(Format).Flags & detail::CAP_COLORSPACE_SRGB_BIT ? true : false;
Expand Down
3 changes: 3 additions & 0 deletions gli/format.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,9 @@ namespace gli
/// Evaluate whether a format is compressed
bool is_compressed(format Format);

/// Evaluate whether a format is compressed with an S3TC algorithm.
bool is_s3tc_compressed(format Format);

/// Evaluate whether a format stores sRGB color space values
bool is_srgb(format Format);

Expand Down
24 changes: 24 additions & 0 deletions test/core/core_flip.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ int test_texture

int main()
{
static_assert(sizeof(gli::detail::dxt1_block) == 8, "DXT1-compressed block must be of size 8.");
static_assert(sizeof(gli::detail::dxt3_block) == 16, "DXT3-compressed block must be of size 16.");
static_assert(sizeof(gli::detail::dxt5_block) == 16, "DXT5-compressed block must be of size 16.");

int Error(0);

gli::texture2d::extent_type const TextureSize(32);
Expand Down Expand Up @@ -54,5 +58,25 @@ int main()
gli::texture2d_array(gli::FORMAT_RGBA32_SFLOAT_PACK32, TextureSize, 4, Levels),
glm::f32vec4(1.0, 0.5, 0.0, 1.0), glm::f32vec4(0.0, 0.5, 1.0, 1.0));

Error += test_texture(
gli::texture2d(gli::FORMAT_RGB_DXT1_UNORM_BLOCK8, TextureSize, Levels),
gli::detail::dxt1_block{63721, 255, 228, 144, 64, 0},
gli::detail::dxt1_block{2516, 215, 152, 173, 215, 106});

Error += test_texture(
gli::texture2d(gli::FORMAT_RGBA_DXT1_UNORM_BLOCK8, TextureSize, Levels),
gli::detail::dxt1_block{63721, 255, 228, 144, 64, 0},
gli::detail::dxt1_block{2516, 215, 152, 173, 215, 106});

Error += test_texture(
gli::texture2d(gli::FORMAT_RGBA_DXT3_UNORM_BLOCK16, TextureSize, Levels),
gli::detail::dxt3_block{12514, 1512, 12624, 16614, 63712, 255, 228, 144, 64, 0},
gli::detail::dxt3_block{36125, 2416, 46314, 10515, 2516, 215, 152, 173, 215, 106});

Error += test_texture(
gli::texture2d(gli::FORMAT_RGBA_DXT5_UNORM_BLOCK16, TextureSize, Levels),
gli::detail::dxt5_block{255, 0, 0b01000000, 0b00011110, 0b00110010, 0b00101101, 0b11110010, 0b01000100, 63712, 255, 228, 144, 64, 0},
gli::detail::dxt5_block{0, 255, 0b00111110, 0b10010000, 0b11100100, 0b11010110, 0b00111011, 0b11001000, 2516, 215, 152, 173, 215, 106});

return Error;
}