📷 A modern, cross-platform, 2D Graphics library for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

281 lines
11 KiB

// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Linq;
using SixLabors.ImageSharp.Formats.Tiff.Compression;
using SixLabors.ImageSharp.Formats.Tiff.Constants;
using SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
namespace SixLabors.ImageSharp.Formats.Tiff
{
/// <summary>
/// The decoder options parser.
/// </summary>
internal static class TiffDecoderOptionsParser
{
private const TiffPlanarConfiguration DefaultPlanarConfiguration = TiffPlanarConfiguration.Chunky;
/// <summary>
/// Determines the TIFF compression and color types, and reads any associated parameters.
/// </summary>
/// <param name="options">The options.</param>
/// <param name="exifProfile">The exif profile of the frame to decode.</param>
/// <param name="frameMetadata">The IFD entries container to read the image format information for current frame.</param>
public static void VerifyAndParse(this TiffDecoderCore options, ExifProfile exifProfile, TiffFrameMetadata frameMetadata)
{
if (exifProfile.GetValue(ExifTag.TileOffsets)?.Value != null)
{
TiffThrowHelper.ThrowNotSupported("Tiled images are not supported.");
}
if (exifProfile.GetValue(ExifTag.ExtraSamples)?.Value != null)
{
TiffThrowHelper.ThrowNotSupported("ExtraSamples is not supported.");
}
TiffFillOrder fillOrder = (TiffFillOrder?)exifProfile.GetValue(ExifTag.FillOrder)?.Value ?? TiffFillOrder.MostSignificantBitFirst;
if (fillOrder != TiffFillOrder.MostSignificantBitFirst)
{
TiffThrowHelper.ThrowNotSupported("The lower-order bits of the byte FillOrder is not supported.");
}
if (frameMetadata.Predictor == TiffPredictor.FloatingPoint)
{
TiffThrowHelper.ThrowNotSupported("TIFF images with FloatingPoint horizontal predictor are not supported.");
}
TiffSampleFormat[] sampleFormat = exifProfile.GetValue(ExifTag.SampleFormat)?.Value?.Select(a => (TiffSampleFormat)a).ToArray();
if (sampleFormat != null)
{
foreach (TiffSampleFormat format in sampleFormat)
{
if (format != TiffSampleFormat.UnsignedInteger)
{
TiffThrowHelper.ThrowNotSupported("ImageSharp only supports the UnsignedInteger SampleFormat.");
}
}
}
if (exifProfile.GetValue(ExifTag.StripRowCounts)?.Value != null)
{
TiffThrowHelper.ThrowNotSupported("Variable-sized strips are not supported.");
}
VerifyRequiredFieldsArePresent(exifProfile, frameMetadata);
options.PlanarConfiguration = (TiffPlanarConfiguration?)exifProfile.GetValue(ExifTag.PlanarConfiguration)?.Value ?? DefaultPlanarConfiguration;
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.ParseColorType(exifProfile);
options.ParseCompression(frameMetadata.Compression, exifProfile);
}
private static void VerifyRequiredFieldsArePresent(ExifProfile exifProfile, TiffFrameMetadata frameMetadata)
{
if (exifProfile.GetValue(ExifTag.StripOffsets) == null)
{
TiffThrowHelper.ThrowImageFormatException("StripOffsets are missing and are required for decoding the TIFF image!");
}
if (exifProfile.GetValue(ExifTag.StripByteCounts) == null)
{
TiffThrowHelper.ThrowImageFormatException("StripByteCounts are missing and are required for decoding the TIFF image!");
}
if (frameMetadata.BitsPerPixel == null)
{
TiffThrowHelper.ThrowNotSupported("The TIFF BitsPerSample entry is missing which is required to decode the image!");
}
}
private static void ParseColorType(this TiffDecoderCore options, ExifProfile exifProfile)
{
switch (options.PhotometricInterpretation)
{
case TiffPhotometricInterpretation.WhiteIsZero:
{
if (options.BitsPerSample.Bits().Length != 1)
{
TiffThrowHelper.ThrowNotSupported("The number of samples in the TIFF BitsPerSample entry is not supported.");
}
switch (options.BitsPerSample)
{
case TiffBitsPerSample.Bit8:
{
options.ColorType = TiffColorType.WhiteIsZero8;
break;
}
case TiffBitsPerSample.Bit4:
{
options.ColorType = TiffColorType.WhiteIsZero4;
break;
}
case TiffBitsPerSample.Bit1:
{
options.ColorType = TiffColorType.WhiteIsZero1;
break;
}
default:
{
options.ColorType = TiffColorType.WhiteIsZero;
break;
}
}
break;
}
case TiffPhotometricInterpretation.BlackIsZero:
{
if (options.BitsPerSample.Bits().Length != 1)
{
TiffThrowHelper.ThrowNotSupported("The number of samples in the TIFF BitsPerSample entry is not supported.");
}
switch (options.BitsPerSample)
{
case TiffBitsPerSample.Bit8:
{
options.ColorType = TiffColorType.BlackIsZero8;
break;
}
case TiffBitsPerSample.Bit4:
{
options.ColorType = TiffColorType.BlackIsZero4;
break;
}
case TiffBitsPerSample.Bit1:
{
options.ColorType = TiffColorType.BlackIsZero1;
break;
}
default:
{
options.ColorType = TiffColorType.BlackIsZero;
break;
}
}
break;
}
case TiffPhotometricInterpretation.Rgb:
{
if (options.BitsPerSample.Bits().Length != 3)
{
TiffThrowHelper.ThrowNotSupported("The number of samples in the TIFF BitsPerSample entry is not supported.");
}
if (options.PlanarConfiguration == TiffPlanarConfiguration.Chunky)
{
options.ColorType = options.BitsPerSample == TiffBitsPerSample.Bit24 ? TiffColorType.Rgb888 : TiffColorType.Rgb;
}
else
{
options.ColorType = TiffColorType.RgbPlanar;
}
break;
}
case TiffPhotometricInterpretation.PaletteColor:
{
options.ColorMap = exifProfile.GetValue(ExifTag.ColorMap)?.Value;
if (options.ColorMap != null)
{
if (options.BitsPerSample.Bits().Length != 1)
{
TiffThrowHelper.ThrowNotSupported("The number of samples in the TIFF BitsPerSample entry is not supported.");
}
options.ColorType = TiffColorType.PaletteColor;
}
else
{
TiffThrowHelper.ThrowNotSupported("The TIFF ColorMap entry is missing for a palette color image.");
}
break;
}
default:
{
TiffThrowHelper.ThrowNotSupported($"The specified TIFF photometric interpretation is not supported: {options.PhotometricInterpretation}");
}
break;
}
}
private static void ParseCompression(this TiffDecoderCore options, TiffCompression? compression, ExifProfile exifProfile)
{
switch (compression)
{
case TiffCompression.None:
{
options.CompressionType = TiffDecoderCompressionType.None;
break;
}
case TiffCompression.PackBits:
{
options.CompressionType = TiffDecoderCompressionType.PackBits;
break;
}
case TiffCompression.Deflate:
case TiffCompression.OldDeflate:
{
options.CompressionType = TiffDecoderCompressionType.Deflate;
break;
}
case TiffCompression.Lzw:
{
options.CompressionType = TiffDecoderCompressionType.Lzw;
break;
}
case TiffCompression.CcittGroup3Fax:
{
options.CompressionType = TiffDecoderCompressionType.T4;
options.FaxCompressionOptions = exifProfile.GetValue(ExifTag.T4Options) != null ? (FaxCompressionOptions)exifProfile.GetValue(ExifTag.T4Options).Value : FaxCompressionOptions.None;
break;
}
case TiffCompression.Ccitt1D:
{
options.CompressionType = TiffDecoderCompressionType.HuffmanRle;
break;
}
default:
{
TiffThrowHelper.ThrowNotSupported($"The specified TIFF compression format {compression} is not supported");
break;
}
}
}
private static TiffBitsPerSample GetBitsPerSample(TiffBitsPerPixel? bitsPerPixel) => bitsPerPixel switch
{
TiffBitsPerPixel.Bit1 => TiffBitsPerSample.Bit1,
TiffBitsPerPixel.Bit4 => TiffBitsPerSample.Bit4,
TiffBitsPerPixel.Bit8 => TiffBitsPerSample.Bit8,
TiffBitsPerPixel.Bit24 => TiffBitsPerSample.Bit24,
_ => TiffBitsPerSample.Bit24,
};
}
}