From 0b9db85b82a419497d2ae67a0292113ca594fe46 Mon Sep 17 00:00:00 2001 From: Pavel Krajcevski Date: Thu, 6 Oct 2016 23:55:11 -0700 Subject: [PATCH] Allow decompressors to decode images whose images are not a multiple of the block size for the given format. Fixes #27. --- ASTCEncoder/src/Decompressor.cpp | 7 ++++-- BPTCEncoder/src/Decompressor.cpp | 11 +++++---- Core/include/FasTC/CompressedImage.h | 8 ++----- Core/src/CompressedImage.cpp | 13 ++++------- Core/src/TexComp.cpp | 9 ++++--- DXTEncoder/src/Decompressor.cpp | 35 ++++++++++++++-------------- ETCEncoder/src/Decompressor.cpp | 24 ++++++++++++------- IO/src/ImageLoaderASTC.cpp | 2 +- IO/src/ImageLoaderKTX.cpp | 2 +- 9 files changed, 56 insertions(+), 55 deletions(-) diff --git a/ASTCEncoder/src/Decompressor.cpp b/ASTCEncoder/src/Decompressor.cpp index 2f47a01..6622a33 100644 --- a/ASTCEncoder/src/Decompressor.cpp +++ b/ASTCEncoder/src/Decompressor.cpp @@ -970,9 +970,12 @@ namespace ASTCC { uint32 uncompData[144]; DecompressBlock(blockPtr, blockWidth, blockHeight, uncompData); + uint32 decompWidth = std::min(blockWidth, dcj.Width() - i); + uint32 decompHeight = std::min(blockHeight, dcj.Height() - j); + uint8 *outRow = dcj.OutBuf() + (j*dcj.Width() + i)*4; - for(uint32 jj = 0; jj < blockHeight; jj++) { - memcpy(outRow + jj*dcj.Width()*4, uncompData + jj*blockWidth, blockWidth*4); + for(uint32 jj = 0; jj < decompHeight; jj++) { + memcpy(outRow + jj*dcj.Width()*4, uncompData + jj*blockWidth, decompWidth*4); } blockIdx++; diff --git a/BPTCEncoder/src/Decompressor.cpp b/BPTCEncoder/src/Decompressor.cpp index 8584e6b..2f2412c 100644 --- a/BPTCEncoder/src/Decompressor.cpp +++ b/BPTCEncoder/src/Decompressor.cpp @@ -353,10 +353,13 @@ void Decompress(const FasTC::DecompressionJob &dj) { uint32 pixels[16]; DecompressBC7Block(inBuf, pixels); - memcpy(outBuf + j*dj.Width() + i, pixels, 4 * sizeof(pixels[0])); - memcpy(outBuf + (j+1)*dj.Width() + i, pixels+4, 4 * sizeof(pixels[0])); - memcpy(outBuf + (j+2)*dj.Width() + i, pixels+8, 4 * sizeof(pixels[0])); - memcpy(outBuf + (j+3)*dj.Width() + i, pixels+12, 4 * sizeof(pixels[0])); + uint32 decompWidth = std::min(4U, dj.Width() - i); + uint32 decompHeight = std::min(4U, dj.Height() - j); + + uint32 *outRow = outBuf + j * dj.Width() + i; + for (uint32 jj = 0; jj < decompHeight; ++jj) { + memcpy(outRow + jj*dj.Width(), pixels + 4 * jj, decompWidth * sizeof(pixels[0])); + } inBuf += 16; } diff --git a/Core/include/FasTC/CompressedImage.h b/Core/include/FasTC/CompressedImage.h index 93b0370..e7f0502 100644 --- a/Core/include/FasTC/CompressedImage.h +++ b/Core/include/FasTC/CompressedImage.h @@ -51,14 +51,10 @@ class CompressedImage : public FasTC::Image { virtual void ComputePixels(); - static uint32 GetCompressedSize(uint32 uncompressedSize, FasTC::ECompressionFormat format); - static uint32 GetUncompressedSize(uint32 compressedSize, FasTC::ECompressionFormat format) { - uint32 cmp = GetCompressedSize(compressedSize, format); - return compressedSize * (compressedSize / cmp); - } + static uint32 GetCompressedSize(uint32 width, uint32 height, FasTC::ECompressionFormat format); uint32 GetCompressedSize() const { - return GetCompressedSize(GetUncompressedSize(), m_Format); + return GetCompressedSize(GetWidth(), GetHeight(), m_Format); } uint32 GetUncompressedSize() const { return GetWidth() * GetHeight() * sizeof(uint32); diff --git a/Core/src/CompressedImage.cpp b/Core/src/CompressedImage.cpp index dd5830d..76aa1f1 100644 --- a/Core/src/CompressedImage.cpp +++ b/Core/src/CompressedImage.cpp @@ -133,21 +133,16 @@ void CompressedImage::ComputePixels() { SetImageData(GetWidth(), GetHeight(), newPixels); } -uint32 CompressedImage::GetCompressedSize(uint32 uncompressedSize, ECompressionFormat format) { - - // Make sure that the uncompressed size is a multiple of the pixel size. - assert(uncompressedSize % sizeof(uint32) == 0); - +uint32 CompressedImage::GetCompressedSize(uint32 width, uint32 height, ECompressionFormat format) { // The compressed size is the block size times the number of blocks uint32 blockDim[2]; GetBlockDimensions(format, blockDim); + const uint32 blocksWide = (width + blockDim[0] - 1) / blockDim[0]; + const uint32 blocksHigh = (height + blockDim[1] - 1) / blockDim[1]; const uint32 uncompBlockSize = blockDim[0] * blockDim[1] * sizeof(uint32); - // The uncompressed block size should be a factor of the uncompressed size. - assert(uncompressedSize % uncompBlockSize == 0); - - const uint32 nBlocks = uncompressedSize / uncompBlockSize; + const uint32 nBlocks = blocksWide * blocksHigh; const uint32 blockSz = GetBlockSize(format); return nBlocks * blockSz; diff --git a/Core/src/TexComp.cpp b/Core/src/TexComp.cpp index 185756d..39dd014 100644 --- a/Core/src/TexComp.cpp +++ b/Core/src/TexComp.cpp @@ -392,14 +392,13 @@ CompressedImage *CompressImage( height = newHeight; } - uint32 dataSz = width * height * 4; - uint32 *data = new uint32[dataSz / 4]; - memset(data, 0, dataSz); + uint32 *data = new uint32[width * height]; + memset(data, 0, width * height * sizeof(data[0])); CompressedImage *outImg = NULL; // Allocate data based on the compression method - uint32 cmpDataSz = CompressedImage::GetCompressedSize(dataSz, settings.format); + uint32 cmpDataSz = CompressedImage::GetCompressedSize(width, height, settings.format); // Make sure that we have RGBA data... img->ComputePixels(); @@ -485,7 +484,7 @@ bool CompressImageData( // Allocate data based on the compression method uint32 compressedDataSzNeeded = - CompressedImage::GetCompressedSize(dataSz, settings.format); + CompressedImage::GetCompressedSize(width, height, settings.format); if(compressedDataSzNeeded == 0) { ReportError("Unknown compression format"); diff --git a/DXTEncoder/src/Decompressor.cpp b/DXTEncoder/src/Decompressor.cpp index 1d5be4a..39b1208 100644 --- a/DXTEncoder/src/Decompressor.cpp +++ b/DXTEncoder/src/Decompressor.cpp @@ -17,6 +17,7 @@ #include "FasTC/DXTCompressor.h" +#include #include #include #include @@ -94,13 +95,9 @@ namespace { namespace DXTC { - void DecompressDXT1(const FasTC::DecompressionJob &dcj) - { - assert(!(dcj.Height() & 3)); - assert(!(dcj.Width() & 3)); - - uint32 blockW = dcj.Width() >> 2; - uint32 blockH = dcj.Height() >> 2; + void DecompressDXT1(const FasTC::DecompressionJob &dcj) { + uint32 blockW = (dcj.Width() + 3) >> 2; + uint32 blockH = (dcj.Height() + 3) >> 2; const uint32 blockSz = GetBlockSize(FasTC::eCompressionFormat_DXT1); @@ -115,8 +112,11 @@ namespace DXTC uint32 offset = (j * blockW + i) * blockSz; DecompressDXT1Block(dcj.InBuf() + offset, outBlock, true); - for(uint32 y = 0; y < 4; y++) - for(uint32 x = 0; x < 4; x++) { + uint32 decompWidth = std::min(4U, dcj.Width() - i * 4); + uint32 decompHeight = std::min(4U, dcj.Height() - j * 4); + + for(uint32 y = 0; y < decompHeight; y++) + for(uint32 x = 0; x < decompWidth; x++) { offset = (j*4 + y)*dcj.Width() + ((i*4)+x); outPixels[offset] = outBlock[y*4 + x]; } @@ -124,13 +124,9 @@ namespace DXTC } } - void DecompressDXT5(const FasTC::DecompressionJob &dcj) - { - assert(!(dcj.Height() & 3)); - assert(!(dcj.Width() & 3)); - - uint32 blockW = dcj.Width() >> 2; - uint32 blockH = dcj.Height() >> 2; + void DecompressDXT5(const FasTC::DecompressionJob &dcj) { + uint32 blockW = (dcj.Width() + 3) >> 2; + uint32 blockH = (dcj.Height() + 3) >> 2; const uint32 blockSz = GetBlockSize(FasTC::eCompressionFormat_DXT5); @@ -146,8 +142,11 @@ namespace DXTC DecompressDXT5Block(dcj.InBuf() + offset, outBlock); DecompressDXT1Block(dcj.InBuf() + offset + blockSz / 2, outBlock, false); - for (uint32 y = 0; y < 4; y++) - for (uint32 x = 0; x < 4; x++) { + uint32 decompWidth = std::min(4U, dcj.Width() - i * 4); + uint32 decompHeight = std::min(4U, dcj.Height() - j * 4); + + for (uint32 y = 0; y < decompHeight; y++) + for (uint32 x = 0; x < decompWidth; x++) { offset = (j * 4 + y)*dcj.Width() + ((i * 4) + x); outPixels[offset] = outBlock[y * 4 + x]; } diff --git a/ETCEncoder/src/Decompressor.cpp b/ETCEncoder/src/Decompressor.cpp index ba3628c..0e3dac0 100644 --- a/ETCEncoder/src/Decompressor.cpp +++ b/ETCEncoder/src/Decompressor.cpp @@ -19,22 +19,28 @@ #include "rg_etc1.h" +#include +#include + namespace ETCC { - void Decompress(const FasTC::DecompressionJob &cj) { - - uint32 blocksX = cj.Width() / 4; - uint32 blocksY = cj.Height() / 4; + void Decompress(const FasTC::DecompressionJob &dcj) { + uint32 blocksX = (dcj.Width() + 3) / 4; + uint32 blocksY = (dcj.Height() + 3) / 4; for(uint32 j = 0; j < blocksY; j++) { for(uint32 i = 0; i < blocksX; i++) { uint32 pixels[16]; uint32 blockIdx = j*blocksX + i; - rg_etc1::unpack_etc1_block(cj.InBuf() + blockIdx * 8, pixels); - for(uint32 y = 0; y < 4; y++) - for(uint32 x = 0; x < 4; x++) { - uint32 *out = reinterpret_cast(cj.OutBuf()); - out[(j*4 + y)*cj.Width() + (i*4 + x)] = pixels[y*4 + x]; + rg_etc1::unpack_etc1_block(dcj.InBuf() + blockIdx * 8, pixels); + + uint32 decompWidth = std::min(4U, dcj.Width() - i * 4); + uint32 decompHeight = std::min(4U, dcj.Height() - j * 4); + + for(uint32 y = 0; y < decompHeight; y++) + for(uint32 x = 0; x < decompWidth; x++) { + uint32 *out = reinterpret_cast(dcj.OutBuf()); + out[(j*4 + y)*dcj.Width() + (i*4 + x)] = pixels[y*4 + x]; } } } diff --git a/IO/src/ImageLoaderASTC.cpp b/IO/src/ImageLoaderASTC.cpp index eeecc81..16486dc 100644 --- a/IO/src/ImageLoaderASTC.cpp +++ b/IO/src/ImageLoaderASTC.cpp @@ -188,7 +188,7 @@ bool ImageLoaderASTC::ReadData() { m_Height = pixelHeight; uint32 uncompressedSize = pixelWidth * pixelHeight * 4; - uint32 compressedSize = CompressedImage::GetCompressedSize(uncompressedSize, fmt); + uint32 compressedSize = CompressedImage::GetCompressedSize(pixelWidth, pixelHeight, fmt); assert(compressedSize + 16 == m_NumRawDataBytes); m_PixelData = new uint8[compressedSize]; diff --git a/IO/src/ImageLoaderKTX.cpp b/IO/src/ImageLoaderKTX.cpp index 4e9a5c6..3d57a37 100644 --- a/IO/src/ImageLoaderKTX.cpp +++ b/IO/src/ImageLoaderKTX.cpp @@ -305,7 +305,7 @@ bool ImageLoaderKTX::ReadData() { return false; } - uint32 dataSize = CompressedImage::GetCompressedSize(pixelWidth * pixelHeight * 4, m_Format); + uint32 dataSize = CompressedImage::GetCompressedSize(pixelWidth, pixelHeight, m_Format); m_PixelData = new uint8[dataSize]; memcpy(m_PixelData, rdr.GetData(), dataSize); rdr.Advance(dataSize);