mirror of
https://github.com/GerbilSoft/rom-properties.git
synced 2025-06-18 11:35:38 -04:00
[librptexture] Add EAC R11 and RG11 decoding.
These are reduced to 8-bit for ARGB32. ImageDecoder_ETC1.cpp: Renamed decodeBlock_ETC2_alpha() to T_decodeBlock_EAC() and templatized the byteOffset parameter to allow it to be used to decode alpha (regular), or R or G (EAC).
This commit is contained in:
parent
1f6d742d59
commit
3da780cf57
6
NEWS.md
6
NEWS.md
@ -21,6 +21,12 @@
|
||||
the LDR decoder is rather slow.)
|
||||
* The GBS parser now partially supports the older GBR format.
|
||||
|
||||
* New compressed texture formats:
|
||||
* EAC R11 and RG11, which uses ETC2's alpha channel compression for
|
||||
1-channel and 2-channel textures. Note that the channels are truncated
|
||||
from 11-bit to 8-bit for display purposes, and signed int versions
|
||||
might not be displayed correctly.
|
||||
|
||||
* Bug fixes:
|
||||
* EXE: Improve runtime DLL detection in some cases.
|
||||
* SNES: Fix detection of games that declare usage of the S-RTC chip
|
||||
|
@ -169,6 +169,7 @@ button.
|
||||
* GameCube: Tiled RGB5A3 and CI8 with RGB5A3 palette
|
||||
* S3TC: DXT1, DXT2, DXT3, DXT4, DXT5, BC4, and BC5 codecs.
|
||||
* GameCube 2x2-tiled DXT1 is supported in GVR texture files.
|
||||
* ETCn: ETC1, ETC2 (RGB, RGBA, RGB_A1), EAC
|
||||
* BC7: Supported in multiple texture file formats.
|
||||
* The implementation is somewhat slow. (Contributions welcome.)
|
||||
* PVRTC: Supported in multiple texture file formats.
|
||||
|
@ -32,6 +32,19 @@ union argb32_t {
|
||||
};
|
||||
ASSERT_STRUCT(argb32_t, 4);
|
||||
|
||||
// Byte offsets for certain functions.
|
||||
#if SYS_BYTEORDER == SYS_LIL_ENDIAN
|
||||
# define ARGB32_BYTE_OFFSET_B 0
|
||||
# define ARGB32_BYTE_OFFSET_G 1
|
||||
# define ARGB32_BYTE_OFFSET_R 2
|
||||
# define ARGB32_BYTE_OFFSET_A 3
|
||||
#else /* SYS_BYTEORDER == SYS_BIG_ENDIAN */
|
||||
# define ARGB32_BYTE_OFFSET_A 0
|
||||
# define ARGB32_BYTE_OFFSET_R 1
|
||||
# define ARGB32_BYTE_OFFSET_G 2
|
||||
# define ARGB32_BYTE_OFFSET_B 3
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#endif /* __ROMPROPERTIES_LIBRPTEXTURE_ARGB32_T_HPP__ */
|
||||
|
@ -2,7 +2,7 @@
|
||||
* ROM Properties Page shell extension. (librptexture) *
|
||||
* ImageDecoder.cpp: Image decoding functions. *
|
||||
* *
|
||||
* Copyright (c) 2016-2020 by David Korth. *
|
||||
* Copyright (c) 2016-2021 by David Korth. *
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later *
|
||||
***************************************************************************/
|
||||
|
||||
@ -772,7 +772,7 @@ static inline int calcDreamcastSmallVQPaletteEntries_WithMipmaps(int width)
|
||||
}
|
||||
}
|
||||
|
||||
/* ETC1 */
|
||||
/* ETCn */
|
||||
|
||||
/**
|
||||
* Convert an ETC1 image to rp_image.
|
||||
@ -822,6 +822,30 @@ ATTR_ACCESS_SIZE(read_only, 3, 4)
|
||||
rp_image *fromETC2_RGB_A1(int width, int height,
|
||||
const uint8_t *RESTRICT img_buf, int img_siz);
|
||||
|
||||
/**
|
||||
* Convert an EAC R11 image to rp_image.
|
||||
* @param width Image width.
|
||||
* @param height Image height.
|
||||
* @param img_buf EAC R11 image buffer.
|
||||
* @param img_siz Size of image data. [must be >= (w*h)/2]
|
||||
* @return rp_image, or nullptr on error.
|
||||
*/
|
||||
ATTR_ACCESS_SIZE(read_only, 3, 4)
|
||||
rp_image *fromEAC_R11(int width, int height,
|
||||
const uint8_t *RESTRICT img_buf, int img_siz);
|
||||
|
||||
/**
|
||||
* Convert an EAC RG11 image to rp_image.
|
||||
* @param width Image width.
|
||||
* @param height Image height.
|
||||
* @param img_buf EAC R11 image buffer.
|
||||
* @param img_siz Size of image data. [must be >= (w*h)]
|
||||
* @return rp_image, or nullptr on error.
|
||||
*/
|
||||
ATTR_ACCESS_SIZE(read_only, 3, 4)
|
||||
rp_image *fromEAC_RG11(int width, int height,
|
||||
const uint8_t *RESTRICT img_buf, int img_siz);
|
||||
|
||||
#ifdef ENABLE_PVRTC
|
||||
/* PVRTC */
|
||||
|
||||
|
@ -737,14 +737,16 @@ rp_image *fromETC2_RGB(int width, int height,
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode an ETC2 alpha block.
|
||||
* Decode an EAC (ETC2) alpha block.
|
||||
* @tparam byteOffset [in] Byte offset. (Usually ARGB32_BYTE_OFFSET_A for alpha.)
|
||||
* @param tileBuf [out] Destination tile buffer.
|
||||
* @param src [in] Source alpha block.
|
||||
*/
|
||||
static void decodeBlock_ETC2_alpha(array<uint32_t, 4*4> &tileBuf, const etc2_alpha *alpha)
|
||||
template<unsigned int byteOffset>
|
||||
static void T_decodeBlock_EAC(array<uint32_t, 4*4> &tileBuf, const etc2_alpha *alpha)
|
||||
{
|
||||
// argb32_t for alpha channel handling.
|
||||
argb32_t *const pArgb = reinterpret_cast<argb32_t*>(tileBuf.data());
|
||||
// uint8_t* for byte offset handling.
|
||||
uint8_t *const pU8 = reinterpret_cast<uint8_t*>(tileBuf.data());
|
||||
|
||||
// Get the base codeword and multiplier.
|
||||
// NOTE: mult == 0 is not allowed to be used by the encoder,
|
||||
@ -760,11 +762,13 @@ static void decodeBlock_ETC2_alpha(array<uint32_t, 4*4> &tileBuf, const etc2_alp
|
||||
// Pixel index.
|
||||
uint64_t alpha48 = extract48(alpha);
|
||||
|
||||
// NOTE: alpha is stored *backwards*.
|
||||
// NOTE: EAC codeword bits are stored *backwards*.
|
||||
// TODO: Optimize to eliminate double-shifting.
|
||||
// TODO: For R11/RG11 EAC, this should result in an 11-bit value, not 8-bit.
|
||||
for (unsigned int i = 0; i < 16; i++, alpha48 <<= 3) {
|
||||
// Calculate the alpha value for this pixel.
|
||||
int A = base + (tbl[(alpha48 >> 45) & 0x07] * mult);
|
||||
// NOTE: For EAC, this is an 11-bit value that must be truncated to 8-bit.
|
||||
if (A > 255) {
|
||||
A = 255;
|
||||
} else if (A < 0) {
|
||||
@ -772,7 +776,7 @@ static void decodeBlock_ETC2_alpha(array<uint32_t, 4*4> &tileBuf, const etc2_alp
|
||||
}
|
||||
|
||||
// Set the new alpha value.
|
||||
pArgb[etc1_mapping[i]].a = static_cast<uint8_t>(A);
|
||||
pU8[(etc1_mapping[i] * sizeof(uint32_t)) + byteOffset] = static_cast<uint8_t>(A);
|
||||
}
|
||||
}
|
||||
|
||||
@ -828,7 +832,7 @@ rp_image *fromETC2_RGBA(int width, int height,
|
||||
|
||||
// Decode the ETC2 alpha block.
|
||||
// TODO: Don't fill in the alpha channel in decodeBlock_ETC2_RGB()?
|
||||
decodeBlock_ETC2_alpha(tileBuf, &etc2_src->alpha);
|
||||
T_decodeBlock_EAC<ARGB32_BYTE_OFFSET_A>(tileBuf, &etc2_src->alpha);
|
||||
|
||||
// Blit the tile to the main image buffer.
|
||||
ImageDecoderPrivate::BlitTile<uint32_t, 4, 4>(img, tileBuf, x, y);
|
||||
@ -914,4 +918,148 @@ rp_image *fromETC2_RGB_A1(int width, int height,
|
||||
return img;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an EAC R11 image to rp_image.
|
||||
* @param width Image width.
|
||||
* @param height Image height.
|
||||
* @param img_buf EAC R11 image buffer.
|
||||
* @param img_siz Size of image data. [must be >= (w*h)/2]
|
||||
* @return rp_image, or nullptr on error.
|
||||
*/
|
||||
rp_image *fromEAC_R11(int width, int height,
|
||||
const uint8_t *RESTRICT img_buf, int img_siz)
|
||||
{
|
||||
// Verify parameters.
|
||||
assert(img_buf != nullptr);
|
||||
assert(width > 0);
|
||||
assert(height > 0);
|
||||
assert(img_siz >= ((width * height) / 2));
|
||||
if (!img_buf || width <= 0 || height <= 0 ||
|
||||
img_siz < ((width * height) / 2))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// ETC2 uses 4x4 tiles, but some container formats allow
|
||||
// the last tile to be cut off, so round up for the
|
||||
// physical tile size.
|
||||
const int physWidth = ALIGN_BYTES(4, width);
|
||||
const int physHeight = ALIGN_BYTES(4, height);
|
||||
|
||||
// Create an rp_image.
|
||||
rp_image *const img = new rp_image(physWidth, physHeight, rp_image::Format::ARGB32);
|
||||
if (!img->isValid()) {
|
||||
// Could not allocate the image.
|
||||
img->unref();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const etc2_alpha *eac_block = reinterpret_cast<const etc2_alpha*>(img_buf);
|
||||
|
||||
// Calculate the total number of tiles.
|
||||
const unsigned int tilesX = static_cast<unsigned int>(physWidth / 4);
|
||||
const unsigned int tilesY = static_cast<unsigned int>(physHeight / 4);
|
||||
|
||||
// Temporary tile buffer.
|
||||
// NOTE: Must be initialized to 0xFF000000U, since
|
||||
// T_decodeBlock_EAC<>() only modifies a single channel.
|
||||
array<uint32_t, 4*4> tileBuf;
|
||||
tileBuf.fill(0xFF000000U);
|
||||
|
||||
for (unsigned int y = 0; y < tilesY; y++) {
|
||||
for (unsigned int x = 0; x < tilesX; x++, eac_block++) {
|
||||
// Decode the EAC R11 block.
|
||||
T_decodeBlock_EAC<ARGB32_BYTE_OFFSET_R>(tileBuf, eac_block);
|
||||
|
||||
// Blit the tile to the main image buffer.
|
||||
ImageDecoderPrivate::BlitTile<uint32_t, 4, 4>(img, tileBuf, x, y);
|
||||
} }
|
||||
|
||||
if (width < physWidth || height < physHeight) {
|
||||
// Shrink the image.
|
||||
img->shrink(width, height);
|
||||
}
|
||||
|
||||
// Set the sBIT metadata.
|
||||
// NOTE: Cannot set the G and B channels to 0, so setting them to 1.
|
||||
static const rp_image::sBIT_t sBIT = {8,1,1,0,0};
|
||||
img->set_sBIT(&sBIT);
|
||||
|
||||
// Image has been converted.
|
||||
return img;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an EAC RG11 image to rp_image.
|
||||
* @param width Image width.
|
||||
* @param height Image height.
|
||||
* @param img_buf EAC R11 image buffer.
|
||||
* @param img_siz Size of image data. [must be >= (w*h)]
|
||||
* @return rp_image, or nullptr on error.
|
||||
*/
|
||||
rp_image *fromEAC_RG11(int width, int height,
|
||||
const uint8_t *RESTRICT img_buf, int img_siz)
|
||||
{
|
||||
// Verify parameters.
|
||||
assert(img_buf != nullptr);
|
||||
assert(width > 0);
|
||||
assert(height > 0);
|
||||
assert(img_siz >= (width * height));
|
||||
if (!img_buf || width <= 0 || height <= 0 ||
|
||||
img_siz < (width * height))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// ETC2 uses 4x4 tiles, but some container formats allow
|
||||
// the last tile to be cut off, so round up for the
|
||||
// physical tile size.
|
||||
const int physWidth = ALIGN_BYTES(4, width);
|
||||
const int physHeight = ALIGN_BYTES(4, height);
|
||||
|
||||
// Create an rp_image.
|
||||
rp_image *const img = new rp_image(physWidth, physHeight, rp_image::Format::ARGB32);
|
||||
if (!img->isValid()) {
|
||||
// Could not allocate the image.
|
||||
img->unref();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const etc2_alpha *eac_block = reinterpret_cast<const etc2_alpha*>(img_buf);
|
||||
|
||||
// Calculate the total number of tiles.
|
||||
const unsigned int tilesX = static_cast<unsigned int>(physWidth / 4);
|
||||
const unsigned int tilesY = static_cast<unsigned int>(physHeight / 4);
|
||||
|
||||
// Temporary tile buffer.
|
||||
// NOTE: Must be initialized to 0xFF000000U, since
|
||||
// T_decodeBlock_EAC<>() only modifies a single channel.
|
||||
array<uint32_t, 4*4> tileBuf;
|
||||
tileBuf.fill(0xFF000000U);
|
||||
|
||||
for (unsigned int y = 0; y < tilesY; y++) {
|
||||
for (unsigned int x = 0; x < tilesX; x++, eac_block += 2) {
|
||||
// Decode the EAC R11 block.
|
||||
T_decodeBlock_EAC<ARGB32_BYTE_OFFSET_R>(tileBuf, &eac_block[0]);
|
||||
// Decode the EAC G11 block.
|
||||
T_decodeBlock_EAC<ARGB32_BYTE_OFFSET_G>(tileBuf, &eac_block[1]);
|
||||
|
||||
// Blit the tile to the main image buffer.
|
||||
ImageDecoderPrivate::BlitTile<uint32_t, 4, 4>(img, tileBuf, x, y);
|
||||
} }
|
||||
|
||||
if (width < physWidth || height < physHeight) {
|
||||
// Shrink the image.
|
||||
img->shrink(width, height);
|
||||
}
|
||||
|
||||
// Set the sBIT metadata.
|
||||
// NOTE: Cannot set the B channel to 0, so setting it to 1 instead.
|
||||
static const rp_image::sBIT_t sBIT = {8,8,1,0,0};
|
||||
img->set_sBIT(&sBIT);
|
||||
|
||||
// Image has been converted.
|
||||
return img;
|
||||
}
|
||||
|
||||
} }
|
||||
|
@ -196,12 +196,12 @@ const ImageSizeCalc::OpCode GodotSTEXPrivate::op_tbl[] = {
|
||||
OpCode::Divide2, // STEX_FORMAT_PVRTC1_4
|
||||
OpCode::Divide2, // STEX_FORMAT_PVRTC1_4A
|
||||
OpCode::Divide2, // STEX_FORMAT_ETC
|
||||
OpCode::Divide2, // STEX_FORMAT_ETC2_R11 // TODO: Verify; Align4?
|
||||
OpCode::Divide2, // STEX_FORMAT_ETC2_R11S // TODO: Verify; Align4?
|
||||
OpCode::Divide2, // STEX_FORMAT_ETC2_R11
|
||||
OpCode::Divide2, // STEX_FORMAT_ETC2_R11S
|
||||
|
||||
// 0x20
|
||||
OpCode::Divide2, // STEX_FORMAT_ETC2_RG11 // TODO: Verify; Align4?
|
||||
OpCode::Divide2, // STEX_FORMAT_ETC2_RG11S // TODO: Verify; Align4?
|
||||
OpCode::None, // STEX_FORMAT_ETC2_RG11
|
||||
OpCode::None, // STEX_FORMAT_ETC2_RG11S
|
||||
OpCode::Align4Divide2, // STEX_FORMAT_ETC2_RGB8 // TODO: Verify?
|
||||
OpCode::Align4, // STEX_FORMAT_ETC2_RGBA8 // TODO: Verify?
|
||||
OpCode::Align4Divide2, // STEX_FORMAT_ETC2_RGB8A1 // TODO: Verify?
|
||||
@ -667,6 +667,23 @@ const rp_image *GodotSTEXPrivate::loadImage(int mip)
|
||||
}
|
||||
break;
|
||||
|
||||
case STEX_FORMAT_ETC2_R11:
|
||||
case STEX_FORMAT_ETC2_R11S:
|
||||
// EAC-compressed R11 texture.
|
||||
// TODO: Does the signed version get decoded differently?
|
||||
img = ImageDecoder::fromEAC_R11(
|
||||
mdata.width, mdata.height,
|
||||
buf.get(), mdata.size);
|
||||
break;
|
||||
case STEX_FORMAT_ETC2_RG11:
|
||||
case STEX_FORMAT_ETC2_RG11S:
|
||||
// EAC-compressed RG11 texture.
|
||||
// TODO: Does the signed version get decoded differently?
|
||||
img = ImageDecoder::fromEAC_RG11(
|
||||
mdata.width, mdata.height,
|
||||
buf.get(), mdata.size);
|
||||
break;
|
||||
|
||||
#ifdef ENABLE_ASTC
|
||||
case STEX_FORMAT_SCU_ASTC_8x8:
|
||||
// NOTE: Only valid for Godot 3.
|
||||
|
@ -536,6 +536,24 @@ const rp_image *KhronosKTXPrivate::loadImage(void)
|
||||
buf.get(), expected_size);
|
||||
break;
|
||||
|
||||
case GL_COMPRESSED_R11_EAC:
|
||||
case GL_COMPRESSED_SIGNED_R11_EAC:
|
||||
// EAC-compressed R11 texture.
|
||||
// TODO: Does the signed version get decoded differently?
|
||||
img = ImageDecoder::fromEAC_R11(
|
||||
ktxHeader.pixelWidth, height,
|
||||
buf.get(), expected_size);
|
||||
break;
|
||||
|
||||
case GL_COMPRESSED_RG11_EAC:
|
||||
case GL_COMPRESSED_SIGNED_RG11_EAC:
|
||||
// EAC-compressed RG11 texture.
|
||||
// TODO: Does the signed version get decoded differently?
|
||||
img = ImageDecoder::fromEAC_RG11(
|
||||
ktxHeader.pixelWidth, height,
|
||||
buf.get(), expected_size);
|
||||
break;
|
||||
|
||||
case GL_COMPRESSED_RED_RGTC1:
|
||||
case GL_COMPRESSED_SIGNED_RED_RGTC1:
|
||||
// RGTC, one component. (BC4)
|
||||
|
@ -547,6 +547,22 @@ const rp_image *KhronosKTX2Private::loadImage(int mip)
|
||||
buf.get(), expected_size);
|
||||
break;
|
||||
|
||||
case VK_FORMAT_EAC_R11_UNORM_BLOCK:
|
||||
case VK_FORMAT_EAC_R11_SNORM_BLOCK:
|
||||
// EAC-compressed R11 texture.
|
||||
// TODO: Does the signed version get decoded differently?
|
||||
img = ImageDecoder::fromEAC_R11(
|
||||
width, height,
|
||||
buf.get(), expected_size);
|
||||
|
||||
case VK_FORMAT_EAC_R11G11_UNORM_BLOCK:
|
||||
case VK_FORMAT_EAC_R11G11_SNORM_BLOCK:
|
||||
// EAC-compressed RG11 texture.
|
||||
// TODO: Does the signed version get decoded differently?
|
||||
img = ImageDecoder::fromEAC_RG11(
|
||||
width, height,
|
||||
buf.get(), expected_size);
|
||||
|
||||
case VK_FORMAT_BC7_UNORM_BLOCK:
|
||||
case VK_FORMAT_BC7_SRGB_BLOCK:
|
||||
// BPTC-compressed RGBA texture. (BC7)
|
||||
|
@ -163,7 +163,7 @@ const struct PowerVR3Private::FmtLkup_t PowerVR3Private::fmtLkup_tbl_U8[] = {
|
||||
// TODO: "Weird" formats.
|
||||
{ 'abgr', 0x10101010, ImageDecoder::PixelFormat::A16B16G16R16, 64},
|
||||
{ '\0bgr', 0x00101010, ImageDecoder::PixelFormat::B16G16R16, 48},
|
||||
{ '\0rgb', 0x000B0B0A, ImageDecoder::PixelFormat::R11G11B10, 32},
|
||||
{ '\0rgb', 0x000B0B0A, ImageDecoder::PixelFormat::R11G11B10, 32}, // NOTE: May be float.
|
||||
#endif
|
||||
|
||||
{0, 0, ImageDecoder::PixelFormat::Unknown, 0}
|
||||
@ -623,6 +623,16 @@ const rp_image *PowerVR3Private::loadImage(int mip)
|
||||
img = ImageDecoder::fromETC2_RGBA(width, height, buf.get(), expected_size);
|
||||
break;
|
||||
|
||||
case PVR3_PXF_EAC_R11:
|
||||
// EAC-compressed R11 texture.
|
||||
img = ImageDecoder::fromEAC_R11(width, height, buf.get(), expected_size);
|
||||
break;
|
||||
|
||||
case PVR3_PXF_EAC_RG11:
|
||||
// EAC-compressed RG11 texture.
|
||||
img = ImageDecoder::fromEAC_RG11(width, height, buf.get(), expected_size);
|
||||
break;
|
||||
|
||||
case PVR3_PXF_DXT1:
|
||||
// DXT1-compressed texture.
|
||||
img = ImageDecoder::fromDXT1(width, height, buf.get(), expected_size);
|
||||
|
Loading…
Reference in New Issue
Block a user