Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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
9 changes: 5 additions & 4 deletions src/osg/Image.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -806,15 +806,15 @@ unsigned int Image::computePixelSizeInBits(GLenum format,GLenum type)
{
osg::Vec3i footprint = computeBlockFootprint(format);
unsigned int pixelsPerBlock = footprint.x() * footprint.y();
unsigned int bitsPerBlock = computeBlockSize(format, 0);//16 x 8 = 128
unsigned int bitsPerBlock = computeBlockSize(format, 0) * 8; // Convert bytes to bits
unsigned int bitsPerPixel = bitsPerBlock / pixelsPerBlock;
if (bitsPerBlock == bitsPerPixel * pixelsPerBlock) {
OSG_WARN << "Image::computePixelSizeInBits(format,type) : bits per pixel (" << bitsPerPixel << ") is not an integer for GL_KHR_texture_compression_astc_hdr sizes other than 4x4 and 8x8." << std::endl;
// Integer division worked perfectly
return bitsPerPixel;
} else {
OSG_WARN << "Image::computePixelSizeInBits(format,type) : bits per pixel (" << bitsPerBlock << "/" << pixelsPerBlock << ") is not an integer for GL_KHR_texture_compression_astc_hdr size" << footprint.x() << "x" << footprint.y() << "." << std::endl;
return 0;
}
return 0;
}
default: break;
}
Expand Down Expand Up @@ -1827,6 +1827,7 @@ void Image::flipVertical()

const bool dxtc(dxtc_tool::isDXTC(_pixelFormat));
const bool rgtc(dxtc_tool::isRGTC(_pixelFormat));

