// Draw the source parallelogram of an image into the parallelogram // defined by dest. Point[] has 3 Points, Top-Left, Top-Right and Bottom-Left. // The remaining point is inferred. public virtual void DrawImage(IToolkitImage image, Point[] src, Point[] dest) { int originX = dest[0].X; int originY = dest[0].Y; for (int i = 1; i < 3; i++) { if (originX > dest[i].X) { originX = dest[i].X; } if (originY > dest[i].Y) { originY = dest[i].Y; } } DotGNU.Images.Image gnuImage = (image as ToolkitImageBase).image; // Currently we only draw the first frame. Frame frame = gnuImage.GetFrame(0); frame = frame.AdjustImage(src[0].X, src[0].Y, src[1].X, src[1].Y, src[2].X, src[2].Y, dest[0].X - originX, dest[0].Y - originY, dest[1].X - originX, dest[1].Y - originY, dest[2].X - originX, dest[2].Y - originY); // Setup the new image and draw it. using (DotGNU.Images.Image newGnuImage = new DotGNU.Images.Image(gnuImage.Width, gnuImage.Height, gnuImage.PixelFormat)) { newGnuImage.AddFrame(frame); IToolkitImage newImage = Toolkit.CreateImage(newGnuImage, 0); DrawImage(newImage, originX, originY); } }
// This is an internal member and should not be used. // Returns a bitmap which is the resized (to newWidth and newHeight) of the first frame. public Image Resize(int newWidth, int newHeight) { Frame frame = dgImage.GetFrame(0); Frame newFrame = frame.AdjustImage(0, 0, width, 0, 0, height, 0, 0, newWidth, 0, 0, newHeight); DotGNU.Images.Image newImage = new DotGNU.Images.Image(newWidth, newHeight, newFrame.PixelFormat); newImage.AddFrame(newFrame); return(new Bitmap(newImage)); }
public Image Reformat(PixelFormat newFormat) { Image newImage = new Image(this, newFormat); for (int i = 0; i < frames.Length; i++) { newImage.AddFrame(frames[i].Reformat(newFormat)); } return(newImage); }
public Image Reformat(PixelFormat newFormat) { Image newImage = new Image(this, newFormat); for (int i = 0; i < frames.Length; i++) newImage.AddFrame(frames[i].Reformat(newFormat)); return newImage; }
// Create a device independant bitmap from a frame. Optionally set all bits that are masked to black. // This is required for icons. private IntPtr HandleFromBitmap(Frame frame, bool andMask) { // By default we use the data straight from the frame. byte[] data = frame.Data; if (andMask) { //TODO: this could be slow. // Create a new image that we will copy the pixels to, leaving the masked pixels black. DotGNU.Images.Image newImage = new DotGNU.Images.Image(frame.Width, frame.Height, frame.PixelFormat); Frame newFrame = newImage.AddFrame(); data = new byte[data.Length]; for (int y = 0; y < frame.Height; y++) { for (int x = 0; x < frame.Width; x++) { if (frame.GetMask(x, y) != 0) newFrame.SetPixel(x, y, frame.GetPixel(x, y)); } } data = newFrame.Data; } // Create BITMAPINFO structure. int bitmapInfoSize = 40; int bitCount = frame.BitsPerPixel; // Do we have a palette? if(bitCount <= 8) bitmapInfoSize += 1 << bitCount * 4; byte[] bitmapInfo = new byte[bitmapInfoSize]; // Build and write the BITMAPINFOHEADER structure. WriteInt32(bitmapInfo, 0, 40);// biSize WriteInt32(bitmapInfo, 4, frame.Width); WriteInt32(bitmapInfo, 8, -frame.Height);// upside down so make the height negative. WriteUInt16(bitmapInfo, 12, 1);// biPlanes WriteUInt16(bitmapInfo, 14, bitCount); WriteInt32(bitmapInfo, 16, 0);// biCompression WriteInt32(bitmapInfo, 20, 0);// size of image WriteInt32(bitmapInfo, 24, 3780);// biXPelsPerMeter WriteInt32(bitmapInfo, 28, 3780);// biYPelsPerMeter WriteInt32(bitmapInfo, 32, 0); // biClrUsed WriteInt32(bitmapInfo, 36, 0); // biClrImportant // Write the palette. if(bitCount <= 8) { int count = (1 << bitCount); for(int index = 0; index < count; ++index) { if(frame.Palette != null && index < frame.Palette.Length) WriteBGR(bitmapInfo, index * 4 + 40, frame.Palette[index]); else { // Short palette: pad with black pixels. WriteBGR(bitmapInfo, index * 4 + 40, 0); } } } return Win32.Api.CreateDIBitmap( Win32.Api.GetDC(hwnd), bitmapInfo, 4 /*CBM_INIT*/, data, bitmapInfo, 0 /*DIB_RGB_COLORS*/); }
// Load a BMP image from the specified stream. The first 4 bytes // have already been read and discarded. public static void Load(Stream stream, Image 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 = Image.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); }
// 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, Image 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 = Image.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(); } } }
// This is an internal member and should not be used. // Returns a bitmap which is the resized (to newWidth and newHeight) of the first frame. public Image Resize(int newWidth, int newHeight) { Frame frame = dgImage.GetFrame(0); Frame newFrame = frame.AdjustImage(0, 0, width, 0, 0, height, 0, 0, newWidth, 0, 0, newHeight); DotGNU.Images.Image newImage = new DotGNU.Images.Image(newWidth, newHeight, newFrame.PixelFormat); newImage.AddFrame(newFrame); return new Bitmap(newImage); }
// Create a gradient for the background of a title bar. private DotGNU.Images.Image CreateGradient (int width, int height, Color startColor, Color endColor) { if(startColor.Index != StandardColor.RGB) { startColor = screen.ToColor(startColor.Index); } if(endColor.Index != StandardColor.RGB) { endColor = screen.ToColor(endColor.Index); } int startR = startColor.Red; int startG = startColor.Green; int startB = startColor.Blue; int lenR = endColor.Red - startR; int lenG = endColor.Green - startG; int lenB = endColor.Blue - startB; DotGNU.Images.Image image = new DotGNU.Images.Image (width, height, PixelFormat.Format24bppRgb); Frame frame = image.AddFrame(); int x, y, red, green, blue; for(y = 0; y < height; ++y) { for(x = 0; x < width; ++x) { red = startR + lenR * x / width; green = startG + lenG * x / width; blue = startB + lenB * x / width; frame.SetPixel(x, y, (red << 16) + (green << 8) + blue); } } return image; }
// Draw the source parallelogram of an image into the parallelogram // defined by dest. Point[] has 3 Points, Top-Left, Top-Right and Bottom-Left. // The remaining point is inferred. public virtual void DrawImage(IToolkitImage image, Point[] src, Point[] dest) { int originX = dest[0].X; int originY = dest[0].Y; for (int i = 1; i < 3; i++) { if (originX > dest[i].X) originX = dest[i].X; if (originY > dest[i].Y) originY = dest[i].Y; } DotGNU.Images.Image gnuImage = (image as ToolkitImageBase).image; // Currently we only draw the first frame. Frame frame = gnuImage.GetFrame(0); frame = frame.AdjustImage(src[0].X, src[0].Y, src[1].X, src[1].Y, src[2].X, src[2].Y, dest[0].X - originX, dest[0].Y - originY, dest[1].X - originX, dest[1].Y - originY, dest[2].X - originX, dest[2].Y - originY); // Setup the new image and draw it. using (DotGNU.Images.Image newGnuImage = new DotGNU.Images.Image(gnuImage.Width, gnuImage.Height, gnuImage.PixelFormat)) { newGnuImage.AddFrame(frame); IToolkitImage newImage = Toolkit.CreateImage(newGnuImage, 0); DrawImage( newImage, originX, originY); } }
// Load a PNG image from the specified stream. The first 4 bytes // have already been read and discarded. public static void Load(Stream stream, Image image) { byte[] buffer = new byte [1024]; int width = 0; int height = 0; int bitDepth = 0; int colorType = 0; int compressionMethod = 0; int filterMethod = 0; int interlaceMethod = 0; Frame frame = null; PixelFormat format = 0; int index; int significant = 0; ZlibDecompressor decompress = null; ScanlineReader scanlineReader; int pass, passWidth, passHeight; PassFunc passFunc; // Read the rest of the magic number and check it. if(stream.Read(buffer, 0, 4) != 4) { throw new FormatException("could not read magic number"); } if(buffer[0] != (byte)13 || buffer[1] != (byte)10 || buffer[2] != (byte)26 || buffer[3] != (byte)10) { throw new FormatException("invalid magic number"); } // Create a chunk reader for the stream. ChunkReader reader = new ChunkReader(stream, buffer); // Read all of the chunks from the stream. while(reader.Type != IEND) { // Process the critical chunk types. if(reader.Type == IHDR) { // We can only have one header per PNG image. if(image.NumFrames > 0) { throw new FormatException("multiple headers"); } // Read the contents of the image header. if(reader.Read(buffer, 0, 13) != 13) { throw new FormatException("truncated header"); } width = Utils.ReadInt32B(buffer, 0); height = Utils.ReadInt32B(buffer, 4); bitDepth = buffer[8]; colorType = buffer[9]; compressionMethod = buffer[10]; filterMethod = buffer[11]; interlaceMethod = buffer[12]; // Sanity-check the values. if(width < 1 || height < 1) { throw new FormatException("invalid size"); } if(colorType == 0) { if(bitDepth != 1 && bitDepth != 2 && bitDepth != 4 && bitDepth != 8 && bitDepth != 16) { throw new FormatException ("invalid depth for color type 0"); } } else if(colorType == 2 || colorType == 4 || colorType == 6) { if(bitDepth != 8 && bitDepth != 16) { throw new FormatException ("invalid depth for color type " + colorType.ToString()); } } else if(colorType == 3) { if(bitDepth != 1 && bitDepth != 2 && bitDepth != 4 && bitDepth != 8) { throw new FormatException ("invalid depth for color type 3"); } } else { throw new FormatException("invalid color type"); } if(compressionMethod != 0) { throw new FormatException ("invalid compression method"); } if(filterMethod != 0) { throw new FormatException ("invalid filter method"); } if(interlaceMethod != 0 && interlaceMethod != 1) { throw new FormatException ("invalid interlace method"); } // Create the image frame with the requested values. if(colorType == 3) { format = PixelFormat.Format8bppIndexed; } else if((colorType & 4) != 0) { if(significant == 0x01050505 && bitDepth == 8) { format = PixelFormat.Format16bppArgb1555; } else if(bitDepth == 8) { format = PixelFormat.Format32bppArgb; } else { format = PixelFormat.Format64bppArgb; } } else if(colorType == 0 && bitDepth == 16) { format = PixelFormat.Format16bppGrayScale; } else { if(significant == 0x00050505 && bitDepth == 8) { format = PixelFormat.Format16bppRgb555; } else if(significant == 0x00050605 && bitDepth == 8) { format = PixelFormat.Format16bppRgb565; } else if(bitDepth == 8) { format = PixelFormat.Format24bppRgb; } else { format = PixelFormat.Format48bppRgb; } } image.Width = width; image.Height = height; image.PixelFormat = format; image.LoadFormat = Image.Png; frame = image.AddFrame(width, height, format); } else if(reader.Type == PLTE) { // We must have a frame at this point. if(frame == null) { throw new FormatException ("palette specified before image header"); } // The palette is only required for color type 3. // Other color types use it as a hint only. if(colorType == 3) { int[] palette = new int [256]; frame.Palette = palette; Array.Clear(buffer, 0, buffer.Length); if(reader.Length > 768) { reader.Read(buffer, 0, 768); } else { reader.Read(buffer, 0, buffer.Length); } for(index = 0; index < 256; ++index) { palette[index] = Utils.ReadRGB(buffer, index * 3); } } } else if(reader.Type == tRNS) { // We must have a frame at this point. if(frame == null) { throw new FormatException ("transparency specified before image header"); } // We only support simple transparencies for // color type 3 at present. The transparency // information is ignored for other color types. if(colorType == 3) { index = 0; while(index < 256 && reader.Length > 0) { if(reader.Read(buffer, 0, 1) != 1) { break; } if(buffer[0] < 0x80) { frame.TransparentPixel = index; break; } ++index; } } } else if(reader.Type == sBIT) { // Read the number of significant bits so that // we can detect images that started off life // as 15-bit or 16-bit RGB. if(reader.Length == 3) { reader.Read(buffer, 0, 3); significant = Utils.ReadRGB(buffer, 0); } else if(reader.Length == 4) { reader.Read(buffer, 0, 4); significant = Utils.ReadRGB(buffer, 0) | (buffer[3] << 24); } } else if(reader.Type == IDAT) { // We must have a frame at this point. if(frame == null) { throw new FormatException ("image data specified before image header"); } // There can be only one set of data chunks. if(decompress != null) { throw new FormatException ("multiple image data blocks encountered"); } // Create a zlib decompressor. decompress = new ZlibDecompressor(reader); // Get the pass processing function. passFunc = GetPassFunc(colorType, bitDepth, format); // Process the data in the image. if(interlaceMethod == 0) { // No interlacing. scanlineReader = new ScanlineReader (decompress, width, height, colorType, bitDepth); passFunc(frame, scanlineReader, width, height, 0, 0, 1, 1); } else { // Use Adam7 interlacing. for(pass = 0; pass < 7; ++pass) { // Calculate the width and height of the pass. // Please refer "PNG - The Definitive Guide" // for a totally misleading and incompatible // description of the following code - Gopal passWidth = width + adam7Rules[(pass+1) * 4 + 2] - 1; passWidth /= adam7Rules[pass * 4 + 2]; if(passWidth <= 0) { continue; } passHeight = height + adam7Rules[(pass+1) * 4 + 3 ] - 1; passHeight /= adam7Rules[pass * 4 + 3]; if(passHeight <= 0) { continue; } // Create a scanline reader for the pass. scanlineReader = new ScanlineReader (decompress, passWidth, passHeight, colorType, bitDepth); // Process the Adam7 pass. passFunc(frame, scanlineReader, passWidth, passHeight, adam7Rules[pass * 4], adam7Rules[pass * 4 + 1], adam7Rules[pass * 4 + 2], adam7Rules[pass * 4 + 3]); } } // Eat any remaining IDAT data blocks. decompress.EatRemaining(); // Skip the "Reset", because we've already done it. continue; } // Reset the chunk reader and move on to the next chunk. reader.Reset(buffer); } // Skip the contents of the IEND chunk and check its CRC. reader.Skip(buffer); // If we don't have a frame or decompressor, // then the PNG stream was empty. if(frame == null || decompress == null) { throw new FormatException("PNG did not contain an image"); } }
// Load a JPEG image from the specified stream. The first 4 bytes // have already been read and discarded. public static void Load(Stream stream, Image image, byte[] prime, int primeLen) { // Determine if we actually have the JPEG library. if(!JpegLib.JpegLibraryPresent()) { throw new FormatException("libjpeg is not available"); } // Create the decompression object. JpegLib.jpeg_decompress_struct cinfo; cinfo = new JpegLib.jpeg_decompress_struct(); cinfo.err = JpegLib.CreateErrorHandler(); JpegLib.jpeg_create_decompress(ref cinfo); // Initialize the source manager. JpegLib.StreamToSourceManager (ref cinfo, stream, prime, primeLen); // Read the JPEG header. JpegLib.jpeg_read_header(ref cinfo, (Int)1); // Set the decompression parameters the way we want them. cinfo.out_color_space = JpegLib.J_COLOR_SPACE.JCS_RGB; // Start the decompression process. JpegLib.jpeg_start_decompress(ref cinfo); // Initialize the image to 24-bit RGB, to match the JPEG file. image.Width = (int)(cinfo.output_width); image.Height = (int)(cinfo.output_height); image.PixelFormat = PixelFormat.Format24bppRgb; if(prime[3] == 0xE1) { image.LoadFormat = Image.Exif; } else { image.LoadFormat = Image.Jpeg; } Frame frame = image.AddFrame(); // Read the scanlines from the image. int posn, width, offset, stride, y, twidth; width = frame.Width; twidth = width * 3; stride = frame.Stride; byte[] data = frame.Data; IntPtr buf = Marshal.AllocHGlobal(width * 3); byte *pbuf = (byte *)buf; y = 0; while(((int)(cinfo.output_scanline)) < ((int)(cinfo.output_height))) { JpegLib.jpeg_read_scanlines (ref cinfo, ref buf, (UInt)1); offset = (y++) * stride; for(posn = 0; posn < twidth; posn += 3) { // Convert the JPEG RGB data into BGR for the frame. data[offset] = pbuf[posn + 2]; data[offset + 1] = pbuf[posn + 1]; data[offset + 2] = pbuf[posn]; offset += 3; } } Marshal.FreeHGlobal(buf); // Finish the decompression process. JpegLib.jpeg_finish_decompress(ref cinfo); // Clean everything up. JpegLib.FreeSourceManager(ref cinfo); JpegLib.jpeg_destroy_decompress(ref cinfo); JpegLib.FreeErrorHandler(cinfo.err); }
// Load a BMP image from the specified stream. The first 4 bytes // have already been read and discarded. public static void Load(Stream stream, Image 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 = Image.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); }
// 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, Image 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 = Image.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 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, Image 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 = Image.Cursor; } else { image.LoadFormat = Image.Icon; } }