Browse Source

Merge branch 'master' into js/fix-divide-by-zero

pull/1721/head
James Jackson-South 5 years ago
committed by GitHub
parent
commit
3e56885e3b
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 61
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs
  2. 11
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor{TPixel}.cs
  3. 5
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor{TPixel}.cs
  4. 5
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor{TPixel}.cs
  5. 47
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs
  6. 72
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs
  7. 4
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor{TPixel}.cs
  8. 10
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor{TPixel}.cs
  9. 5
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor{TPixel}.cs
  10. 28
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffBasePlanarColorDecoder{TPixel}.cs
  11. 17
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs
  12. 10
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs
  13. 61
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs
  14. 11
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor{TPixel}.cs
  15. 5
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor{TPixel}.cs
  16. 2
      src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
  17. 12
      src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs
  18. 58
      src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs
  19. 5
      tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
  20. 5
      tests/ImageSharp.Tests/TestImages.cs
  21. 3
      tests/Images/Input/Tiff/flower-minisblack-16_lsb.tiff
  22. 3
      tests/Images/Input/Tiff/flower-miniswhite-16.tiff
  23. 3
      tests/Images/Input/Tiff/flower-miniswhite-16_lsb.tiff
  24. 3
      tests/Images/Input/Tiff/flower-rgb-contig-16_lsb.tiff
  25. 3
      tests/Images/Input/Tiff/flower-rgb-planar-16_lsb.tiff

61
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs

@ -0,0 +1,61 @@
// 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
{
/// <summary>
/// Implements the 'BlackIsZero' photometric interpretation for 16-bit grayscale images.
/// </summary>
internal class BlackIsZero16TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
private readonly bool isBigEndian;
/// <summary>
/// Initializes a new instance of the <see cref="BlackIsZero16TiffColor{TPixel}" /> class.
/// </summary>
/// <param name="isBigEndian">if set to <c>true</c> decodes the pixel data as big endian, otherwise as little endian.</param>
public BlackIsZero16TiffColor(bool isBigEndian) => this.isBigEndian = isBigEndian;
/// <inheritdoc/>
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> 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;
for (int y = top; y < top + height; y++)
{
Span<TPixel> pixelRow = pixels.GetRowSpan(y).Slice(left, width);
if (this.isBigEndian)
{
for (int x = 0; x < pixelRow.Length; x++)
{
ushort intensity = TiffUtils.ConvertToShortBigEndian(data.Slice(offset, 2));
offset += 2;
pixelRow[x] = TiffUtils.ColorFromL16(l16, intensity, color);
}
}
else
{
for (int x = 0; x < pixelRow.Length; x++)
{
ushort intensity = TiffUtils.ConvertToShortLittleEndian(data.Slice(offset, 2));
offset += 2;
pixelRow[x] = TiffUtils.ColorFromL16(l16, intensity, color);
}
}
}
}
}
}

11
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++)
{
for (int x = left; x < left + width; x++)
Span<TPixel> pixelRow = pixels.GetRowSpan(y).Slice(left, width);
for (int x = 0; x < pixelRow.Length; x++)
{
byte intensity = data[offset++];
l8.PackedValue = intensity;
color.FromL8(l8);
pixels[x, y] = color;
pixelRow[x] = TiffUtils.ColorFromL8(l8, intensity, color);
}
}
}

5
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++)
{
for (int x = left; x < left + width; x++)
Span<TPixel> 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;
color.FromVector4(new Vector4(intensity, intensity, intensity, 1.0f));
pixels[x, y] = color;
pixelRow[x] = color;
}
bitReader.NextRow();

