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; }
// 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; } }
// 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); }