Ejemplo n.º 1
0
        /// <summary>
        /// Write the PNG file bytes to the provided stream.
        /// </summary>
        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++)
                {
                    // None filter - we don't use filtering for palette images.
                    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)
                        {
                            // apply mask and shift
                            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;
                File.WriteAllBytes(@"C:\temp\mycompressed.bin", rawData.Take(dataLength).ToArray());
            }
            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();
        }