From 5d888bef8f733bd67476d5c711dab26fa32777e1 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 2 Aug 2021 15:46:00 +0200 Subject: [PATCH 1/9] Tiff decoder now respects byte order for 16 bit gray images --- .../BlackIsZero16TiffColor{TPixel}.cs | 48 +++++++++++++++++++ .../Rgb161616TiffColor{TPixel}.cs | 11 ++--- .../TiffColorDecoderFactory{TPixel}.cs | 5 ++ .../TiffColorType.cs | 5 ++ .../Formats/Tiff/TiffDecoderOptionsParser.cs | 6 +++ .../Formats/Tiff/Utils/TiffUtils.cs | 18 +++++++ .../Formats/Tiff/TiffDecoderTests.cs | 1 + tests/ImageSharp.Tests/TestImages.cs | 1 + .../Input/Tiff/flower-minisblack-16_lsb.tiff | 3 ++ 9 files changed, 91 insertions(+), 7 deletions(-) create mode 100644 src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs create mode 100644 src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs create mode 100644 tests/Images/Input/Tiff/flower-minisblack-16_lsb.tiff diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs new file mode 100644 index 000000000..13a570dbc --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs @@ -0,0 +1,48 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.Formats.Tiff.Utils; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation +{ + /// + /// Implements the 'BlackIsZero' photometric interpretation for 16-bit grayscale images. + /// + internal class BlackIsZero16TiffColor : TiffBaseColorDecoder + where TPixel : unmanaged, IPixel + { + private readonly bool isBigEndian; + + /// + /// Initializes a new instance of the class. + /// + /// if set to true decodes the pixel data as big endian, otherwise as little endian. + public BlackIsZero16TiffColor(bool isBigEndian) => this.isBigEndian = isBigEndian; + + /// + public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) + { + var color = default(TPixel); + + int offset = 0; + + var l16 = default(L16); + for (int y = top; y < top + height; y++) + { + for (int x = left; x < left + width; x++) + { + ushort intensity = TiffUtils.ConvertToShort(data.Slice(offset, 2), this.isBigEndian); + offset += 2; + + l16.PackedValue = intensity; + color.FromL16(l16); + + pixels[x, y] = color; + } + } + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs index 635be95f4..b0106cc09 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs @@ -3,6 +3,7 @@ using System; using System.Buffers.Binary; +using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -36,11 +37,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation for (int x = left; x < left + width; x++) { - ulong r = this.ConvertToShort(data.Slice(offset, 2)); + ulong r = TiffUtils.ConvertToShort(data.Slice(offset, 2), this.isBigEndian); offset += 2; - ulong g = this.ConvertToShort(data.Slice(offset, 2)); + ulong g = TiffUtils.ConvertToShort(data.Slice(offset, 2), this.isBigEndian); offset += 2; - ulong b = this.ConvertToShort(data.Slice(offset, 2)); + ulong b = TiffUtils.ConvertToShort(data.Slice(offset, 2), this.isBigEndian); offset += 2; rgba.PackedValue = r | (g << 16) | (b << 32) | (0xfffful << 48); @@ -50,9 +51,5 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation } } } - - private ushort ConvertToShort(ReadOnlySpan buffer) => this.isBigEndian - ? BinaryPrimitives.ReadUInt16BigEndian(buffer) - : BinaryPrimitives.ReadUInt16LittleEndian(buffer); } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs index 8e711d3eb..ad9c478d2 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs @@ -52,6 +52,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation DebugGuard.IsTrue(colorMap == null, "colorMap"); return new BlackIsZero8TiffColor(); + case TiffColorType.BlackIsZero16: + DebugGuard.IsTrue(bitsPerSample.Channels == 1 && bitsPerSample.Channel0 == 16, "bitsPerSample"); + DebugGuard.IsTrue(colorMap == null, "colorMap"); + return new BlackIsZero16TiffColor(byteOrder == ByteOrder.BigEndian); + case TiffColorType.Rgb: DebugGuard.IsTrue(colorMap == null, "colorMap"); return new RgbTiffColor(bitsPerSample); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs index 517926c23..3c003cf92 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs @@ -28,6 +28,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// BlackIsZero8, + /// + /// Grayscale: 0 is imaged as black. The maximum value is imaged as white. Optimized implementation for 16-bit images. + /// + BlackIsZero16, + /// /// Grayscale: 0 is imaged as white. The maximum value is imaged as black. /// diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs index 288f01cd1..0ac9fd7c9 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs @@ -154,6 +154,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff switch (bitsPerChannel) { + case 16: + { + options.ColorType = TiffColorType.BlackIsZero16; + break; + } + case 8: { options.ColorType = TiffColorType.BlackIsZero8; diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs new file mode 100644 index 000000000..5a31b231e --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs @@ -0,0 +1,18 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers.Binary; + +namespace SixLabors.ImageSharp.Formats.Tiff.Utils +{ + /// + /// Helper methods for TIFF decoding. + /// + internal static class TiffUtils + { + public static ushort ConvertToShort(ReadOnlySpan buffer, bool isBigEndian) => isBigEndian + ? BinaryPrimitives.ReadUInt16BigEndian(buffer) + : BinaryPrimitives.ReadUInt16LittleEndian(buffer); + } +} diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index ab53ca156..95d5437e2 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -140,6 +140,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); [Theory] + [WithFile(Flower16BitGrayLittleEndian, PixelTypes.Rgba32)] [WithFile(Flower16BitGray, PixelTypes.Rgba32)] public void TiffDecoder_CanDecode_16Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index c54c82d7d..d5aeba0bb 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -582,6 +582,7 @@ namespace SixLabors.ImageSharp.Tests public const string Flower12BitGray = "Tiff/flower-minisblack-12.tiff"; public const string Flower14BitGray = "Tiff/flower-minisblack-14.tiff"; public const string Flower16BitGray = "Tiff/flower-minisblack-16.tiff"; + public const string Flower16BitGrayLittleEndian = "Tiff/flower-minisblack-16_lsb.tiff"; public const string Issues1716Rgb161616BitLittleEndian = "Tiff/Issues/Issue1716.tiff"; public const string SmallRgbDeflate = "Tiff/rgb_small_deflate.tiff"; diff --git a/tests/Images/Input/Tiff/flower-minisblack-16_lsb.tiff b/tests/Images/Input/Tiff/flower-minisblack-16_lsb.tiff new file mode 100644 index 000000000..62061bfaf --- /dev/null +++ b/tests/Images/Input/Tiff/flower-minisblack-16_lsb.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c3806304a5453a6ec8a6795bc77b967b9aa8593288af36bbf9802f22ee27869e +size 6588 From cc16677172175c5554df37807e4ca47e0afb2f53 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 2 Aug 2021 16:13:45 +0200 Subject: [PATCH 2/9] Tiff decoder now respects byte order for 16 bit gray images with white is zero --- .../TiffColorDecoderFactory{TPixel}.cs | 5 ++ .../TiffColorType.cs | 5 ++ .../WhiteIsZero16TiffColor{TPixel}.cs | 48 +++++++++++++++++++ .../Formats/Tiff/TiffDecoderOptionsParser.cs | 6 +++ .../Formats/Tiff/TiffDecoderTests.cs | 1 + tests/ImageSharp.Tests/TestImages.cs | 1 + .../Input/Tiff/flower-miniswhite-16_lsb.tiff | 3 ++ 7 files changed, 69 insertions(+) create mode 100644 src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs create mode 100644 tests/Images/Input/Tiff/flower-miniswhite-16_lsb.tiff diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs index ad9c478d2..25b441ab0 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs @@ -32,6 +32,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation DebugGuard.IsTrue(colorMap == null, "colorMap"); return new WhiteIsZero8TiffColor(); + case TiffColorType.WhiteIsZero16: + DebugGuard.IsTrue(bitsPerSample.Channels == 1 && bitsPerSample.Channel0 == 16, "bitsPerSample"); + DebugGuard.IsTrue(colorMap == null, "colorMap"); + return new WhiteIsZero16TiffColor(byteOrder == ByteOrder.BigEndian); + case TiffColorType.BlackIsZero: DebugGuard.IsTrue(bitsPerSample.Channels == 1, "bitsPerSample"); DebugGuard.IsTrue(colorMap == null, "colorMap"); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs index 3c003cf92..331065d27 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs @@ -53,6 +53,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// WhiteIsZero8, + /// + /// Grayscale: 0 is imaged as white. The maximum value is imaged as black. Optimized implementation for 16-bit images. + /// + WhiteIsZero16, + /// /// Palette-color. /// diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs new file mode 100644 index 000000000..d84efef66 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs @@ -0,0 +1,48 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.Formats.Tiff.Utils; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation +{ + /// + /// Implements the 'WhiteIsZero' photometric interpretation for 16-bit grayscale images. + /// + internal class WhiteIsZero16TiffColor : TiffBaseColorDecoder + where TPixel : unmanaged, IPixel + { + private readonly bool isBigEndian; + + /// + /// Initializes a new instance of the class. + /// + /// if set to true decodes the pixel data as big endian, otherwise as little endian. + public WhiteIsZero16TiffColor(bool isBigEndian) => this.isBigEndian = isBigEndian; + + /// + public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) + { + var color = default(TPixel); + + int offset = 0; + + var l16 = default(L16); + for (int y = top; y < top + height; y++) + { + for (int x = left; x < left + width; x++) + { + ushort intensity = (ushort)(ushort.MaxValue - TiffUtils.ConvertToShort(data.Slice(offset, 2), this.isBigEndian)); + offset += 2; + + l16.PackedValue = intensity; + color.FromL16(l16); + + pixels[x, y] = color; + } + } + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs index 0ac9fd7c9..14c527a34 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs @@ -111,6 +111,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff switch (bitsPerChannel) { + case 16: + { + options.ColorType = TiffColorType.WhiteIsZero16; + break; + } + case 8: { options.ColorType = TiffColorType.WhiteIsZero8; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index 95d5437e2..9346c4bfd 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -142,6 +142,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Theory] [WithFile(Flower16BitGrayLittleEndian, PixelTypes.Rgba32)] [WithFile(Flower16BitGray, PixelTypes.Rgba32)] + [WithFile(Flower16BitGrayMinIsWhiteLittleEndian, PixelTypes.Rgba32)] public void TiffDecoder_CanDecode_16Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index d5aeba0bb..212bb94e3 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -583,6 +583,7 @@ namespace SixLabors.ImageSharp.Tests public const string Flower14BitGray = "Tiff/flower-minisblack-14.tiff"; public const string Flower16BitGray = "Tiff/flower-minisblack-16.tiff"; public const string Flower16BitGrayLittleEndian = "Tiff/flower-minisblack-16_lsb.tiff"; + public const string Flower16BitGrayMinIsWhiteLittleEndian = "Tiff/flower-miniswhite-16_lsb.tiff"; public const string Issues1716Rgb161616BitLittleEndian = "Tiff/Issues/Issue1716.tiff"; public const string SmallRgbDeflate = "Tiff/rgb_small_deflate.tiff"; diff --git a/tests/Images/Input/Tiff/flower-miniswhite-16_lsb.tiff b/tests/Images/Input/Tiff/flower-miniswhite-16_lsb.tiff new file mode 100644 index 000000000..ec9ceb184 --- /dev/null +++ b/tests/Images/Input/Tiff/flower-miniswhite-16_lsb.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:435c92b453587e1943940111b66afabf70307beb0e1d65e9701fd9bb753eead2 +size 6588 From a1ee0d638d01b2d4c18ebfe233f354a63261ee5b Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 2 Aug 2021 21:15:16 +0200 Subject: [PATCH 3/9] Decoding 16bit rgb planar now also respects byte order --- .../Rgb161616TiffColor{TPixel}.cs | 1 - .../Rgb16PlanarTiffColor{TPixel}.cs | 53 +++++++++++++++++++ .../RgbPlanarTiffColor{TPixel}.cs | 4 +- .../TiffBasePlanarColorDecoder{TPixel}.cs | 28 ++++++++++ .../TiffColorDecoderFactory{TPixel}.cs | 7 ++- .../Formats/Tiff/TiffDecoderCore.cs | 2 +- .../Formats/Tiff/TiffDecoderTests.cs | 1 + tests/ImageSharp.Tests/TestImages.cs | 1 + .../Input/Tiff/flower-rgb-planar-16_lsb.tiff | 3 ++ 9 files changed, 95 insertions(+), 5 deletions(-) create mode 100644 src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs create mode 100644 src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffBasePlanarColorDecoder{TPixel}.cs create mode 100644 tests/Images/Input/Tiff/flower-rgb-planar-16_lsb.tiff diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs index b0106cc09..89cf1cb48 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Buffers.Binary; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs new file mode 100644 index 000000000..75b0b3a8b --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs @@ -0,0 +1,53 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System.Buffers; +using SixLabors.ImageSharp.Formats.Tiff.Utils; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation +{ + /// + /// Implements the 'RGB' photometric interpretation with 'Planar' layout for all 16 bit. + /// + internal class Rgb16PlanarTiffColor : TiffBasePlanarColorDecoder + where TPixel : unmanaged, IPixel + { + private readonly bool isBigEndian; + + /// + /// Initializes a new instance of the class. + /// + /// if set to true decodes the pixel data as big endian, otherwise as little endian. + public Rgb16PlanarTiffColor(bool isBigEndian) => this.isBigEndian = isBigEndian; + + /// + public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height) + { + var color = default(TPixel); + + System.Span redData = data[0].GetSpan(); + System.Span greenData = data[1].GetSpan(); + System.Span blueData = data[2].GetSpan(); + + int offset = 0; + var rgba = default(Rgba64); + for (int y = top; y < top + height; y++) + { + for (int x = left; x < left + width; x++) + { + ulong r = TiffUtils.ConvertToShort(redData.Slice(offset, 2), this.isBigEndian); + ulong g = TiffUtils.ConvertToShort(greenData.Slice(offset, 2), this.isBigEndian); + ulong b = TiffUtils.ConvertToShort(blueData.Slice(offset, 2), this.isBigEndian); + + offset += 2; + + rgba.PackedValue = r | (g << 16) | (b << 32) | (0xfffful << 48); + color.FromRgba64(rgba); + pixels[x, y] = color; + } + } + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor{TPixel}.cs index 3400bd65d..5df68ee59 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor{TPixel}.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// /// Implements the 'RGB' photometric interpretation with 'Planar' layout (for all bit depths). /// - internal class RgbPlanarTiffColor + internal class RgbPlanarTiffColor : TiffBasePlanarColorDecoder where TPixel : unmanaged, IPixel { private readonly float rFactor; @@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// The y-coordinate of the top of the image block. /// The width of the image block. /// The height of the image block. - public void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height) + public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height) { var color = default(TPixel); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffBasePlanarColorDecoder{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffBasePlanarColorDecoder{TPixel}.cs new file mode 100644 index 000000000..57d8588ce --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffBasePlanarColorDecoder{TPixel}.cs @@ -0,0 +1,28 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System.Buffers; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation +{ + /// + /// The base class for planar color decoders. + /// + /// The pixel format. + internal abstract class TiffBasePlanarColorDecoder + where TPixel : unmanaged, IPixel + { + /// + /// Decodes source raw pixel data using the current photometric interpretation. + /// + /// The buffers to read image data from. + /// The image buffer to write pixels to. + /// The x-coordinate of the left-hand side of the image block. + /// The y-coordinate of the top of the image block. + /// The width of the image block. + /// The height of the image block. + public abstract void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height); + } +} diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs index 25b441ab0..0c93998c4 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs @@ -145,12 +145,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation } } - public static RgbPlanarTiffColor CreatePlanar(TiffColorType colorType, TiffBitsPerSample bitsPerSample, ushort[] colorMap) + public static TiffBasePlanarColorDecoder CreatePlanar(TiffColorType colorType, TiffBitsPerSample bitsPerSample, ushort[] colorMap, ByteOrder byteOrder) { switch (colorType) { case TiffColorType.RgbPlanar: DebugGuard.IsTrue(colorMap == null, "colorMap"); + if (bitsPerSample.Channel0 == 16 && bitsPerSample.Channel1 == 16 && bitsPerSample.Channel2 == 16) + { + return new Rgb16PlanarTiffColor(byteOrder == ByteOrder.BigEndian); + } + return new RgbPlanarTiffColor(bitsPerSample); default: diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 484e182c5..72f2336a8 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -266,7 +266,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff using TiffBaseDecompressor decompressor = TiffDecompressorsFactory.Create(this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, frame.Width, bitsPerPixel, this.Predictor, this.FaxCompressionOptions); - RgbPlanarTiffColor colorDecoder = TiffColorDecoderFactory.CreatePlanar(this.ColorType, this.BitsPerSample, this.ColorMap); + TiffBasePlanarColorDecoder colorDecoder = TiffColorDecoderFactory.CreatePlanar(this.ColorType, this.BitsPerSample, this.ColorMap, this.byteOrder); for (int i = 0; i < stripsPerPlane; i++) { diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index 9346c4bfd..105b871da 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -166,6 +166,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Theory] [WithFile(FlowerRgb161616Contiguous, PixelTypes.Rgba32)] [WithFile(FlowerRgb161616Planar, PixelTypes.Rgba32)] + [WithFile(FlowerRgb161616PlanarLittleEndian, PixelTypes.Rgba32)] [WithFile(Issues1716Rgb161616BitLittleEndian, PixelTypes.Rgba32)] public void TiffDecoder_CanDecode_48Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 212bb94e3..e0ae8f63d 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -563,6 +563,7 @@ namespace SixLabors.ImageSharp.Tests public const string RgbPaletteDeflate = "Tiff/rgb_palette_deflate.tiff"; public const string FlowerRgb161616Contiguous = "Tiff/flower-rgb-contig-16.tiff"; public const string FlowerRgb161616Planar = "Tiff/flower-rgb-planar-16.tiff"; + public const string FlowerRgb161616PlanarLittleEndian = "Tiff/flower-rgb-planar-16_lsb.tiff"; public const string FlowerRgb141414Contiguous = "Tiff/flower-rgb-contig-14.tiff"; public const string FlowerRgb141414Planar = "Tiff/flower-rgb-planar-14.tiff"; public const string FlowerRgb101010Contiguous = "Tiff/flower-rgb-contig-10.tiff"; diff --git a/tests/Images/Input/Tiff/flower-rgb-planar-16_lsb.tiff b/tests/Images/Input/Tiff/flower-rgb-planar-16_lsb.tiff new file mode 100644 index 000000000..425ea42ef --- /dev/null +++ b/tests/Images/Input/Tiff/flower-rgb-planar-16_lsb.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:46a60552a7ff37f2c16c43e030e7180872af712f5d9c9c7673e2547049af3da9 +size 19168 From 6b538f89671d67a6115d4c472281351851bc67af Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 3 Aug 2021 14:05:14 +0200 Subject: [PATCH 4/9] Check for isBigEndian per row not per pixel --- .../BlackIsZero16TiffColor{TPixel}.cs | 22 ++++++++---- .../Rgb161616TiffColor{TPixel}.cs | 34 +++++++++++++------ .../Rgb16PlanarTiffColor{TPixel}.cs | 30 +++++++++++----- .../WhiteIsZero16TiffColor{TPixel}.cs | 22 ++++++++---- .../Formats/Tiff/Utils/TiffUtils.cs | 30 ++++++++++++++-- 5 files changed, 105 insertions(+), 33 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs index 13a570dbc..79017d8ce 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs @@ -32,15 +32,25 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation var l16 = default(L16); for (int y = top; y < top + height; y++) { - for (int x = left; x < left + width; x++) + if (this.isBigEndian) { - ushort intensity = TiffUtils.ConvertToShort(data.Slice(offset, 2), this.isBigEndian); - offset += 2; + for (int x = left; x < left + width; x++) + { + ushort intensity = TiffUtils.ConvertToShortBigEndian(data.Slice(offset, 2)); + offset += 2; - l16.PackedValue = intensity; - color.FromL16(l16); + pixels[x, y] = TiffUtils.ColorFromL16(l16, intensity, color); + } + } + else + { + for (int x = left; x < left + width; x++) + { + ushort intensity = TiffUtils.ConvertToShortLittleEndian(data.Slice(offset, 2)); + offset += 2; - pixels[x, y] = color; + pixels[x, y] = TiffUtils.ColorFromL16(l16, intensity, color); + } } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs index 89cf1cb48..d2cb65cd5 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs @@ -34,19 +34,33 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation { Span pixelRow = pixels.GetRowSpan(y); - for (int x = left; x < left + width; x++) + if (this.isBigEndian) { - ulong r = TiffUtils.ConvertToShort(data.Slice(offset, 2), this.isBigEndian); - offset += 2; - ulong g = TiffUtils.ConvertToShort(data.Slice(offset, 2), this.isBigEndian); - offset += 2; - ulong b = TiffUtils.ConvertToShort(data.Slice(offset, 2), this.isBigEndian); - offset += 2; + for (int x = left; x < left + width; x++) + { + ulong r = TiffUtils.ConvertToShortBigEndian(data.Slice(offset, 2)); + offset += 2; + ulong g = TiffUtils.ConvertToShortBigEndian(data.Slice(offset, 2)); + offset += 2; + ulong b = TiffUtils.ConvertToShortBigEndian(data.Slice(offset, 2)); + offset += 2; - rgba.PackedValue = r | (g << 16) | (b << 32) | (0xfffful << 48); - color.FromRgba64(rgba); + pixelRow[x] = TiffUtils.ColorFromRgba64(rgba, r, g, b, color); + } + } + else + { + for (int x = left; x < left + width; x++) + { + ulong r = TiffUtils.ConvertToShortLittleEndian(data.Slice(offset, 2)); + offset += 2; + ulong g = TiffUtils.ConvertToShortLittleEndian(data.Slice(offset, 2)); + offset += 2; + ulong b = TiffUtils.ConvertToShortLittleEndian(data.Slice(offset, 2)); + offset += 2; - pixelRow[x] = color; + pixelRow[x] = TiffUtils.ColorFromRgba64(rgba, r, g, b, color); + } } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs index 75b0b3a8b..43b985c90 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs @@ -35,17 +35,31 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation var rgba = default(Rgba64); for (int y = top; y < top + height; y++) { - for (int x = left; x < left + width; x++) + if (this.isBigEndian) { - ulong r = TiffUtils.ConvertToShort(redData.Slice(offset, 2), this.isBigEndian); - ulong g = TiffUtils.ConvertToShort(greenData.Slice(offset, 2), this.isBigEndian); - ulong b = TiffUtils.ConvertToShort(blueData.Slice(offset, 2), this.isBigEndian); + for (int x = left; x < left + width; x++) + { + ulong r = TiffUtils.ConvertToShortBigEndian(redData.Slice(offset, 2)); + ulong g = TiffUtils.ConvertToShortBigEndian(greenData.Slice(offset, 2)); + ulong b = TiffUtils.ConvertToShortBigEndian(blueData.Slice(offset, 2)); - offset += 2; + offset += 2; - rgba.PackedValue = r | (g << 16) | (b << 32) | (0xfffful << 48); - color.FromRgba64(rgba); - pixels[x, y] = color; + pixels[x, y] = TiffUtils.ColorFromRgba64(rgba, r, g, b, color); + } + } + else + { + for (int x = left; x < left + width; x++) + { + ulong r = TiffUtils.ConvertToShortLittleEndian(redData.Slice(offset, 2)); + ulong g = TiffUtils.ConvertToShortLittleEndian(greenData.Slice(offset, 2)); + ulong b = TiffUtils.ConvertToShortLittleEndian(blueData.Slice(offset, 2)); + + offset += 2; + + pixels[x, y] = TiffUtils.ColorFromRgba64(rgba, r, g, b, color); + } } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs index d84efef66..7a0aae267 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs @@ -32,15 +32,25 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation var l16 = default(L16); for (int y = top; y < top + height; y++) { - for (int x = left; x < left + width; x++) + if (this.isBigEndian) { - ushort intensity = (ushort)(ushort.MaxValue - TiffUtils.ConvertToShort(data.Slice(offset, 2), this.isBigEndian)); - offset += 2; + for (int x = left; x < left + width; x++) + { + ushort intensity = (ushort)(ushort.MaxValue - TiffUtils.ConvertToShortBigEndian(data.Slice(offset, 2))); + offset += 2; - l16.PackedValue = intensity; - color.FromL16(l16); + pixels[x, y] = TiffUtils.ColorFromL16(l16, intensity, color); + } + } + else + { + for (int x = left; x < left + width; x++) + { + ushort intensity = (ushort)(ushort.MaxValue - TiffUtils.ConvertToShortLittleEndian(data.Slice(offset, 2))); + offset += 2; - pixels[x, y] = color; + pixels[x, y] = TiffUtils.ColorFromL16(l16, intensity, color); + } } } } diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs index 5a31b231e..4f769c047 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs @@ -3,6 +3,8 @@ using System; using System.Buffers.Binary; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Tiff.Utils { @@ -11,8 +13,30 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Utils /// internal static class TiffUtils { - public static ushort ConvertToShort(ReadOnlySpan buffer, bool isBigEndian) => isBigEndian - ? BinaryPrimitives.ReadUInt16BigEndian(buffer) - : BinaryPrimitives.ReadUInt16LittleEndian(buffer); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ushort ConvertToShortBigEndian(ReadOnlySpan buffer) => + BinaryPrimitives.ReadUInt16BigEndian(buffer); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ushort ConvertToShortLittleEndian(ReadOnlySpan buffer) => + BinaryPrimitives.ReadUInt16LittleEndian(buffer); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorFromL16(L16 l16, ushort intensity, TPixel color) + where TPixel : unmanaged, IPixel + { + l16.PackedValue = intensity; + color.FromL16(l16); + return color; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorFromRgba64(Rgba64 rgba, ulong r, ulong g, ulong b, TPixel color) + where TPixel : unmanaged, IPixel + { + rgba.PackedValue = r | (g << 16) | (b << 32) | (0xfffful << 48); + color.FromRgba64(rgba); + return color; + } } } From 0ae95f17d970f4ba663434d5f7be4a6cf7c862a6 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 3 Aug 2021 14:34:03 +0200 Subject: [PATCH 5/9] Use pixel row span to access pixels --- .../BlackIsZero16TiffColor{TPixel}.cs | 5 +++-- .../BlackIsZero8TiffColor{TPixel}.cs | 9 +++------ .../BlackIsZeroTiffColor{TPixel}.cs | 3 ++- .../PaletteTiffColor{TPixel}.cs | 3 ++- .../Rgb16PlanarTiffColor{TPixel}.cs | 5 +++-- .../RgbPlanarTiffColor{TPixel}.cs | 4 +++- .../PhotometricInterpretation/RgbTiffColor{TPixel}.cs | 3 ++- .../WhiteIsZero16TiffColor{TPixel}.cs | 5 +++-- .../WhiteIsZero8TiffColor{TPixel}.cs | 9 +++------ .../WhiteIsZeroTiffColor{TPixel}.cs | 3 ++- src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs | 9 +++++++++ 11 files changed, 35 insertions(+), 23 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs index 79017d8ce..735dad1e2 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs @@ -32,6 +32,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation var l16 = default(L16); for (int y = top; y < top + height; y++) { + Span pixelRow = pixels.GetRowSpan(y); if (this.isBigEndian) { for (int x = left; x < left + width; x++) @@ -39,7 +40,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation ushort intensity = TiffUtils.ConvertToShortBigEndian(data.Slice(offset, 2)); offset += 2; - pixels[x, y] = TiffUtils.ColorFromL16(l16, intensity, color); + pixelRow[x] = TiffUtils.ColorFromL16(l16, intensity, color); } } else @@ -49,7 +50,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation ushort intensity = TiffUtils.ConvertToShortLittleEndian(data.Slice(offset, 2)); offset += 2; - pixels[x, y] = TiffUtils.ColorFromL16(l16, intensity, color); + pixelRow[x] = TiffUtils.ColorFromL16(l16, intensity, color); } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor{TPixel}.cs index 096f0449b..3a0f4771b 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor{TPixel}.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; - +using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -24,14 +24,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation var l8 = default(L8); for (int y = top; y < top + height; y++) { + Span pixelRow = pixels.GetRowSpan(y); for (int x = left; x < left + width; x++) { byte intensity = data[offset++]; - - l8.PackedValue = intensity; - color.FromL8(l8); - - pixels[x, y] = color; + pixelRow[x] = TiffUtils.ColorFromL8(l8, intensity, color); } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor{TPixel}.cs index a4e5e45df..79ee04e29 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor{TPixel}.cs @@ -34,13 +34,14 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation for (int y = top; y < top + height; y++) { + Span pixelRow = pixels.GetRowSpan(y); for (int x = left; x < left + width; x++) { int value = bitReader.ReadBits(this.bitsPerSample0); float intensity = value / this.factor; color.FromVector4(new Vector4(intensity, intensity, intensity, 1.0f)); - pixels[x, y] = color; + pixelRow[x] = color; } bitReader.NextRow(); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor{TPixel}.cs index 796227953..c14ff2385 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor{TPixel}.cs @@ -35,10 +35,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation for (int y = top; y < top + height; y++) { + Span pixelRow = pixels.GetRowSpan(y); for (int x = left; x < left + width; x++) { int index = bitReader.ReadBits(this.bitsPerSample0); - pixels[x, y] = this.palette[index]; + pixelRow[x] = this.palette[index]; } bitReader.NextRow(); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs index 43b985c90..3bad6c78c 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs @@ -35,6 +35,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation var rgba = default(Rgba64); for (int y = top; y < top + height; y++) { + System.Span pixelRow = pixels.GetRowSpan(y); if (this.isBigEndian) { for (int x = left; x < left + width; x++) @@ -45,7 +46,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation offset += 2; - pixels[x, y] = TiffUtils.ColorFromRgba64(rgba, r, g, b, color); + pixelRow[x] = TiffUtils.ColorFromRgba64(rgba, r, g, b, color); } } else @@ -58,7 +59,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation offset += 2; - pixels[x, y] = TiffUtils.ColorFromRgba64(rgba, r, g, b, color); + pixelRow[x] = TiffUtils.ColorFromRgba64(rgba, r, g, b, color); } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor{TPixel}.cs index 5df68ee59..11611907b 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor{TPixel}.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; using System.Buffers; using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; @@ -57,6 +58,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation for (int y = top; y < top + height; y++) { + Span pixelRow = pixels.GetRowSpan(y); for (int x = left; x < left + width; x++) { float r = rBitReader.ReadBits(this.bitsPerSampleR) / this.rFactor; @@ -64,7 +66,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation float b = bBitReader.ReadBits(this.bitsPerSampleB) / this.bFactor; color.FromVector4(new Vector4(r, g, b, 1.0f)); - pixels[x, y] = color; + pixelRow[x] = color; } rBitReader.NextRow(); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor{TPixel}.cs index 259bb8efa..01574c7aa 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor{TPixel}.cs @@ -47,6 +47,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation for (int y = top; y < top + height; y++) { + Span pixelRow = pixels.GetRowSpan(y); for (int x = left; x < left + width; x++) { float r = bitReader.ReadBits(this.bitsPerSampleR) / this.rFactor; @@ -54,7 +55,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation float b = bitReader.ReadBits(this.bitsPerSampleB) / this.bFactor; color.FromVector4(new Vector4(r, g, b, 1.0f)); - pixels[x, y] = color; + pixelRow[x] = color; } bitReader.NextRow(); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs index 7a0aae267..8bda36899 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs @@ -32,6 +32,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation var l16 = default(L16); for (int y = top; y < top + height; y++) { + Span pixelRow = pixels.GetRowSpan(y); if (this.isBigEndian) { for (int x = left; x < left + width; x++) @@ -39,7 +40,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation ushort intensity = (ushort)(ushort.MaxValue - TiffUtils.ConvertToShortBigEndian(data.Slice(offset, 2))); offset += 2; - pixels[x, y] = TiffUtils.ColorFromL16(l16, intensity, color); + pixelRow[x] = TiffUtils.ColorFromL16(l16, intensity, color); } } else @@ -49,7 +50,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation ushort intensity = (ushort)(ushort.MaxValue - TiffUtils.ConvertToShortLittleEndian(data.Slice(offset, 2))); offset += 2; - pixels[x, y] = TiffUtils.ColorFromL16(l16, intensity, color); + pixelRow[x] = TiffUtils.ColorFromL16(l16, intensity, color); } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor{TPixel}.cs index 1b141f9f6..eba6f6b72 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor{TPixel}.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; - +using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -24,14 +24,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation var l8 = default(L8); for (int y = top; y < top + height; y++) { + Span pixelRow = pixels.GetRowSpan(y); for (int x = left; x < left + width; x++) { byte intensity = (byte)(255 - data[offset++]); - - l8.PackedValue = intensity; - color.FromL8(l8); - - pixels[x, y] = color; + pixelRow[x] = TiffUtils.ColorFromL8(l8, intensity, color); } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor{TPixel}.cs index 04b6f98e5..5fb5fefb4 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor{TPixel}.cs @@ -34,13 +34,14 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation for (int y = top; y < top + height; y++) { + Span pixelRow = pixels.GetRowSpan(y); for (int x = left; x < left + width; x++) { int value = bitReader.ReadBits(this.bitsPerSample0); float intensity = 1.0f - (value / this.factor); color.FromVector4(new Vector4(intensity, intensity, intensity, 1.0f)); - pixels[x, y] = color; + pixelRow[x] = color; } bitReader.NextRow(); diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs index 4f769c047..0a6e539b8 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs @@ -21,6 +21,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Utils public static ushort ConvertToShortLittleEndian(ReadOnlySpan buffer) => BinaryPrimitives.ReadUInt16LittleEndian(buffer); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorFromL8(L8 l8, byte intensity, TPixel color) + where TPixel : unmanaged, IPixel + { + l8.PackedValue = intensity; + color.FromL8(l8); + return color; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ColorFromL16(L16 l16, ushort intensity, TPixel color) where TPixel : unmanaged, IPixel From 522a879a1826a1fb47aafae6eaa61ccd7ab5562f Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 3 Aug 2021 15:55:49 +0200 Subject: [PATCH 6/9] Avoid using defaults, because of issue with netcore2.1 in Release mode --- .../BlackIsZero16TiffColor{TPixel}.cs | 6 ++++-- .../Rgb161616TiffColor{TPixel}.cs | 5 ++++- .../Rgb16PlanarTiffColor{TPixel}.cs | 6 +++++- .../WhiteIsZero16TiffColor{TPixel}.cs | 6 ++++-- src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs | 7 +++++++ 5 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs index 735dad1e2..e80b1d99d 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs @@ -25,11 +25,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { + // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, + // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 + L16 l16 = TiffUtils.L16Default; var color = default(TPixel); + color.FromVector4(TiffUtils.Vector4Default); int offset = 0; - - var l16 = default(L16); for (int y = top; y < top + height; y++) { Span pixelRow = pixels.GetRowSpan(y); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs index d2cb65cd5..2e10916c0 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs @@ -25,11 +25,14 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { + // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, + // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 + Rgba64 rgba = TiffUtils.Rgba64Default; var color = default(TPixel); + color.FromVector4(TiffUtils.Vector4Default); int offset = 0; - var rgba = default(Rgba64); for (int y = top; y < top + height; y++) { Span pixelRow = pixels.GetRowSpan(y); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs index 3bad6c78c..6adaab8bd 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System.Buffers; +using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -25,14 +26,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height) { + // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, + // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 + Rgba64 rgba = TiffUtils.Rgba64Default; var color = default(TPixel); + color.FromVector4(TiffUtils.Vector4Default); System.Span redData = data[0].GetSpan(); System.Span greenData = data[1].GetSpan(); System.Span blueData = data[2].GetSpan(); int offset = 0; - var rgba = default(Rgba64); for (int y = top; y < top + height; y++) { System.Span pixelRow = pixels.GetRowSpan(y); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs index 8bda36899..3c7bfc99c 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs @@ -25,11 +25,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { + // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, + // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 + L16 l16 = TiffUtils.L16Default; var color = default(TPixel); + color.FromVector4(TiffUtils.Vector4Default); int offset = 0; - - var l16 = default(L16); for (int y = top; y < top + height; y++) { Span pixelRow = pixels.GetRowSpan(y); diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs index 0a6e539b8..a0ba40f4e 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs @@ -3,6 +3,7 @@ using System; using System.Buffers.Binary; +using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; @@ -13,6 +14,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Utils /// internal static class TiffUtils { + public static Vector4 Vector4Default { get; } = new Vector4(0.0f, 0.0f, 0.0f, 0.0f); + + public static Rgba64 Rgba64Default { get; } = new Rgba64(0, 0, 0, 0); + + public static L16 L16Default { get; } = new L16(0); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ushort ConvertToShortBigEndian(ReadOnlySpan buffer) => BinaryPrimitives.ReadUInt16BigEndian(buffer); From f1834c7816d90597c8681e777954a5fc7816ac2c Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 3 Aug 2021 16:40:33 +0200 Subject: [PATCH 7/9] Avoid bounds checks --- .../BlackIsZero16TiffColor{TPixel}.cs | 6 +++--- .../BlackIsZero8TiffColor{TPixel}.cs | 4 ++-- .../BlackIsZeroTiffColor{TPixel}.cs | 4 ++-- .../PaletteTiffColor{TPixel}.cs | 4 ++-- .../Rgb161616TiffColor{TPixel}.cs | 4 ++-- .../Rgb16PlanarTiffColor{TPixel}.cs | 14 +++++++------- .../Rgb888TiffColor{TPixel}.cs | 4 ++-- .../RgbPlanarTiffColor{TPixel}.cs | 4 ++-- .../RgbTiffColor{TPixel}.cs | 4 ++-- .../WhiteIsZero16TiffColor{TPixel}.cs | 6 +++--- .../WhiteIsZero8TiffColor{TPixel}.cs | 4 ++-- .../WhiteIsZeroTiffColor{TPixel}.cs | 4 ++-- 12 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs index e80b1d99d..a31ff7a9f 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs @@ -34,10 +34,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation int offset = 0; for (int y = top; y < top + height; y++) { - Span pixelRow = pixels.GetRowSpan(y); + Span pixelRow = pixels.GetRowSpan(y).Slice(left, width); if (this.isBigEndian) { - for (int x = left; x < left + width; x++) + for (int x = 0; x < pixelRow.Length; x++) { ushort intensity = TiffUtils.ConvertToShortBigEndian(data.Slice(offset, 2)); offset += 2; @@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation } else { - for (int x = left; x < left + width; x++) + for (int x = 0; x < pixelRow.Length; x++) { ushort intensity = TiffUtils.ConvertToShortLittleEndian(data.Slice(offset, 2)); offset += 2; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor{TPixel}.cs index 3a0f4771b..ea2608f6f 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor{TPixel}.cs @@ -24,8 +24,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation var l8 = default(L8); for (int y = top; y < top + height; y++) { - Span pixelRow = pixels.GetRowSpan(y); - for (int x = left; x < left + width; x++) + Span pixelRow = pixels.GetRowSpan(y).Slice(left, width); + for (int x = 0; x < pixelRow.Length; x++) { byte intensity = data[offset++]; pixelRow[x] = TiffUtils.ColorFromL8(l8, intensity, color); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor{TPixel}.cs index 79ee04e29..9956db523 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor{TPixel}.cs @@ -34,8 +34,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation for (int y = top; y < top + height; y++) { - Span pixelRow = pixels.GetRowSpan(y); - for (int x = left; x < left + width; x++) + Span pixelRow = pixels.GetRowSpan(y).Slice(left, width); + for (int x = 0; x < pixelRow.Length; x++) { int value = bitReader.ReadBits(this.bitsPerSample0); float intensity = value / this.factor; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor{TPixel}.cs index c14ff2385..b392fe1a3 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor{TPixel}.cs @@ -35,8 +35,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation for (int y = top; y < top + height; y++) { - Span pixelRow = pixels.GetRowSpan(y); - for (int x = left; x < left + width; x++) + Span pixelRow = pixels.GetRowSpan(y).Slice(left, width); + for (int x = 0; x < pixelRow.Length; x++) { int index = bitReader.ReadBits(this.bitsPerSample0); pixelRow[x] = this.palette[index]; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs index 2e10916c0..00dc9fd43 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs @@ -35,11 +35,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation for (int y = top; y < top + height; y++) { - Span pixelRow = pixels.GetRowSpan(y); + Span pixelRow = pixels.GetRowSpan(y).Slice(left, width); if (this.isBigEndian) { - for (int x = left; x < left + width; x++) + for (int x = 0; x < pixelRow.Length; x++) { ulong r = TiffUtils.ConvertToShortBigEndian(data.Slice(offset, 2)); offset += 2; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs index 6adaab8bd..c24fada54 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs @@ -1,8 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; using System.Buffers; -using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -32,17 +32,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation var color = default(TPixel); color.FromVector4(TiffUtils.Vector4Default); - System.Span redData = data[0].GetSpan(); - System.Span greenData = data[1].GetSpan(); - System.Span blueData = data[2].GetSpan(); + Span redData = data[0].GetSpan(); + Span greenData = data[1].GetSpan(); + Span blueData = data[2].GetSpan(); int offset = 0; for (int y = top; y < top + height; y++) { - System.Span pixelRow = pixels.GetRowSpan(y); + Span pixelRow = pixels.GetRowSpan(y).Slice(left, width); if (this.isBigEndian) { - for (int x = left; x < left + width; x++) + for (int x = 0; x < pixelRow.Length; x++) { ulong r = TiffUtils.ConvertToShortBigEndian(redData.Slice(offset, 2)); ulong g = TiffUtils.ConvertToShortBigEndian(greenData.Slice(offset, 2)); @@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation } else { - for (int x = left; x < left + width; x++) + for (int x = 0; x < pixelRow.Length; x++) { ulong r = TiffUtils.ConvertToShortLittleEndian(redData.Slice(offset, 2)); ulong g = TiffUtils.ConvertToShortLittleEndian(greenData.Slice(offset, 2)); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor{TPixel}.cs index e45863a57..536dece8e 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor{TPixel}.cs @@ -24,9 +24,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation var rgba = default(Rgba32); for (int y = top; y < top + height; y++) { - Span pixelRow = pixels.GetRowSpan(y); + Span pixelRow = pixels.GetRowSpan(y).Slice(left, width); - for (int x = left; x < left + width; x++) + for (int x = 0; x < pixelRow.Length; x++) { byte r = data[offset++]; byte g = data[offset++]; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor{TPixel}.cs index 11611907b..b442c4ae4 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor{TPixel}.cs @@ -58,8 +58,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation for (int y = top; y < top + height; y++) { - Span pixelRow = pixels.GetRowSpan(y); - for (int x = left; x < left + width; x++) + Span pixelRow = pixels.GetRowSpan(y).Slice(left, width); + for (int x = 0; x < pixelRow.Length; x++) { float r = rBitReader.ReadBits(this.bitsPerSampleR) / this.rFactor; float g = gBitReader.ReadBits(this.bitsPerSampleG) / this.gFactor; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor{TPixel}.cs index 01574c7aa..1377598cc 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor{TPixel}.cs @@ -47,8 +47,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation for (int y = top; y < top + height; y++) { - Span pixelRow = pixels.GetRowSpan(y); - for (int x = left; x < left + width; x++) + Span pixelRow = pixels.GetRowSpan(y).Slice(left, width); + for (int x = 0; x < pixelRow.Length; x++) { float r = bitReader.ReadBits(this.bitsPerSampleR) / this.rFactor; float g = bitReader.ReadBits(this.bitsPerSampleG) / this.gFactor; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs index 3c7bfc99c..a6b5151d7 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs @@ -34,10 +34,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation int offset = 0; for (int y = top; y < top + height; y++) { - Span pixelRow = pixels.GetRowSpan(y); + Span pixelRow = pixels.GetRowSpan(y).Slice(left, width); if (this.isBigEndian) { - for (int x = left; x < left + width; x++) + for (int x = 0; x < pixelRow.Length; x++) { ushort intensity = (ushort)(ushort.MaxValue - TiffUtils.ConvertToShortBigEndian(data.Slice(offset, 2))); offset += 2; @@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation } else { - for (int x = left; x < left + width; x++) + for (int x = 0; x < pixelRow.Length; x++) { ushort intensity = (ushort)(ushort.MaxValue - TiffUtils.ConvertToShortLittleEndian(data.Slice(offset, 2))); offset += 2; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor{TPixel}.cs index eba6f6b72..6a6c2af22 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor{TPixel}.cs @@ -24,8 +24,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation var l8 = default(L8); for (int y = top; y < top + height; y++) { - Span pixelRow = pixels.GetRowSpan(y); - for (int x = left; x < left + width; x++) + Span pixelRow = pixels.GetRowSpan(y).Slice(left, width); + for (int x = 0; x < pixelRow.Length; x++) { byte intensity = (byte)(255 - data[offset++]); pixelRow[x] = TiffUtils.ColorFromL8(l8, intensity, color); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor{TPixel}.cs index 5fb5fefb4..912955964 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor{TPixel}.cs @@ -34,8 +34,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation for (int y = top; y < top + height; y++) { - Span pixelRow = pixels.GetRowSpan(y); - for (int x = left; x < left + width; x++) + Span pixelRow = pixels.GetRowSpan(y).Slice(left, width); + for (int x = 0; x < pixelRow.Length; x++) { int value = bitReader.ReadBits(this.bitsPerSample0); float intensity = 1.0f - (value / this.factor); From 9e12a72a55487dd877613208abf01c91c3069b48 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 3 Aug 2021 16:57:03 +0200 Subject: [PATCH 8/9] Fix loop bounds for little endian, add little endian test file --- .../PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs | 2 +- tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs | 1 + tests/ImageSharp.Tests/TestImages.cs | 1 + tests/Images/Input/Tiff/flower-rgb-contig-16_lsb.tiff | 3 +++ 4 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 tests/Images/Input/Tiff/flower-rgb-contig-16_lsb.tiff diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs index 00dc9fd43..4b34d5a0d 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs @@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation } else { - for (int x = left; x < left + width; x++) + for (int x = 0; x < pixelRow.Length; x++) { ulong r = TiffUtils.ConvertToShortLittleEndian(data.Slice(offset, 2)); offset += 2; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index 105b871da..7ee6e70e6 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -165,6 +165,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Theory] [WithFile(FlowerRgb161616Contiguous, PixelTypes.Rgba32)] + [WithFile(FlowerRgb161616ContiguousLittleEndian, PixelTypes.Rgba32)] [WithFile(FlowerRgb161616Planar, PixelTypes.Rgba32)] [WithFile(FlowerRgb161616PlanarLittleEndian, PixelTypes.Rgba32)] [WithFile(Issues1716Rgb161616BitLittleEndian, PixelTypes.Rgba32)] diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index e0ae8f63d..e7641a7ca 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -562,6 +562,7 @@ namespace SixLabors.ImageSharp.Tests public const string Rgb4BitPalette = "Tiff/bike_colorpalette_4bit.tiff"; public const string RgbPaletteDeflate = "Tiff/rgb_palette_deflate.tiff"; public const string FlowerRgb161616Contiguous = "Tiff/flower-rgb-contig-16.tiff"; + public const string FlowerRgb161616ContiguousLittleEndian = "Tiff/flower-rgb-contig-16_lsb.tiff"; public const string FlowerRgb161616Planar = "Tiff/flower-rgb-planar-16.tiff"; public const string FlowerRgb161616PlanarLittleEndian = "Tiff/flower-rgb-planar-16_lsb.tiff"; public const string FlowerRgb141414Contiguous = "Tiff/flower-rgb-contig-14.tiff"; diff --git a/tests/Images/Input/Tiff/flower-rgb-contig-16_lsb.tiff b/tests/Images/Input/Tiff/flower-rgb-contig-16_lsb.tiff new file mode 100644 index 000000000..967d8bbf3 --- /dev/null +++ b/tests/Images/Input/Tiff/flower-rgb-contig-16_lsb.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0951a9c2207eb6864b6a19ec8513a28a874adddb37c3c06b9fd07831372924e3 +size 19150 From f868b7a2aa2a7a2cf2f9836fd8c84e5dbfe08544 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 3 Aug 2021 17:21:08 +0200 Subject: [PATCH 9/9] Add min is white 16 bit gray big endian test file --- tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs | 1 + tests/ImageSharp.Tests/TestImages.cs | 1 + tests/Images/Input/Tiff/flower-miniswhite-16.tiff | 3 +++ 3 files changed, 5 insertions(+) create mode 100644 tests/Images/Input/Tiff/flower-miniswhite-16.tiff diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index 7ee6e70e6..3bf1c25f3 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -143,6 +143,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [WithFile(Flower16BitGrayLittleEndian, PixelTypes.Rgba32)] [WithFile(Flower16BitGray, PixelTypes.Rgba32)] [WithFile(Flower16BitGrayMinIsWhiteLittleEndian, PixelTypes.Rgba32)] + [WithFile(Flower16BitGrayMinIsWhiteBigEndian, PixelTypes.Rgba32)] public void TiffDecoder_CanDecode_16Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index e7641a7ca..9c2418436 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -586,6 +586,7 @@ namespace SixLabors.ImageSharp.Tests public const string Flower16BitGray = "Tiff/flower-minisblack-16.tiff"; public const string Flower16BitGrayLittleEndian = "Tiff/flower-minisblack-16_lsb.tiff"; public const string Flower16BitGrayMinIsWhiteLittleEndian = "Tiff/flower-miniswhite-16_lsb.tiff"; + public const string Flower16BitGrayMinIsWhiteBigEndian = "Tiff/flower-miniswhite-16.tiff"; public const string Issues1716Rgb161616BitLittleEndian = "Tiff/Issues/Issue1716.tiff"; public const string SmallRgbDeflate = "Tiff/rgb_small_deflate.tiff"; diff --git a/tests/Images/Input/Tiff/flower-miniswhite-16.tiff b/tests/Images/Input/Tiff/flower-miniswhite-16.tiff new file mode 100644 index 000000000..83266873c --- /dev/null +++ b/tests/Images/Input/Tiff/flower-miniswhite-16.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d8f2c2afd8f1645717087bd2edbc3e8a46b88a54a4996c0e9350fdd652b5c382 +size 6588