private FI.FIBITMAP _encode(Stream input, CodecData codecData) { var ret = new FI.FIBITMAP(); ret.SetNull(); var imgData = codecData as ImageData; if (imgData != null) { var data = new byte[(int)input.Length]; input.Read(data, 0, data.Length); var dataPtr = BufferBase.Wrap(data); var 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. var requiredFormat = imgData.format; // determine the settings var imageType = FI.FREE_IMAGE_TYPE.FIT_UNKNOWN; var 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 :{0}", determiningFormat.ToString()); } //end switch // Check support for this image type & bit depth if (!FI.FreeImage.FIFSupportsExportType((FI.FREE_IMAGE_FORMAT) this._freeImageType, imageType) || !FI.FreeImage.FIFSupportsExportBPP((FI.FREE_IMAGE_FORMAT) this._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; } } var conversionRequired = false; input.Position = 0; var srcData = new byte[(int)input.Length]; input.Read(srcData, 0, srcData.Length); var srcDataPtr = BufferBase.Wrap(srcData); // Check BPP var bpp = PixelUtil.GetNumElemBits(requiredFormat); if (!FI.FreeImage.FIFSupportsExportBPP((FI.FREE_IMAGE_FORMAT) this._freeImageType, bpp)) { if (bpp == 32 && PixelUtil.HasAlpha(imgData.format) && FI.FreeImage.FIFSupportsExportBPP((FI.FREE_IMAGE_FORMAT) this._freeImageType, 24)) { // drop to 24 bit (lose alpha) if (FI.FreeImage.IsLittleEndian()) { requiredFormat = PixelFormat.BYTE_BGR; } else { requiredFormat = PixelFormat.BYTE_RGB; } bpp = 24; } else if (bpp == 128 && PixelUtil.HasAlpha(imgData.format) && FI.FreeImage.FIFSupportsExportBPP((FI.FREE_IMAGE_FORMAT) this._freeImageType, 96)) { // drop to 96-bit floating point requiredFormat = PixelFormat.FLOAT32_RGB; } } var convBox = new PixelBox(imgData.width, imgData.height, 1, requiredFormat); if (requiredFormat != imgData.format) { conversionRequired = true; // Allocate memory var convData = new byte[convBox.ConsecutiveSize]; convBox.Data = BufferBase.Wrap(convData); // perform conversion and reassign source var 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) { srcDataPtr.SafeDispose(); 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). var tmp = FI.FreeImage.ConvertToGreyscale(ret); FI.FreeImage.Unload(ret); ret = tmp; } var dstPitch = (int)FI.FreeImage.GetPitch(ret); var srcPitch = imgData.width * PixelUtil.GetNumElemBytes(requiredFormat); // Copy data, invert scanlines and respect FreeImage pitch var pSrc = srcDataPtr; using (var pDest = BufferBase.Wrap(FI.FreeImage.GetBits(ret), imgData.height * srcPitch)) { var byteSrcData = pSrc; var byteDstData = pDest; for (var y = 0; y < imgData.height; ++y) { byteSrcData += (imgData.height - y - 1) * srcPitch; Memory.Copy(pSrc, pDest, srcPitch); byteDstData += dstPitch; } } if (conversionRequired) { // delete temporary conversion area srcDataPtr.SafeDispose(); convBox = null; } } return(ret); }
public static void ConvertToIL(PixelBox src) { // ilTexImage http://openil.sourceforge.net/docs/il/f00059.htm var ifmt = Convert(src.Format); if (src.IsConsecutive && ifmt.IsValid) { // The easy case, the buffer is laid out in memory just like // we want it to be and is in a format DevIL can understand directly // We could even save the copy if DevIL would let us Il.ilTexImage(src.Width, src.Height, src.Depth, (byte)ifmt.Channels, ifmt.Format, ifmt.Type, src.Data.Pin()); src.Data.UnPin(); } else if (ifmt.IsValid) { // The format can be understood directly by DevIL. The only // problem is that ilTexImage expects our image data consecutively // so we cannot use that directly. // Let DevIL allocate the memory for us, and copy the data consecutively // to its memory Il.ilTexImage(src.Width, src.Height, src.Depth, (byte)ifmt.Channels, ifmt.Format, ifmt.Type, IntPtr.Zero); using (var dstbuf = BufferBase.Wrap(Il.ilGetData(), Il.ilGetInteger(Il.IL_IMAGE_SIZE_OF_DATA))) { var dst = new PixelBox(src.Width, src.Height, src.Depth, src.Format, dstbuf); PixelConverter.BulkPixelConversion(src, dst); } } else { // Here it gets ugly. We're stuck with a pixel format that DevIL // can't do anything with. We will do a bulk pixel conversion and // then feed it to DevIL anyway. The problem is finding the best // format to convert to. // most general format supported by Axiom and DevIL var fmt = PixelUtil.HasAlpha(src.Format) ? PixelFormat.FLOAT32_RGBA : PixelFormat.FLOAT32_RGB; // Make up a pixel format // We don't have to consider luminance formats as they have // straight conversions to DevIL, just weird permutations of RGBA an LA var depths = PixelUtil.GetBitDepths(src.Format); // Native endian format with all bit depths<8 can safely and quickly be // converted to 24/32 bit if (PixelUtil.IsNativeEndian(src.Format) && depths[0] <= 8 && depths[1] <= 8 && depths[2] <= 8 && depths[3] <= 8) { if (PixelUtil.HasAlpha(src.Format)) { fmt = PixelFormat.A8R8G8B8; } else { fmt = PixelFormat.R8G8B8; } } // Let DevIL allocate the memory for us, then do the conversion ourselves ifmt = Convert(fmt); Il.ilTexImage(src.Width, src.Height, src.Depth, (byte)ifmt.Channels, ifmt.Format, ifmt.Type, IntPtr.Zero); // TAO 2.0 //Il.ilTexImage( src.Width, src.Height, src.Depth, (byte)ifmt.Channels, ifmt.Format, ifmt.Type, null ); using (var dstbuf = BufferBase.Wrap(Il.ilGetData(), Il.ilGetInteger(Il.IL_IMAGE_SIZE_OF_DATA))) { var dst = new PixelBox(src.Width, src.Height, src.Depth, fmt, dstbuf); PixelConverter.BulkPixelConversion(src, dst); } } }