From 5550d52ca8543e28f84ab7ee220eb8abd9e8cf48 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 26 May 2017 15:24:26 +1000 Subject: [PATCH] Speed up png plus fix encoding bug --- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 74 +++++++++----------- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 40 +++++------ 2 files changed, 51 insertions(+), 63 deletions(-) diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index f8be0f6cc..b18845aaa 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -178,7 +178,7 @@ namespace ImageSharp.Formats /// /// Thrown if the stream does not contain and end chunk. /// - /// + /// /// Thrown if the image is larger than the maximum allowable size. /// /// The decoded image @@ -189,7 +189,6 @@ namespace ImageSharp.Formats this.currentStream = stream; this.currentStream.Skip(8); Image image = null; - PixelAccessor pixels = null; try { using (var deframeStream = new ZlibInflateStream(this.currentStream)) @@ -211,11 +210,11 @@ namespace ImageSharp.Formats case PngChunkTypes.Data: if (image == null) { - this.InitializeImage(metadata, out image, out pixels); + this.InitializeImage(metadata, out image); } deframeStream.AllocateNewBytes(currentChunk.Length); - this.ReadScanlines(deframeStream.CompressedStream, pixels); + this.ReadScanlines(deframeStream.CompressedStream, image); stream.Read(this.crcBuffer, 0, 4); break; case PngChunkTypes.Palette: @@ -252,7 +251,6 @@ namespace ImageSharp.Formats } finally { - pixels?.Dispose(); this.scanline?.Dispose(); this.previousScanline?.Dispose(); } @@ -324,8 +322,7 @@ namespace ImageSharp.Formats /// The type the pixels will be /// The metadata information for the image /// The image that we will populate - /// The pixel accessor - private void InitializeImage(ImageMetaData metadata, out Image image, out PixelAccessor pixels) + private void InitializeImage(ImageMetaData metadata, out Image image) where TPixel : struct, IPixel { if (this.header.Width > Image.MaxWidth || this.header.Height > Image.MaxHeight) @@ -334,7 +331,6 @@ namespace ImageSharp.Formats } image = new Image(this.configuration, this.header.Width, this.header.Height, metadata); - pixels = image.Lock(); this.bytesPerPixel = this.CalculateBytesPerPixel(); this.bytesPerScanline = this.CalculateScanlineLength(this.header.Width) + 1; this.bytesPerSample = 1; @@ -398,17 +394,17 @@ namespace ImageSharp.Formats /// /// The pixel format. /// The containing data. - /// The pixel data. - private void ReadScanlines(Stream dataStream, PixelAccessor pixels) + /// The pixel data. + private void ReadScanlines(Stream dataStream, Image image) where TPixel : struct, IPixel { if (this.header.InterlaceMethod == PngInterlaceMode.Adam7) { - this.DecodeInterlacedPixelData(dataStream, pixels); + this.DecodeInterlacedPixelData(dataStream, image); } else { - this.DecodePixelData(dataStream, pixels); + this.DecodePixelData(dataStream, image); } } @@ -417,8 +413,8 @@ namespace ImageSharp.Formats /// /// The pixel format. /// The compressed pixel data stream. - /// The image pixel accessor. - private void DecodePixelData(Stream compressedStream, PixelAccessor pixels) + /// The image to decode to. + private void DecodePixelData(Stream compressedStream, Image image) where TPixel : struct, IPixel { while (this.currentRow < this.header.Height) @@ -462,7 +458,7 @@ namespace ImageSharp.Formats throw new ImageFormatException("Unknown filter type."); } - this.ProcessDefilteredScanline(this.scanline.Array, pixels); + this.ProcessDefilteredScanline(this.scanline.Array, image); Swap(ref this.scanline, ref this.previousScanline); this.currentRow++; @@ -475,8 +471,8 @@ namespace ImageSharp.Formats /// /// The pixel format. /// The compressed pixel data stream. - /// The image pixel accessor. - private void DecodeInterlacedPixelData(Stream compressedStream, PixelAccessor pixels) + /// The current image. + private void DecodeInterlacedPixelData(Stream compressedStream, Image image) where TPixel : struct, IPixel { while (true) @@ -537,7 +533,8 @@ namespace ImageSharp.Formats throw new ImageFormatException("Unknown filter type."); } - this.ProcessInterlacedDefilteredScanline(this.scanline.Array, this.currentRow, pixels, Adam7FirstColumn[this.pass], Adam7ColumnIncrement[this.pass]); + Span rowSpan = image.GetRowSpan(this.currentRow); + this.ProcessInterlacedDefilteredScanline(this.scanline.Array, rowSpan, Adam7FirstColumn[this.pass], Adam7ColumnIncrement[this.pass]); Swap(ref this.scanline, ref this.previousScanline); @@ -561,12 +558,12 @@ namespace ImageSharp.Formats /// /// The pixel format. /// The de-filtered scanline - /// The image pixels - private void ProcessDefilteredScanline(byte[] defilteredScanline, PixelAccessor pixels) + /// The image + private void ProcessDefilteredScanline(byte[] defilteredScanline, Image pixels) where TPixel : struct, IPixel { var color = default(TPixel); - Span pixelBuffer = pixels.GetRowSpan(this.currentRow); + Span rowSpan = pixels.GetRowSpan(this.currentRow); var scanlineBuffer = new Span(defilteredScanline, 1); switch (this.PngColorType) @@ -578,7 +575,7 @@ namespace ImageSharp.Formats { byte intensity = (byte)(newScanline1[x] * factor); color.PackFromBytes(intensity, intensity, intensity, 255); - pixels[x, this.currentRow] = color; + rowSpan[x] = color; } break; @@ -593,26 +590,26 @@ namespace ImageSharp.Formats byte alpha = defilteredScanline[offset + this.bytesPerSample]; color.PackFromBytes(intensity, intensity, intensity, alpha); - pixels[x, this.currentRow] = color; + rowSpan[x] = color; } break; case PngColorType.Palette: - this.ProcessScanlineFromPalette(defilteredScanline, pixels); + this.ProcessScanlineFromPalette(defilteredScanline, rowSpan); break; case PngColorType.Rgb: - PixelOperations.Instance.PackFromXyzBytes(scanlineBuffer, pixelBuffer, this.header.Width); + PixelOperations.Instance.PackFromXyzBytes(scanlineBuffer, rowSpan, this.header.Width); break; case PngColorType.RgbWithAlpha: - PixelOperations.Instance.PackFromXyzwBytes(scanlineBuffer, pixelBuffer, this.header.Width); + PixelOperations.Instance.PackFromXyzwBytes(scanlineBuffer, rowSpan, this.header.Width); break; } @@ -623,8 +620,8 @@ namespace ImageSharp.Formats /// /// The type of pixel we are expanding to /// The scanline - /// The output pixels - private void ProcessScanlineFromPalette(byte[] defilteredScanline, PixelAccessor pixels) + /// Thecurrent output image row + private void ProcessScanlineFromPalette(byte[] defilteredScanline, Span row) where TPixel : struct, IPixel { byte[] newScanline = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth); @@ -654,7 +651,7 @@ namespace ImageSharp.Formats color.PackFromBytes(0, 0, 0, 0); } - pixels[x, this.currentRow] = color; + row[x] = color; } } else @@ -669,7 +666,7 @@ namespace ImageSharp.Formats byte b = palette[pixelOffset + 2]; color.PackFromBytes(r, g, b, 255); - pixels[x, this.currentRow] = color; + row[x] = color; } } } @@ -679,11 +676,10 @@ namespace ImageSharp.Formats /// /// The pixel format. /// The de-filtered scanline - /// The current image row. - /// The image pixels + /// The current image row. /// The column start index. Always 0 for none interlaced images. /// The column increment. Always 1 for none interlaced images. - private void ProcessInterlacedDefilteredScanline(byte[] defilteredScanline, int row, PixelAccessor pixels, int pixelOffset = 0, int increment = 1) + private void ProcessInterlacedDefilteredScanline(byte[] defilteredScanline, Span rowSpan, int pixelOffset = 0, int increment = 1) where TPixel : struct, IPixel { var color = default(TPixel); @@ -697,7 +693,7 @@ namespace ImageSharp.Formats { byte intensity = (byte)(newScanline1[o] * factor); color.PackFromBytes(intensity, intensity, intensity, 255); - pixels[x, row] = color; + rowSpan[x] = color; } break; @@ -710,7 +706,7 @@ namespace ImageSharp.Formats byte alpha = defilteredScanline[o + this.bytesPerSample]; color.PackFromBytes(intensity, intensity, intensity, alpha); - pixels[x, row] = color; + rowSpan[x] = color; } break; @@ -742,7 +738,7 @@ namespace ImageSharp.Formats color.PackFromBytes(0, 0, 0, 0); } - pixels[x, row] = color; + rowSpan[x] = color; } } else @@ -757,7 +753,7 @@ namespace ImageSharp.Formats byte b = this.palette[offset + 2]; color.PackFromBytes(r, g, b, 255); - pixels[x, row] = color; + rowSpan[x] = color; } } @@ -772,7 +768,7 @@ namespace ImageSharp.Formats byte b = defilteredScanline[o + (2 * this.bytesPerSample)]; color.PackFromBytes(r, g, b, 255); - pixels[x, row] = color; + rowSpan[x] = color; } break; @@ -787,7 +783,7 @@ namespace ImageSharp.Formats byte a = defilteredScanline[o + (3 * this.bytesPerSample)]; color.PackFromBytes(r, g, b, a); - pixels[x, row] = color; + rowSpan[x] = color; } break; diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 0482b2691..3fcf1fc81 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -220,11 +220,7 @@ namespace ImageSharp.Formats this.WritePhysicalChunk(stream, image); this.WriteGammaChunk(stream); - using (PixelAccessor pixels = image.Lock()) - { - this.WriteDataChunks(pixels, stream); - } - + this.WriteDataChunks(image, stream); this.WriteEndChunk(stream); stream.Flush(); } @@ -302,9 +298,8 @@ namespace ImageSharp.Formats /// Collects a row of grayscale pixels. /// /// The pixel format. - /// The image pixels accessor. - /// The row index. - private void CollectGrayscaleBytes(PixelAccessor pixels, int row) + /// The image row span. + private void CollectGrayscaleBytes(Span rowSpan) where TPixel : struct, IPixel { byte[] rawScanlineArray = this.rawScanline.Array; @@ -316,7 +311,7 @@ namespace ImageSharp.Formats // Convert the color to YCbCr and store the luminance // Optionally store the original color alpha. int offset = x * this.bytesPerPixel; - pixels[x, row].ToXyzwBytes(this.chunkTypeBuffer, 0); + rowSpan[x].ToXyzwBytes(this.chunkTypeBuffer, 0); byte luminance = (byte)((0.299F * this.chunkTypeBuffer[0]) + (0.587F * this.chunkTypeBuffer[1]) + (0.114F * this.chunkTypeBuffer[2])); for (int i = 0; i < this.bytesPerPixel; i++) @@ -337,13 +332,10 @@ namespace ImageSharp.Formats /// Collects a row of true color pixel data. /// /// The pixel format. - /// The image pixel accessor. - /// The row index. - private void CollecTPixelBytes(PixelAccessor pixels, int row) + /// The row span. + private void CollecTPixelBytes(Span rowSpan) where TPixel : struct, IPixel { - Span rowSpan = pixels.GetRowSpan(row); - if (this.bytesPerPixel == 4) { PixelOperations.Instance.ToXyzwBytes(rowSpan, this.rawScanline, this.width); @@ -359,10 +351,10 @@ namespace ImageSharp.Formats /// Each scanline is encoded in the most optimal manner to improve compression. /// /// The pixel format. - /// The image pixel accessor. + /// The row span. /// The row. /// The - private Buffer EncodePixelRow(PixelAccessor pixels, int row) + private Buffer EncodePixelRow(Span rowSpan, int row) where TPixel : struct, IPixel { switch (this.pngColorType) @@ -372,10 +364,10 @@ namespace ImageSharp.Formats break; case PngColorType.Grayscale: case PngColorType.GrayscaleWithAlpha: - this.CollectGrayscaleBytes(pixels, row); + this.CollectGrayscaleBytes(rowSpan); break; default: - this.CollecTPixelBytes(pixels, row); + this.CollecTPixelBytes(rowSpan); break; } @@ -637,17 +629,17 @@ namespace ImageSharp.Formats /// Writes the pixel information to the stream. /// /// The pixel format. - /// The pixel accessor. + /// The image. /// The stream. - private void WriteDataChunks(PixelAccessor pixels, Stream stream) + private void WriteDataChunks(Image pixels, Stream stream) where TPixel : struct, IPixel { this.bytesPerScanline = this.width * this.bytesPerPixel; int resultLength = this.bytesPerScanline + 1; - this.previousScanline = new Buffer(this.bytesPerScanline); - this.rawScanline = new Buffer(this.bytesPerScanline); - this.result = new Buffer(resultLength); + this.previousScanline = Buffer.CreateClean(this.bytesPerScanline); + this.rawScanline = Buffer.CreateClean(this.bytesPerScanline); + this.result = Buffer.CreateClean(resultLength); if (this.pngColorType != PngColorType.Palette) { @@ -667,7 +659,7 @@ namespace ImageSharp.Formats { for (int y = 0; y < this.height; y++) { - Buffer r = this.EncodePixelRow(pixels, y); + Buffer r = this.EncodePixelRow(pixels.GetRowSpan(y), y); deflateStream.Write(r.Array, 0, resultLength); Swap(ref this.rawScanline, ref this.previousScanline);