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