public override object Decode( System.IO.Stream input, System.IO.Stream output, params object[] args )
{
// Set error handler
FI.FreeImageEngine.Message += new FI.OutputMessageFunction( FreeImageLoadErrorHandler );
// Buffer stream into memory (TODO: override IO functions instead?)
byte[] data = new byte[ (int)input.Length ];
input.Read( data, 0, data.Length );
IntPtr datPtr = Memory.PinObject( data );
FI.FIMEMORY fiMem = FI.FreeImage.OpenMemory( datPtr, (uint)data.Length );
FI.FREE_IMAGE_FORMAT ff = (FI.FREE_IMAGE_FORMAT)_freeImageType;
FI.FIBITMAP fiBitmap = FI.FreeImage.LoadFromMemory( (FI.FREE_IMAGE_FORMAT)_freeImageType, fiMem, FI.FREE_IMAGE_LOAD_FLAGS.DEFAULT );
if ( fiBitmap.IsNull )
{
Debugger.Break();
throw new AxiomException( "Error decoding image" );
}
ImageData imgData = new ImageData();
//output = new System.IO.MemoryStream();
imgData.depth = 1;// only 2D formats handled by this codec
imgData.width = (int)FI.FreeImage.GetWidth( fiBitmap );
imgData.height = (int)FI.FreeImage.GetHeight( fiBitmap );
imgData.numMipMaps = 0;// no mipmaps in non-DDS
// Must derive format first, this may perform conversions
FI.FREE_IMAGE_TYPE imageType = FI.FreeImage.GetImageType( fiBitmap );
FI.FREE_IMAGE_COLOR_TYPE colorType = FI.FreeImage.GetColorType( fiBitmap );
int bpp = (int)FI.FreeImage.GetBPP( fiBitmap );
switch ( imageType )
{
case FI.FREE_IMAGE_TYPE.FIT_UNKNOWN:
case FI.FREE_IMAGE_TYPE.FIT_COMPLEX:
case FI.FREE_IMAGE_TYPE.FIT_UINT32:
case FI.FREE_IMAGE_TYPE.FIT_INT32:
case FI.FREE_IMAGE_TYPE.FIT_DOUBLE:
default:
throw new AxiomException( "Unknown or unsupported image format" );
break;
case FI.FREE_IMAGE_TYPE.FIT_BITMAP:
// Standard image type
// Perform any colour conversions for greyscale
if ( colorType == FI.FREE_IMAGE_COLOR_TYPE.FIC_MINISWHITE || colorType == FI.FREE_IMAGE_COLOR_TYPE.FIC_MINISBLACK )
{
FI.FIBITMAP newBitmap = FI.FreeImage.ConvertToGreyscale( fiBitmap );
// free old bitmap and replace
FI.FreeImage.Unload( fiBitmap );
fiBitmap = newBitmap;
// get new formats
bpp = (int)FI.FreeImage.GetBPP( fiBitmap );
colorType = FI.FreeImage.GetColorType( fiBitmap );
}
// Perform any colour conversions for RGB
else if ( bpp < 8 || colorType == FI.FREE_IMAGE_COLOR_TYPE.FIC_PALETTE || colorType == FI.FREE_IMAGE_COLOR_TYPE.FIC_CMYK )
{
FI.FIBITMAP newBitmap;
if ( FI.FreeImage.IsTransparent( fiBitmap ) )
{
// convert to 32 bit to preserve the transparency
// (the alpha byte will be 0 if pixel is transparent)
newBitmap = FI.FreeImage.ConvertTo32Bits( fiBitmap );
}
else
{
// no transparency - only 3 bytes are needed
newBitmap = FI.FreeImage.ConvertTo24Bits( fiBitmap );
}
// free old bitmap and replace
FI.FreeImage.Unload( fiBitmap );
fiBitmap = newBitmap;
// get new formats
bpp = (int)FI.FreeImage.GetBPP( fiBitmap );
colorType = FI.FreeImage.GetColorType( fiBitmap );
}
// by this stage, 8-bit is greyscale, 16/24/32 bit are RGB[A]
switch ( bpp )
{
case 8:
imgData.format = PixelFormat.L8;
break;
case 16:
// Determine 555 or 565 from green mask
// cannot be 16-bit greyscale since that's FIT_UINT16
if ( FI.FreeImage.GetGreenMask( fiBitmap ) == FI.FreeImage.FI16_565_GREEN_MASK )
{
imgData.format = PixelFormat.R5G6B5;
}
else
{
// FreeImage doesn't support 4444 format so must be 1555
imgData.format = PixelFormat.A1R5G5B5;
}
break;
case 24:
// FreeImage differs per platform
// PixelFormat.BYTE_BGR[A] for little endian (== PixelFormat.ARGB native)
// PixelFormat.BYTE_RGB[A] for big endian (== PixelFormat.RGBA native)
if ( FI.FreeImage.IsLittleEndian() )
{
imgData.format = PixelFormat.BYTE_BGR;
}
else
{
imgData.format = PixelFormat.BYTE_RGB;
}
break;
case 32:
if ( FI.FreeImage.IsLittleEndian() )
{
imgData.format = PixelFormat.BYTE_BGRA;
}
else
{
imgData.format = PixelFormat.BYTE_RGBA;
}
break;
};
break;
case FI.FREE_IMAGE_TYPE.FIT_UINT16:
case FI.FREE_IMAGE_TYPE.FIT_INT16:
// 16-bit greyscale
imgData.format = PixelFormat.L16;
break;
case FI.FREE_IMAGE_TYPE.FIT_FLOAT:
// Single-component floating point data
imgData.format = PixelFormat.FLOAT32_R;
break;
case FI.FREE_IMAGE_TYPE.FIT_RGB16:
imgData.format = PixelFormat.SHORT_RGB;
break;
case FI.FREE_IMAGE_TYPE.FIT_RGBA16:
imgData.format = PixelFormat.SHORT_RGBA;
break;
case FI.FREE_IMAGE_TYPE.FIT_RGBF:
imgData.format = PixelFormat.FLOAT32_RGB;
break;
case FI.FREE_IMAGE_TYPE.FIT_RGBAF:
imgData.format = PixelFormat.FLOAT32_RGBA;
break;
}
IntPtr srcData = FI.FreeImage.GetBits( fiBitmap );
int srcPitch = (int)FI.FreeImage.GetPitch( fiBitmap );
// Final data - invert image and trim pitch at the same time
int dstPitch = imgData.width * PixelUtil.GetNumElemBytes( imgData.format );
imgData.size = dstPitch * imgData.height;
// Bind output buffer
byte[] outPutData = new byte[ imgData.size ];
unsafe
{
fixed ( byte* pDstPtr = outPutData )//(byte*)Memory.PinObject( outPutData );
{
byte* pDst = pDstPtr;
byte* pSrc = (byte*)IntPtr.Zero;
byte* byteSrcData = (byte*)srcData;
for ( int y = 0; y < imgData.height; y++ )
{
pSrc = byteSrcData + ( imgData.height - y - 1 ) * srcPitch;
Memory.Copy( (IntPtr)pSrc, (IntPtr)pDst, dstPitch );
pDst += dstPitch;
}
}
}
//for ( int z = 0; z < outPutData.Length; z += 4 )
//{
// byte tmp = outPutData[ z ];
// outPutData[ z ] = outPutData[ z + 2 ];
// outPutData[ z + 2 ] = tmp;
//}
//output = new System.IO.MemoryStream( outPutData );//.Write( outPutData, 0, outPutData.Length );
output.Write( outPutData, 0, outPutData.Length );
FI.FreeImage.Unload( fiBitmap );
FI.FreeImage.CloseMemory( fiMem );
return imgData;
}
/// <summary>