From fdcd60200e904a05a1f34d76885a238d2f1e4471 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 5 Jun 2024 22:04:16 +1000 Subject: [PATCH] Begin migrating TiffMetadata --- src/ImageSharp/Formats/Bmp/BmpMetadata.cs | 29 ++- src/ImageSharp/Formats/Gif/GifMetadata.cs | 24 +- src/ImageSharp/Formats/IFormatMetadata.cs | 8 + src/ImageSharp/Formats/Jpeg/JpegMetadata.cs | 22 +- src/ImageSharp/Formats/Pbm/PbmMetadata.cs | 20 +- src/ImageSharp/Formats/Png/PngMetadata.cs | 22 +- src/ImageSharp/Formats/Qoi/QoiMetadata.cs | 20 +- src/ImageSharp/Formats/Tga/TgaMetadata.cs | 21 +- .../Formats/Tiff/Constants/TiffConstants.cs | 45 +++- .../Formats/Tiff/Ifd/EntryReader.cs | 2 +- .../Formats/Tiff/TiffBitsPerSample.cs | 8 +- .../Formats/Tiff/TiffDecoderOptionsParser.cs | 78 +----- src/ImageSharp/Formats/Tiff/TiffEncoder.cs | 2 +- .../Formats/Tiff/TiffEncoderCore.cs | 239 ++++++------------ .../Formats/Tiff/TiffFrameMetadata.cs | 13 +- src/ImageSharp/Formats/Tiff/TiffMetadata.cs | 18 +- .../Formats/Tiff/TiffEncoderBaseTester.cs | 2 +- .../Formats/Tiff/TiffEncoderHeaderTests.cs | 4 +- .../Formats/Tiff/TiffEncoderTests.cs | 36 +-- .../TestUtilities/TestImageExtensions.cs | 17 -- 20 files changed, 267 insertions(+), 363 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpMetadata.cs b/src/ImageSharp/Formats/Bmp/BmpMetadata.cs index bb6b87e58..d44520a4f 100644 --- a/src/ImageSharp/Formats/Bmp/BmpMetadata.cs +++ b/src/ImageSharp/Formats/Bmp/BmpMetadata.cs @@ -50,15 +50,18 @@ public class BmpMetadata : IFormatMetadata, IFormatFrameMetadata new BmpMetadata { BitsPerPixel = BmpBitsPerPixel.Pixel8 }, <= 16 => new BmpMetadata { - BitsPerPixel = BmpBitsPerPixel.Pixel16, InfoHeaderType = BmpInfoHeaderType.WinVersion3 + BitsPerPixel = BmpBitsPerPixel.Pixel16, + InfoHeaderType = BmpInfoHeaderType.WinVersion3 }, <= 24 => new BmpMetadata { - BitsPerPixel = BmpBitsPerPixel.Pixel24, InfoHeaderType = BmpInfoHeaderType.WinVersion4 + BitsPerPixel = BmpBitsPerPixel.Pixel24, + InfoHeaderType = BmpInfoHeaderType.WinVersion4 }, _ => new BmpMetadata { - BitsPerPixel = BmpBitsPerPixel.Pixel32, InfoHeaderType = BmpInfoHeaderType.WinVersion5 + BitsPerPixel = BmpBitsPerPixel.Pixel32, + InfoHeaderType = BmpInfoHeaderType.WinVersion5 } }; } @@ -68,7 +71,7 @@ public class BmpMetadata : IFormatMetadata, IFormatFrameMetadata new(); /// - public FormatConnectingMetadata ToFormatConnectingMetadata() + public PixelTypeInfo GetPixelTypeInfo() { int bpp = (int)this.BitsPerPixel; @@ -122,17 +125,21 @@ public class BmpMetadata : IFormatMetadata, IFormatFrameMetadata + public FormatConnectingMetadata ToFormatConnectingMetadata() + => new() + { + PixelTypeInfo = this.GetPixelTypeInfo() + }; + /// public FormatConnectingFrameMetadata ToFormatConnectingFrameMetadata() => new(); diff --git a/src/ImageSharp/Formats/Gif/GifMetadata.cs b/src/ImageSharp/Formats/Gif/GifMetadata.cs index 64c7edbaf..90d3312c8 100644 --- a/src/ImageSharp/Formats/Gif/GifMetadata.cs +++ b/src/ImageSharp/Formats/Gif/GifMetadata.cs @@ -126,6 +126,20 @@ public class GifMetadata : IFormatMetadata }; } + /// + public PixelTypeInfo GetPixelTypeInfo() + { + int bpp = this.GlobalColorTable.HasValue + ? Numerics.Clamp(ColorNumerics.GetBitsNeededForColorDepth(this.GlobalColorTable.Value.Length), 1, 8) + : 8; + + return new PixelTypeInfo(bpp) + { + ColorType = PixelColorType.Indexed, + ComponentInfo = PixelComponentInfo.Create(1, bpp, bpp), + }; + } + /// public FormatConnectingMetadata ToFormatConnectingMetadata() { @@ -133,21 +147,13 @@ public class GifMetadata : IFormatMetadata ? this.GlobalColorTable.Value.Span[this.BackgroundColorIndex] : Color.Transparent; - int bpp = this.GlobalColorTable.HasValue - ? Numerics.Clamp(ColorNumerics.GetBitsNeededForColorDepth(this.GlobalColorTable.Value.Length), 1, 8) - : 8; - return new() { AnimateRootFrame = true, BackgroundColor = color, ColorTable = this.GlobalColorTable, ColorTableMode = this.ColorTableMode, - PixelTypeInfo = new PixelTypeInfo(bpp) - { - ColorType = PixelColorType.Indexed, - ComponentInfo = PixelComponentInfo.Create(1, bpp, bpp), - }, + PixelTypeInfo = this.GetPixelTypeInfo(), RepeatCount = this.RepeatCount, }; } diff --git a/src/ImageSharp/Formats/IFormatMetadata.cs b/src/ImageSharp/Formats/IFormatMetadata.cs index f542f92db..e0c32e8b2 100644 --- a/src/ImageSharp/Formats/IFormatMetadata.cs +++ b/src/ImageSharp/Formats/IFormatMetadata.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.PixelFormats; + namespace SixLabors.ImageSharp.Formats; /// @@ -8,6 +10,12 @@ namespace SixLabors.ImageSharp.Formats; /// public interface IFormatMetadata : IDeepCloneable { + /// + /// Converts the metadata to a instance. + /// + /// The pixel type info. + PixelTypeInfo GetPixelTypeInfo(); + /// /// Converts the metadata to a instance. /// diff --git a/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs b/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs index b12ee524d..d15abafde 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs @@ -139,7 +139,7 @@ public class JpegMetadata : IFormatMetadata } /// - public FormatConnectingMetadata ToFormatConnectingMetadata() + public PixelTypeInfo GetPixelTypeInfo() { int bpp; PixelColorType colorType; @@ -173,18 +173,22 @@ public class JpegMetadata : IFormatMetadata break; } - return new FormatConnectingMetadata + return new PixelTypeInfo(bpp) { - PixelTypeInfo = new PixelTypeInfo(bpp) - { - AlphaRepresentation = PixelAlphaRepresentation.None, - ColorType = colorType, - ComponentInfo = info, - }, - Quality = this.Quality, + AlphaRepresentation = PixelAlphaRepresentation.None, + ColorType = colorType, + ComponentInfo = info, }; } + /// + public FormatConnectingMetadata ToFormatConnectingMetadata() + => new() + { + PixelTypeInfo = this.GetPixelTypeInfo(), + Quality = this.Quality, + }; + /// IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); diff --git a/src/ImageSharp/Formats/Pbm/PbmMetadata.cs b/src/ImageSharp/Formats/Pbm/PbmMetadata.cs index 49a13d5c3..49a36de41 100644 --- a/src/ImageSharp/Formats/Pbm/PbmMetadata.cs +++ b/src/ImageSharp/Formats/Pbm/PbmMetadata.cs @@ -85,7 +85,7 @@ public class PbmMetadata : IFormatMetadata } /// - public FormatConnectingMetadata ToFormatConnectingMetadata() + public PixelTypeInfo GetPixelTypeInfo() { int bpp; PixelColorType colorType; @@ -114,17 +114,21 @@ public class PbmMetadata : IFormatMetadata break; } - return new FormatConnectingMetadata + return new PixelTypeInfo(bpp) { - PixelTypeInfo = new PixelTypeInfo(bpp) - { - AlphaRepresentation = PixelAlphaRepresentation.None, - ColorType = colorType, - ComponentInfo = info, - }, + AlphaRepresentation = PixelAlphaRepresentation.None, + ColorType = colorType, + ComponentInfo = info, }; } + /// + public FormatConnectingMetadata ToFormatConnectingMetadata() + => new() + { + PixelTypeInfo = this.GetPixelTypeInfo(), + }; + /// IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); diff --git a/src/ImageSharp/Formats/Png/PngMetadata.cs b/src/ImageSharp/Formats/Png/PngMetadata.cs index d789ae8fa..d72dc5184 100644 --- a/src/ImageSharp/Formats/Png/PngMetadata.cs +++ b/src/ImageSharp/Formats/Png/PngMetadata.cs @@ -191,7 +191,7 @@ public class PngMetadata : IFormatMetadata } /// - public FormatConnectingMetadata ToFormatConnectingMetadata() + public PixelTypeInfo GetPixelTypeInfo() { int bpp; PixelColorType colorType; @@ -262,19 +262,23 @@ public class PngMetadata : IFormatMetadata break; } - return new() + return new PixelTypeInfo(bpp) + { + AlphaRepresentation = alpha, + ColorType = colorType, + ComponentInfo = info, + }; + } + + /// + public FormatConnectingMetadata ToFormatConnectingMetadata() + => new() { ColorTable = this.ColorTable, ColorTableMode = FrameColorTableMode.Global, - PixelTypeInfo = new PixelTypeInfo(bpp) - { - AlphaRepresentation = alpha, - ColorType = colorType, - ComponentInfo = info, - }, + PixelTypeInfo = this.GetPixelTypeInfo(), RepeatCount = (ushort)Numerics.Clamp(this.RepeatCount, 0, ushort.MaxValue), }; - } /// IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); diff --git a/src/ImageSharp/Formats/Qoi/QoiMetadata.cs b/src/ImageSharp/Formats/Qoi/QoiMetadata.cs index 4be714fc3..c8bcd9748 100644 --- a/src/ImageSharp/Formats/Qoi/QoiMetadata.cs +++ b/src/ImageSharp/Formats/Qoi/QoiMetadata.cs @@ -51,7 +51,7 @@ public class QoiMetadata : IFormatMetadata } /// - public FormatConnectingMetadata ToFormatConnectingMetadata() + public PixelTypeInfo GetPixelTypeInfo() { int bpp; PixelColorType colorType; @@ -73,17 +73,21 @@ public class QoiMetadata : IFormatMetadata break; } - return new() + return new PixelTypeInfo(bpp) { - PixelTypeInfo = new PixelTypeInfo(bpp) - { - AlphaRepresentation = alpha, - ColorType = colorType, - ComponentInfo = info, - } + AlphaRepresentation = alpha, + ColorType = colorType, + ComponentInfo = info, }; } + /// + public FormatConnectingMetadata ToFormatConnectingMetadata() + => new() + { + PixelTypeInfo = this.GetPixelTypeInfo() + }; + /// IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); diff --git a/src/ImageSharp/Formats/Tga/TgaMetadata.cs b/src/ImageSharp/Formats/Tga/TgaMetadata.cs index 345d1698e..fa8e8b6f4 100644 --- a/src/ImageSharp/Formats/Tga/TgaMetadata.cs +++ b/src/ImageSharp/Formats/Tga/TgaMetadata.cs @@ -49,10 +49,9 @@ public class TgaMetadata : IFormatMetadata } /// - public FormatConnectingMetadata ToFormatConnectingMetadata() + public PixelTypeInfo GetPixelTypeInfo() { int bpp = (int)this.BitsPerPixel; - PixelComponentInfo info; PixelColorType color; PixelAlphaRepresentation alpha; @@ -80,17 +79,21 @@ public class TgaMetadata : IFormatMetadata break; } - return new() + return new PixelTypeInfo(bpp) { - PixelTypeInfo = new PixelTypeInfo(bpp) - { - AlphaRepresentation = alpha, - ComponentInfo = info, - ColorType = color - } + AlphaRepresentation = alpha, + ComponentInfo = info, + ColorType = color }; } + /// + public FormatConnectingMetadata ToFormatConnectingMetadata() + => new() + { + PixelTypeInfo = this.GetPixelTypeInfo() + }; + /// IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs index 978860910..c24eee484 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs @@ -39,9 +39,9 @@ internal static class TiffConstants public const ushort BigTiffHeaderMagicNumber = 43; /// - /// The big tiff bytesize of offsets value. + /// The big tiff byte size of offsets value. /// - public const ushort BigTiffBytesize = 8; + public const ushort BigTiffByteSize = 8; /// /// RowsPerStrip default value, which is effectively infinity. @@ -58,38 +58,63 @@ internal static class TiffConstants /// public const int DefaultStripSize = 8 * 1024; + /// + /// The default predictor is None. + /// + public const TiffPredictor DefaultPredictor = TiffPredictor.None; + + /// + /// The default bits per pixel is Bit24. + /// + public const TiffBitsPerPixel DefaultBitsPerPixel = TiffBitsPerPixel.Bit24; + + /// + /// The default bits per sample for color images with 8 bits for each color channel. + /// + public static readonly TiffBitsPerSample DefaultBitsPerSample = BitsPerSampleRgb8Bit; + + /// + /// The default compression is None. + /// + public const TiffCompression DefaultCompression = TiffCompression.None; + + /// + /// The default photometric interpretation is Rgb. + /// + public const TiffPhotometricInterpretation DefaultPhotometricInterpretation = TiffPhotometricInterpretation.Rgb; + /// /// The bits per sample for 1 bit bicolor images. /// - public static readonly TiffBitsPerSample BitsPerSample1Bit = new TiffBitsPerSample(1, 0, 0); + public static readonly TiffBitsPerSample BitsPerSample1Bit = new(1, 0, 0); /// /// The bits per sample for images with a 4 color palette. /// - public static readonly TiffBitsPerSample BitsPerSample4Bit = new TiffBitsPerSample(4, 0, 0); + public static readonly TiffBitsPerSample BitsPerSample4Bit = new(4, 0, 0); /// /// The bits per sample for 8 bit images. /// - public static readonly TiffBitsPerSample BitsPerSample8Bit = new TiffBitsPerSample(8, 0, 0); + public static readonly TiffBitsPerSample BitsPerSample8Bit = new(8, 0, 0); /// /// The bits per sample for 16-bit grayscale images. /// - public static readonly TiffBitsPerSample BitsPerSample16Bit = new TiffBitsPerSample(16, 0, 0); + public static readonly TiffBitsPerSample BitsPerSample16Bit = new(16, 0, 0); /// /// The bits per sample for color images with 8 bits for each color channel. /// - public static readonly TiffBitsPerSample BitsPerSampleRgb8Bit = new TiffBitsPerSample(8, 8, 8); + public static readonly TiffBitsPerSample BitsPerSampleRgb8Bit = new(8, 8, 8); /// - /// The list of mimetypes that equate to a tiff. + /// The list of mime types that equate to a tiff. /// - public static readonly IEnumerable MimeTypes = new[] { "image/tiff", "image/tiff-fx" }; + public static readonly IEnumerable MimeTypes = ["image/tiff", "image/tiff-fx"]; /// /// The list of file extensions that equate to a tiff. /// - public static readonly IEnumerable FileExtensions = new[] { "tiff", "tif" }; + public static readonly IEnumerable FileExtensions = ["tiff", "tif"]; } diff --git a/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs b/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs index 267370009..4496de6fb 100644 --- a/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs +++ b/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs @@ -64,7 +64,7 @@ internal class HeaderReader : BaseExifReader ushort bytesize = this.ReadUInt16(); ushort reserve = this.ReadUInt16(); - if (bytesize == TiffConstants.BigTiffBytesize && reserve == 0) + if (bytesize == TiffConstants.BigTiffByteSize && reserve == 0) { this.FirstIfdOffset = this.ReadUInt64(); return; diff --git a/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs b/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs index 382faa387..2bfd9a626 100644 --- a/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs +++ b/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs @@ -147,20 +147,20 @@ public readonly struct TiffBitsPerSample : IEquatable { if (this.Channel1 == 0) { - return new[] { this.Channel0 }; + return [this.Channel0]; } if (this.Channel2 == 0) { - return new[] { this.Channel0, this.Channel1 }; + return [this.Channel0, this.Channel1]; } if (this.Channel3 == 0) { - return new[] { this.Channel0, this.Channel1, this.Channel2 }; + return [this.Channel0, this.Channel1, this.Channel2]; } - return new[] { this.Channel0, this.Channel1, this.Channel2, this.Channel3 }; + return [this.Channel0, this.Channel1, this.Channel2, this.Channel3]; } /// diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs index 5a5c2b3e5..186a4bd31 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs @@ -63,7 +63,7 @@ internal static class TiffDecoderOptionsParser } TiffSampleFormat? sampleFormat = null; - if (exifProfile.TryGetValue(ExifTag.SampleFormat, out var formatValue)) + if (exifProfile.TryGetValue(ExifTag.SampleFormat, out IExifValue formatValue)) { TiffSampleFormat[] sampleFormats = formatValue.Value.Select(a => (TiffSampleFormat)a).ToArray(); sampleFormat = sampleFormats[0]; @@ -106,11 +106,11 @@ internal static class TiffDecoderOptionsParser options.PlanarConfiguration = DefaultPlanarConfiguration; } - options.Predictor = frameMetadata.Predictor ?? TiffPredictor.None; - options.PhotometricInterpretation = frameMetadata.PhotometricInterpretation ?? TiffPhotometricInterpretation.Rgb; + options.Predictor = frameMetadata.Predictor; + options.PhotometricInterpretation = frameMetadata.PhotometricInterpretation; options.SampleFormat = sampleFormat ?? TiffSampleFormat.UnsignedInteger; - options.BitsPerPixel = frameMetadata.BitsPerPixel != null ? (int)frameMetadata.BitsPerPixel.Value : (int)TiffBitsPerPixel.Bit24; - options.BitsPerSample = frameMetadata.BitsPerSample ?? new TiffBitsPerSample(0, 0, 0); + options.BitsPerPixel = (int)frameMetadata.BitsPerPixel; + options.BitsPerSample = frameMetadata.BitsPerSample; if (exifProfile.TryGetValue(ExifTag.ReferenceBlackWhite, out IExifValue blackWhiteValue)) { @@ -142,9 +142,7 @@ internal static class TiffDecoderOptionsParser options.ParseCompression(frameMetadata.Compression, exifProfile); options.ParseColorType(exifProfile); - bool isTiled = VerifyRequiredFieldsArePresent(exifProfile, frameMetadata, options.PlanarConfiguration); - - return isTiled; + return VerifyRequiredFieldsArePresent(exifProfile, frameMetadata, options.PlanarConfiguration); } /// @@ -194,13 +192,6 @@ internal static class TiffDecoderOptionsParser } } - // For BiColor compressed images, the BitsPerPixel value will be set explicitly to 1, so we don't throw in those cases. - // See: https://github.com/SixLabors/ImageSharp/issues/2587 - if (frameMetadata.BitsPerPixel == null && !IsBiColorCompression(frameMetadata.Compression)) - { - TiffThrowHelper.ThrowNotSupported("The TIFF BitsPerSample entry is missing which is required to decode the image!"); - } - return isTiled; } @@ -224,7 +215,6 @@ internal static class TiffDecoderOptionsParser switch (bitsPerChannel) { case 32: - { if (options.SampleFormat == TiffSampleFormat.Float) { options.ColorType = TiffColorType.WhiteIsZero32Float; @@ -233,43 +223,30 @@ internal static class TiffDecoderOptionsParser options.ColorType = TiffColorType.WhiteIsZero32; break; - } case 24: - { options.ColorType = TiffColorType.WhiteIsZero24; break; - } case 16: - { options.ColorType = TiffColorType.WhiteIsZero16; break; - } case 8: - { options.ColorType = TiffColorType.WhiteIsZero8; break; - } case 4: - { options.ColorType = TiffColorType.WhiteIsZero4; break; - } case 1: - { options.ColorType = TiffColorType.WhiteIsZero1; break; - } default: - { options.ColorType = TiffColorType.WhiteIsZero; break; - } } break; @@ -291,7 +268,6 @@ internal static class TiffDecoderOptionsParser switch (bitsPerChannel) { case 32: - { if (options.SampleFormat == TiffSampleFormat.Float) { options.ColorType = TiffColorType.BlackIsZero32Float; @@ -300,43 +276,30 @@ internal static class TiffDecoderOptionsParser options.ColorType = TiffColorType.BlackIsZero32; break; - } case 24: - { options.ColorType = TiffColorType.BlackIsZero24; break; - } case 16: - { options.ColorType = TiffColorType.BlackIsZero16; break; - } case 8: - { options.ColorType = TiffColorType.BlackIsZero8; break; - } case 4: - { options.ColorType = TiffColorType.BlackIsZero4; break; - } case 1: - { options.ColorType = TiffColorType.BlackIsZero1; break; - } default: - { options.ColorType = TiffColorType.BlackIsZero; break; - } } break; @@ -535,29 +498,21 @@ internal static class TiffDecoderOptionsParser switch (compression ?? TiffCompression.None) { 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: { @@ -599,16 +554,13 @@ internal static class TiffDecoderOptionsParser } case TiffCompression.Ccitt1D: - { options.CompressionType = TiffDecoderCompressionType.HuffmanRle; options.BitsPerSample = new TiffBitsPerSample(1, 0, 0); options.BitsPerPixel = 1; break; - } case TiffCompression.OldJpeg: - { if (!options.OldJpegCompressionStartOfImageMarker.HasValue) { TiffThrowHelper.ThrowNotSupported("Missing SOI marker offset for tiff with old jpeg compression"); @@ -629,10 +581,8 @@ internal static class TiffDecoderOptionsParser } break; - } case TiffCompression.Jpeg: - { options.CompressionType = TiffDecoderCompressionType.Jpeg; if (options.PhotometricInterpretation is TiffPhotometricInterpretation.YCbCr && options.JpegTables is null) @@ -643,30 +593,14 @@ internal static class TiffDecoderOptionsParser } break; - } case TiffCompression.Webp: - { options.CompressionType = TiffDecoderCompressionType.Webp; break; - } default: - { TiffThrowHelper.ThrowNotSupported($"The specified TIFF compression format '{compression}' is not supported"); break; - } } } - - private static bool IsBiColorCompression(TiffCompression? compression) - { - if (compression is TiffCompression.Ccitt1D or TiffCompression.CcittGroup3Fax or - TiffCompression.CcittGroup4Fax) - { - return true; - } - - return false; - } } diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs index ea64e8289..a068613bf 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs @@ -47,7 +47,7 @@ public class TiffEncoder : QuantizingImageEncoder /// protected override void Encode(Image image, Stream stream, CancellationToken cancellationToken) { - TiffEncoderCore encode = new(this, image.Configuration.MemoryAllocator); + TiffEncoderCore encode = new(this, image.Configuration); encode.Encode(image, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 149f23f1b..c702252cc 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -1,8 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable -using SixLabors.ImageSharp.Advanced; +using System.Diagnostics.CodeAnalysis; using SixLabors.ImageSharp.Compression.Zlib; using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Formats.Tiff.Constants; @@ -50,41 +49,22 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals /// private readonly DeflateCompressionLevel compressionLevel; - /// - /// The default predictor is None. - /// - private const TiffPredictor DefaultPredictor = TiffPredictor.None; - - /// - /// The default bits per pixel is Bit24. - /// - private const TiffBitsPerPixel DefaultBitsPerPixel = TiffBitsPerPixel.Bit24; - - /// - /// The default compression is None. - /// - private const TiffCompression DefaultCompression = TiffCompression.None; - - /// - /// The default photometric interpretation is Rgb. - /// - private const TiffPhotometricInterpretation DefaultPhotometricInterpretation = TiffPhotometricInterpretation.Rgb; - /// /// Whether to skip metadata during encoding. /// private readonly bool skipMetadata; - private readonly List<(long, uint)> frameMarkers = new(); + private readonly List<(long, uint)> frameMarkers = []; /// /// Initializes a new instance of the class. /// /// The options for the encoder. - /// The memory allocator. - public TiffEncoderCore(TiffEncoder options, MemoryAllocator memoryAllocator) + /// The global configuration. + public TiffEncoderCore(TiffEncoder options, Configuration configuration) { - this.memoryAllocator = memoryAllocator; + this.configuration = configuration; + this.memoryAllocator = configuration.MemoryAllocator; this.PhotometricInterpretation = options.PhotometricInterpretation; this.quantizer = options.Quantizer ?? KnownQuantizers.Octree; this.pixelSamplingStrategy = options.PixelSamplingStrategy; @@ -135,35 +115,29 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals // Determine the correct values to encode with. // EncoderOptions > Metadata > Default. - TiffBitsPerPixel? bitsPerPixel = this.BitsPerPixel ?? rootFrameTiffMetaData.BitsPerPixel; + TiffBitsPerPixel bitsPerPixel = this.BitsPerPixel ?? rootFrameTiffMetaData.BitsPerPixel; - TiffPhotometricInterpretation? photometricInterpretation = this.PhotometricInterpretation ?? rootFrameTiffMetaData.PhotometricInterpretation; + TiffPhotometricInterpretation photometricInterpretation = this.PhotometricInterpretation ?? rootFrameTiffMetaData.PhotometricInterpretation; - TiffPredictor predictor = - this.HorizontalPredictor - ?? rootFrameTiffMetaData.Predictor - ?? DefaultPredictor; + TiffPredictor predictor = this.HorizontalPredictor ?? rootFrameTiffMetaData.Predictor; - TiffCompression compression = - this.CompressionType - ?? rootFrameTiffMetaData.Compression - ?? DefaultCompression; + TiffCompression compression = this.CompressionType ?? rootFrameTiffMetaData.Compression; - // Make sure, the Encoder options makes sense in combination with each other. - this.SanitizeAndSetEncoderOptions(bitsPerPixel, image.PixelType.BitsPerPixel, photometricInterpretation, compression, predictor); + // Make sure the Encoder options makes sense in combination with each other. + this.SanitizeAndSetEncoderOptions(bitsPerPixel, photometricInterpretation, compression, predictor); using TiffStreamWriter writer = new(stream); Span buffer = stackalloc byte[4]; long ifdMarker = WriteHeader(writer, buffer); - Image metadataImage = image; + Image? metadataImage = image; foreach (ImageFrame frame in image.Frames) { cancellationToken.ThrowIfCancellationRequested(); - ifdMarker = this.WriteFrame(writer, frame, image.Metadata, metadataImage, ifdMarker); + ifdMarker = this.WriteFrame(writer, frame, image.Metadata, metadataImage, this.BitsPerPixel.Value, this.CompressionType.Value, ifdMarker); metadataImage = null; } @@ -199,6 +173,8 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals /// The tiff frame. /// The image metadata (resolution values for each frame). /// The image (common metadata for root frame). + /// The bits per pixel. + /// The compression type. /// The marker to write this IFD offset. /// /// The next IFD offset value. @@ -207,16 +183,18 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals TiffStreamWriter writer, ImageFrame frame, ImageMetadata imageMetadata, - Image image, + Image? image, + TiffBitsPerPixel bitsPerPixel, + TiffCompression compression, long ifdOffset) where TPixel : unmanaged, IPixel { using TiffBaseCompressor compressor = TiffCompressorFactory.Create( - this.CompressionType ?? TiffCompression.None, + compression, writer.BaseStream, this.memoryAllocator, frame.Width, - (int)this.BitsPerPixel, + (int)bitsPerPixel, this.compressionLevel, this.HorizontalPredictor == TiffPredictor.Horizontal ? this.HorizontalPredictor.Value : TiffPredictor.None); @@ -229,7 +207,7 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals this.memoryAllocator, this.configuration, entriesCollector, - (int)this.BitsPerPixel); + (int)bitsPerPixel); int rowsPerStrip = CalcRowsPerStrip(frame.Height, colorWriter.BytesPerRow, this.CompressionType); @@ -307,7 +285,7 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals } uint dataOffset = (uint)writer.Position + (uint)(6 + (entries.Count * 12)); - List largeDataBlocks = new(); + List largeDataBlocks = []; entries.Sort((a, b) => (ushort)a.Tag - (ushort)b.Tag); @@ -354,135 +332,80 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals return nextIfdMarker; } + [MemberNotNull(nameof(BitsPerPixel), nameof(PhotometricInterpretation), nameof(CompressionType), nameof(HorizontalPredictor))] private void SanitizeAndSetEncoderOptions( - TiffBitsPerPixel? bitsPerPixel, - int inputBitsPerPixel, - TiffPhotometricInterpretation? photometricInterpretation, + TiffBitsPerPixel bitsPerPixel, + TiffPhotometricInterpretation photometricInterpretation, TiffCompression compression, TiffPredictor predictor) { // BitsPerPixel should be the primary source of truth for the encoder options. - if (bitsPerPixel.HasValue) - { - switch (bitsPerPixel) - { - case TiffBitsPerPixel.Bit1: - if (IsOneBitCompression(compression)) - { - // The “normal” PhotometricInterpretation for bilevel CCITT compressed data is WhiteIsZero. - this.SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.WhiteIsZero, compression, TiffPredictor.None); - break; - } - - this.SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.BlackIsZero, compression, TiffPredictor.None); - break; - case TiffBitsPerPixel.Bit4: - this.SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.PaletteColor, compression, TiffPredictor.None); - break; - case TiffBitsPerPixel.Bit8: - this.SetEncoderOptions(bitsPerPixel, photometricInterpretation ?? TiffPhotometricInterpretation.BlackIsZero, compression, predictor); - break; - case TiffBitsPerPixel.Bit16: - // Assume desire to encode as L16 grayscale - this.SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.BlackIsZero, compression, predictor); - break; - case TiffBitsPerPixel.Bit6: - case TiffBitsPerPixel.Bit10: - case TiffBitsPerPixel.Bit12: - case TiffBitsPerPixel.Bit14: - case TiffBitsPerPixel.Bit30: - case TiffBitsPerPixel.Bit36: - case TiffBitsPerPixel.Bit42: - case TiffBitsPerPixel.Bit48: - // Encoding not yet supported bits per pixel will default to 24 bits. - this.SetEncoderOptions(TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, compression, TiffPredictor.None); - break; - case TiffBitsPerPixel.Bit64: - // Encoding not yet supported bits per pixel will default to 32 bits. - this.SetEncoderOptions(TiffBitsPerPixel.Bit32, TiffPhotometricInterpretation.Rgb, compression, TiffPredictor.None); - break; - default: - this.SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.Rgb, compression, predictor); - break; - } - - // Make sure 1 Bit compression is only used with 1 bit pixel type. - if (IsOneBitCompression(this.CompressionType) && this.BitsPerPixel != TiffBitsPerPixel.Bit1) - { - // Invalid compression / bits per pixel combination, fallback to no compression. - this.CompressionType = DefaultCompression; - } - - return; - } - - // If no photometric interpretation was chosen, the input image bit per pixel should be preserved. - if (!photometricInterpretation.HasValue) + switch (bitsPerPixel) { - if (IsOneBitCompression(this.CompressionType)) - { - // We need to make sure bits per pixel is set to Bit1 now. WhiteIsZero is set because its the default for bilevel compressed data. - this.SetEncoderOptions(TiffBitsPerPixel.Bit1, TiffPhotometricInterpretation.WhiteIsZero, compression, TiffPredictor.None); - return; - } - - // At the moment only 8, 16 and 32 bits per pixel can be preserved by the tiff encoder. - if (inputBitsPerPixel == 8) - { - this.SetEncoderOptions(TiffBitsPerPixel.Bit8, TiffPhotometricInterpretation.BlackIsZero, compression, predictor); - return; - } - - if (inputBitsPerPixel == 16) - { - // Assume desire to encode as L16 grayscale - this.SetEncoderOptions(TiffBitsPerPixel.Bit16, TiffPhotometricInterpretation.BlackIsZero, compression, predictor); - return; - } - - this.SetEncoderOptions(TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, compression, predictor); - return; - } - - switch (photometricInterpretation) - { - case TiffPhotometricInterpretation.BlackIsZero: - case TiffPhotometricInterpretation.WhiteIsZero: - if (IsOneBitCompression(this.CompressionType)) - { - this.SetEncoderOptions(TiffBitsPerPixel.Bit1, photometricInterpretation, compression, TiffPredictor.None); - return; - } - - if (inputBitsPerPixel == 16) + case TiffBitsPerPixel.Bit1: + if (IsOneBitCompression(compression)) { - this.SetEncoderOptions(TiffBitsPerPixel.Bit16, photometricInterpretation, compression, predictor); - return; + // The “normal” PhotometricInterpretation for bilevel CCITT compressed data is WhiteIsZero. + this.SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.WhiteIsZero, compression, TiffPredictor.None); + break; } - this.SetEncoderOptions(TiffBitsPerPixel.Bit8, photometricInterpretation, compression, predictor); - return; - - case TiffPhotometricInterpretation.PaletteColor: - this.SetEncoderOptions(TiffBitsPerPixel.Bit8, photometricInterpretation, compression, predictor); - return; - - case TiffPhotometricInterpretation.Rgb: - // Make sure 1 Bit compression is only used with 1 bit pixel type. - if (IsOneBitCompression(this.CompressionType)) + this.SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.BlackIsZero, compression, TiffPredictor.None); + break; + case TiffBitsPerPixel.Bit4: + this.SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.PaletteColor, compression, TiffPredictor.None); + break; + case TiffBitsPerPixel.Bit8: + + // Allow any combination of the below for 8 bit images. + if (photometricInterpretation is TiffPhotometricInterpretation.BlackIsZero + or TiffPhotometricInterpretation.WhiteIsZero + or TiffPhotometricInterpretation.PaletteColor) { - // Invalid compression / bits per pixel combination, fallback to no compression. - compression = DefaultCompression; + this.SetEncoderOptions(bitsPerPixel, photometricInterpretation, compression, predictor); + break; } - this.SetEncoderOptions(TiffBitsPerPixel.Bit24, photometricInterpretation, compression, predictor); - return; + this.SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.PaletteColor, compression, predictor); + break; + case TiffBitsPerPixel.Bit16: + // Assume desire to encode as L16 grayscale + this.SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.BlackIsZero, compression, predictor); + break; + case TiffBitsPerPixel.Bit6: + case TiffBitsPerPixel.Bit10: + case TiffBitsPerPixel.Bit12: + case TiffBitsPerPixel.Bit14: + case TiffBitsPerPixel.Bit30: + case TiffBitsPerPixel.Bit36: + case TiffBitsPerPixel.Bit42: + case TiffBitsPerPixel.Bit48: + // Encoding not yet supported bits per pixel will default to 24 bits. + this.SetEncoderOptions(TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, compression, TiffPredictor.None); + break; + case TiffBitsPerPixel.Bit64: + // Encoding not yet supported bits per pixel will default to 32 bits. + this.SetEncoderOptions(TiffBitsPerPixel.Bit32, TiffPhotometricInterpretation.Rgb, compression, TiffPredictor.None); + break; + default: + this.SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.Rgb, compression, predictor); + break; } - this.SetEncoderOptions(DefaultBitsPerPixel, DefaultPhotometricInterpretation, compression, predictor); + // Make sure 1 Bit compression is only used with 1 bit pixel type. + if (IsOneBitCompression(this.CompressionType) && this.BitsPerPixel != TiffBitsPerPixel.Bit1) + { + // Invalid compression / bits per pixel combination, fallback to no compression. + this.CompressionType = TiffCompression.None; + } } - private void SetEncoderOptions(TiffBitsPerPixel? bitsPerPixel, TiffPhotometricInterpretation? photometricInterpretation, TiffCompression compression, TiffPredictor predictor) + [MemberNotNull(nameof(BitsPerPixel), nameof(PhotometricInterpretation), nameof(CompressionType), nameof(HorizontalPredictor))] + private void SetEncoderOptions( + TiffBitsPerPixel bitsPerPixel, + TiffPhotometricInterpretation photometricInterpretation, + TiffCompression compression, + TiffPredictor predictor) { this.BitsPerPixel = bitsPerPixel; this.PhotometricInterpretation = photometricInterpretation; diff --git a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs index e30983098..114fe703c 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs @@ -34,27 +34,27 @@ public class TiffFrameMetadata : IDeepCloneable /// /// Gets or sets the bits per pixel. /// - public TiffBitsPerPixel? BitsPerPixel { get; set; } + public TiffBitsPerPixel BitsPerPixel { get; set; } = TiffConstants.DefaultBitsPerPixel; /// /// Gets or sets number of bits per component. /// - public TiffBitsPerSample? BitsPerSample { get; set; } + public TiffBitsPerSample BitsPerSample { get; set; } = TiffConstants.DefaultBitsPerSample; /// /// Gets or sets the compression scheme used on the image data. /// - public TiffCompression? Compression { get; set; } + public TiffCompression Compression { get; set; } = TiffConstants.DefaultCompression; /// /// Gets or sets the color space of the image data. /// - public TiffPhotometricInterpretation? PhotometricInterpretation { get; set; } + public TiffPhotometricInterpretation PhotometricInterpretation { get; set; } = TiffConstants.DefaultPhotometricInterpretation; /// /// Gets or sets a mathematical operator that is applied to the image data before an encoding scheme is applied. /// - public TiffPredictor? Predictor { get; set; } + public TiffPredictor Predictor { get; set; } = TiffConstants.DefaultPredictor; /// /// Gets or sets the set of inks used in a separated () image. @@ -89,7 +89,7 @@ public class TiffFrameMetadata : IDeepCloneable meta.BitsPerSample = bitsPerSample; } - meta.BitsPerPixel = meta.BitsPerSample?.BitsPerPixel(); + meta.BitsPerPixel = meta.BitsPerSample.BitsPerPixel(); if (profile.TryGetValue(ExifTag.Compression, out IExifValue? compressionValue)) { @@ -111,6 +111,7 @@ public class TiffFrameMetadata : IDeepCloneable meta.InkSet = (TiffInkSet)inkSetValue.Value; } + // TODO: Why do we remove this? Encoding should overwrite. profile.RemoveValue(ExifTag.BitsPerSample); profile.RemoveValue(ExifTag.Compression); profile.RemoveValue(ExifTag.PhotometricInterpretation); diff --git a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs index 2759d0130..b95b9f92f 100644 --- a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs @@ -1,12 +1,14 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.PixelFormats; + namespace SixLabors.ImageSharp.Formats.Tiff; /// /// Provides Tiff specific metadata information for the image. /// -public class TiffMetadata : IDeepCloneable +public class TiffMetadata : IFormatMetadata { /// /// Initializes a new instance of the class. @@ -36,5 +38,17 @@ public class TiffMetadata : IDeepCloneable public TiffFormatType FormatType { get; set; } /// - public IDeepCloneable DeepClone() => new TiffMetadata(this); + public static TiffMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata) => throw new NotImplementedException(); + + /// + public PixelTypeInfo GetPixelTypeInfo() => throw new NotImplementedException(); + + /// + public FormatConnectingMetadata ToFormatConnectingMetadata() => throw new NotImplementedException(); + + /// + IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); + + /// + public TiffMetadata DeepClone() => new(this); } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderBaseTester.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderBaseTester.cs index 2a822e705..0cff35217 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderBaseTester.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderBaseTester.cs @@ -30,7 +30,7 @@ public abstract class TiffEncoderBaseTester using Image input = provider.GetImage(); using var memStream = new MemoryStream(); TiffFrameMetadata inputMeta = input.Frames.RootFrame.Metadata.GetTiffMetadata(); - TiffCompression inputCompression = inputMeta.Compression ?? TiffCompression.None; + TiffCompression inputCompression = inputMeta.Compression; // act input.Save(memStream, tiffEncoder); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs index 872414730..282966ea8 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs @@ -15,7 +15,7 @@ public class TiffEncoderHeaderTests public void WriteHeader_WritesValidHeader() { using MemoryStream stream = new(); - TiffEncoderCore encoder = new(Encoder, Configuration.Default.MemoryAllocator); + TiffEncoderCore encoder = new(Encoder, Configuration.Default); using (TiffStreamWriter writer = new(stream)) { @@ -29,7 +29,7 @@ public class TiffEncoderHeaderTests public void WriteHeader_ReturnsFirstIfdMarker() { using MemoryStream stream = new(); - TiffEncoderCore encoder = new(Encoder, Configuration.Default.MemoryAllocator); + TiffEncoderCore encoder = new(Encoder, Configuration.Default); using TiffStreamWriter writer = new(stream); long firstIfdMarker = TiffEncoderCore.WriteHeader(writer, stackalloc byte[4]); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 1fafb4cd0..197210116 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -31,7 +31,7 @@ public class TiffEncoderTests : TiffEncoderBaseTester public void EncoderOptions_SetPhotometricInterpretation_Works(TiffPhotometricInterpretation? photometricInterpretation, TiffBitsPerPixel expectedBitsPerPixel) { // arrange - TiffEncoder tiffEncoder = new() { PhotometricInterpretation = photometricInterpretation }; + TiffEncoder tiffEncoder = new() { BitsPerPixel = expectedBitsPerPixel, PhotometricInterpretation = photometricInterpretation }; using Image input = expectedBitsPerPixel is TiffBitsPerPixel.Bit16 ? new Image(10, 10) : new Image(10, 10); @@ -57,8 +57,7 @@ public class TiffEncoderTests : TiffEncoderBaseTester public void EncoderOptions_SetBitPerPixel_Works(TiffBitsPerPixel bitsPerPixel) { // arrange - TiffEncoder tiffEncoder = new() - { BitsPerPixel = bitsPerPixel }; + TiffEncoder tiffEncoder = new() { BitsPerPixel = bitsPerPixel }; using Image input = new Image(10, 10); using MemoryStream memStream = new(); @@ -156,7 +155,11 @@ public class TiffEncoderTests : TiffEncoderBaseTester { // arrange TiffEncoder tiffEncoder = new() - { PhotometricInterpretation = photometricInterpretation, Compression = compression }; + { + BitsPerPixel = expectedBitsPerPixel, + PhotometricInterpretation = photometricInterpretation, + Compression = compression + }; using Image input = expectedBitsPerPixel is TiffBitsPerPixel.Bit16 ? new Image(10, 10) : new Image(10, 10); @@ -199,25 +202,6 @@ public class TiffEncoderTests : TiffEncoderBaseTester Assert.Equal(expectedBitsPerPixel, frameMetaData.BitsPerPixel); } - [Fact] - public void TiffEncoder_PreservesBitsPerPixel_WhenInputIsL8() - { - // arrange - TiffEncoder tiffEncoder = new(); - using Image input = new Image(10, 10); - using MemoryStream memStream = new(); - const TiffBitsPerPixel expectedBitsPerPixel = TiffBitsPerPixel.Bit8; - - // act - input.Save(memStream, tiffEncoder); - - // assert - memStream.Position = 0; - using Image output = Image.Load(memStream); - TiffFrameMetadata frameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata(); - Assert.Equal(expectedBitsPerPixel, frameMetaData.BitsPerPixel); - } - [Theory] [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffCompression.None)] [WithFile(RgbLzwNoPredictor, PixelTypes.Rgba32, TiffCompression.Lzw)] @@ -241,11 +225,11 @@ public class TiffEncoderTests : TiffEncoderBaseTester } [Theory] - [WithFile(RgbLzwNoPredictor, PixelTypes.Rgba32, null)] + [WithFile(RgbLzwNoPredictor, PixelTypes.Rgba32, TiffPredictor.None)] [WithFile(RgbLzwPredictor, PixelTypes.Rgba32, TiffPredictor.Horizontal)] - [WithFile(RgbDeflate, PixelTypes.Rgba32, null)] + [WithFile(RgbDeflate, PixelTypes.Rgba32, TiffPredictor.None)] [WithFile(RgbDeflatePredictor, PixelTypes.Rgba32, TiffPredictor.Horizontal)] - public void TiffEncoder_PreservesPredictor(TestImageProvider provider, TiffPredictor? expectedPredictor) + public void TiffEncoder_PreservesPredictor(TestImageProvider provider, TiffPredictor expectedPredictor) where TPixel : unmanaged, IPixel { // arrange diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index 5da12f264..05abedbd8 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -697,23 +697,6 @@ public static class TestImageExtensions return new AllocatorBufferCapacityConfigurator(allocator, Unsafe.SizeOf()); } - internal static Image ToGrayscaleImage(this Buffer2D buffer, float scale) - { - Image image = new(buffer.Width, buffer.Height); - - Assert.True(image.Frames.RootFrame.DangerousTryGetSinglePixelMemory(out Memory pixelMem)); - Span pixels = pixelMem.Span; - Span bufferSpan = buffer.DangerousGetSingleSpan(); - - for (int i = 0; i < bufferSpan.Length; i++) - { - float value = bufferSpan[i] * scale; - pixels[i] = Rgba32.FromVector4(new Vector4(value, value, value, 1f)); - } - - return image; - } - private class MakeOpaqueProcessor : IImageProcessor { public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle)