src/BigGustave/PngBuilder.cs
/// <summary>
/// Write the PNG file bytes to the provided stream.
/// </summary>
Write the PNG file bytes to the provided stream.
Write the PNG file bytes to the provided stream .
public void Save(Stream outputStream, SaveOptions options = null)
        {
            options = options ?? new SaveOptions();
            byte[] palette = null;
            var dataLength = rawData.Length;
            var bitDepth = 8;
            if (!hasTooManyColorsForPalette)
            {
                var paletteColors = colorCounts.OrderByDescending(x => x.Value).Select(x => x.Key).ToList();
                bitDepth = paletteColors.Count >= 128 ? 8 : 4;
                var samplesPerByte = bitDepth == 8 ? 1 : 2;
                var applyShift = samplesPerByte == 2;
                palette = new byte[3 * paletteColors.Count];
                for (var i = 0; i < paletteColors.Count; i++)
                {
                    var color = ColorIntToPixel(paletteColors[i]);
                    var startIndex = i * 3;
                    palette[startIndex++] = color.r;
                    palette[startIndex++] = color.g;
                    palette[startIndex] = color.b;
                }
                var rawDataIndex = 0;
                for (var y = 0; y < height; y++)
                {
                    rawData[rawDataIndex++] = 0;
                    for (var x = 0; x < width; x++)
                    {
                        var index = ((y * width * bytesPerPixel) + y + 1) + (x * bytesPerPixel);
                        var r = rawData[index++];
                        var g = rawData[index++];
                        var b = rawData[index];
                        var colorInt = PixelToColorInt(r, g, b);
                        var value = (byte)paletteColors.IndexOf(colorInt);
                        if (applyShift)
                        {
                            var withinByteIndex = x % 2;
                            if (withinByteIndex == 1)
                            {
                                rawData[rawDataIndex] = (byte)(rawData[rawDataIndex] + value);
                                rawDataIndex++;
                            }
                            else
                            {
                                rawData[rawDataIndex] = (byte)(value << 4);
                            }
                        }
                        else
                        {
                            rawData[rawDataIndex++] = value;
                        }
                    }
                }
                dataLength = rawDataIndex;
            }
            else
            {
                AttemptCompressionOfRawData(rawData, options);
            }
            outputStream.Write(HeaderValidationResult.ExpectedHeader, 0, HeaderValidationResult.ExpectedHeader.Length);
            var stream = new PngStreamWriteHelper(outputStream);
            stream.WriteChunkLength(13);
            stream.WriteChunkHeader(ImageHeader.HeaderBytes);
            StreamHelper.WriteBigEndianInt32(stream, width);
            StreamHelper.WriteBigEndianInt32(stream, height);
            stream.WriteByte((byte)bitDepth);
            var colorType = ColorType.ColorUsed;
            if (hasAlphaChannel)
            {
                colorType |= ColorType.AlphaChannelUsed;
            }
            if (palette != null)
            {
                colorType |= ColorType.PaletteUsed;
            }
            stream.WriteByte((byte)colorType);
            stream.WriteByte((byte)CompressionMethod.DeflateWithSlidingWindow);
            stream.WriteByte((byte)FilterMethod.AdaptiveFiltering);
            stream.WriteByte((byte)InterlaceMethod.None);
            stream.WriteCrc();
            if (palette != null)
            {
                stream.WriteChunkLength(palette.Length);
                stream.WriteChunkHeader(Encoding.ASCII.GetBytes("PLTE"));
                stream.Write(palette, 0, palette.Length);
                stream.WriteCrc();
            }
            var imageData = Compress(rawData, dataLength, options);
            stream.WriteChunkLength(imageData.Length);
            stream.WriteChunkHeader(Encoding.ASCII.GetBytes("IDAT"));
            stream.Write(imageData, 0, imageData.Length);
            stream.WriteCrc();
            stream.WriteChunkLength(0);
            stream.WriteChunkHeader(Encoding.ASCII.GetBytes("IEND"));
            stream.WriteCrc();
        }
