// Process a 16-bit RGB image with alpha. private static void RgbAlpha16bpp(Frame frame, ScanlineReader reader, int width, int height, int offsetX, int offsetY, int multX, int multY) { int x, y, offset, posn; byte[] scanline; byte[] data = frame.Data; for(y = 0; y < height; ++y) { offset = ((y * multY) + offsetY) * frame.Stride + offsetX * 8; posn = 0; scanline = reader.Read(); for(x = 0; x < width; ++x) { // Flip the RGB PNG data into BGR form. data[offset] = scanline[posn + 5]; data[offset + 1] = scanline[posn + 4]; data[offset + 2] = scanline[posn + 3]; data[offset + 3] = scanline[posn + 2]; data[offset + 4] = scanline[posn + 1]; data[offset + 5] = scanline[posn]; data[offset + 6] = scanline[posn + 7]; data[offset + 7] = scanline[posn + 6]; offset += multX * 8; posn += 8; } } }
// Process a RGB image with alpha and 555 bit fields. private static void RgbAlpha555(Frame frame, ScanlineReader reader, int width, int height, int offsetX, int offsetY, int multX, int multY) { int x, y, offset, posn, value; byte[] scanline; byte[] data = frame.Data; for(y = 0; y < height; ++y) { offset = ((y * multY) + offsetY) * frame.Stride + offsetX * 2; posn = 0; scanline = reader.Read(); for(x = 0; x < width; ++x) { value = ((scanline[posn] & 0xF8) << 7) | ((scanline[posn + 1] & 0xF8) << 2) | ((scanline[posn + 2] & 0xF8) >> 3) | ((scanline[posn + 3] & 0x80) << 8); data[offset] = (byte)value; data[offset + 1] = (byte)(value >> 8); offset += multX * 2; posn += 4; } } }
// Process a 8-bit indexed image. private static void Indexed8bpp(Frame frame, ScanlineReader reader, int width, int height, int offsetX, int offsetY, int multX, int multY) { int x, y, offset, posn; byte[] scanline; byte[] data = frame.Data; for(y = 0; y < height; ++y) { offset = ((y * multY) + offsetY) * frame.Stride + offsetX; scanline = reader.Read(); if(multX == 1) { Array.Copy(scanline, 0, data, offset, width); } else { posn = 0; for(x = 0; x < width; ++x) { data[offset] = scanline[posn]; offset += multX; ++posn; } } } }
// Process a 16-bit gray scale image with alpha. private static void GrayScaleAlpha16bpp(Frame frame, ScanlineReader reader, int width, int height, int offsetX, int offsetY, int multX, int multY) { int x, y, offset, posn; byte value; byte[] scanline; byte[] data = frame.Data; for(y = 0; y < height; ++y) { offset = ((y * multY) + offsetY) * frame.Stride + offsetX * 4; posn = 0; scanline = reader.Read(); for(x = 0; x < width; ++x) { value = scanline[posn]; data[offset] = value; data[offset + 1] = value; data[offset + 2] = value; data[offset + 3] = scanline[posn + 2]; offset += multX * 4; posn += 4; } } }
// Process a 4-bit gray scale image. private static void GrayScale4bpp(Frame frame, ScanlineReader reader, int width, int height, int offsetX, int offsetY, int multX, int multY) { int x, y, offset, posn, bit; byte value; byte[] scanline; byte[] data = frame.Data; for(y = 0; y < height; ++y) { offset = ((y * multY) + offsetY) * frame.Stride + offsetX * 3; posn = 0; bit = 4; scanline = reader.Read(); for(x = 0; x < width; ++x) { value = (byte)(((scanline[posn] >> bit) & 0x0F) * 0x11); data[offset] = value; data[offset + 1] = value; data[offset + 2] = value; offset += multX * 3; bit -= 4; if(bit < 0) { ++posn; bit = 4; } } } }
// 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"); } }
// Process a 1-bit gray scale image. private static void GrayScale1bpp(Frame frame, ScanlineReader reader, int width, int height, int offsetX, int offsetY, int multX, int multY) { int x, y, offset, posn, bit; byte value; byte[] scanline; byte[] data = frame.Data; for(y = 0; y < height; ++y) { offset = ((y * multY) + offsetY) * frame.Stride + offsetX * 3; posn = 0; bit = 0x80; scanline = reader.Read(); for(x = 0; x < width; ++x) { if((scanline[posn] & bit) != 0) { value = 0xFF; } else { value = 0x00; } data[offset] = value; data[offset + 1] = value; data[offset + 2] = value; offset += multX * 3; bit >>= 1; if(bit == 0) { ++posn; bit = 0x80; } } } }