diff --git a/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs b/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs index 3cbe5a81dd..108b6ae6ed 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats; @@ -31,6 +32,41 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression } } + /// + /// Applies a horizontal predictor to the rgb row. + /// Make use of the fact that many continuous-tone images rarely vary much in pixel value from one pixel to the next. + /// In such images, if we replace the pixel values by differences between consecutive pixels, many of the differences should be 0, plus + /// or minus 1, and so on.This reduces the apparent information content and allows LZW to encode the data more compactly. + /// + /// The rgb pixel row. + [MethodImpl(InliningOptions.ShortMethod)] + public static void ApplyHorizontalPrediction24Bit(Span rowSpan) + { + Span rowRgb = MemoryMarshal.Cast(rowSpan); + + for (int x = rowRgb.Length - 1; x >= 1; x--) + { + byte r = (byte)(rowRgb[x].R - rowRgb[x - 1].R); + byte g = (byte)(rowRgb[x].G - rowRgb[x - 1].G); + byte b = (byte)(rowRgb[x].B - rowRgb[x - 1].B); + var rgb = new Rgb24(r, g, b); + rowRgb[x].FromRgb24(rgb); + } + } + + /// + /// Applies a horizontal predictor to a gray pixel row. + /// + /// The gray pixel row. + [MethodImpl(InliningOptions.ShortMethod)] + public static void ApplyHorizontalPrediction8Bit(Span rowSpan) + { + for (int x = rowSpan.Length - 1; x >= 1; x--) + { + rowSpan[x] -= rowSpan[x - 1]; + } + } + private static void Undo8Bit(Span pixelBytes, int width) { var rowBytesCount = width; diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs index 1ce03cf2eb..d427e9c668 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -7,9 +7,9 @@ using System.IO; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; -using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Png.Zlib; +using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; @@ -197,7 +197,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils if (useHorizontalPredictor) { - this.ApplyHorizontalPredictionRgb(rowSpan); + HorizontalPredictor.ApplyHorizontalPrediction24Bit(rowSpan); } deflateStream.Write(rowSpan); @@ -234,7 +234,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils if (useHorizontalPredictor) { - this.ApplyHorizontalPredictionRgb(rowSpan); + HorizontalPredictor.ApplyHorizontalPrediction24Bit(rowSpan); } rowSpan.CopyTo(pixels.Slice(y * image.Width * 3)); @@ -249,27 +249,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils return bytesWritten; } - /// - /// Applies a horizontal predictor to the rgb row. - /// Make use of the fact that many continuous-tone images rarely vary much in pixel value from one pixel to the next. - /// In such images, if we replace the pixel values by differences between consecutive pixels, many of the differences should be 0, plus - /// or minus 1, and so on.This reduces the apparent information content and allows LZW to encode the data more compactly. - /// - /// The rgb pixel row. - private void ApplyHorizontalPredictionRgb(Span rowSpan) - { - Span rowRgb = MemoryMarshal.Cast(rowSpan); - - for (int x = rowRgb.Length - 1; x >= 1; x--) - { - byte r = (byte)(rowRgb[x].R - rowRgb[x - 1].R); - byte g = (byte)(rowRgb[x].G - rowRgb[x - 1].G); - byte b = (byte)(rowRgb[x].B - rowRgb[x - 1].B); - var rgb = new Rgb24(r, g, b); - rowRgb[x].FromRgb24(rgb); - } - } - /// /// Writes the image data as RGB with packed bits compression to the stream. /// @@ -281,7 +260,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils where TPixel : unmanaged, IPixel { // Worst case is that the actual compressed data is larger then the input data. In this case we need 1 additional byte per 127 bytes. - int additionalBytes = (image.Width * 3 / 127) + 1; + int additionalBytes = ((image.Width * 3) / 127) + 1; using IManagedByteBuffer compressedRow = this.memoryAllocator.AllocateManagedByteBuffer((image.Width * 3) + additionalBytes, AllocationOptions.Clean); Span compressedRowSpan = compressedRow.GetSpan(); int bytesWritten = 0; @@ -527,7 +506,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils if (useHorizontalPredictor) { - this.ApplyHorizontalPredictionGray(rowSpan); + HorizontalPredictor.ApplyHorizontalPrediction8Bit(rowSpan); } deflateStream.Write(rowSpan); @@ -562,7 +541,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils PixelOperations.Instance.ToL8Bytes(this.configuration, pixelRow, rowSpan, pixelRow.Length); if (useHorizontalPredictor) { - this.ApplyHorizontalPredictionGray(rowSpan); + HorizontalPredictor.ApplyHorizontalPrediction8Bit(rowSpan); } rowSpan.CopyTo(pixels.Slice(y * image.Width)); @@ -577,18 +556,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils return bytesWritten; } - /// - /// Applies a horizontal predictor to a gray pixel row. - /// - /// The gray pixel row. - private void ApplyHorizontalPredictionGray(Span rowSpan) - { - for (int x = rowSpan.Length - 1; x >= 1; x--) - { - rowSpan[x] -= rowSpan[x - 1]; - } - } - /// /// Writes the image data as 8 bit gray to the stream. ///