5
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++)
{
for (int x = left; x < left + width; x++)
Span<TPixel> pixelRow = pixels.GetRowSpan(y).Slice(left, width);
for (int x = 0; x < pixelRow.Length; x++)
{
int index = bitReader.ReadBits(this.bitsPerSample0);
pixels[x, y] = this.palette[index];
pixelRow[x] = this.palette[index];
}
bitReader.NextRow();

47
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs

@ -2,7 +2,7 @@
// 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;
@ -25,34 +25,47 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
/// <inheritdoc/>
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> 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<TPixel> pixelRow = pixels.GetRowSpan(y);
Span<TPixel> pixelRow = pixels.GetRowSpan(y).Slice(left, width);
for (int x = left; x < left + width; x++)
if (this.isBigEndian)
{
ulong r = this.ConvertToShort(data.Slice(offset, 2));
offset += 2;
ulong g = this.ConvertToShort(data.Slice(offset, 2));
offset += 2;
ulong b = this.ConvertToShort(data.Slice(offset, 2));
offset += 2;
for (int x = 0; x < pixelRow.Length; 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 = 0; x < pixelRow.Length; 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);
}
}
}
}
private ushort ConvertToShort(ReadOnlySpan<byte> buffer) => this.isBigEndian
? BinaryPrimitives.ReadUInt16BigEndian(buffer)
: BinaryPrimitives.ReadUInt16LittleEndian(buffer);
}
}

72
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs

@ -0,0 +1,72 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
{
/// <summary>
/// Implements the 'RGB' photometric interpretation with 'Planar' layout for all 16 bit.
/// </summary>
internal class Rgb16PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
private readonly bool isBigEndian;
/// <summary>
/// Initializes a new instance of the <see cref="Rgb16PlanarTiffColor{TPixel}" /> class.
/// </summary>
/// <param name="isBigEndian">if set to <c>true</c> decodes the pixel data as big endian, otherwise as little endian.</param>
public Rgb16PlanarTiffColor(bool isBigEndian) => this.isBigEndian = isBigEndian;
/// <inheritdoc/>
public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> 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);
Span<byte> redData = data[0].GetSpan();
Span<byte> greenData = data[1].GetSpan();
Span<byte> blueData = data[2].GetSpan();
int offset = 0;
for (int y = top; y < top + height; y++)
{
Span<TPixel> pixelRow = pixels.GetRowSpan(y).Slice(left, width);
if (this.isBigEndian)
{
for (int x = 0; x < pixelRow.Length; 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;
pixelRow[x] = TiffUtils.ColorFromRgba64(rgba, r, g, b, color);
}
}
else
{
for (int x = 0; x < pixelRow.Length; 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;
pixelRow[x] = TiffUtils.ColorFromRgba64(rgba, r, g, b, color);
}
}
}
}
}
}

4
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<TPixel> pixelRow = pixels.GetRowSpan(y);
Span<TPixel> 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++];

