From b768b69857da93fdf4435dedb4868c1e60fdbc4f Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 22 Jan 2016 12:42:33 +1100 Subject: [PATCH] Perf improvements + fix memory leak Former-commit-id: 20e073d2b951f20450b55df9d78dc6966a48f4a1 Former-commit-id: a8927a2352e8803cde9c7cf887d5c64e061f7726 Former-commit-id: 5e630441e640043b0840b486d00bc8d868838502 --- src/ImageProcessor/Formats/Bmp/BmpEncoder.cs | 17 ++--- src/ImageProcessor/Formats/Jpg/JpegDecoder.cs | 2 + src/ImageProcessor/Formats/Jpg/JpegEncoder.cs | 67 +++++-------------- src/ImageProcessor/Formats/Png/PngEncoder.cs | 38 +++-------- 4 files changed, 33 insertions(+), 91 deletions(-) diff --git a/src/ImageProcessor/Formats/Bmp/BmpEncoder.cs b/src/ImageProcessor/Formats/Bmp/BmpEncoder.cs index ad80bbc76..b76b52055 100644 --- a/src/ImageProcessor/Formats/Bmp/BmpEncoder.cs +++ b/src/ImageProcessor/Formats/Bmp/BmpEncoder.cs @@ -100,29 +100,20 @@ namespace ImageProcessor.Formats amount = 4 - amount; } - float[] data = image.Pixels; - for (int y = image.Height - 1; y >= 0; y--) { for (int x = 0; x < image.Width; x++) { - int offset = ((y * image.Width) + x) * 4; - // Limit the output range and multiply out from our floating point. // Convert back to b-> g-> r-> a order. // Convert to non-premultiplied color. - float r = data[offset]; - float g = data[offset + 1]; - float b = data[offset + 2]; - float a = data[offset + 3]; - - Bgra32 color = Color.ToNonPremultiplied(new Color(r, g, b, a)); + Bgra32 color = Color.ToNonPremultiplied(image[x, y]); - writer.Write(color.B); - writer.Write(color.G); - writer.Write(color.R); + // Allocate 1 array instead of allocating 3. + writer.Write(new[] { color.B, color.G, color.R }); } + // Pad for (int i = 0; i < amount; i++) { writer.Write((byte)0); diff --git a/src/ImageProcessor/Formats/Jpg/JpegDecoder.cs b/src/ImageProcessor/Formats/Jpg/JpegDecoder.cs index 2f456dfc0..6b706a40f 100644 --- a/src/ImageProcessor/Formats/Jpg/JpegDecoder.cs +++ b/src/ImageProcessor/Formats/Jpg/JpegDecoder.cs @@ -152,6 +152,8 @@ namespace ImageProcessor.Formats } image.SetPixels(pixelWidth, pixelHeight, pixels); + + jpg.Dispose(); } /// diff --git a/src/ImageProcessor/Formats/Jpg/JpegEncoder.cs b/src/ImageProcessor/Formats/Jpg/JpegEncoder.cs index 0517b422c..abf96dd03 100644 --- a/src/ImageProcessor/Formats/Jpg/JpegEncoder.cs +++ b/src/ImageProcessor/Formats/Jpg/JpegEncoder.cs @@ -35,25 +35,10 @@ namespace ImageProcessor.Formats /// public string MimeType => "image/jpeg"; - /// - /// Gets the default file extension for this encoder. - /// - /// The default file extension for this encoder. + /// public string Extension => "jpg"; - /// - /// Indicates if the image encoder supports the specified - /// file extension. - /// - /// The file extension. - /// - /// true, if the encoder supports the specified - /// extensions; otherwise false. - /// - /// - /// is null (Nothing in Visual Basic). - /// is a string - /// of length zero or contains only blanks. + /// public bool IsSupportedFileExtension(string extension) { Guard.NotNullOrEmpty(extension, "extension"); @@ -68,61 +53,41 @@ namespace ImageProcessor.Formats extension.Equals("jfif", StringComparison.OrdinalIgnoreCase); } - /// - /// Encodes the data of the specified image and writes the result to - /// the specified stream. - /// - /// The image, where the data should be get from. - /// Cannot be null (Nothing in Visual Basic). - /// The stream, where the image data should be written to. - /// Cannot be null (Nothing in Visual Basic). - /// - /// is null (Nothing in Visual Basic). - /// - or - - /// is null (Nothing in Visual Basic). - /// + /// public void Encode(ImageBase image, Stream stream) { Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); - int pixelWidth = image.Width; - int pixelHeight = image.Height; - - float[] sourcePixels = image.Pixels; + int imageWidth = image.Width; + int imageHeight = image.Height; - SampleRow[] rows = new SampleRow[pixelHeight]; + SampleRow[] rows = new SampleRow[imageHeight]; Parallel.For( 0, - pixelHeight, + imageHeight, y => { - byte[] samples = new byte[pixelWidth * 3]; + byte[] samples = new byte[imageWidth * 3]; - for (int x = 0; x < pixelWidth; x++) + for (int x = 0; x < imageWidth; x++) { - int start = x * 3; - int source = ((y * pixelWidth) + x) * 4; - - // Convert to non-premultiplied color. - float r = sourcePixels[source]; - float g = sourcePixels[source + 1]; - float b = sourcePixels[source + 2]; - float a = sourcePixels[source + 3]; - - Bgra32 color = Color.ToNonPremultiplied(new Color(r, g, b, a)); + Bgra32 color = Color.ToNonPremultiplied(image[x, y]); + int start = x * 3; samples[start] = color.R; samples[start + 1] = color.G; samples[start + 2] = color.B; } - rows[y] = new SampleRow(samples, pixelWidth, 8, 3); + rows[y] = new SampleRow(samples, imageWidth, 8, 3); }); - JpegImage jpg = new JpegImage(rows, Colorspace.RGB); - jpg.WriteJpeg(stream, new CompressionParameters { Quality = this.Quality }); + using (JpegImage jpg = new JpegImage(rows, Colorspace.RGB)) + { + jpg.WriteJpeg(stream, new CompressionParameters { Quality = this.Quality }); + } } } } diff --git a/src/ImageProcessor/Formats/Png/PngEncoder.cs b/src/ImageProcessor/Formats/Png/PngEncoder.cs index efb3b1ef4..9d23a09b6 100644 --- a/src/ImageProcessor/Formats/Png/PngEncoder.cs +++ b/src/ImageProcessor/Formats/Png/PngEncoder.cs @@ -196,16 +196,17 @@ namespace ImageProcessor.Formats /// Writes the pixel information to the stream. /// /// The containing image data. - /// The image base. - private void WriteDataChunks(Stream stream, ImageBase imageBase) + /// The image base. + private void WriteDataChunks(Stream stream, ImageBase image) { - float[] pixels = imageBase.Pixels; + int imageWidth = image.Width; + int imageHeight = image.Height; - byte[] data = new byte[(imageBase.Width * imageBase.Height * 4) + imageBase.Height]; + byte[] data = new byte[(imageWidth * imageHeight * 4) + image.Height]; - int rowLength = (imageBase.Width * 4) + 1; + int rowLength = (imageWidth * 4) + 1; - Parallel.For(0, imageBase.Height, y => + Parallel.For(0, imageHeight, y => { byte compression = 0; if (y > 0) @@ -215,22 +216,12 @@ namespace ImageProcessor.Formats data[y * rowLength] = compression; - for (int x = 0; x < imageBase.Width; x++) + for (int x = 0; x < imageWidth; x++) { + Bgra32 color = Color.ToNonPremultiplied(image[x, y]); + // Calculate the offset for the new array. int dataOffset = (y * rowLength) + (x * 4) + 1; - - // Calculate the offset for the original pixel array. - int pixelOffset = ((y * imageBase.Width) + x) * 4; - - // Convert to non-premultiplied color. - float r = pixels[pixelOffset]; - float g = pixels[pixelOffset + 1]; - float b = pixels[pixelOffset + 2]; - float a = pixels[pixelOffset + 3]; - - Bgra32 color = Color.ToNonPremultiplied(new Color(r, g, b, a)); - data[dataOffset] = color.R; data[dataOffset + 1] = color.G; data[dataOffset + 2] = color.B; @@ -238,14 +229,7 @@ namespace ImageProcessor.Formats if (y > 0) { - int lastOffset = (((y - 1) * imageBase.Width) + x) * 4; - - r = pixels[lastOffset]; - g = pixels[lastOffset + 1]; - b = pixels[lastOffset + 2]; - a = pixels[lastOffset + 3]; - - color = Color.ToNonPremultiplied(new Color(r, g, b, a)); + color = Color.ToNonPremultiplied(image[x, y - 1]); data[dataOffset] -= color.R; data[dataOffset + 1] -= color.G;