if (_mipmapData.empty())
{
// no mipmaps,
Expand All @@ -1842,7 +1843,7 @@ void Image::flipVertical()
}
else
{
if (isCompressed()) OSG_NOTICE << "Notice Image::flipVertical(): image is compressed but normal v-flip is used" << std::endl;
if (isCompressed()) OSG_NOTICE << "Notice Image::flipVertical(): file=" << _fileName << " image is compressed but normal v-flip is used" << std::endl;
// its not a compressed image, so implement flip oursleves.
unsigned char* top = data(0,0,r);
unsigned char* bottom = top + (_t-1)*rowStep;
Expand Down
28 changes: 14 additions & 14 deletions src/osg/Texture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1995,85 +1995,85 @@ void Texture::getCompressedSize(GLenum internalFormat, GLint width, GLint height
else if (internalFormat == GL_COMPRESSED_RGBA_ASTC_4x4_KHR || internalFormat == GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR)
{
blockSize = 16;
size = ceil(width/4.0)*ceil(height/4.0)*blockSize;
size = ((width+3)/4)*((height+3)/4)*depth*blockSize;
return;
}
else if (internalFormat == GL_COMPRESSED_RGBA_ASTC_5x4_KHR || internalFormat == GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR)
{
blockSize = 16;
size = ceil(width/5.0)*ceil(height/4.0)*blockSize;
size = ((width+4)/5)*((height+3)/4)*depth*blockSize;
return;
}
else if (internalFormat == GL_COMPRESSED_RGBA_ASTC_5x5_KHR || internalFormat == GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR)
{
blockSize = 16;
size = ceil(width/5.0)*ceil(height/5.0)*blockSize;
size = ((width+4)/5)*((height+4)/5)*depth*blockSize;
return;
}
else if (internalFormat == GL_COMPRESSED_RGBA_ASTC_6x5_KHR || internalFormat == GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR)
{
blockSize = 16;
size = ceil(width/6.0)*ceil(height/5.0)*blockSize;
size = ((width+5)/6)*((height+4)/5)*depth*blockSize;
return;
}
else if (internalFormat == GL_COMPRESSED_RGBA_ASTC_6x6_KHR || internalFormat == GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR)
{
blockSize = 16;
size = ceil(width/6.0)*ceil(height/6.0)*blockSize;
size = ((width+5)/6)*((height+5)/6)*depth*blockSize;
return;
}
else if (internalFormat == GL_COMPRESSED_RGBA_ASTC_8x5_KHR || internalFormat == GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR)
{
blockSize = 16;
size = ceil(width/8.0)*ceil(height/5.0)*blockSize;
size = ((width+7)/8)*((height+4)/5)*depth*blockSize;
return;
}
else if (internalFormat == GL_COMPRESSED_RGBA_ASTC_8x6_KHR || internalFormat == GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR)
{
blockSize = 16;
size = ceil(width/8.0)*ceil(height/6.0)*blockSize;
size = ((width+7)/8)*((height+5)/6)*depth*blockSize;
return;
}
else if (internalFormat == GL_COMPRESSED_RGBA_ASTC_8x8_KHR || internalFormat == GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR)
{
blockSize = 16;
size = ceil(width/8.0)*ceil(height/8.0)*blockSize;
size = ((width+7)/8)*((height+7)/8)*depth*blockSize;
return;
}
else if (internalFormat == GL_COMPRESSED_RGBA_ASTC_10x5_KHR || internalFormat == GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR)
{
blockSize = 16;
size = ceil(width/10.0)*ceil(height/5.0)*blockSize;
size = ((width+9)/10)*((height+4)/5)*depth*blockSize;
return;
}
else if (internalFormat == GL_COMPRESSED_RGBA_ASTC_10x6_KHR || internalFormat == GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR)
{
blockSize = 16;
size = ceil(width/10.0)*ceil(height/6.0)*blockSize;
size = ((width+9)/10)*((height+5)/6)*depth*blockSize;
return;
}
else if (internalFormat == GL_COMPRESSED_RGBA_ASTC_10x8_KHR || internalFormat == GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR)
{
blockSize = 16;
size = ceil(width/10.0)*ceil(height/8.0)*blockSize;
size = ((width+9)/10)*((height+7)/8)*depth*blockSize;
return;
}
else if (internalFormat == GL_COMPRESSED_RGBA_ASTC_10x10_KHR || internalFormat == GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR)
{
blockSize = 16;
size = ceil(width/10.0)*ceil(height/10.0)*blockSize;
size = ((width+9)/10)*((height+9)/10)*depth*blockSize;
return;
}
else if (internalFormat == GL_COMPRESSED_RGBA_ASTC_12x10_KHR || internalFormat == GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR)
{
blockSize = 16;
size = ceil(width/12.0)*ceil(height/10.0)*blockSize;
size = ((width+11)/12)*((height+9)/10)*depth*blockSize;
return;
}
else if (internalFormat == GL_COMPRESSED_RGBA_ASTC_12x12_KHR || internalFormat == GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR)
{
blockSize = 16;
size = ceil(width/12.0)*ceil(height/12.0)*blockSize;
size = ((width+11)/12)*((height+11)/12)*depth*blockSize;
return;
}
else
Expand Down
97 changes: 95 additions & 2 deletions src/osgPlugins/ktx/ReaderWriterKTX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,13 @@

#include "ReaderWriterKTX.h"
#include <osg/Endian>
#include <osg/ValueObject>
#include <osgDB/FileNameUtils>
#include <osgDB/FileUtils>
#include <istream>
#include <vector>
#include <cstring>
#include <map>

// Macro similar to what's in FLT/TRP plugins (except it uses wide char under Windows if OSG_USE_UTF8_FILENAME)
#if defined(_WIN32)
Expand Down Expand Up @@ -115,8 +119,71 @@ osgDB::ReaderWriter::ReadResult ReaderWriterKTX::readKTXStream(std::istream& fin
if (header.numberOfMipmapLevels == 0)
header.numberOfMipmapLevels = 1;

//read keyvalue data. Will be ignoring for now
fin.ignore(header.bytesOfKeyValueData);
// Parse key-value metadata
std::map<std::string, std::string> ktxMetadata;

if (header.bytesOfKeyValueData > 0)
{
std::vector<char> kvData(header.bytesOfKeyValueData);
fin.read(kvData.data(), header.bytesOfKeyValueData);
if (!fin.good())
{
OSG_WARN << "Failed to read KTX key-value data." << std::endl;
return ReadResult(ReadResult::ERROR_IN_READING_FILE);
}

// Parse key-value pairs
size_t offset = 0;
while (offset < header.bytesOfKeyValueData)
{
if (offset + sizeof(uint32_t) > header.bytesOfKeyValueData)
break;

uint32_t keyAndValueByteSize;
memcpy(&keyAndValueByteSize, kvData.data() + offset, sizeof(uint32_t));
if (header.endianness != MyEndian)
osg::swapBytes4(reinterpret_cast<char*>(&keyAndValueByteSize));
offset += sizeof(uint32_t);

if (offset + keyAndValueByteSize > header.bytesOfKeyValueData)
break;

// Find the null terminator to separate key from value
std::string key;
size_t keyStart = offset;
size_t keyEnd = keyStart;
while (keyEnd < offset + keyAndValueByteSize && kvData[keyEnd] != '\0')
keyEnd++;

if (keyEnd < offset + keyAndValueByteSize)
{
key = std::string(kvData.data() + keyStart, keyEnd - keyStart);

// Extract the value (everything after the null terminator)
size_t valueStart = keyEnd + 1;
size_t valueSize = keyAndValueByteSize - (valueStart - keyStart);
std::string value(kvData.data() + valueStart, valueSize);

// Remove any trailing null bytes from the value
size_t nullPos = value.find('\0');
if (nullPos != std::string::npos)
value = value.substr(0, nullPos);

// Store the metadata
ktxMetadata[key] = value;
}

// Align to 4 bytes
offset += keyAndValueByteSize;
uint32_t padding = (4 - (keyAndValueByteSize % 4)) % 4;
offset += padding;
}
}
else
{
// No key-value data, skip
fin.ignore(0);
}

uint32_t imageSize;
uint32_t totalImageSize = fileLength -
Expand Down Expand Up @@ -210,9 +277,35 @@ osgDB::ReaderWriter::ReadResult ReaderWriterKTX::readKTXStream(std::istream& fin
header.glInternalFormat, header.glFormat,
header.glType, totalImageData, osg::Image::USE_NEW_DELETE);

// Set origin based on KTXorientation metadata
// S=r,T=u means OpenGL orientation (bottom-left origin)
// S=r,T=d means standard image orientation (top-left origin)
// Note: some files incorrectly use KTXOrientation with capital O
auto orientIt = ktxMetadata.find("KTXorientation");
if (orientIt == ktxMetadata.end())
orientIt = ktxMetadata.find("KTXOrientation");

if (orientIt != ktxMetadata.end() &&
(orientIt->second == "S=r,T=u" || orientIt->second == "S=r,T=u,R=o"))
{
image->setOrigin(osg::Image::BOTTOM_LEFT);
}
else
{
// Default to TOP_LEFT for S=r,T=d or when no orientation is specified
image->setOrigin(osg::Image::TOP_LEFT);
}

if (header.numberOfMipmapLevels > 1)
image->setMipmapLevels(mipmapData);

// Store all KTX metadata in the image's userdata with "KTX:" prefix
// This avoids conflicts with other OSG metadata
for (const auto& kv : ktxMetadata)
{
image->setUserValue("KTX:" + kv.first, kv.second);
}

return image.get();
}

Expand Down