public override object Decode( Stream input, Stream output, params object[] args )
{
ImageData data = new ImageData();
int imageID;
int format, bytesPerPixel;
// create and bind a new image
Il.ilGenImages( 1, out imageID );
Il.ilBindImage( imageID );
// create a temp buffer and write the stream into it
byte[] buffer = new byte[ input.Length ];
input.Read( buffer, 0, buffer.Length );
// load the data into DevIL
Il.ilLoadL( this.ILType, buffer, buffer.Length );
// check for an error
int ilError = Il.ilGetError();
if ( ilError != Il.IL_NO_ERROR )
{
throw new AxiomException( "Error while decoding image data: '{0}'", Ilu.iluErrorString( ilError ) );
}
format = Il.ilGetInteger( Il.IL_IMAGE_FORMAT );
int imageType = Il.ilGetInteger( Il.IL_IMAGE_TYPE );
// Convert image if imageType is incompatible with us (double or long)
if ( imageType != Il.IL_BYTE && imageType != Il.IL_UNSIGNED_BYTE &&
imageType != Il.IL_FLOAT &&
imageType != Il.IL_UNSIGNED_SHORT && imageType != Il.IL_SHORT )
{
Il.ilConvertImage( format, Il.IL_FLOAT );
imageType = Il.IL_FLOAT;
}
// Converted paletted images
if ( format == Il.IL_COLOR_INDEX )
{
Il.ilConvertImage( Il.IL_BGRA, Il.IL_UNSIGNED_BYTE );
format = Il.IL_BGRA;
imageType = Il.IL_UNSIGNED_BYTE;
}
// populate the image data
bytesPerPixel = Il.ilGetInteger( Il.IL_IMAGE_BYTES_PER_PIXEL );
data.width = Il.ilGetInteger( Il.IL_IMAGE_WIDTH );
data.height = Il.ilGetInteger( Il.IL_IMAGE_HEIGHT );
data.depth = Il.ilGetInteger( Il.IL_IMAGE_DEPTH );
data.numMipMaps = Il.ilGetInteger( Il.IL_NUM_MIPMAPS );
data.format = ILUtil.Convert( format, imageType );
data.size = data.width * data.height * bytesPerPixel;
if ( data.format == PixelFormat.Unknown )
{
throw new AxiomException( "Unsupported devil format ImageFormat={0} ImageType={1}", format, imageType );
}
// Check for cubemap
int numFaces = Il.ilGetInteger( Il.IL_NUM_IMAGES ) + 1;
if ( numFaces == 6 )
data.flags |= ImageFlags.CubeMap;
else
numFaces = 1; // Support only 1 or 6 face images for now
// Keep DXT data (if present at all and the GPU supports it)
int dxtFormat = Il.ilGetInteger( Il.IL_DXTC_DATA_FORMAT );
if ( dxtFormat != Il.IL_DXT_NO_COMP && Root.Instance.RenderSystem.Capabilities.HasCapability( Axiom.Graphics.Capabilities.TextureCompressionDXT ) )
{
data.format = ILUtil.Convert( dxtFormat, imageType );
data.flags |= ImageFlags.Compressed;
// Validate that this devil version loads DXT mipmaps
if ( data.numMipMaps > 0 )
{
Il.ilBindImage( imageID );
Il.ilActiveMipmap( 1 );
if ( (uint)Il.ilGetInteger( Il.IL_DXTC_DATA_FORMAT ) != dxtFormat )
{
data.numMipMaps = 0;
LogManager.Instance.Write( "Warning: Custom mipmaps for compressed image were ignored because they are not loaded by this DevIL version." );
}
}
}
// Calculate total size from number of mipmaps, faces and size
data.size = Image.CalculateSize( data.numMipMaps, numFaces, data.width, data.height, data.depth, data.format );
// get the decoded data
GCHandle BufferHandle;
IntPtr pBuffer;
// Dimensions of current mipmap
int width = data.width;
int height = data.height;
int depth = data.depth;
// Transfer data
for ( int mip = 0; mip <= data.numMipMaps; ++mip )
{
for ( int i = 0; i < numFaces; ++i )
{
Il.ilBindImage( imageID );
if ( numFaces > 1 )
Il.ilActiveImage( i );
if ( data.numMipMaps > 0 )
Il.ilActiveMipmap( mip );
/// Size of this face
int imageSize = PixelUtil.GetMemorySize( width, height, depth, data.format );
buffer = new byte[ imageSize ];
if ( ( data.flags & ImageFlags.Compressed ) != 0 )
{
// Compare DXT size returned by DevIL with our idea of the compressed size
if ( imageSize == Il.ilGetDXTCData( IntPtr.Zero, 0, dxtFormat ) )
{
// Retrieve data from DevIL
BufferHandle = GCHandle.Alloc( buffer, GCHandleType.Pinned );
Il.ilGetDXTCData( BufferHandle.AddrOfPinnedObject(), imageSize, dxtFormat );
BufferHandle.Free();
}
else
{
LogManager.Instance.Write( "Warning: compressed image size mismatch, devilsize={0} oursize={1}", Il.ilGetDXTCData( IntPtr.Zero, 0, dxtFormat ), imageSize );
}
}
else
{
/// Retrieve data from DevIL
BufferHandle = GCHandle.Alloc( buffer, GCHandleType.Pinned );
PixelBox dst = new PixelBox( width, height, depth, data.format, BufferHandle.AddrOfPinnedObject() );
ILUtil.ConvertFromIL( dst );
BufferHandle.Free();
}
// write the decoded data to the output stream
output.Write( buffer, 0, buffer.Length );
}
/// Next mip
if ( width != 1 )
width /= 2;
if ( height != 1 )
height /= 2;
if ( depth != 1 )
depth /= 2;
}
// Restore IL state
Il.ilDisable( Il.IL_ORIGIN_SET );
Il.ilDisable( Il.IL_FORMAT_SET );
Il.ilDeleteImages( 1, ref imageID );
return data;
}