void ImageDecoder::decompress(MipMap &out, const MipMap &in, PixelFormatRaw format) { if ((format != kPixelFormatDXT1) && (format != kPixelFormatDXT3) && (format != kPixelFormatDXT5)) throw Common::Exception("Unknown compressed format %d", format); /* The DXT algorithms work on 4x4 pixel blocks. Textures smaller than one * block will be padded, but larger textures need to be correctly aligned. */ if (!hasValidDimensions(format, in.width, in.height)) throw Common::Exception("Invalid dimensions (%dx%d) for format %d", in.width, in.height, format); out.width = in.width; out.height = in.height; out.size = out.width * out.height * 4; out.data.reset(new byte[out.size]); Common::ScopedPtr<Common::MemoryReadStream> stream(new Common::MemoryReadStream(in.data.get(), in.size)); if (format == kPixelFormatDXT1) decompressDXT1(out.data.get(), *stream, out.width, out.height, out.width * 4); else if (format == kPixelFormatDXT3) decompressDXT3(out.data.get(), *stream, out.width, out.height, out.width * 4); else if (format == kPixelFormatDXT5) decompressDXT5(out.data.get(), *stream, out.width, out.height, out.width * 4); }
void TPC::readHeader(Common::SeekableReadStream &tpc, byte &encoding) { // Number of bytes for the pixel data in one full image uint32 dataSize = tpc.readUint32LE(); tpc.skip(4); // Some float // Image dimensions uint32 width = tpc.readUint16LE(); uint32 height = tpc.readUint16LE(); if ((width >= 0x8000) || (height >= 0x8000)) throw Common::Exception("Unsupported image dimensions (%ux%u)", width, height); // How's the pixel data encoded? encoding = tpc.readByte(); // Number of mip maps in the image size_t mipMapCount = tpc.readByte(); tpc.skip(114); // Reserved uint32 minDataSize = 0; if (dataSize == 0) { // Uncompressed if (encoding == kEncodingGray) { // 8bpp grayscale _format = kPixelFormatR8G8B8; minDataSize = 1; dataSize = width * height; } else if (encoding == kEncodingRGB) { // RGB, no alpha channel _format = kPixelFormatR8G8B8; minDataSize = 3; dataSize = width * height * 3; } else if (encoding == kEncodingRGBA) { // RGBA, alpha channel _format = kPixelFormatR8G8B8A8; minDataSize = 4; dataSize = width * height * 4; } else if (encoding == kEncodingSwizzledBGRA) { // BGRA, alpha channel, texture memory layout is "swizzled" _format = kPixelFormatB8G8R8A8; minDataSize = 4; dataSize = width * height * 4; } else throw Common::Exception("Unknown TPC raw encoding: %d (%d), %dx%d, %u", encoding, dataSize, width, height, (uint) mipMapCount); } else if (encoding == kEncodingRGB) { // S3TC DXT1 _format = kPixelFormatDXT1; minDataSize = 8; checkCubeMap(width, height); if (dataSize != ((width * height) / 2)) throw Common::Exception("Invalid data size for a texture of %ux%u pixels and format %u", width, height, encoding); } else if (encoding == kEncodingRGBA) { // S3TC DXT5 _format = kPixelFormatDXT5; minDataSize = 16; checkCubeMap(width, height); if (dataSize != (width * height)) throw Common::Exception("Invalid data size for a texture of %ux%u pixels and format %u", width, height, encoding); } else throw Common::Exception("Unknown TPC encoding: %d (%d)", encoding, dataSize); if (!hasValidDimensions(_format, width, height)) throw Common::Exception("Invalid dimensions (%dx%d) for format %d", width, height, _format); const size_t fullImageDataSize = getDataSize(_format, width, height); size_t fullDataSize = tpc.size() - 128; if (fullDataSize < (_layerCount * fullImageDataSize)) throw Common::Exception("Image wouldn't fit into data"); _mipMaps.reserve(mipMapCount * _layerCount); size_t layerCount; for (layerCount = 0; layerCount < _layerCount; layerCount++) { uint32 layerWidth = width; uint32 layerHeight = height; uint32 layerSize = dataSize; for (size_t i = 0; i < mipMapCount; i++) { MipMap *mipMap = new MipMap; mipMap->width = MAX<uint32>(layerWidth, 1); mipMap->height = MAX<uint32>(layerHeight, 1); mipMap->size = MAX<uint32>(layerSize, minDataSize); mipMap->data = 0; const size_t mipMapDataSize = getDataSize(_format, mipMap->width, mipMap->height); if ((fullDataSize < mipMap->size) || (mipMap->size < mipMapDataSize)) { // Wouldn't fit delete mipMap; break; } fullDataSize -= mipMap->size; _mipMaps.push_back(mipMap); layerWidth >>= 1; layerHeight >>= 1; layerSize >>= 2; if ((layerWidth < 1) && (layerHeight < 1)) break; } } if ((layerCount != _layerCount) || ((_mipMaps.size() % _layerCount) != 0)) throw Common::Exception("Failed to correctly read all texture layers (%u, %u, %u, %u)", (uint) _layerCount, (uint) mipMapCount, (uint) layerCount, (uint) _mipMaps.size()); }
void TXB::readHeader(Common::SeekableReadStream &txb, bool &needDeSwizzle, uint32 &dataSize) { // Number of bytes for the pixel data in one full image dataSize = txb.readUint32LE(); txb.skip(4); // Some float // Image dimensions uint32 width = txb.readUint16LE(); uint32 height = txb.readUint16LE(); if ((width >= 0x8000) || (height >= 0x8000)) throw Common::Exception("Unsupported image dimensions (%ux%u)", width, height); // How's the pixel data encoded? byte encoding = txb.readByte(); // Number of mip maps in the image byte mipMapCount = txb.readByte(); txb.skip(2); // Unknown (Always 0x0101 on 0x0A and 0x0C types, 0x0100 on 0x09?) txb.skip(4); // Some float txb.skip(108); // Reserved needDeSwizzle = false; uint32 minDataSize, mipMapSize; if (encoding == kEncodingBGRA) { // Raw BGRA needDeSwizzle = true; _compressed = false; _hasAlpha = true; _format = kPixelFormatBGRA; _formatRaw = kPixelFormatRGBA8; _dataType = kPixelDataType8; minDataSize = 4; mipMapSize = width * height * 4; } else if (encoding == kEncodingDXT1) { // S3TC DXT1 _compressed = true; _hasAlpha = false; _format = kPixelFormatBGR; _formatRaw = kPixelFormatDXT1; _dataType = kPixelDataType8; minDataSize = 8; mipMapSize = width * height / 2; } else if (encoding == kEncodingDXT5) { // S3TC DXT5 _compressed = true; _hasAlpha = true; _format = kPixelFormatBGRA; _formatRaw = kPixelFormatDXT5; _dataType = kPixelDataType8; minDataSize = 16; mipMapSize = width * height; } else if (encoding == 0x09) // TODO: This seems to be some compression with 8bit per pixel. No min // data size; 2*2 and 1*1 mipmaps seem to be just that big. // Image data doesn't seem to be simple grayscale, paletted, // RGB2222 or RGB332 data either. throw Common::Exception("Unsupported TXB encoding 0x09"); else throw Common::Exception("Unknown TXB encoding 0x%02X (%dx%d, %d, %d)", encoding, width, height, mipMapCount, dataSize); if (!hasValidDimensions(_formatRaw, width, height)) throw Common::Exception("Invalid dimensions (%dx%d) for format %d", width, height, _formatRaw); const size_t fullImageDataSize = getDataSize(_formatRaw, width, height); if (dataSize < fullImageDataSize) throw Common::Exception("Image wouldn't fit into data"); _mipMaps.reserve(mipMapCount); for (uint32 i = 0; i < mipMapCount; i++) { MipMap *mipMap = new MipMap(this); mipMap->width = MAX<uint32>(width, 1); mipMap->height = MAX<uint32>(height, 1); if (((mipMap->width < 4) || (mipMap->height < 4)) && (mipMap->width != mipMap->height)) { // Invalid mipmap dimensions delete mipMap; break; } mipMap->size = MAX<uint32>(mipMapSize, minDataSize); mipMap->data = 0; const size_t mipMapDataSize = getDataSize(_formatRaw, mipMap->width, mipMap->height); if ((dataSize < mipMap->size) || (mipMap->size < mipMapDataSize)) { // Wouldn't fit delete mipMap; break; } dataSize -= mipMap->size; _mipMaps.push_back(mipMap); width >>= 1; height >>= 1; mipMapSize >>= 2; if ((width < 1) && (height < 1)) break; } }