diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs index 6fe412b925..5733bada97 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs @@ -85,16 +85,46 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Constants /// public static readonly ushort[] BitsPerSample1Bit = { 1 }; + /// + /// The bits per sample for images with a 2 color palette. + /// + public static readonly ushort[] BitsPerSample2Bit = { 2 }; + /// /// The bits per sample for images with a 4 color palette. /// public static readonly ushort[] BitsPerSample4Bit = { 4 }; + /// + /// The bits per sample for 6 bit gray images. + /// + public static readonly ushort[] BitsPerSample6Bit = { 6 }; + /// /// The bits per sample for 8 bit images. /// public static readonly ushort[] BitsPerSample8Bit = { 8 }; + /// + /// The bits per sample for 10 bit gray images. + /// + public static readonly ushort[] BitsPerSample10Bit = { 10 }; + + /// + /// The bits per sample for 12 bit gray images. + /// + public static readonly ushort[] BitsPerSample12Bit = { 12 }; + + /// + /// The bits per sample for 14 bit gray images. + /// + public static readonly ushort[] BitsPerSample14Bit = { 14 }; + + /// + /// The bits per sample for 16 bit gray images. + /// + public static readonly ushort[] BitsPerSample16Bit = { 16 }; + /// /// The bits per sample for color images with 2 bits for each color channel. /// @@ -115,11 +145,21 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Constants /// public static readonly ushort[] BitsPerSampleRgb10Bit = { 10, 10, 10 }; + /// + /// The bits per sample for color images with 12 bits for each color channel. + /// + public static readonly ushort[] BitsPerSampleRgb12Bit = { 12, 12, 12 }; + /// /// The bits per sample for color images with 14 bits for each color channel. /// public static readonly ushort[] BitsPerSampleRgb14Bit = { 14, 14, 14 }; + /// + /// The bits per sample for color images with 14 bits for each color channel. + /// + public static readonly ushort[] BitsPerSampleRgb16Bit = { 16, 16, 16 }; + /// /// The list of mimetypes that equate to a tiff. /// diff --git a/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs b/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs new file mode 100644 index 0000000000..71f6b5bf9f --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs @@ -0,0 +1,96 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Tiff +{ + /// + /// The number of bits per component. + /// + public enum TiffBitsPerSample + { + /// + /// The bits per samples is not known. + /// + Unknown = 0, + + /// + /// One bit per sample for bicolor images. + /// + Bit1, + + /// + /// Two bits per sample for grayscale images with 4 different levels of gray or paletted images with a palette of 4 colors. + /// + Bit2, + + /// + /// Four bits per sample for grayscale images with 16 different levels of gray or paletted images with a palette of 16 colors. + /// + Bit4, + + /// + /// Six bits per sample for grayscale images. + /// + Bit6, + + /// + /// Eight bits per sample for grayscale images with 256 different levels of gray or paletted images with a palette of 256 colors. + /// + Bit8, + + /// + /// Ten bits per sample for grayscale images. + /// + Bit10, + + /// + /// Twelve bits per sample for grayscale images. + /// + Bit12, + + /// + /// Fourteen bits per sample for grayscale images. + /// + Bit14, + + /// + /// Sixteen bits per sample for grayscale images. + /// + Bit16, + + /// + /// 6 bits per sample, each channel has 2 bits. + /// + Rgb222, + + /// + /// Twelve bits per sample, each channel has 4 bits. + /// + Rgb444, + + /// + /// 24 bits per sample, each color channel has 8 Bits. + /// + Rgb888, + + /// + /// Thirty bits per sample, each channel has 10 bits. + /// + Rgb101010, + + /// + /// Thirty six bits per sample, each channel has 12 bits. + /// + Rgb121212, + + /// + /// Forty two bits per sample, each channel has 14 bits. + /// + Rgb141414, + + /// + /// Forty eight bits per sample, each channel has 16 bits. + /// + Rgb161616, + } +} diff --git a/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs b/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs new file mode 100644 index 0000000000..5ec1331b31 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs @@ -0,0 +1,170 @@ +// 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 + { + /// + /// Gets the bits per channel array for a given BitsPerSample value, e,g, for RGB888: [8, 8, 8] + /// + /// The tiff bits per sample. + /// Bits per sample array. + public static ushort[] BitsPerChannel(this TiffBitsPerSample tiffBitsPerSample) + { + switch (tiffBitsPerSample) + { + case TiffBitsPerSample.Bit1: + return TiffConstants.BitsPerSample1Bit; + case TiffBitsPerSample.Bit2: + return TiffConstants.BitsPerSample2Bit; + case TiffBitsPerSample.Bit4: + return TiffConstants.BitsPerSample4Bit; + case TiffBitsPerSample.Bit6: + return TiffConstants.BitsPerSample6Bit; + case TiffBitsPerSample.Bit8: + return TiffConstants.BitsPerSample8Bit; + case TiffBitsPerSample.Bit10: + return TiffConstants.BitsPerSample10Bit; + case TiffBitsPerSample.Bit12: + return TiffConstants.BitsPerSample12Bit; + case TiffBitsPerSample.Bit14: + return TiffConstants.BitsPerSample14Bit; + case TiffBitsPerSample.Bit16: + return TiffConstants.BitsPerSample16Bit; + case TiffBitsPerSample.Rgb222: + return TiffConstants.BitsPerSampleRgb2Bit; + case TiffBitsPerSample.Rgb444: + return TiffConstants.BitsPerSampleRgb4Bit; + case TiffBitsPerSample.Rgb888: + return TiffConstants.BitsPerSampleRgb8Bit; + case TiffBitsPerSample.Rgb101010: + return TiffConstants.BitsPerSampleRgb10Bit; + case TiffBitsPerSample.Rgb121212: + return TiffConstants.BitsPerSampleRgb12Bit; + case TiffBitsPerSample.Rgb141414: + return TiffConstants.BitsPerSampleRgb14Bit; + case TiffBitsPerSample.Rgb161616: + return TiffConstants.BitsPerSampleRgb16Bit; + default: + return Array.Empty(); + } + } + + /// + /// Maps an array of bits per sample to a concrete enum value. + /// + /// The bits per sample array. + /// TiffBitsPerSample enum value. + public static TiffBitsPerSample GetBitsPerSample(this ushort[] bitsPerSample) + { + switch (bitsPerSample.Length) + { + case 3: + if (bitsPerSample[2] == TiffConstants.BitsPerSampleRgb16Bit[2] && + bitsPerSample[1] == TiffConstants.BitsPerSampleRgb16Bit[1] && + bitsPerSample[0] == TiffConstants.BitsPerSampleRgb16Bit[0]) + { + return TiffBitsPerSample.Rgb161616; + } + + if (bitsPerSample[2] == TiffConstants.BitsPerSampleRgb14Bit[2] && + bitsPerSample[1] == TiffConstants.BitsPerSampleRgb14Bit[1] && + bitsPerSample[0] == TiffConstants.BitsPerSampleRgb14Bit[0]) + { + return TiffBitsPerSample.Rgb141414; + } + + if (bitsPerSample[2] == TiffConstants.BitsPerSampleRgb12Bit[2] && + bitsPerSample[1] == TiffConstants.BitsPerSampleRgb12Bit[1] && + bitsPerSample[0] == TiffConstants.BitsPerSampleRgb12Bit[0]) + { + return TiffBitsPerSample.Rgb121212; + } + + if (bitsPerSample[2] == TiffConstants.BitsPerSampleRgb10Bit[2] && + bitsPerSample[1] == TiffConstants.BitsPerSampleRgb10Bit[1] && + bitsPerSample[0] == TiffConstants.BitsPerSampleRgb10Bit[0]) + { + return TiffBitsPerSample.Rgb101010; + } + + if (bitsPerSample[2] == TiffConstants.BitsPerSampleRgb8Bit[2] && + bitsPerSample[1] == TiffConstants.BitsPerSampleRgb8Bit[1] && + bitsPerSample[0] == TiffConstants.BitsPerSampleRgb8Bit[0]) + { + return TiffBitsPerSample.Rgb888; + } + + if (bitsPerSample[2] == TiffConstants.BitsPerSampleRgb4Bit[2] && + bitsPerSample[1] == TiffConstants.BitsPerSampleRgb4Bit[1] && + bitsPerSample[0] == TiffConstants.BitsPerSampleRgb4Bit[0]) + { + return TiffBitsPerSample.Rgb444; + } + + if (bitsPerSample[2] == TiffConstants.BitsPerSampleRgb2Bit[2] && + bitsPerSample[1] == TiffConstants.BitsPerSampleRgb2Bit[1] && + bitsPerSample[0] == TiffConstants.BitsPerSampleRgb2Bit[0]) + { + return TiffBitsPerSample.Rgb222; + } + + break; + + case 1: + if (bitsPerSample[0] == TiffConstants.BitsPerSample1Bit[0]) + { + return TiffBitsPerSample.Bit1; + } + + if (bitsPerSample[0] == TiffConstants.BitsPerSample2Bit[0]) + { + return TiffBitsPerSample.Bit2; + } + + if (bitsPerSample[0] == TiffConstants.BitsPerSample4Bit[0]) + { + return TiffBitsPerSample.Bit4; + } + + if (bitsPerSample[0] == TiffConstants.BitsPerSample6Bit[0]) + { + return TiffBitsPerSample.Bit6; + } + + if (bitsPerSample[0] == TiffConstants.BitsPerSample8Bit[0]) + { + return TiffBitsPerSample.Bit8; + } + + if (bitsPerSample[0] == TiffConstants.BitsPerSample10Bit[0]) + { + return TiffBitsPerSample.Bit10; + } + + if (bitsPerSample[0] == TiffConstants.BitsPerSample12Bit[0]) + { + return TiffBitsPerSample.Bit12; + } + + if (bitsPerSample[0] == TiffConstants.BitsPerSample14Bit[0]) + { + return TiffBitsPerSample.Bit14; + } + + if (bitsPerSample[0] == TiffConstants.BitsPerSample16Bit[0]) + { + return TiffBitsPerSample.Bit16; + } + + break; + } + + return TiffBitsPerSample.Unknown; + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs index 014dd55380..1efc826027 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs +++ b/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 = frameMetadata.BitsPerSample ?? Array.Empty(); + options.BitsPerSample = frameMetadata.BitsPerSample != null ? frameMetadata.BitsPerSample?.BitsPerChannel() : Array.Empty(); options.ParseColorType(exifProfile); options.ParseCompression(frameMetadata.Compression, exifProfile); diff --git a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs index ef7573d3e0..62e9fb4e21 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs @@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Gets or sets number of bits per component. /// - public ushort[] BitsPerSample { get; set; } + public TiffBitsPerSample? BitsPerSample { get; set; } /// /// Gets or sets the compression scheme used on the image data. @@ -77,11 +77,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff { if (profile != null) { - meta.BitsPerSample = profile.GetValue(ExifTag.BitsPerSample)?.Value; - meta.BitsPerPixel = BitsPerPixelFromBitsPerSample(meta.BitsPerSample); + meta.BitsPerSample = profile.GetValue(ExifTag.BitsPerSample) != null ? profile.GetValue(ExifTag.BitsPerSample)?.Value.GetBitsPerSample() : null; + meta.BitsPerPixel = BitsPerPixelFromBitsPerSample(meta.BitsPerSample?.BitsPerChannel()); meta.Compression = (TiffCompression?)profile.GetValue(ExifTag.Compression)?.Value; - meta.PhotometricInterpretation = - (TiffPhotometricInterpretation?)profile.GetValue(ExifTag.PhotometricInterpretation)?.Value; + meta.PhotometricInterpretation = (TiffPhotometricInterpretation?)profile.GetValue(ExifTag.PhotometricInterpretation)?.Value; meta.Predictor = (TiffPredictor?)profile.GetValue(ExifTag.Predictor)?.Value; profile.RemoveValue(ExifTag.BitsPerSample); diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 28ef20cf4c..7eca4795df 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -558,8 +558,6 @@ namespace SixLabors.ImageSharp.Tests public const string RgbPalette = "Tiff/rgb_palette.tiff"; public const string Rgb4BitPalette = "Tiff/bike_colorpalette_4bit.tiff"; public const string RgbPaletteDeflate = "Tiff/rgb_palette_deflate.tiff"; - public const string Flower4BitPalette = "Tiff/flower-palette-04.tiff"; - public const string Flower4BitPaletteGray = "Tiff/flower-minisblack-04.tiff"; public const string FlowerRgb161616Contiguous = "Tiff/flower-rgb-contig-16.tiff"; public const string FlowerRgb161616Planar = "Tiff/flower-rgb-planar-16.tiff"; public const string FlowerRgb141414Contiguous = "Tiff/flower-rgb-contig-14.tiff"; @@ -573,6 +571,8 @@ namespace SixLabors.ImageSharp.Tests public const string FlowerRgb222Planar = "Tiff/flower-rgb-planar-02.tiff"; public const string Flower2BitGray = "Tiff/flower-minisblack-02.tiff"; public const string Flower2BitPalette = "Tiff/flower-palette-02.tiff"; + public const string Flower4BitPalette = "Tiff/flower-palette-04.tiff"; + public const string Flower4BitPaletteGray = "Tiff/flower-minisblack-04.tiff"; public const string Flower6BitGray = "Tiff/flower-minisblack-06.tiff"; public const string Flower8BitGray = "Tiff/flower-minisblack-08.tiff"; public const string Flower10BitGray = "Tiff/flower-minisblack-10.tiff";