// Determine if an image only has indexed frames in it and // that all frames have the same pixel format. public static bool IsGifEncodable(PortableImage image) { int index; Frame frame; PixelFormat format = (PixelFormat)(-1); if (image.NumFrames == 0) { return(false); } for (index = 0; index < image.NumFrames; ++index) { frame = image.GetFrame(index); if ((frame.PixelFormat & PixelFormat.Indexed) == 0) { return(false); } if (format != (PixelFormat)(-1) && format != frame.PixelFormat) { return(false); } format = frame.PixelFormat; } return(true); }
public PortableImage Reformat(PixelFormat newFormat) { PortableImage newImage = new PortableImage(this, newFormat); for (int i = 0; i < frames.Length; i++) newImage.AddFrame(frames[i].Reformat(newFormat)); return newImage; }
private PortableImage(PortableImage image, PixelFormat format) { Width = image.Width; Height = image.Height; pixelFormat = image.pixelFormat; LoadFormat = image.LoadFormat; if (image.Palette != null) { Palette = (int[]) (image.Palette.Clone()); } TransparentPixel = image.TransparentPixel; }
public static void Load(Stream stream, PortableImage image) { // This just reads enough headers to set size. var ms = new MemoryStream(); stream.CopyTo(ms); var data = new ReadOnlySequence <byte>(ms.ToArray()); var decoder = new JpegDecoder(); decoder.SetInput(data); decoder.Identify(); if (decoder.NumberOfComponents != 1 && decoder.NumberOfComponents != 3) { // We only support Grayscale and YCbCr. throw new NotSupportedException("This color space is not supported"); } image.Width = decoder.Width; image.Height = decoder.Height; image.LoadFormat = PortableImage.Jpeg; image.imageFlags = decoder.NumberOfComponents == 1 ? ImageFlags.ColorSpaceGray : ImageFlags.ColorSpaceRgb; /* * // TODO: bring in the huge complexity that is the JPEG read/write format. * // The PDF stuff doesn't need to read frames, so it would be neat to have this 'lazy' * // Add a frame to the image object. * Frame frame = image.AddFrame(image.Width, image.Height, PixelFormat.Format24bppRgb); * * if (decoder.Precision == 8) * { * // This is the most common case for JPEG, and we have an optimised implementation. * decoder.SetOutputWriter(new JpegBufferOutputWriter8Bit(image.Width, image.Height, 3, frame.data)); * } * else if (decoder.Precision < 8) * { * decoder.SetOutputWriter(new JpegBufferOutputWriterLessThan8Bit(image.Width, image.Height, decoder.Precision, 3, frame.data)); * } * else * { * decoder.SetOutputWriter(new JpegBufferOutputWriterGreaterThan8Bit(image.Width, image.Height, decoder.Precision, 3, frame.data)); * } * * decoder.Decode(); * // TODO: our frame buffer should now be populated with Y,Cr,Cb data. It should get converted to RGB. */ }
// Stretch this image to a new size. public PortableImage Stretch(int width, int height) { Width = width; Height = height; PortableImage newImage = null; for (int i = 0; i < frames.Length; i++) { if (i == 0) newImage = new PortableImage(this, frames[0].Scale(width, height)); else newImage.AddFrame(frames[i].Scale(width, height)); } return newImage; }
// Save a BMP image to the specified stream. public static void Save(Stream stream, PortableImage image) { byte[] buffer = new byte [1024]; int bitCount; int offset; int size; // We can only save the first frame in BMP formats. Frame frame = image.GetFrame(0); if (frame == null) { return; } // Determine the size of the bitmap and the offset to it. bitCount = Utils.FormatToBitCount(frame.pixelFormat); size = frame.Stride * frame.Height; offset = 14 + 40; if (bitCount <= 8) { offset += (1 << bitCount) * 4; } // Build and write the BITMAPFILEHEADER structure. buffer[0] = (byte)'B'; buffer[1] = (byte)'M'; Utils.WriteInt32(buffer, 2, offset + size); buffer[6] = (byte)0; buffer[7] = (byte)0; buffer[8] = (byte)0; buffer[9] = (byte)0; Utils.WriteInt32(buffer, 10, offset); stream.Write(buffer, 0, 14); // Build and write the BITMAPINFO details. SaveBitmapInfo(stream, frame, bitCount, size, buffer, frame.Height); // Write the bitmap data in the frame to the stream. SaveBitmapData(stream, frame, false, false); }
private PortableImage(PortableImage image, Frame thisFrameOnly) : this(image, image.PixelFormat) { if (thisFrameOnly != null) { NumFrames = 1; frames = new Frame [1]; frames[0] = thisFrameOnly.CloneFrame(this); } else { NumFrames = image.NumFrames; if (image.frames != null) { int frame; frames = new Frame [NumFrames]; for (frame = 0; frame < NumFrames; ++frame) { frames[frame] = image.frames[frame].CloneFrame(this); } } } }
// Save a GIF image to the specified stream. This uses the "ungif" // approach of writing uncompressed code samples to avoid infringing // on the LZW patent. The patent may have expired already, but we // aren't taking any chances. Use PNG instead - it compresses better. public static void Save(Stream stream, PortableImage image) { byte[] buffer = new byte [1024]; Frame frame0, frame; int bitCount, index; int[] palette; // Get the first frame in the image and the image's bit depth. frame0 = image.GetFrame(0); bitCount = Utils.FormatToBitCount(frame0.PixelFormat); // Write the GIF file header. buffer[0] = (byte)'G'; buffer[1] = (byte)'I'; buffer[2] = (byte)'F'; buffer[3] = (byte)'8'; buffer[4] = (byte)'9'; buffer[5] = (byte)'a'; Utils.WriteUInt16(buffer, 6, image.Width); Utils.WriteUInt16(buffer, 8, image.Height); buffer[10] = (byte)((bitCount - 1) | ((bitCount - 1) << 4) | 0x80); if (frame0.TransparentPixel != -1) { // Use the transparent pixel as the background color. buffer[11] = (byte)(frame0.TransparentPixel); } else { // Assume that index zero is the background color. buffer[11] = (byte)0x00; } buffer[12] = (byte)0x00; // No aspect ratio specified. stream.Write(buffer, 0, 13); // Write the global color table, which is the palette // for the first frame. We use local color tables only // if the palette changes for subsequent frames. palette = frame0.Palette; WriteGifPalette(stream, palette, bitCount, buffer); // Write out the frames. for (index = 0; index < image.NumFrames; ++index) { // Get the object for this frame. frame = image.GetFrame(index); // Write a graphics control extension for transparencies. if (frame.TransparentPixel != -1) { buffer[0] = (byte)0x21; buffer[1] = (byte)0xF9; buffer[2] = (byte)0x04; buffer[3] = (byte)0x01; buffer[4] = (byte)0x00; buffer[5] = (byte)0x00; buffer[6] = (byte)(frame.TransparentPixel); buffer[7] = (byte)0x00; stream.Write(buffer, 0, 8); } // Write the image descriptor header. buffer[0] = (byte)0x2C; Utils.WriteUInt16(buffer, 1, frame.OffsetX); Utils.WriteUInt16(buffer, 3, frame.OffsetY); Utils.WriteUInt16(buffer, 5, frame.Width); Utils.WriteUInt16(buffer, 7, frame.Height); if (frame.Palette != palette) { buffer[9] = (byte)((bitCount - 1) | 0x80); } else { buffer[9] = (byte)0x00; } stream.Write(buffer, 0, 10); // Write the local color table if necessary. if (frame.Palette != palette) { WriteGifPalette (stream, frame.Palette, bitCount, buffer); } // Compress and output the frame's contents. Compress(stream, buffer, bitCount, frame); } // Write the GIF file terminator. buffer[0] = (byte)0x3B; stream.Write(buffer, 0, 1); }
// Save a PNG image to the specified stream. public static void Save(Stream stream, PortableImage image) { Frame frame = image.GetFrame(0); byte[] buffer = new byte [1024]; ChunkWriter writer = new ChunkWriter(stream); int colorType, bitDepth; int sigRed, sigGreen, sigBlue, sigAlpha; int paletteSize, posn; int[] palette; ZlibCompressor compressor; ScanlineWriter scanlineWriter; OutputFunc func; int y; // Determine the color type and bit depth for the image. sigRed = -1; sigGreen = -1; sigBlue = -1; sigAlpha = -1; paletteSize = 0; switch (frame.PixelFormat) { case PixelFormat.Format16bppRgb555: { sigRed = 5; sigGreen = 5; sigBlue = 5; colorType = 2; bitDepth = 8; } break; case PixelFormat.Format16bppRgb565: { sigRed = 5; sigGreen = 6; sigBlue = 5; colorType = 2; bitDepth = 8; } break; case PixelFormat.Format24bppRgb: case PixelFormat.Format32bppRgb: { colorType = 2; bitDepth = 8; } break; case PixelFormat.Format1bppIndexed: { colorType = 3; bitDepth = 1; paletteSize = 2; } break; case PixelFormat.Format4bppIndexed: { colorType = 3; bitDepth = 4; paletteSize = 16; } break; case PixelFormat.Format8bppIndexed: { colorType = 3; bitDepth = 8; paletteSize = 256; } break; case PixelFormat.Format16bppArgb1555: { sigRed = 5; sigGreen = 5; sigBlue = 5; sigAlpha = 1; colorType = 6; bitDepth = 8; } break; case PixelFormat.Format32bppPArgb: case PixelFormat.Format32bppArgb: { colorType = 6; bitDepth = 8; } break; case PixelFormat.Format16bppGrayScale: { colorType = 0; bitDepth = 16; } break; case PixelFormat.Format48bppRgb: { colorType = 2; bitDepth = 16; } break; case PixelFormat.Format64bppPArgb: case PixelFormat.Format64bppArgb: { colorType = 6; bitDepth = 16; } break; default: throw new FormatException("unknown format"); } // Write out the PNG magic number. stream.Write(magic, 0, magic.Length); // Write the header chunk. Utils.WriteInt32B(buffer, 0, frame.Width); Utils.WriteInt32B(buffer, 4, frame.Height); buffer[8] = (byte)bitDepth; buffer[9] = (byte)colorType; buffer[10] = (byte)0; // Compression method. buffer[11] = (byte)0; // Filter method. buffer[12] = (byte)0; // Interlace method. writer.Write(PngReader.IHDR, buffer, 0, 13); // Write the significant bits chunk if necessary. if (sigAlpha != -1) { buffer[0] = (byte)sigRed; buffer[1] = (byte)sigGreen; buffer[2] = (byte)sigBlue; buffer[3] = (byte)sigAlpha; writer.Write(PngReader.sBIT, buffer, 0, 4); } else if (sigRed != -1) { buffer[0] = (byte)sigRed; buffer[1] = (byte)sigGreen; buffer[2] = (byte)sigBlue; writer.Write(PngReader.sBIT, buffer, 0, 3); } // Write the palette and transparency chunks. if (paletteSize > 0) { Array.Clear(buffer, 0, buffer.Length); palette = frame.Palette; if (palette != null) { for (posn = 0; posn < palette.Length && posn < paletteSize; ++posn) { buffer[posn * 3] = (byte)(palette[posn] >> 16); buffer[posn * 2 + 1] = (byte)(palette[posn] >> 8); buffer[posn * 2 + 2] = (byte)(palette[posn]); } } writer.Write(PngReader.PLTE, buffer, 0, paletteSize * 3); if (frame.TransparentPixel >= 0 && frame.TransparentPixel < paletteSize) { for (posn = 0; posn < paletteSize; ++posn) { buffer[posn] = (byte)0xFF; } buffer[frame.TransparentPixel] = (byte)0x00; writer.Write(PngReader.tRNS, buffer, 0, frame.TransparentPixel + 1); } } // Compress and write the scanlines to the output stream. compressor = new ZlibCompressor(writer); scanlineWriter = new ScanlineWriter (compressor, frame.Width, frame.PixelFormat); func = GetOutputFunc(frame.PixelFormat); for (y = 0; y < frame.Height; ++y) { func(frame, y, scanlineWriter.Buffer); scanlineWriter.FlushScanline(); } compressor.Finish(); // Write the end chunk. writer.Write(PngReader.IEND, buffer, 0, 0); }
// Load a Windows icon image from the specified stream. The first // 4 bytes have already been read and discarded. If "hotspots" is // "true", then the image is actually a Windows cursor with hotspots. public static void Load(Stream stream, PortableImage image, bool hotspots) { byte[] buffer = new byte [1024]; int offset = 4; int numImages, index; int width, height, bpp; PixelFormat format; Frame frame; int[] palette; int paletteCount; int paletteIndex; // Read the number of images in the file. if (stream.Read(buffer, 0, 2) != 2) { throw new FormatException(); } numImages = Utils.ReadUInt16(buffer, 0); offset += 2; // Read the resource directory. int[] offsetList = new int [numImages]; int[] hotspotX = null; int[] hotspotY = null; if (hotspots) { hotspotX = new int[numImages]; hotspotY = new int[numImages]; } for (index = 0; index < numImages; ++index) { if (stream.Read(buffer, 0, 16) != 16) { throw new FormatException(); } offset += 16; if (hotspots) { hotspotX[index] = Utils.ReadUInt16(buffer, 4); hotspotY[index] = Utils.ReadUInt16(buffer, 6); } offsetList[index] = Utils.ReadInt32(buffer, 12); } // Read the contents of the images in the stream. for (index = 0; index < numImages; ++index) { // Seek to the start of the image. Utils.Seek(stream, offset, offsetList[index]); offset = offsetList[index]; // Read the DIB header. if (stream.Read(buffer, 0, 40) != 40) { throw new FormatException(); } offset += 40; width = Utils.ReadUInt16(buffer, 4); // The DIB height is the mask and the bitmap. height = Utils.ReadUInt16(buffer, 8) / 2; bpp = Utils.ReadUInt16(buffer, 14); if (bpp == 1) { format = PixelFormat.Format1bppIndexed; } else if (bpp == 4) { format = PixelFormat.Format4bppIndexed; } else if (bpp == 8) { format = PixelFormat.Format8bppIndexed; } else if (bpp == 24) { format = PixelFormat.Format24bppRgb; } else if (bpp == 32) { format = PixelFormat.Format32bppArgb; } else { throw new FormatException(); } // Create a new frame for this icon. frame = new Frame(image, width, height, format); image.AddFrame(frame); if (hotspots) { frame.HotspotX = hotspotX[index]; frame.HotspotY = hotspotY[index]; } // Copy some of the format information up to the image. if (frame.Width > image.Width) { image.Width = frame.Width; } if (frame.Height > image.Height) { image.Height = frame.Height; } if (image.NumFrames == 1) { image.PixelFormat = format; } // If indexed, get the palette. if ((frame.pixelFormat & PixelFormat.Indexed) != 0) { paletteCount = (1 << Utils.FormatToBitCount(frame.pixelFormat)); if (stream.Read(buffer, 0, paletteCount * 4) != paletteCount * 4) { throw new FormatException(); } offset += paletteCount * 4; palette = new int [paletteCount]; for (paletteIndex = 0; paletteIndex < paletteCount; ++paletteIndex) { palette[paletteIndex] = Utils.ReadBGR (buffer, paletteIndex * 4); } frame.Palette = palette; } // Read the main part of the icon or cursor. BmpReader.LoadBitmapData(stream, frame, false, true); offset += frame.Height * frame.Stride; // Read the mask. BmpReader.LoadBitmapData(stream, frame, true, true); offset += frame.Height * frame.MaskStride; // Invert the mask, because we want 1 to mean "active". InvertMask(frame); } // Set the appropriate load format. if (hotspots) { image.LoadFormat = PortableImage.Cursor; } else { image.LoadFormat = PortableImage.Icon; } }
// Save a Windows icon image to the specified stream. If "hotspots" // is "true", then the image is actually a Windows cursor with hotspots. public static void Save(Stream stream, PortableImage image, bool hotspots) { byte[] buffer = new byte [1024]; int numImages = image.NumFrames; int offset, index, size, bitCount; int[] offsetList; int[] sizeList; Frame frame; // Write the image header. buffer[0] = 0; buffer[1] = 0; buffer[2] = (byte)(hotspots ? 2 : 1); buffer[3] = 0; Utils.WriteUInt16(buffer, 4, numImages); stream.Write(buffer, 0, 6); // Infer the starting offsets and sizes for each of the images. offset = 6 + numImages * 16; offsetList = new int [numImages]; sizeList = new int [numImages]; for (index = 0; index < numImages; ++index) { frame = image.GetFrame(index); size = 40; if ((frame.pixelFormat & PixelFormat.Indexed) != 0) { size += 4 * (1 << Utils.FormatToBitCount(frame.pixelFormat)); } size += frame.Height * (frame.Stride + frame.MaskStride); offsetList[index] = offset; sizeList[index] = size; offset += size; } // Write the contents of the resource directory. for (index = 0; index < image.NumFrames; ++index) { frame = image.GetFrame(index); bitCount = Utils.FormatToBitCount(frame.pixelFormat); buffer[0] = (byte)(frame.Width); buffer[1] = (byte)(frame.Height); buffer[2] = (byte)(1 << bitCount); buffer[3] = 0; if (hotspots) { Utils.WriteUInt16(buffer, 4, frame.HotspotX); Utils.WriteUInt16(buffer, 6, frame.HotspotY); } else { Utils.WriteUInt16(buffer, 4, 0); Utils.WriteUInt16(buffer, 6, 0); } Utils.WriteInt32(buffer, 8, sizeList[index]); Utils.WriteInt32(buffer, 12, offsetList[index]); stream.Write(buffer, 0, 16); } // Write each of the images. for (index = 0; index < image.NumFrames; ++index) { frame = image.GetFrame(index); bitCount = Utils.FormatToBitCount(frame.pixelFormat); BmpWriter.SaveBitmapInfo (stream, frame, bitCount, (frame.Stride + frame.MaskStride) * frame.Height, buffer, frame.Height * 2); BmpWriter.SaveBitmapData(stream, frame, false, false); BmpWriter.SaveBitmapData(stream, frame, true, true); } }
// Load a GIF image from the specified stream. The first 4 bytes // have already been read and discarded. We always load GIF's // as 8bpp because that makes it easier to handle decompression. // GIF's with lower bit depths will be expanded appropriately. public static void Load(Stream stream, PortableImage image) { byte[] buffer = new byte [1024]; int logicalWidth, logicalHeight; int flags, bitCount, numColors, tag; int[] palette; int transparentPixel; int imageWidth, imageHeight; Frame frame; // Read the rest of the GIF header and validate it. if (stream.Read(buffer, 0, 9) != 9) { throw new FormatException(); } if ((buffer[0] != (byte)'7' && buffer[0] != (byte)'9') || buffer[1] != (byte)'a') { throw new FormatException(); } logicalWidth = Utils.ReadUInt16(buffer, 2); logicalHeight = Utils.ReadUInt16(buffer, 4); flags = buffer[6]; // buffer[7] is the background index, which we ignore. // buffer[8] is the aspect ratio, which we ignore. if (logicalWidth == 0 || logicalHeight == 0) { throw new FormatException(); } // Set the global image information. bitCount = (flags & 0x07) + 1; numColors = (1 << bitCount); image.Width = logicalWidth; image.Height = logicalHeight; image.PixelFormat = PixelFormat.Format8bppIndexed; image.LoadFormat = PortableImage.Gif; // Read the global color table, if present. if ((flags & 0x80) != 0) { image.Palette = ReadGifPalette(stream, buffer, numColors); } // Process the image and extension blocks in the image. transparentPixel = -1; while (stream.Read(buffer, 0, 1) == 1) { tag = buffer[0]; if (tag == 0x2C) { // Read the image descriptor. if (stream.Read(buffer, 0, 9) != 9) { throw new FormatException(); } imageWidth = Utils.ReadUInt16(buffer, 4); imageHeight = Utils.ReadUInt16(buffer, 6); flags = buffer[8]; if (imageWidth == 0 || imageHeight == 0) { throw new FormatException(); } frame = image.AddFrame(imageWidth, imageHeight, image.PixelFormat); frame.TransparentPixel = transparentPixel; frame.OffsetX = Utils.ReadUInt16(buffer, 0); frame.OffsetY = Utils.ReadUInt16(buffer, 2); transparentPixel = -1; // Read the local color table, if any. if ((flags & 0x80) != 0) { tag = (1 << ((flags & 0x07) + 1)); frame.Palette = ReadGifPalette (stream, buffer, tag); } // Decompress the image into the frame. Decompress(stream, buffer, frame, (flags & 0x40) != 0); } else if (tag == 0x21) { // Process an extension. if (stream.Read(buffer, 0, 1) != 1) { throw new FormatException(); } if (buffer[0] == (byte)0xF9) { // Graphic control extension sub-block. if (stream.Read(buffer, 0, 1) != 1) { throw new FormatException(); } tag = buffer[0]; if (stream.Read(buffer, 0, tag) != tag) { throw new FormatException(); } if (tag >= 4) { if ((buffer[0] & 0x01) != 0) { transparentPixel = buffer[3]; } else { transparentPixel = -1; } } } // Skip the remaining extension sub-blocks. SkipSubBlocks(stream, buffer); } else if (tag == 0x3B) { // End of the GIF file. break; } else { // Invalid GIF file. throw new FormatException(); } } }
// Load a BMP image from the specified stream. The first 4 bytes // have already been read and discarded. public static void Load(Stream stream, PortableImage image) { byte[] buffer = new byte [1024]; int width, height, planes, bitCount; int compression; bool quads; // Read the rest of the BITMAPFILEHEADER. if (stream.Read(buffer, 0, 10) != 10) { throw new FormatException(); } int bfOffBits = Utils.ReadInt32(buffer, 6); // The current file offset at the end of the BITMAPFILEHEADER. int offset = 14; // Get the size of the BITMAPINFOHEADER structure that follows, // and then read it into the buffer. if (stream.Read(buffer, 0, 4) != 4) { throw new FormatException(); } int size = Utils.ReadInt32(buffer, 0); if (size <= 4 || size > 1024) { throw new FormatException(); } if (stream.Read(buffer, 4, size - 4) != (size - 4)) { throw new FormatException(); } offset += size; if (size >= 40) { // This is a BITMAPINFOHEADER structure (Windows bitmaps). width = Utils.ReadInt32(buffer, 4); height = Utils.ReadInt32(buffer, 8); planes = Utils.ReadUInt16(buffer, 12); bitCount = Utils.ReadUInt16(buffer, 14); compression = Utils.ReadInt32(buffer, 16); quads = true; } else if (size == 12) { // This is a BITMAPCOREHEADER structure (OS/2 bitmaps). width = Utils.ReadUInt16(buffer, 4); height = Utils.ReadUInt16(buffer, 6); planes = Utils.ReadUInt16(buffer, 8); bitCount = Utils.ReadUInt16(buffer, 10); compression = 0; // BI_RGB quads = false; } else { throw new FormatException(); } // Perform a sanity check on the header values. if (width <= 0 || planes != 1) { throw new FormatException(); } if (bitCount != 1 && bitCount != 4 && bitCount != 16 && bitCount != 8 && bitCount != 24) { // TODO: non-traditional BMP formats. throw new FormatException(); } if (compression != 0 && compression != 3 /*BI_BITFIELDS*/) { // TODO: RLE bitmaps throw new FormatException(); } // Set the basic image properties. image.Width = width; image.Height = height < 0 ? -height : height; image.PixelFormat = Utils.BitCountToFormat(bitCount); image.LoadFormat = PortableImage.Bmp; // Do the unusual 16 bit formats. if (compression == 3) { if (stream.Read(buffer, 0, 3 * 4) != (3 * 4)) { throw new FormatException(); } int redMask = Utils.ReadInt32(buffer, 0); int greenMask = Utils.ReadInt32(buffer, 4); int blueMask = Utils.ReadInt32(buffer, 8); if (blueMask == 0x001F && redMask == 0x7C00 && greenMask == 0x03E0) { image.PixelFormat = PixelFormat.Format16bppRgb555; } else if (blueMask == 0x001F && redMask == 0xF800 && greenMask == 0x07E0) { image.PixelFormat = PixelFormat.Format16bppRgb565; } else { throw new FormatException(); } } // Read the palette into memory and set it. if (bitCount <= 8) { int colors = (1 << bitCount); int index; int[] palette = new int [colors]; if (quads) { // The RGB values are specified as RGBQUAD's. if (stream.Read(buffer, 0, colors * 4) != (colors * 4)) { throw new FormatException(); } offset += colors * 4; for (index = 0; index < colors; ++index) { palette[index] = Utils.ReadBGR(buffer, index * 4); } } else { // The RGB values are specified as RGBTRIPLE's. if (stream.Read(buffer, 0, colors * 3) != (colors * 3)) { throw new FormatException(); } offset += colors * 3; for (index = 0; index < colors; ++index) { palette[index] = Utils.ReadBGR(buffer, index * 3); } } image.Palette = palette; } // Seek to the start of the bitmap data. Utils.Seek(stream, offset, bfOffBits); // Add a frame to the image object. Frame frame = image.AddFrame(); // Load the bitmap data from the stream into the frame. LoadBitmapData(stream, frame, false, height > 0); }