From 1f960ae41d98ecf88f97373452ef6faddc83c755 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 9 Nov 2016 23:09:52 +1100 Subject: [PATCH] Encode grayscale png by row. --- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 71 ++++++++++---------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 1053a2548..a1fd559a3 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -181,7 +181,7 @@ namespace ImageSharp.Formats } else if (this.PngColorType == PngColorType.Grayscale || this.PngColorType == PngColorType.GrayscaleWithAlpha) { - this.CollectGrayscaleBytes(image); + //this.CollectGrayscaleBytes(image); } else { @@ -253,40 +253,38 @@ namespace ImageSharp.Formats } /// - /// Collects the grayscale pixel data. + /// Collects a row of grayscale pixels. /// /// The pixel format. /// The packed format. uint, long, float. /// The image to encode. - private void CollectGrayscaleBytes(ImageBase image) + /// The row index. + /// The raw scanline. + private void CollectGrayscaleBytes(ImageBase image, int row, byte[] rawScanline) where TColor : struct, IPackedPixel where TPacked : struct { // Copy the pixels across from the image. - this.pixelData = new byte[this.width * this.height * this.bytesPerPixel]; - int stride = this.width * this.bytesPerPixel; byte[] bytes = new byte[4]; using (PixelAccessor pixels = image.Lock()) { - for (int y = 0; y < this.height; y++) + for (int x = 0; x < this.width; x++) { - for (int x = 0; x < this.width; x++) + // Convert the color to YCbCr and store the luminance + // Optionally store the original color alpha. + int offset = x * this.bytesPerPixel; + pixels[x, row].ToBytes(bytes, 0, ComponentOrder.XYZW); + byte luminance = (byte)((0.299F * bytes[0]) + (0.587F * bytes[1]) + (0.114F * bytes[2])); + + for (int i = 0; i < this.bytesPerPixel; i++) { - // Convert the color to YCbCr and store the luminance - // Optionally store the original color alpha. - int dataOffset = (y * stride) + (x * this.bytesPerPixel); - pixels[x, y].ToBytes(bytes, 0, ComponentOrder.XYZW); - YCbCr luminance = new Color(bytes[0], bytes[1], bytes[2], bytes[3]); - for (int i = 0; i < this.bytesPerPixel; i++) + if (i == 0) + { + rawScanline[offset] = luminance; + } + else { - if (i == 0) - { - this.pixelData[dataOffset] = (byte)luminance.Y; - } - else - { - this.pixelData[dataOffset + i] = bytes[3]; - } + rawScanline[offset + i] = bytes[3]; } } } @@ -294,13 +292,13 @@ namespace ImageSharp.Formats } /// - /// Collects the true color pixel data. + /// Collects a row of true color pixel data. /// /// The pixel format. /// The packed format. uint, long, float. /// The image to encode. /// The row index. - /// The raw Scanline. + /// The raw scanline. private void CollectColorBytes(ImageBase image, int row, byte[] rawScanline) where TColor : struct, IPackedPixel where TPacked : struct @@ -312,11 +310,6 @@ namespace ImageSharp.Formats { pixels[x, row].ToBytes(rawScanline, x * this.bytesPerPixel, bpp == 4 ? ComponentOrder.XYZW : ComponentOrder.XYZ); } - - if (rawScanline.Any(x => x > 0)) - { - var t = rawScanline; - } } } @@ -336,13 +329,18 @@ namespace ImageSharp.Formats where TColor : struct, IPackedPixel where TPacked : struct { - if (this.PngColorType == PngColorType.Palette || this.PngColorType == PngColorType.Grayscale || this.PngColorType == PngColorType.GrayscaleWithAlpha) - { - Buffer.BlockCopy(this.pixelData, row * bytesPerScanline, rawScanline, 0, bytesPerScanline); - } - else + switch (this.PngColorType) { - this.CollectColorBytes(image, row, rawScanline); + case PngColorType.Palette: + Buffer.BlockCopy(this.pixelData, row * bytesPerScanline, rawScanline, 0, bytesPerScanline); + break; + case PngColorType.Grayscale: + case PngColorType.GrayscaleWithAlpha: + this.CollectGrayscaleBytes(image, row, rawScanline); + break; + default: + this.CollectColorBytes(image, row, rawScanline); + break; } byte[] filteredScanline = this.GetOptimalFilteredScanline(rawScanline, previousScanline, bytesPerScanline, this.bytesPerPixel); @@ -363,9 +361,10 @@ namespace ImageSharp.Formats { Tuple[] candidates; + // Palette images don't compress well with adaptive filtering. if (this.PngColorType == PngColorType.Palette) { - candidates = new Tuple[4]; + candidates = new Tuple[1]; byte[] none = NoneFilter.Encode(rawScanline); candidates[0] = new Tuple(none, this.CalculateTotalVariation(none)); @@ -634,6 +633,8 @@ namespace ImageSharp.Formats memoryStream?.Dispose(); } + // Store the chunks in repeated 64k blocks. + // This reduces the memory load for decoding the image for many decoders. int numChunks = bufferLength / MaxBlockSize; if (bufferLength % MaxBlockSize != 0)