Browse Source

Keep BitsPerSample array when decoding tiff, otherwise bits per channel would be ambiguous if we only keep bits per pixel

pull/1652/head
Brian Popow 5 years ago
parent
commit
3b4be36313
  1. 56
      src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs
  2. 111
      src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs
  3. 12
      src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
  4. 54
      src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs
  5. 9
      src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs
  6. 2
      tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs

56
src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs

@ -1,56 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Tiff
{
/// <summary>
/// The number of bits per component.
/// </summary>
public enum TiffBitsPerSample
{
/// <summary>
/// The Bits per samples is not known.
/// </summary>
Unknown = 0,
/// <summary>
/// One bit per sample for bicolor images.
/// </summary>
Bit1 = 1,
/// <summary>
/// Four bits per sample for grayscale images with 16 different levels of gray or paletted images with a palette of 16 colors.
/// </summary>
Bit4 = 4,
/// <summary>
/// Eight bits per sample for grayscale images with 256 different levels of gray or paletted images with a palette of 256 colors.
/// </summary>
Bit8 = 8,
/// <summary>
/// Six bits per sample, each channel has 2 bits.
/// </summary>
Bit6 = 6,
/// <summary>
/// Twelve bits per sample, each channel has 4 bits.
/// </summary>
Bit12 = 12,
/// <summary>
/// 24 bits per sample, each color channel has 8 Bits.
/// </summary>
Bit24 = 24,
/// <summary>
/// Thirty bits per sample, each channel has 10 bits.
/// </summary>
Bit30 = 30,
/// <summary>
/// Forty two bits per sample, each channel has 14 bits.
/// </summary>
Bit42 = 42,
}
}

111
src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs

@ -1,111 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.Formats.Tiff.Constants;
namespace SixLabors.ImageSharp.Formats.Tiff
{
internal static class TiffBitsPerSampleExtensions
{
/// <summary>
/// Gets the bits per channel array for a given BitsPerSample value, e,g, for RGB888: [8, 8, 8]
/// </summary>
/// <param name="tiffBitsPerSample">The tiff bits per sample.</param>
/// <returns>Bits per sample array.</returns>
public static ushort[] Bits(this TiffBitsPerSample tiffBitsPerSample)
{
switch (tiffBitsPerSample)
{
case TiffBitsPerSample.Bit1:
return TiffConstants.BitsPerSample1Bit;
case TiffBitsPerSample.Bit4:
return TiffConstants.BitsPerSample4Bit;
case TiffBitsPerSample.Bit6:
return TiffConstants.BitsPerSampleRgb2Bit;
case TiffBitsPerSample.Bit8:
return TiffConstants.BitsPerSample8Bit;
case TiffBitsPerSample.Bit12:
return TiffConstants.BitsPerSampleRgb4Bit;
case TiffBitsPerSample.Bit24:
return TiffConstants.BitsPerSampleRgb8Bit;
case TiffBitsPerSample.Bit30:
return TiffConstants.BitsPerSampleRgb10Bit;
case TiffBitsPerSample.Bit42:
return TiffConstants.BitsPerSampleRgb14Bit;
default:
return Array.Empty<ushort>();
}
}
/// <summary>
/// Maps an array of bits per sample to a concrete enum value.
/// </summary>
/// <param name="bitsPerSample">The bits per sample array.</param>
/// <returns>TiffBitsPerSample enum value.</returns>
public static TiffBitsPerSample GetBitsPerSample(this ushort[] bitsPerSample)
{
switch (bitsPerSample.Length)
{
case 3:
if (bitsPerSample[2] == TiffConstants.BitsPerSampleRgb14Bit[2] &&
bitsPerSample[1] == TiffConstants.BitsPerSampleRgb14Bit[1] &&
bitsPerSample[0] == TiffConstants.BitsPerSampleRgb14Bit[0])
{
return TiffBitsPerSample.Bit42;
}
if (bitsPerSample[2] == TiffConstants.BitsPerSampleRgb10Bit[2] &&
bitsPerSample[1] == TiffConstants.BitsPerSampleRgb10Bit[1] &&
bitsPerSample[0] == TiffConstants.BitsPerSampleRgb10Bit[0])
{
return TiffBitsPerSample.Bit30;
}
if (bitsPerSample[2] == TiffConstants.BitsPerSampleRgb8Bit[2] &&
bitsPerSample[1] == TiffConstants.BitsPerSampleRgb8Bit[1] &&
bitsPerSample[0] == TiffConstants.BitsPerSampleRgb8Bit[0])
{
return TiffBitsPerSample.Bit24;
}
if (bitsPerSample[2] == TiffConstants.BitsPerSampleRgb4Bit[2] &&
bitsPerSample[1] == TiffConstants.BitsPerSampleRgb4Bit[1] &&
bitsPerSample[0] == TiffConstants.BitsPerSampleRgb4Bit[0])
{
return TiffBitsPerSample.Bit12;
}
if (bitsPerSample[2] == TiffConstants.BitsPerSampleRgb2Bit[2] &&
bitsPerSample[1] == TiffConstants.BitsPerSampleRgb2Bit[1] &&
bitsPerSample[0] == TiffConstants.BitsPerSampleRgb2Bit[0])
{
return TiffBitsPerSample.Bit6;
}
break;
case 1:
if (bitsPerSample[0] == TiffConstants.BitsPerSample1Bit[0])
{
return TiffBitsPerSample.Bit1;
}
if (bitsPerSample[0] == TiffConstants.BitsPerSample4Bit[0])
{
return TiffBitsPerSample.Bit4;
}
if (bitsPerSample[0] == TiffConstants.BitsPerSample8Bit[0])
{
return TiffBitsPerSample.Bit8;
}
break;
}
return TiffBitsPerSample.Unknown;
}
}
}