public void Save ( Stream outputStream , SaveOptions options = null ) { options = options ?? new SaveOptions ( ) ; byte [ ] palette = null ; var dataLength = rawData . Length ; var bitDepth = 8 ; if ( ! hasTooManyColorsForPalette ) { var paletteColors = colorCounts . OrderByDescending ( x => x . Value ) . Select ( x => x . Key ) . ToList ( ) ; bitDepth = paletteColors . Count >= 128 ? 8 : 4 ; var samplesPerByte = bitDepth == 8 ? 1 : 2 ; var applyShift = samplesPerByte == 2 ; palette = new byte [ 3 * paletteColors . Count ] ; for ( var i = 0 ; i < paletteColors . Count ; i ++ ) { var color = ColorIntToPixel ( paletteColors [ i ] ) ; var startIndex = i * 3 ; palette [ startIndex ++ ] = color . r ; palette [ startIndex ++ ] = color . g ; palette [ startIndex ] = color . b ; } var rawDataIndex = 0 ; for ( var y = 0 ; y < height ; y ++ ) { rawData [ rawDataIndex ++ ] = 0 ; for ( var x = 0 ; x < width ; x ++ ) { var index = ( ( y * width * bytesPerPixel ) + y + 1 ) + ( x * bytesPerPixel ) ; var r = rawData [ index ++ ] ; var g = rawData [ index ++ ] ; var b = rawData [ index ] ; var colorInt = PixelToColorInt ( r , g , b ) ; var value = ( byte ) paletteColors . IndexOf ( colorInt ) ; if ( applyShift ) { var withinByteIndex = x % 2 ; if ( withinByteIndex == 1 ) { rawData [ rawDataIndex ] = ( byte ) ( rawData [ rawDataIndex ] + value ) ; rawDataIndex ++ ; } else { rawData [ rawDataIndex ] = ( byte ) ( value << 4 ) ; } } else { rawData [ rawDataIndex ++ ] = value ; } } } dataLength = rawDataIndex ; } else { AttemptCompressionOfRawData ( rawData , options ) ; } outputStream . Write ( HeaderValidationResult . ExpectedHeader , 0 , HeaderValidationResult . ExpectedHeader . Length ) ; var stream = new PngStreamWriteHelper ( outputStream ) ; stream . WriteChunkLength ( 13 ) ; stream . WriteChunkHeader ( ImageHeader . HeaderBytes ) ; StreamHelper . WriteBigEndianInt32 ( stream , width ) ; StreamHelper . WriteBigEndianInt32 ( stream , height ) ; stream . WriteByte ( ( byte ) bitDepth ) ; var colorType = ColorType . ColorUsed ; if ( hasAlphaChannel ) { colorType |= ColorType . AlphaChannelUsed ; } if ( palette != null ) { colorType |= ColorType . PaletteUsed ; } stream . WriteByte ( ( byte ) colorType ) ; stream . WriteByte ( ( byte ) CompressionMethod . DeflateWithSlidingWindow ) ; stream . WriteByte ( ( byte ) FilterMethod . AdaptiveFiltering ) ; stream . WriteByte ( ( byte ) InterlaceMethod . None ) ; stream . WriteCrc ( ) ; if ( palette != null ) { stream . WriteChunkLength ( palette . Length ) ; stream . WriteChunkHeader ( Encoding . ASCII . GetBytes ( " " ) ) ; stream . Write ( palette , 0 , palette . Length ) ; stream . WriteCrc ( ) ; } var imageData = Compress ( rawData , dataLength , options ) ; stream . WriteChunkLength ( imageData . Length ) ; stream . WriteChunkHeader ( Encoding . ASCII . GetBytes ( " " ) ) ; stream . Write ( imageData , 0 , imageData . Length ) ; stream . WriteCrc ( ) ; stream . WriteChunkLength ( 0 ) ; stream . WriteChunkHeader ( Encoding . ASCII . GetBytes ( " " ) ) ; stream . WriteCrc ( ) ; }
Write the PNG file bytes to the provided stream.
Write the PNG file bytes to the provided stream .
// None filter - we don't use filtering for palette images. // apply mask and shift
outputStream
Stream
options
SaveOptions