10
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;
@ -12,7 +13,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
/// <summary>
/// Implements the 'RGB' photometric interpretation with 'Planar' layout (for all bit depths).
/// </summary>
internal class RgbPlanarTiffColor<TPixel>
internal class RgbPlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
private readonly float rFactor;
@ -47,7 +48,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
/// <param name="top">The y-coordinate of the top of the image block.</param>
/// <param name="width">The width of the image block.</param>
/// <param name="height">The height of the image block.</param>
public void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{
var color = default(TPixel);
@ -57,14 +58,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
for (int y = top; y < top + height; y++)
{
for (int x = left; x < left + width; x++)
Span<TPixel> 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;
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();

5
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor{TPixel}.cs

@ -47,14 +47,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
for (int y = top; y < top + height; y++)
{
for (int x = left; x < left + width; x++)
Span<TPixel> 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;
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();

28
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
{
/// <summary>
/// The base class for planar color decoders.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal abstract class TiffBasePlanarColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
/// <summary>
/// Decodes source raw pixel data using the current photometric interpretation.
/// </summary>
/// <param name="data">The buffers to read image data from.</param>
/// <param name="pixels">The image buffer to write pixels to.</param>
/// <param name="left">The x-coordinate of the left-hand side of the image block.</param>
/// <param name="top">The y-coordinate of the top of the image block.</param>
/// <param name="width">The width of the image block.</param>
/// <param name="height">The height of the image block.</param>
public abstract void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels, int left, int top, int width, int height);
}
}

17
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<TPixel>();
case TiffColorType.WhiteIsZero16:
DebugGuard.IsTrue(bitsPerSample.Channels == 1 && bitsPerSample.Channel0 == 16, "bitsPerSample");
DebugGuard.IsTrue(colorMap == null, "colorMap");
return new WhiteIsZero16TiffColor<TPixel>(byteOrder == ByteOrder.BigEndian);
case TiffColorType.BlackIsZero:
DebugGuard.IsTrue(bitsPerSample.Channels == 1, "bitsPerSample");
DebugGuard.IsTrue(colorMap == null, "colorMap");
@ -52,6 +57,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
DebugGuard.IsTrue(colorMap == null, "colorMap");
return new BlackIsZero8TiffColor<TPixel>();
case TiffColorType.BlackIsZero16:
DebugGuard.IsTrue(bitsPerSample.Channels == 1 && bitsPerSample.Channel0 == 16, "bitsPerSample");
DebugGuard.IsTrue(colorMap == null, "colorMap");
return new BlackIsZero16TiffColor<TPixel>(byteOrder == ByteOrder.BigEndian);
case TiffColorType.Rgb:
DebugGuard.IsTrue(colorMap == null, "colorMap");
return new RgbTiffColor<TPixel>(bitsPerSample);
@ -135,12 +145,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
}
}
public static RgbPlanarTiffColor<TPixel> CreatePlanar(TiffColorType colorType, TiffBitsPerSample bitsPerSample, ushort[] colorMap)
public static TiffBasePlanarColorDecoder<TPixel> 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<TPixel>(byteOrder == ByteOrder.BigEndian);
}
return new RgbPlanarTiffColor<TPixel>(bitsPerSample);
default:

10
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs

@ -28,6 +28,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
/// </summary>
BlackIsZero8,
/// <summary>
/// Grayscale: 0 is imaged as black. The maximum value is imaged as white. Optimized implementation for 16-bit images.
/// </summary>
BlackIsZero16,
/// <summary>
/// Grayscale: 0 is imaged as white. The maximum value is imaged as black.
/// </summary>
@ -48,6 +53,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
/// </summary>
WhiteIsZero8,
/// <summary>
/// Grayscale: 0 is imaged as white. The maximum value is imaged as black. Optimized implementation for 16-bit images.
/// </summary>
WhiteIsZero16,
/// <summary>
/// Palette-color.
/// </summary>

61
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs

@ -0,0 +1,61 @@
// 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
{
/// <summary>
/// Implements the 'WhiteIsZero' photometric interpretation for 16-bit grayscale images.
/// </summary>
internal class WhiteIsZero16TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
private readonly bool isBigEndian;
/// <summary>
/// Initializes a new instance of the <see cref="WhiteIsZero16TiffColor{TPixel}" /> class.
/// </summary>
/// <param name="isBigEndian">if set to <c>true</c> decodes the pixel data as big endian, otherwise as little endian.</param>
public WhiteIsZero16TiffColor(bool isBigEndian) => this.isBigEndian = isBigEndian;
/// <inheritdoc/>
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> 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;
for (int y = top; y < top + height; y++)
{
Span<TPixel> pixelRow = pixels.GetRowSpan(y).Slice(left, width);
if (this.isBigEndian)
{
for (int x = 0; x < pixelRow.Length; x++)
{
ushort intensity = (ushort)(ushort.MaxValue - TiffUtils.ConvertToShortBigEndian(data.Slice(offset, 2)));
offset += 2;
pixelRow[x] = TiffUtils.ColorFromL16(l16, intensity, color);
}
}
else
{
for (int x = 0; x < pixelRow.Length; x++)
{
ushort intensity = (ushort)(ushort.MaxValue - TiffUtils.ConvertToShortLittleEndian(data.Slice(offset, 2)));
offset += 2;
pixelRow[x] = TiffUtils.ColorFromL16(l16, intensity, color);
}
}
}
}
}
}

