private FI.FIBITMAP Encode( System.IO.Stream input, object codecData )
{
FI.FIBITMAP ret = new FI.FIBITMAP();
ret.SetNull();
ImageData imgData = codecData as ImageData;
if ( imgData != null )
{
byte[] data = new byte[ (int)input.Length ];
input.Read( data, 0, data.Length );
IntPtr dataPtr = Memory.PinObject( data );
PixelBox src = new PixelBox( imgData.width, imgData.height, imgData.depth, imgData.format, dataPtr );
// The required format, which will adjust to the format
// actually supported by FreeImage.
PixelFormat requiredFormat = imgData.format;
// determine the settings
FI.FREE_IMAGE_TYPE imageType = FI.FREE_IMAGE_TYPE.FIT_UNKNOWN;
PixelFormat determiningFormat = imgData.format;
switch ( determiningFormat )
{
case PixelFormat.R5G6B5:
case PixelFormat.B5G6R5:
case PixelFormat.R8G8B8:
case PixelFormat.B8G8R8:
case PixelFormat.A8R8G8B8:
case PixelFormat.X8R8G8B8:
case PixelFormat.A8B8G8R8:
case PixelFormat.X8B8G8R8:
case PixelFormat.B8G8R8A8:
case PixelFormat.R8G8B8A8:
case PixelFormat.A4L4:
case PixelFormat.BYTE_LA:
case PixelFormat.R3G3B2:
case PixelFormat.A4R4G4B4:
case PixelFormat.A1R5G5B5:
case PixelFormat.A2R10G10B10:
case PixelFormat.A2B10G10R10:
// I'd like to be able to use r/g/b masks to get FreeImage to load the data
// in it's existing format, but that doesn't work, FreeImage needs to have
// data in RGB[A] (big endian) and BGR[A] (little endian), always.
if ( PixelUtil.HasAlpha( determiningFormat ) )
{
if ( FI.FreeImageEngine.IsLittleEndian )
{
requiredFormat = PixelFormat.BYTE_BGRA;
}
else
{
requiredFormat = PixelFormat.BYTE_RGBA;
}
}
else
{
if ( FI.FreeImageEngine.IsLittleEndian )
{
requiredFormat = PixelFormat.BYTE_BGR;
}
else
{
requiredFormat = PixelFormat.BYTE_RGB;
}
}
imageType = FI.FREE_IMAGE_TYPE.FIT_BITMAP;
break;
case PixelFormat.L8:
case PixelFormat.A8:
imageType = FI.FREE_IMAGE_TYPE.FIT_BITMAP;
break;
case PixelFormat.L16:
imageType = FI.FREE_IMAGE_TYPE.FIT_UINT16;
break;
case PixelFormat.SHORT_GR:
requiredFormat = PixelFormat.SHORT_RGB;
break;
case PixelFormat.SHORT_RGB:
imageType = FI.FREE_IMAGE_TYPE.FIT_RGB16;
break;
case PixelFormat.SHORT_RGBA:
imageType = FI.FREE_IMAGE_TYPE.FIT_RGBA16;
break;
case PixelFormat.FLOAT16_R:
requiredFormat = PixelFormat.FLOAT32_R;
break;
case PixelFormat.FLOAT32_R:
imageType = FI.FREE_IMAGE_TYPE.FIT_FLOAT;
break;
case PixelFormat.FLOAT16_GR:
case PixelFormat.FLOAT16_RGB:
case PixelFormat.FLOAT32_GR:
requiredFormat = PixelFormat.FLOAT32_RGB;
break;
case PixelFormat.FLOAT32_RGB:
imageType = FI.FREE_IMAGE_TYPE.FIT_RGBF;
break;
case PixelFormat.FLOAT16_RGBA:
requiredFormat = PixelFormat.FLOAT32_RGBA;
break;
case PixelFormat.FLOAT32_RGBA:
imageType = FI.FREE_IMAGE_TYPE.FIT_RGBAF;
break;
default:
throw new AxiomException( "Not Supported image format :" + determiningFormat.ToString() );
}//end switch
// Check support for this image type & bit depth
if ( !FI.FreeImage.FIFSupportsExportType( (FI.FREE_IMAGE_FORMAT)_freeImageType, imageType ) ||
!FI.FreeImage.FIFSupportsExportBPP( (FI.FREE_IMAGE_FORMAT)_freeImageType, PixelUtil.GetNumElemBits( requiredFormat ) ) )
{
// Ok, need to allocate a fallback
// Only deal with RGBA . RGB for now
switch ( requiredFormat )
{
case PixelFormat.BYTE_RGBA:
requiredFormat = PixelFormat.BYTE_RGB;
break;
case PixelFormat.BYTE_BGRA:
requiredFormat = PixelFormat.BYTE_BGR;
break;
default:
break;
}
}
bool conversionRequired = false;
input.Position = 0;
byte[] srcData = new byte[ (int)input.Length ];
input.Read( srcData, 0, srcData.Length );
IntPtr srcDataPtr = Memory.PinObject( srcData );
int bpp = PixelUtil.GetNumElemBits( requiredFormat );
if ( !FI.FreeImage.FIFSupportsExportBPP( (FI.FREE_IMAGE_FORMAT)_freeImageType, bpp ) )
{
if ( bpp == 32 && PixelUtil.HasAlpha( imgData.format ) && FI.FreeImage.FIFSupportsExportBPP( (FI.FREE_IMAGE_FORMAT)_freeImageType, 24 ) )
{
// drop to 24 bit (lose alpha)
if ( FI.FreeImage.IsLittleEndian() )
{
requiredFormat = PixelFormat.BYTE_BGR;
}
else
{
requiredFormat = PixelFormat.BYTE_RGB;
}
}
else if ( bpp == 128 && PixelUtil.HasAlpha( imgData.format ) && FI.FreeImage.FIFSupportsExportBPP( (FI.FREE_IMAGE_FORMAT)_freeImageType, 96 ) )
{
//// drop to 96-bit floating point
requiredFormat = PixelFormat.FLOAT32_RGB;
}
}
PixelBox convBox = new PixelBox( imgData.width, imgData.height, 1, requiredFormat );
if ( requiredFormat != imgData.format )
{
conversionRequired = true;
// Allocate memory
byte[] convData = new byte[ convBox.ConsecutiveSize ];
convBox.Data = Memory.PinObject( convData );
PixelBox newSrc = new PixelBox( imgData.width, imgData.height, 1, imgData.format, dataPtr );
PixelConverter.BulkPixelConversion( newSrc, convBox );
srcDataPtr = convBox.Data;
}
ret = FI.FreeImage.AllocateT( imageType, imgData.width, imgData.height, bpp );
if ( ret.IsNull )
{
if ( conversionRequired )
{
Memory.UnpinObject( srcData );
convBox = null;
}
throw new AxiomException( "FreeImage.AllocateT failed - possibly out of memory. " );
}
if ( requiredFormat == PixelFormat.L8 || requiredFormat == PixelFormat.A8 )
{
// Must explicitly tell FreeImage that this is greyscale by setting
// a "grey" palette (otherwise it will save as a normal RGB
// palettized image).
FI.FIBITMAP tmp = FI.FreeImage.ConvertToGreyscale( ret );
FI.FreeImage.Unload( ret );
ret = tmp;
}
int dstPitch = (int)FI.FreeImage.GetPitch( ret );
int srcPitch = imgData.width * PixelUtil.GetNumElemBytes( requiredFormat );
// Copy data, invert scanlines and respect FreeImage pitch
IntPtr pSrc = srcDataPtr;
IntPtr pDest = FI.FreeImage.GetBits( ret );
unsafe
{
byte* byteSrcData = (byte*)( pSrc );
byte* byteDstData = (byte*)( pDest );
for ( int y = 0; y < imgData.height; y++ )
{
byteSrcData = byteSrcData + ( imgData.height - y - 1 ) * srcPitch;
Memory.Copy( pSrc, pDest, srcPitch );
byteDstData += dstPitch;
}
}
if ( conversionRequired )
{
// delete temporary conversion area
Memory.UnpinObject( srcData );
convBox = null;
}
}
return ret;
}
public override void Encode( System.IO.Stream input, System.IO.Stream output, params object[] args )