diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index d476f9bb7c..b977c25365 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -150,10 +150,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff framesMetadata.Add(meta); } - ImageMetadata metadata = TiffDecoderMetadataCreator.Create(framesMetadata, reader.ByteOrder); - TiffFrameMetadata root = framesMetadata[0]; ExifProfile rootFrameExifProfile = directories.First(); + ImageMetadata metadata = TiffDecoderMetadataCreator.Create(framesMetadata, reader.ByteOrder, rootFrameExifProfile); int width = GetImageWidth(rootFrameExifProfile); int height = GetImageHeight(rootFrameExifProfile); @@ -177,15 +176,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff new ImageFrameMetadata { ExifProfile = tags, XmpProfile = tags.GetValue(ExifTag.XMP)?.Value }; tiffFrameMetaData = new TiffFrameMetadata(tags); - this.VerifyAndParse(tiffFrameMetaData); + this.VerifyAndParse(tags, tiffFrameMetaData); int width = GetImageWidth(tags); int height = GetImageHeight(tags); var frame = new ImageFrame(this.Configuration, width, height, imageFrameMetaData); - int rowsPerStrip = (int)tiffFrameMetaData.RowsPerStrip; - Number[] stripOffsets = tiffFrameMetaData.StripOffsets; - Number[] stripByteCounts = tiffFrameMetaData.StripByteCounts; + int rowsPerStrip = tags.GetValue(ExifTag.RowsPerStrip) != null ? (int)tags.GetValue(ExifTag.RowsPerStrip).Value : TiffConstants.RowsPerStripInfinity; + Number[] stripOffsets = tags.GetValue(ExifTag.StripOffsets)?.Value; + Number[] stripByteCounts = tags.GetValue(ExifTag.StripByteCounts)?.Value; if (this.PlanarConfiguration == TiffPlanarConfiguration.Planar) { diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs index 3264d2cba3..95d931b0e2 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Icc; @@ -28,8 +29,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff } var imageMetaData = new ImageMetadata(); - TiffFrameMetadata rootFrameMetadata = framesMetaData[0]; - SetResolution(imageMetaData, rootFrameMetadata); + ExifProfile exifProfileRootFrame = frames[0].Metadata.ExifProfile; + + SetResolution(imageMetaData, exifProfileRootFrame); TiffMetadata tiffMetadata = imageMetaData.GetTiffMetadata(); tiffMetadata.ByteOrder = byteOrder; @@ -56,7 +58,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff return imageMetaData; } - public static ImageMetadata Create(List framesMetaData, ByteOrder byteOrder) + public static ImageMetadata Create(List framesMetaData, ByteOrder byteOrder, ExifProfile exifProfile) { if (framesMetaData.Count < 1) { @@ -64,8 +66,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff } var imageMetaData = new ImageMetadata(); - TiffFrameMetadata rootFrameMetadata = framesMetaData[0]; - SetResolution(imageMetaData, rootFrameMetadata); + SetResolution(imageMetaData, exifProfile); TiffMetadata tiffMetadata = imageMetaData.GetTiffMetadata(); tiffMetadata.ByteOrder = byteOrder; @@ -73,17 +74,19 @@ namespace SixLabors.ImageSharp.Formats.Tiff return imageMetaData; } - private static void SetResolution(ImageMetadata imageMetaData, TiffFrameMetadata rootFrameMetadata) + private static void SetResolution(ImageMetadata imageMetaData, ExifProfile exifProfile) { - imageMetaData.ResolutionUnits = rootFrameMetadata.ResolutionUnit; - if (rootFrameMetadata.HorizontalResolution != null) + imageMetaData.ResolutionUnits = exifProfile != null ? UnitConverter.ExifProfileToResolutionUnit(exifProfile) : PixelResolutionUnit.PixelsPerInch; + double? horizontalResolution = exifProfile?.GetValue(ExifTag.XResolution)?.Value.ToDouble(); + if (horizontalResolution != null) { - imageMetaData.HorizontalResolution = rootFrameMetadata.HorizontalResolution.Value; + imageMetaData.HorizontalResolution = horizontalResolution.Value; } - if (rootFrameMetadata.VerticalResolution != null) + double? verticalResolution = exifProfile?.GetValue(ExifTag.YResolution)?.Value.ToDouble(); + if (verticalResolution != null) { - imageMetaData.VerticalResolution = rootFrameMetadata.VerticalResolution.Value; + imageMetaData.VerticalResolution = verticalResolution.Value; } } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs index 45a55b274b..9ce9ce8e69 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs @@ -1,9 +1,11 @@ // 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 { @@ -12,36 +14,44 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// internal static class TiffDecoderOptionsParser { + private const TiffPredictor DefaultPredictor = TiffPredictor.None; + + private const TiffPlanarConfiguration DefaultPlanarConfiguration = TiffPlanarConfiguration.Chunky; + /// /// Determines the TIFF compression and color types, and reads any associated parameters. /// /// The options. + /// The exif profile of the frame to decode. /// The IFD entries container to read the image format information for. - public static void VerifyAndParse(this TiffDecoderCore options, TiffFrameMetadata entries) + public static void VerifyAndParse(this TiffDecoderCore options, ExifProfile exifProfile, TiffFrameMetadata entries) { - if (entries.TileOffsets != null) + if (exifProfile.GetValue(ExifTag.TileOffsets)?.Value != null) { TiffThrowHelper.ThrowNotSupported("Tiled images are not supported."); } - if (entries.ExtraSamples != null) + if (exifProfile.GetValue(ExifTag.ExtraSamples)?.Value != null) { TiffThrowHelper.ThrowNotSupported("ExtraSamples is not supported."); } - if (entries.FillOrder != TiffFillOrder.MostSignificantBitFirst) + 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 (entries.Predictor == TiffPredictor.FloatingPoint) + TiffPredictor predictor = (TiffPredictor?)exifProfile.GetValue(ExifTag.Predictor)?.Value ?? DefaultPredictor; + if (predictor == TiffPredictor.FloatingPoint) { TiffThrowHelper.ThrowNotSupported("TIFF images with FloatingPoint horizontal predictor are not supported."); } - if (entries.SampleFormat != null) + TiffSampleFormat[] sampleFormat = exifProfile.GetValue(ExifTag.SampleFormat)?.Value?.Select(a => (TiffSampleFormat)a).ToArray(); + if (sampleFormat != null) { - foreach (TiffSampleFormat format in entries.SampleFormat) + foreach (TiffSampleFormat format in sampleFormat) { if (format != TiffSampleFormat.UnsignedInteger) { @@ -50,24 +60,43 @@ namespace SixLabors.ImageSharp.Formats.Tiff } } - if (entries.StripRowCounts != null) + if (exifProfile.GetValue(ExifTag.StripRowCounts)?.Value != null) { TiffThrowHelper.ThrowNotSupported("Variable-sized strips are not supported."); } - entries.VerifyRequiredFieldsArePresent(); + VerifyRequiredFieldsArePresent(exifProfile); - options.PlanarConfiguration = entries.PlanarConfiguration; - options.Predictor = entries.Predictor; - options.PhotometricInterpretation = entries.PhotometricInterpretation; + options.PlanarConfiguration = (TiffPlanarConfiguration?)exifProfile.GetValue(ExifTag.PlanarConfiguration)?.Value ?? DefaultPlanarConfiguration; + options.Predictor = predictor; + options.PhotometricInterpretation = exifProfile.GetValue(ExifTag.PhotometricInterpretation) != null ? + (TiffPhotometricInterpretation)exifProfile.GetValue(ExifTag.PhotometricInterpretation).Value : TiffPhotometricInterpretation.WhiteIsZero; options.BitsPerSample = entries.BitsPerSample.GetValueOrDefault(); options.BitsPerPixel = entries.BitsPerSample.GetValueOrDefault().BitsPerPixel(); - ParseColorType(options, entries); - ParseCompression(options, entries); + ParseColorType(options, exifProfile); + ParseCompression(options, exifProfile); + } + + private static void VerifyRequiredFieldsArePresent(ExifProfile exifProfile) + { + 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 (exifProfile.GetValue(ExifTag.BitsPerSample) == null) + { + TiffThrowHelper.ThrowNotSupported("The TIFF BitsPerSample entry is missing which is required to decode the image!"); + } } - private static void ParseColorType(this TiffDecoderCore options, TiffFrameMetadata entries) + private static void ParseColorType(this TiffDecoderCore options, ExifProfile exifProfile) { switch (options.PhotometricInterpretation) { @@ -166,7 +195,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff case TiffPhotometricInterpretation.PaletteColor: { - options.ColorMap = entries.ColorMap; + options.ColorMap = exifProfile.GetValue(ExifTag.ColorMap)?.Value; if (options.ColorMap != null) { if (options.BitsPerSample.Bits().Length != 1) @@ -193,9 +222,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff } } - private static void ParseCompression(this TiffDecoderCore options, TiffFrameMetadata tiffFrameMetaData) + private static void ParseCompression(this TiffDecoderCore options, ExifProfile exifProfile) { - TiffCompression compression = tiffFrameMetaData.Compression; + TiffCompression compression = exifProfile.GetValue(ExifTag.Compression) != null ? (TiffCompression)exifProfile.GetValue(ExifTag.Compression).Value : TiffCompression.None; switch (compression) { case TiffCompression.None: @@ -226,7 +255,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff case TiffCompression.CcittGroup3Fax: { options.CompressionType = TiffDecoderCompressionType.T4; - options.FaxCompressionOptions = tiffFrameMetaData.FaxCompressionOptions; + options.FaxCompressionOptions = exifProfile.GetValue(ExifTag.T4Options) != null ? (FaxCompressionOptions)exifProfile.GetValue(ExifTag.T4Options).Value : FaxCompressionOptions.None; break; } diff --git a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs index 61cce1573b..7376b114b6 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs @@ -1,12 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Linq; -using SixLabors.ImageSharp.Common.Helpers; -using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Formats.Tiff.Constants; -using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; namespace SixLabors.ImageSharp.Formats.Tiff @@ -16,10 +11,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// internal class TiffFrameMetadata : IDeepCloneable { - private const TiffPlanarConfiguration DefaultPlanarConfiguration = TiffPlanarConfiguration.Chunky; - - private const TiffPredictor DefaultPredictor = TiffPredictor.None; - /// /// Initializes a new instance of the class. /// @@ -34,17 +25,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff public TiffFrameMetadata(ExifProfile frameTags) => this.Initialize(frameTags ?? new ExifProfile()); /// - /// Gets or sets a general indication of the kind of data contained in this subfile. - /// - public TiffNewSubfileType? SubfileType { get; set; } - - /// - /// Gets or sets a general indication of the kind of data contained in this subfile. - /// - public TiffSubfileType? OldSubfileType { get; set; } - - /// - /// Gets or sets the number of bits per component. + /// Gets or sets the number of bits per component. /// public TiffBitsPerSample? BitsPerSample { get; set; } @@ -53,131 +34,19 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// public TiffBitsPerPixel BitsPerPixel { get; set; } - /// - /// Gets or sets the compression scheme used on the image data. - /// - public TiffCompression Compression { get; set; } - - /// - /// Gets or sets the fax compression options. - /// - public FaxCompressionOptions FaxCompressionOptions { get; set; } - - /// - /// Gets or sets the color space of the image data. - /// - public TiffPhotometricInterpretation PhotometricInterpretation { get; set; } - - /// - /// Gets or sets the logical order of bits within a byte. - /// - internal TiffFillOrder FillOrder { get; set; } - - /// - /// Gets or sets for each strip, the byte offset of that strip. - /// - public Number[] StripOffsets { get; set; } - - /// - /// Gets or sets the strip row counts. - /// - public uint[] StripRowCounts { get; set; } - - /// - /// Gets or sets the number of components per pixel. - /// - public ushort? SamplesPerPixel { get; set; } - - /// - /// Gets or sets the number of rows per strip. - /// - public Number RowsPerStrip { get; set; } - - /// - /// Gets or sets for each strip, the number of bytes in the strip after compression. - /// - public Number[] StripByteCounts { get; set; } - - /// - /// Gets or sets the resolution of the image in x-direction. - /// - public double? HorizontalResolution { get; set; } - - /// - /// Gets or sets the resolution of the image in y-direction. - /// - public double? VerticalResolution { get; set; } - - /// - /// Gets or sets how the components of each pixel are stored. - /// - public TiffPlanarConfiguration PlanarConfiguration { get; set; } - - /// - /// Gets or sets the unit of measurement for XResolution and YResolution. - /// - public PixelResolutionUnit ResolutionUnit { get; set; } - - /// - /// Gets or sets a color map for palette color images. - /// - public ushort[] ColorMap { get; set; } - - /// - /// Gets or sets the description of extra components. - /// - public ushort[] ExtraSamples { get; set; } - - /// - /// Gets or sets the tile offsets. - /// - public uint[] TileOffsets { get; set; } - - /// - /// Gets or sets a mathematical operator that is applied to the image data before an encoding scheme is applied. - /// - public TiffPredictor Predictor { get; set; } - - /// - /// Gets or sets the specifies how to interpret each data sample in a pixel. - /// - public TiffSampleFormat[] SampleFormat { get; set; } - /// /// Initializes a new instance of the class with a given ExifProfile. /// /// The Tiff frame directory tags. public void Initialize(ExifProfile frameTags) { - this.FillOrder = (TiffFillOrder?)frameTags.GetValue(ExifTag.FillOrder)?.Value ?? TiffFillOrder.MostSignificantBitFirst; - this.Compression = frameTags.GetValue(ExifTag.Compression) != null ? (TiffCompression)frameTags.GetValue(ExifTag.Compression).Value : TiffCompression.None; - this.FaxCompressionOptions = frameTags.GetValue(ExifTag.T4Options) != null ? (FaxCompressionOptions)frameTags.GetValue(ExifTag.T4Options).Value : FaxCompressionOptions.None; - this.SubfileType = (TiffNewSubfileType?)frameTags.GetValue(ExifTag.SubfileType)?.Value ?? TiffNewSubfileType.FullImage; - this.OldSubfileType = (TiffSubfileType?)frameTags.GetValue(ExifTag.OldSubfileType)?.Value; - this.HorizontalResolution = frameTags.GetValue(ExifTag.XResolution)?.Value.ToDouble(); - this.VerticalResolution = frameTags.GetValue(ExifTag.YResolution)?.Value.ToDouble(); - this.ResolutionUnit = UnitConverter.ExifProfileToResolutionUnit(frameTags); - this.PlanarConfiguration = (TiffPlanarConfiguration?)frameTags.GetValue(ExifTag.PlanarConfiguration)?.Value ?? DefaultPlanarConfiguration; - this.ColorMap = frameTags.GetValue(ExifTag.ColorMap)?.Value; - this.ExtraSamples = frameTags.GetValue(ExifTag.ExtraSamples)?.Value; - this.Predictor = (TiffPredictor?)frameTags.GetValue(ExifTag.Predictor)?.Value ?? DefaultPredictor; - this.SampleFormat = frameTags.GetValue(ExifTag.SampleFormat)?.Value?.Select(a => (TiffSampleFormat)a).ToArray(); - this.SamplesPerPixel = frameTags.GetValue(ExifTag.SamplesPerPixel)?.Value; - this.StripRowCounts = frameTags.GetValue(ExifTag.StripRowCounts)?.Value; - this.RowsPerStrip = frameTags.GetValue(ExifTag.RowsPerStrip) != null ? frameTags.GetValue(ExifTag.RowsPerStrip).Value : TiffConstants.RowsPerStripInfinity; - this.TileOffsets = frameTags.GetValue(ExifTag.TileOffsets)?.Value; - - this.PhotometricInterpretation = frameTags.GetValue(ExifTag.PhotometricInterpretation) != null ? + TiffPhotometricInterpretation photometricInterpretation = frameTags.GetValue(ExifTag.PhotometricInterpretation) != null ? (TiffPhotometricInterpretation)frameTags.GetValue(ExifTag.PhotometricInterpretation).Value : TiffPhotometricInterpretation.WhiteIsZero; - // Required Fields for decoding the image. - this.StripOffsets = frameTags.GetValue(ExifTag.StripOffsets)?.Value; - this.StripByteCounts = frameTags.GetValue(ExifTag.StripByteCounts)?.Value; - ushort[] bits = frameTags.GetValue(ExifTag.BitsPerSample)?.Value; if (bits == null) { - if (this.PhotometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero || this.PhotometricInterpretation == TiffPhotometricInterpretation.BlackIsZero) + if (photometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero || photometricInterpretation == TiffPhotometricInterpretation.BlackIsZero) { this.BitsPerSample = TiffBitsPerSample.Bit1; } @@ -192,97 +61,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff this.BitsPerPixel = (TiffBitsPerPixel)this.BitsPerSample.GetValueOrDefault().BitsPerPixel(); } - /// - /// Verifies that the required fields for decoding an image are present. - /// If not, a ImageFormatException will be thrown. - /// - public void VerifyRequiredFieldsArePresent() - { - if (this.StripOffsets == null) - { - TiffThrowHelper.ThrowImageFormatException("StripOffsets are missing and are required for decoding the TIFF image!"); - } - - if (this.StripByteCounts == null) - { - TiffThrowHelper.ThrowImageFormatException("StripByteCounts are missing and are required for decoding the TIFF image!"); - } - - if (this.BitsPerSample == null) - { - TiffThrowHelper.ThrowNotSupported("The TIFF BitsPerSample entry is missing which is required to decode the image!"); - } - } - /// public IDeepCloneable DeepClone() { var clone = new TiffFrameMetadata { - FillOrder = this.FillOrder, - Compression = this.Compression, - FaxCompressionOptions = this.FaxCompressionOptions, - SubfileType = this.SubfileType ?? TiffNewSubfileType.FullImage, - OldSubfileType = this.OldSubfileType ?? TiffSubfileType.FullImage, - HorizontalResolution = this.HorizontalResolution ?? ImageMetadata.DefaultHorizontalResolution, - VerticalResolution = this.VerticalResolution ?? ImageMetadata.DefaultVerticalResolution, - ResolutionUnit = this.ResolutionUnit, - PlanarConfiguration = this.PlanarConfiguration + BitsPerSample = this.BitsPerSample, + BitsPerPixel = this.BitsPerPixel }; - if (this.ColorMap != null) - { - clone.ColorMap = new ushort[this.ColorMap.Length]; - this.ColorMap.AsSpan().CopyTo(clone.ColorMap); - } - - if (this.ExtraSamples != null) - { - clone.ExtraSamples = new ushort[this.ExtraSamples.Length]; - this.ExtraSamples.AsSpan().CopyTo(clone.ExtraSamples); - } - - clone.Predictor = this.Predictor; - - if (this.SampleFormat != null) - { - clone.SampleFormat = new TiffSampleFormat[this.SampleFormat.Length]; - this.SampleFormat.AsSpan().CopyTo(clone.SampleFormat); - } - - clone.SamplesPerPixel = this.SamplesPerPixel; - - if (this.StripRowCounts != null) - { - clone.StripRowCounts = new uint[this.StripRowCounts.Length]; - this.StripRowCounts.AsSpan().CopyTo(clone.StripRowCounts); - } - - clone.RowsPerStrip = this.RowsPerStrip; - - if (this.TileOffsets != null) - { - clone.TileOffsets = new uint[this.TileOffsets.Length]; - this.TileOffsets.AsSpan().CopyTo(clone.TileOffsets); - } - - clone.PhotometricInterpretation = this.PhotometricInterpretation; - - if (this.StripOffsets != null) - { - clone.StripOffsets = new Number[this.StripOffsets.Length]; - this.StripOffsets.AsSpan().CopyTo(clone.StripOffsets); - } - - if (this.StripByteCounts != null) - { - clone.StripByteCounts = new Number[this.StripByteCounts.Length]; - this.StripByteCounts.AsSpan().CopyTo(clone.StripByteCounts); - } - - clone.BitsPerSample = this.BitsPerSample; - clone.BitsPerPixel = this.BitsPerPixel; - return clone; } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 85229a470e..2fdc663478 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -340,6 +340,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff using Image input = provider.GetImage(); using var memStream = new MemoryStream(); ExifProfile exifProfileInput = input.Frames.RootFrame.Metadata.ExifProfile; + var inputCompression = (TiffCompression)exifProfileInput.GetValue(ExifTag.Compression).Value; var inputMeta = new TiffFrameMetadata(exifProfileInput); // act @@ -352,11 +353,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff var outputMeta = new TiffFrameMetadata(exifProfileOutput); ImageFrame rootFrame = output.Frames.RootFrame; - Assert.True(output.Height > (int)outputMeta.RowsPerStrip); - Assert.True(outputMeta.StripOffsets.Length > 1); - Assert.True(outputMeta.StripByteCounts.Length > 1); + Number rowsPerStrip = exifProfileOutput.GetValue(ExifTag.RowsPerStrip) != null ? exifProfileOutput.GetValue(ExifTag.RowsPerStrip).Value : TiffConstants.RowsPerStripInfinity; + Assert.True(output.Height > (int)rowsPerStrip); + Assert.True(exifProfileOutput.GetValue(ExifTag.StripOffsets)?.Value.Length > 1); + Number[] stripByteCounts = exifProfileOutput.GetValue(ExifTag.StripByteCounts)?.Value; + Assert.True(stripByteCounts.Length > 1); - foreach (Number sz in outputMeta.StripByteCounts) + foreach (Number sz in stripByteCounts) { Assert.True((uint)sz <= TiffConstants.DefaultStripSize); } @@ -364,10 +367,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // For uncompressed more accurate test. if (compression == TiffCompression.None) { - for (int i = 0; i < outputMeta.StripByteCounts.Length - 1; i++) + for (int i = 0; i < stripByteCounts.Length - 1; i++) { // The difference must be less than one row. - int stripBytes = (int)outputMeta.StripByteCounts[i]; + int stripBytes = (int)stripByteCounts[i]; int widthBytes = ((int)outputMeta.BitsPerPixel + 7) / 8 * rootFrame.Width; Assert.True((TiffConstants.DefaultStripSize - stripBytes) < widthBytes); @@ -379,7 +382,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff provider, inputMeta.BitsPerPixel, mode, - inputMeta.Compression); + inputCompression); } private static void TestTiffEncoderCore( diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index 5298c9998c..3a6dea9aa5 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -3,7 +3,7 @@ using System.IO; using System.Linq; - +using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Metadata; @@ -55,15 +55,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff ExifProfile exifProfile = image.Frames.RootFrame.Metadata.ExifProfile; var meta = new TiffFrameMetadata(exifProfile); var cloneSameAsSampleMetaData = (TiffFrameMetadata)meta.DeepClone(); - VerifyExpectedTiffFrameMetaDataIsPresent(cloneSameAsSampleMetaData); + Assert.NotNull(cloneSameAsSampleMetaData); + Assert.Equal(TiffBitsPerSample.Bit4, cloneSameAsSampleMetaData.BitsPerSample); var clone = (TiffFrameMetadata)meta.DeepClone(); clone.BitsPerSample = TiffBitsPerSample.Bit1; - clone.ColorMap = new ushort[] { 1, 2, 3 }; Assert.False(meta.BitsPerSample == clone.BitsPerSample); - Assert.False(meta.ColorMap.SequenceEqual(clone.ColorMap)); } } @@ -156,6 +155,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff ExifProfile exifProfile = rootFrame.Metadata.ExifProfile; Assert.NotNull(exifProfile); Assert.Equal(30, exifProfile.Values.Count); + Assert.Equal(TiffCompression.Lzw, (TiffCompression)exifProfile.GetValue(ExifTag.Compression).Value); Assert.Equal("This is Название", exifProfile.GetValue(ExifTag.ImageDescription).Value); Assert.Equal("This is Изготовитель камеры", exifProfile.GetValue(ExifTag.Make).Value); Assert.Equal("This is Модель камеры", exifProfile.GetValue(ExifTag.Model).Value); @@ -166,6 +166,25 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal("This is Авторские права", exifProfile.GetValue(ExifTag.Copyright).Value); Assert.Equal(4, exifProfile.GetValue(ExifTag.Rating).Value); Assert.Equal(75, exifProfile.GetValue(ExifTag.RatingPercent).Value); + var expectedResolution = new Rational(10000, 1000, simplify: false); + Assert.Equal(expectedResolution, exifProfile.GetValue(ExifTag.XResolution).Value); + Assert.Equal(expectedResolution, exifProfile.GetValue(ExifTag.YResolution).Value); + Assert.Equal(new Number[] { 8u }, exifProfile.GetValue(ExifTag.StripOffsets)?.Value, new NumberComparer()); + Assert.Equal(new Number[] { 297u }, exifProfile.GetValue(ExifTag.StripByteCounts)?.Value, new NumberComparer()); + Assert.Null(exifProfile.GetValue(ExifTag.ExtraSamples)?.Value); + Assert.Equal(32u, exifProfile.GetValue(ExifTag.RowsPerStrip).Value); + Assert.Null(exifProfile.GetValue(ExifTag.SampleFormat)); + Assert.Equal(TiffPredictor.None, (TiffPredictor?)exifProfile.GetValue(ExifTag.Predictor)?.Value); + Assert.Equal(PixelResolutionUnit.PixelsPerInch, UnitConverter.ExifProfileToResolutionUnit(exifProfile)); + ushort[] colorMap = exifProfile.GetValue(ExifTag.ColorMap)?.Value; + Assert.NotNull(colorMap); + Assert.Equal(48, colorMap.Length); + Assert.Equal(10537, colorMap[0]); + Assert.Equal(14392, colorMap[1]); + Assert.Equal(58596, colorMap[46]); + Assert.Equal(3855, colorMap[47]); + Assert.Equal(TiffPhotometricInterpretation.PaletteColor, (TiffPhotometricInterpretation)exifProfile.GetValue(ExifTag.PhotometricInterpretation).Value); + Assert.Equal(1u, exifProfile.GetValue(ExifTag.SamplesPerPixel).Value); ImageMetadata imageMetaData = image.Metadata; Assert.NotNull(imageMetaData); @@ -181,35 +200,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal(TiffBitsPerPixel.Bit4, frameMetaData.BitsPerPixel); var tiffFrameMetadata = new TiffFrameMetadata(exifProfile); - VerifyExpectedTiffFrameMetaDataIsPresent(tiffFrameMetadata); + Assert.NotNull(frameMetaData); + Assert.Equal(TiffBitsPerSample.Bit4, frameMetaData.BitsPerSample); } } - private static void VerifyExpectedTiffFrameMetaDataIsPresent(TiffFrameMetadata frameMetaData) - { - Assert.NotNull(frameMetaData); - Assert.Equal(TiffBitsPerSample.Bit4, frameMetaData.BitsPerSample); - Assert.Equal(TiffCompression.Lzw, frameMetaData.Compression); - Assert.Equal(TiffPhotometricInterpretation.PaletteColor, frameMetaData.PhotometricInterpretation); - Assert.Equal(new Number[] { 8u }, frameMetaData.StripOffsets, new NumberComparer()); - Assert.Equal(1, frameMetaData.SamplesPerPixel.GetValueOrDefault()); - Assert.Equal(32u, frameMetaData.RowsPerStrip); - Assert.Equal(new Number[] { 297u }, frameMetaData.StripByteCounts, new NumberComparer()); - Assert.Equal(PixelResolutionUnit.PixelsPerInch, frameMetaData.ResolutionUnit); - Assert.Equal(10, frameMetaData.HorizontalResolution); - Assert.Equal(10, frameMetaData.VerticalResolution); - Assert.Equal(TiffPlanarConfiguration.Chunky, frameMetaData.PlanarConfiguration); - Assert.Equal(48, frameMetaData.ColorMap.Length); - Assert.Equal(10537, frameMetaData.ColorMap[0]); - Assert.Equal(14392, frameMetaData.ColorMap[1]); - Assert.Equal(58596, frameMetaData.ColorMap[46]); - Assert.Equal(3855, frameMetaData.ColorMap[47]); - - Assert.Null(frameMetaData.ExtraSamples); - Assert.Equal(TiffPredictor.None, frameMetaData.Predictor); - Assert.Null(frameMetaData.SampleFormat); - } - [Theory] [WithFile(MultiframeDeflateWithPreview, PixelTypes.Rgba32)] public void SubfileType(TestImageProvider provider) @@ -222,13 +217,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal(2, image.Frames.Count); - var frame0MetaData = new TiffFrameMetadata(image.Frames[0].Metadata.ExifProfile); - Assert.Equal(TiffNewSubfileType.FullImage, frame0MetaData.SubfileType); + ExifProfile frame0Exif = image.Frames[0].Metadata.ExifProfile; + Assert.Equal(TiffNewSubfileType.FullImage, (TiffNewSubfileType)frame0Exif.GetValue(ExifTag.SubfileType).Value); Assert.Equal(255, image.Frames[0].Width); Assert.Equal(255, image.Frames[0].Height); - var frame1MetaData = new TiffFrameMetadata(image.Frames[1].Metadata.ExifProfile); - Assert.Equal(TiffNewSubfileType.Preview, frame1MetaData.SubfileType); + ExifProfile frame1Exif = image.Frames[1].Metadata.ExifProfile; + Assert.Equal(TiffNewSubfileType.Preview, (TiffNewSubfileType)frame1Exif.GetValue(ExifTag.SubfileType).Value); Assert.Equal(255, image.Frames[1].Width); Assert.Equal(255, image.Frames[1].Height); } @@ -243,13 +238,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff using Image image = provider.GetImage(new TiffDecoder() { IgnoreMetadata = false }); ImageMetadata inputMetaData = image.Metadata; - TiffMetadata tiffMetaInput = image.Metadata.GetTiffMetadata(); var frameMetaInput = new TiffFrameMetadata(image.Frames.RootFrame.Metadata.ExifProfile); ImageFrame rootFrameInput = image.Frames.RootFrame; byte[] xmpProfileInput = rootFrameInput.Metadata.XmpProfile; - ExifProfile rootFrameExifProfileInput = rootFrameInput.Metadata.ExifProfile; + ExifProfile exifProfileInput = rootFrameInput.Metadata.ExifProfile; - Assert.Equal(TiffCompression.Lzw, frameMetaInput.Compression); + Assert.Equal(TiffCompression.Lzw, (TiffCompression)exifProfileInput.GetValue(ExifTag.Compression).Value); Assert.Equal(TiffBitsPerPixel.Bit4, frameMetaInput.BitsPerPixel); // Save to Tiff @@ -262,14 +256,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff using var encodedImage = Image.Load(this.configuration, ms); ImageMetadata encodedImageMetaData = encodedImage.Metadata; - TiffMetadata tiffMetaDataEncodedImage = encodedImageMetaData.GetTiffMetadata(); ImageFrame rootFrameEncodedImage = encodedImage.Frames.RootFrame; var tiffMetaDataEncodedRootFrame = new TiffFrameMetadata(rootFrameEncodedImage.Metadata.ExifProfile); ExifProfile encodedImageExifProfile = rootFrameEncodedImage.Metadata.ExifProfile; byte[] encodedImageXmpProfile = rootFrameEncodedImage.Metadata.XmpProfile; Assert.Equal(TiffBitsPerPixel.Bit24, tiffMetaDataEncodedRootFrame.BitsPerPixel); - Assert.Equal(TiffCompression.None, tiffMetaDataEncodedRootFrame.Compression); + Assert.Equal(TiffCompression.None, (TiffCompression)encodedImageExifProfile.GetValue(ExifTag.Compression).Value); Assert.Equal(inputMetaData.HorizontalResolution, encodedImageMetaData.HorizontalResolution); Assert.Equal(inputMetaData.VerticalResolution, encodedImageMetaData.VerticalResolution); @@ -277,21 +270,24 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal(rootFrameInput.Width, rootFrameEncodedImage.Width); Assert.Equal(rootFrameInput.Height, rootFrameEncodedImage.Height); - Assert.Equal(frameMetaInput.ResolutionUnit, tiffMetaDataEncodedRootFrame.ResolutionUnit); - Assert.Equal(frameMetaInput.HorizontalResolution, tiffMetaDataEncodedRootFrame.HorizontalResolution); - Assert.Equal(frameMetaInput.VerticalResolution, tiffMetaDataEncodedRootFrame.VerticalResolution); + + PixelResolutionUnit resolutionUnitInput = UnitConverter.ExifProfileToResolutionUnit(exifProfileInput); + PixelResolutionUnit resolutionUnitEncoded = UnitConverter.ExifProfileToResolutionUnit(encodedImageExifProfile); + Assert.Equal(resolutionUnitInput, resolutionUnitEncoded); + Assert.Equal(exifProfileInput.GetValue(ExifTag.XResolution), encodedImageExifProfile.GetValue(ExifTag.XResolution)); + Assert.Equal(exifProfileInput.GetValue(ExifTag.YResolution), encodedImageExifProfile.GetValue(ExifTag.YResolution)); Assert.Equal(xmpProfileInput, encodedImageXmpProfile); - Assert.Equal("IrfanView", rootFrameExifProfileInput.GetValue(ExifTag.Software).Value); - Assert.Equal("This is Название", rootFrameExifProfileInput.GetValue(ExifTag.ImageDescription).Value); - Assert.Equal("This is Изготовитель камеры", rootFrameExifProfileInput.GetValue(ExifTag.Make).Value); - Assert.Equal("This is Авторские права", rootFrameExifProfileInput.GetValue(ExifTag.Copyright).Value); + Assert.Equal("IrfanView", exifProfileInput.GetValue(ExifTag.Software).Value); + Assert.Equal("This is Название", exifProfileInput.GetValue(ExifTag.ImageDescription).Value); + Assert.Equal("This is Изготовитель камеры", exifProfileInput.GetValue(ExifTag.Make).Value); + Assert.Equal("This is Авторские права", exifProfileInput.GetValue(ExifTag.Copyright).Value); - Assert.Equal(rootFrameExifProfileInput.Values.Count, encodedImageExifProfile.Values.Count); - Assert.Equal(rootFrameExifProfileInput.GetValue(ExifTag.ImageDescription).Value, encodedImageExifProfile.GetValue(ExifTag.ImageDescription).Value); - Assert.Equal(rootFrameExifProfileInput.GetValue(ExifTag.Make).Value, encodedImageExifProfile.GetValue(ExifTag.Make).Value); - Assert.Equal(rootFrameExifProfileInput.GetValue(ExifTag.Copyright).Value, encodedImageExifProfile.GetValue(ExifTag.Copyright).Value); + Assert.Equal(exifProfileInput.Values.Count, encodedImageExifProfile.Values.Count); + Assert.Equal(exifProfileInput.GetValue(ExifTag.ImageDescription).Value, encodedImageExifProfile.GetValue(ExifTag.ImageDescription).Value); + Assert.Equal(exifProfileInput.GetValue(ExifTag.Make).Value, encodedImageExifProfile.GetValue(ExifTag.Make).Value); + Assert.Equal(exifProfileInput.GetValue(ExifTag.Copyright).Value, encodedImageExifProfile.GetValue(ExifTag.Copyright).Value); } } }