12
src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs

@ -50,9 +50,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff
}
/// <summary>
/// Gets or sets the number of bits per component of the pixel format used to decode the image.
/// Gets or sets the bits per sample.
/// </summary>
public TiffBitsPerSample BitsPerSample { get; set; }
public ushort[] BitsPerSample { get; set; }
/// <summary>
/// Gets or sets the bits per pixel.
@ -207,7 +207,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
}
else
{
bitsPerPixel = this.BitsPerSample.Bits()[plane];
bitsPerPixel = this.BitsPerSample[plane];
}
int bytesPerRow = ((width * bitsPerPixel) + 7) / 8;
@ -225,7 +225,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
private void DecodeStripsPlanar<TPixel>(ImageFrame<TPixel> frame, int rowsPerStrip, Number[] stripOffsets, Number[] stripByteCounts)
where TPixel : unmanaged, IPixel<TPixel>
{
int stripsPerPixel = this.BitsPerSample.Bits().Length;
int stripsPerPixel = this.BitsPerSample.Length;
int stripsPerPlane = stripOffsets.Length / stripsPerPixel;
int bitsPerPixel = this.BitsPerPixel;
@ -243,7 +243,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.Bits(), this.ColorMap);
RgbPlanarTiffColor<TPixel> colorDecoder = TiffColorDecoderFactory<TPixel>.CreatePlanar(this.ColorType, this.BitsPerSample, this.ColorMap);
for (int i = 0; i < stripsPerPlane; i++)
{
@ -286,7 +286,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
using TiffBaseDecompressor decompressor = TiffDecompressorsFactory.Create(this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, frame.Width, bitsPerPixel, this.Predictor, this.FaxCompressionOptions);
TiffBaseColorDecoder<TPixel> colorDecoder = TiffColorDecoderFactory<TPixel>.Create(this.ColorType, this.BitsPerSample.Bits(), this.ColorMap);
TiffBaseColorDecoder<TPixel> colorDecoder = TiffColorDecoderFactory<TPixel>.Create(this.ColorType, this.BitsPerSample, this.ColorMap);
for (int stripIndex = 0; stripIndex < stripOffsets.Length; stripIndex++)
{

54
src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs

@ -69,7 +69,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
options.Predictor = frameMetadata.Predictor ?? TiffPredictor.None;
options.PhotometricInterpretation = frameMetadata.PhotometricInterpretation ?? TiffPhotometricInterpretation.Rgb;
options.BitsPerPixel = frameMetadata.BitsPerPixel != null ? (int)frameMetadata.BitsPerPixel.Value : (int)TiffBitsPerPixel.Bit24;
options.BitsPerSample = GetBitsPerSample(frameMetadata.BitsPerPixel);
options.BitsPerSample = frameMetadata.BitsPerSample ?? Array.Empty<ushort>();
options.ParseColorType(exifProfile);
options.ParseCompression(frameMetadata.Compression, exifProfile);
@ -99,26 +99,27 @@ namespace SixLabors.ImageSharp.Formats.Tiff
{
case TiffPhotometricInterpretation.WhiteIsZero:
{
if (options.BitsPerSample.Bits().Length != 1)
if (options.BitsPerSample.Length != 1)
{
TiffThrowHelper.ThrowNotSupported("The number of samples in the TIFF BitsPerSample entry is not supported.");
}
switch (options.BitsPerSample)
ushort bitsPerChannel = options.BitsPerSample[0];
switch (bitsPerChannel)
{
case TiffBitsPerSample.Bit8:
case 8:
{
options.ColorType = TiffColorType.WhiteIsZero8;
break;
}
case TiffBitsPerSample.Bit4:
case 4:
{
options.ColorType = TiffColorType.WhiteIsZero4;
break;
}
case TiffBitsPerSample.Bit1:
case 1:
{
options.ColorType = TiffColorType.WhiteIsZero1;
break;
@ -136,26 +137,27 @@ namespace SixLabors.ImageSharp.Formats.Tiff
case TiffPhotometricInterpretation.BlackIsZero:
{
if (options.BitsPerSample.Bits().Length != 1)
if (options.BitsPerSample.Length != 1)
{
TiffThrowHelper.ThrowNotSupported("The number of samples in the TIFF BitsPerSample entry is not supported.");
}
switch (options.BitsPerSample)
ushort bitsPerChannel = options.BitsPerSample[0];
switch (bitsPerChannel)
{
case TiffBitsPerSample.Bit8:
case 8:
{
options.ColorType = TiffColorType.BlackIsZero8;
break;
}
case TiffBitsPerSample.Bit4:
case 4:
{
options.ColorType = TiffColorType.BlackIsZero4;
break;
}
case TiffBitsPerSample.Bit1:
case 1:
{
options.ColorType = TiffColorType.BlackIsZero1;
break;
@ -173,30 +175,31 @@ namespace SixLabors.ImageSharp.Formats.Tiff
case TiffPhotometricInterpretation.Rgb:
{
if (options.BitsPerSample.Bits().Length != 3)
if (options.BitsPerSample.Length != 3)
{
TiffThrowHelper.ThrowNotSupported("The number of samples in the TIFF BitsPerSample entry is not supported.");
}
if (options.PlanarConfiguration == TiffPlanarConfiguration.Chunky)
{
switch (options.BitsPerSample)
ushort bitsPerChannel = options.BitsPerSample[0];
switch (bitsPerChannel)
{
case TiffBitsPerSample.Bit42:
case 14:
options.ColorType = TiffColorType.Rgb141414;
break;
case TiffBitsPerSample.Bit30:
case 10:
options.ColorType = TiffColorType.Rgb101010;
break;
case TiffBitsPerSample.Bit24:
case 8:
options.ColorType = TiffColorType.Rgb888;
break;
case TiffBitsPerSample.Bit12:
case 4:
options.ColorType = TiffColorType.Rgb444;
break;
case TiffBitsPerSample.Bit6:
case 2:
options.ColorType = TiffColorType.Rgb222;
break;
default:
@ -217,7 +220,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
options.ColorMap = exifProfile.GetValue(ExifTag.ColorMap)?.Value;
if (options.ColorMap != null)
{
if (options.BitsPerSample.Bits().Length != 1)
if (options.BitsPerSample.Length != 1)
{
TiffThrowHelper.ThrowNotSupported("The number of samples in the TIFF BitsPerSample entry is not supported.");
}
@ -291,18 +294,5 @@ namespace SixLabors.ImageSharp.Formats.Tiff
}
}
}
private static TiffBitsPerSample GetBitsPerSample(TiffBitsPerPixel? bitsPerPixel) => bitsPerPixel switch
{
TiffBitsPerPixel.Bit1 => TiffBitsPerSample.Bit1,
TiffBitsPerPixel.Bit4 => TiffBitsPerSample.Bit4,
TiffBitsPerPixel.Bit6 => TiffBitsPerSample.Bit6,
TiffBitsPerPixel.Bit8 => TiffBitsPerSample.Bit8,
TiffBitsPerPixel.Bit12 => TiffBitsPerSample.Bit12,
TiffBitsPerPixel.Bit24 => TiffBitsPerSample.Bit24,
TiffBitsPerPixel.Bit30 => TiffBitsPerSample.Bit30,
TiffBitsPerPixel.Bit42 => TiffBitsPerSample.Bit42,
_ => throw new NotSupportedException("The bits per pixel are not supported"),
};
}
}

9
src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs

@ -35,6 +35,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff
/// </summary>
public TiffBitsPerPixel? BitsPerPixel { get; set; }
/// <summary>
/// Gets or sets number of bits per component.
/// </summary>
public ushort[] BitsPerSample { get; set; }
/// <summary>
/// Gets or sets the compression scheme used on the image data.
/// </summary>
@ -72,8 +77,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff
{
if (profile != null)
{
ushort[] bitsPerSample = profile.GetValue(ExifTag.BitsPerSample)?.Value;
meta.BitsPerPixel = BitsPerPixelFromBitsPerSample(bitsPerSample);
meta.BitsPerSample = profile.GetValue(ExifTag.BitsPerSample)?.Value;
meta.BitsPerPixel = BitsPerPixelFromBitsPerSample(meta.BitsPerSample);
meta.Compression = (TiffCompression?)profile.GetValue(ExifTag.Compression)?.Value;
meta.PhotometricInterpretation =
(TiffPhotometricInterpretation?)profile.GetValue(ExifTag.PhotometricInterpretation)?.Value;

2
tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs

@ -66,7 +66,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
{
Assert.NotNull(frameMetaData);
Assert.NotNull(frameMetaData.BitsPerPixel);
Assert.Equal(TiffBitsPerSample.Bit4, (TiffBitsPerSample)frameMetaData.BitsPerPixel);
Assert.Equal(TiffBitsPerPixel.Bit4, frameMetaData.BitsPerPixel);
Assert.Equal(TiffCompression.Lzw, frameMetaData.Compression);
Assert.Equal(TiffPhotometricInterpretation.PaletteColor, frameMetaData.PhotometricInterpretation);
Assert.Equal(TiffPredictor.None, frameMetaData.Predictor);

Loading…
Cancel
Save