From Diane Delallée & Sukender, "Added some support of non-modulus-4 dimensions in S3TC-DXTC images (previous implementation seem just not to handle these properly).

- Added missing packing value on S3TC images. Images are coded with 4x4 blocs, whatever the image size. So there is an horizontal packing of 4 pixels (2 bytes in DXT1, 4 bytes in DXT2-5).
- Added crash guard against writing corrupted S3TC images.
Notes:
- What is missing is a support of "lines packing" in osg::Image (see code comments).
- S3TC-DXTC vertical flipping crashes (access violation) with some unusual dimensions (see code). I could not implement missing cases, so I added guards to avoid crashing."
This commit is contained in:
Robert Osfield
2013-05-28 11:44:36 +00:00
parent 39b9351153
commit 53fd801088

View File

@@ -494,6 +494,8 @@ osg::Image* ReadDDSFile(std::istream& _istream)
// while compressed formats will use DDPF_FOURCC with a four-character code.
bool usingAlpha = ddsd.ddpfPixelFormat.dwFlags & DDPF_ALPHAPIXELS;
int packing(1);
bool isDXTC(false);
// Uncompressed formats.
if(ddsd.ddpfPixelFormat.dwFlags & DDPF_RGB)
@@ -659,16 +661,22 @@ osg::Image* ReadDDSFile(std::istream& _istream)
internalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
pixelFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
}
packing = 2; // 4 bits/pixel. 4 px = 2 bytes
isDXTC = true;
break;
case FOURCC_DXT3:
OSG_INFO << "ReadDDSFile info : format = DXT3" << std::endl;
internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
pixelFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
packing = 4; // 8 bits/pixel. 4 px = 4 bytes
isDXTC = true;
break;
case FOURCC_DXT5:
OSG_INFO << "ReadDDSFile info : format = DXT5" << std::endl;
internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
pixelFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
packing = 4; // 8 bits/pixel. 4 px = 4 bytes
isDXTC = true;
break;
case FOURCC_ATI1:
OSG_INFO << "ReadDDSFile info : format = ATI1" << std::endl;
@@ -965,7 +973,7 @@ osg::Image* ReadDDSFile(std::istream& _istream)
return NULL;
}
unsigned int size = ComputeImageSizeInBytes( s, t, r, pixelFormat, dataType );
unsigned int size = ComputeImageSizeInBytes( s, t, r, pixelFormat, dataType, packing );
// Take care of mipmaps if any.
unsigned int sizeWithMipmaps = size;
@@ -990,7 +998,7 @@ osg::Image* ReadDDSFile(std::istream& _istream)
depth = osg::maximum( depth >> 1, 1 );
sizeWithMipmaps +=
ComputeImageSizeInBytes( width, height, depth, pixelFormat, dataType );
ComputeImageSizeInBytes( width, height, depth, pixelFormat, dataType, packing );
}
}
@@ -1020,7 +1028,7 @@ osg::Image* ReadDDSFile(std::istream& _istream)
// this memory will not be used but it will not cause leak in worst meaning of this word.
}
osgImage->setImage(s,t,r, internalFormat, pixelFormat, dataType, imageData, osg::Image::USE_NEW_DELETE);
osgImage->setImage(s,t,r, internalFormat, pixelFormat, dataType, imageData, osg::Image::USE_NEW_DELETE, packing);
if (mipmap_offsets.size()>0) osgImage->setMipmapLevels(mipmap_offsets);
@@ -1060,6 +1068,19 @@ bool WriteDDSFile(const osg::Image *img, std::ostream& fout)
unsigned int pixelSize = osg::Image::computePixelSizeInBits(pixelFormat, dataType);
unsigned int imageSize = img->getImageSizeInBytes();
// Check that theorical image size (computation taking into account DXTC blocks) is not bigger than actual image size.
// This may happen, for instance, if some operation tuncated the data buffer non block-aligned. Example:
// - Read DXT1 image, size = 8x7. Actually, image data is 8x8 because it stores 4x4 blocks.
// - Some hypothetical operation wrongly assumes the data buffer is 8x7 and truncates the buffer. This may even lead to access violations.
// - Then we write the DXT1 image: last block(s) is (are) corrupt.
// Actually what could be very nice is to handle some "lines packing" (?) in DDS reading, indicating that the image buffer has "additional lines to reach a multiple of 4".
// Please note this can also produce false positives (ie. when data buffer is large enough, but getImageSizeInBytes() returns a smaller value). There is no way to detect this, until we fix getImageSizeInBytes() with "line packing".
unsigned int imageSizeTheorical = ComputeImageSizeInBytes( img->s(), img->t(), img->r(), pixelFormat, dataType, img->getPacking() );
if (imageSize < imageSizeTheorical) {
OSG_FATAL << "Image cannot be written as DDS (Maybe a corrupt S3TC-DXTC image, with non %4 dimensions)." << std::endl;
return false;
}
ddsd.dwWidth = img->s();
ddsd.dwHeight = img->t();
int r = img->r();