11
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++)
{
for (int x = left; x < left + width; x++)
Span<TPixel> pixelRow = pixels.GetRowSpan(y).Slice(left, width);
for (int x = 0; x < pixelRow.Length; x++)
{
byte intensity = (byte)(255 - data[offset++]);
l8.PackedValue = intensity;
color.FromL8(l8);
pixels[x, y] = color;
pixelRow[x] = TiffUtils.ColorFromL8(l8, intensity, color);
}
}
}

5
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++)
{
for (int x = left; x < left + width; x++)
Span<TPixel> 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);
color.FromVector4(new Vector4(intensity, intensity, intensity, 1.0f));
pixels[x, y] = color;
pixelRow[x] = color;
}
bitReader.NextRow();

2
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<TPixel> colorDecoder = TiffColorDecoderFactory<TPixel>.CreatePlanar(this.ColorType, this.BitsPerSample, this.ColorMap);
TiffBasePlanarColorDecoder<TPixel> colorDecoder = TiffColorDecoderFactory<TPixel>.CreatePlanar(this.ColorType, this.BitsPerSample, this.ColorMap, this.byteOrder);
for (int i = 0; i < stripsPerPlane; i++)
{

12
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;
@ -154,6 +160,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff
switch (bitsPerChannel)
{
case 16:
{
options.ColorType = TiffColorType.BlackIsZero16;
break;
}
case 8:
{
options.ColorType = TiffColorType.BlackIsZero8;

58
src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs

@ -0,0 +1,58 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers.Binary;
using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Tiff.Utils
{
/// <summary>
/// Helper methods for TIFF decoding.
/// </summary>
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<byte> buffer) =>
BinaryPrimitives.ReadUInt16BigEndian(buffer);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ushort ConvertToShortLittleEndian(ReadOnlySpan<byte> buffer) =>
BinaryPrimitives.ReadUInt16LittleEndian(buffer);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel ColorFromL8<TPixel>(L8 l8, byte intensity, TPixel color)
where TPixel : unmanaged, IPixel<TPixel>
{
l8.PackedValue = intensity;
color.FromL8(l8);
return color;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel ColorFromL16<TPixel>(L16 l16, ushort intensity, TPixel color)
where TPixel : unmanaged, IPixel<TPixel>
{
l16.PackedValue = intensity;
color.FromL16(l16);
return color;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel ColorFromRgba64<TPixel>(Rgba64 rgba, ulong r, ulong g, ulong b, TPixel color)
where TPixel : unmanaged, IPixel<TPixel>
{
rgba.PackedValue = r | (g << 16) | (b << 32) | (0xfffful << 48);
color.FromRgba64(rgba);
return color;
}
}
}

5
tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs

@ -140,7 +140,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
where TPixel : unmanaged, IPixel<TPixel> => TestTiffDecoder(provider);
[Theory]
[WithFile(Flower16BitGrayLittleEndian, PixelTypes.Rgba32)]
[WithFile(Flower16BitGray, PixelTypes.Rgba32)]
[WithFile(Flower16BitGrayMinIsWhiteLittleEndian, PixelTypes.Rgba32)]
[WithFile(Flower16BitGrayMinIsWhiteBigEndian, PixelTypes.Rgba32)]
public void TiffDecoder_CanDecode_16Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffDecoder(provider);
@ -163,7 +166,9 @@ 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)]
public void TiffDecoder_CanDecode_48Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffDecoder(provider);

5
tests/ImageSharp.Tests/TestImages.cs

@ -562,7 +562,9 @@ 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";
public const string FlowerRgb141414Planar = "Tiff/flower-rgb-planar-14.tiff";
public const string FlowerRgb101010Contiguous = "Tiff/flower-rgb-contig-10.tiff";
@ -582,6 +584,9 @@ 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 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";

3
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

3
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

3
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

3
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

3
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
Loading…
Cancel
Save