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