From 1b568ceedd5190a1ea23b5a5f758d8e921ef54a1 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Sun, 26 Feb 2017 20:47:20 +0000 Subject: [PATCH 001/275] Add initial Tiff codec projects, and TiffGen --- .../ImageSharp.Formats.Tiff.csproj | 7 + src/ImageSharp.Formats.Tiff/TiffTags.cs | 205 ++++++++++++++++++ src/ImageSharp.Formats.Tiff/TiffType.cs | 27 +++ .../ImageSharp.Formats.Tiff.Tests.csproj | 18 ++ .../TestUtilities/ByteArrayUtility.cs | 28 +++ .../TestUtilities/ByteBuffer.cs | 41 ++++ .../TestUtilities/Tiff/ITiffGenDataSource.cs | 18 ++ .../TestUtilities/Tiff/TiffGenDataBlock.cs | 31 +++ .../Tiff/TiffGenDataReference.cs | 25 +++ .../TestUtilities/Tiff/TiffGenEntry.cs | 102 +++++++++ .../TestUtilities/Tiff/TiffGenExtensions.cs | 47 ++++ .../TestUtilities/Tiff/TiffGenHeader.cs | 42 ++++ .../TestUtilities/Tiff/TiffGenIfd.cs | 91 ++++++++ 13 files changed, 682 insertions(+) create mode 100644 src/ImageSharp.Formats.Tiff/ImageSharp.Formats.Tiff.csproj create mode 100644 src/ImageSharp.Formats.Tiff/TiffTags.cs create mode 100644 src/ImageSharp.Formats.Tiff/TiffType.cs create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/ImageSharp.Formats.Tiff.Tests.csproj create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/ByteArrayUtility.cs create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/ByteBuffer.cs create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/ITiffGenDataSource.cs create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenDataBlock.cs create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenDataReference.cs create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenExtensions.cs create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenHeader.cs create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfd.cs diff --git a/src/ImageSharp.Formats.Tiff/ImageSharp.Formats.Tiff.csproj b/src/ImageSharp.Formats.Tiff/ImageSharp.Formats.Tiff.csproj new file mode 100644 index 000000000..23103c903 --- /dev/null +++ b/src/ImageSharp.Formats.Tiff/ImageSharp.Formats.Tiff.csproj @@ -0,0 +1,7 @@ + + + + netstandard1.1 + + + diff --git a/src/ImageSharp.Formats.Tiff/TiffTags.cs b/src/ImageSharp.Formats.Tiff/TiffTags.cs new file mode 100644 index 000000000..db4087d58 --- /dev/null +++ b/src/ImageSharp.Formats.Tiff/TiffTags.cs @@ -0,0 +1,205 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + /// + /// Constants representing tag IDs in the Tiff file-format. + /// + public class TiffTags + { + // Section 8: Baseline Fields + + public const int Artist = 315; + public const int BitsPerSample = 258; + public const int CellLength = 265; + public const int CellWidth = 264; + public const int ColorMap = 320; + public const int Compression = 259; + public const int Copyright = 33432; + public const int DateTime = 306; + public const int ExtraSamples = 338; + public const int FillOrder = 266; + public const int FreeByteCounts = 289; + public const int FreeOffsets = 288; + public const int GrayResponseCurve = 291; + public const int GrayResponseUnit = 290; + public const int HostComputer = 316; + public const int ImageDescription = 270; + public const int ImageLength = 257; + public const int ImageWidth = 256; + public const int Make = 271; + public const int MaxSampleValue = 281; + public const int MinSampleValue = 280; + public const int Model = 272; + public const int NewSubfileType = 254; + public const int Orientation = 274; + public const int PhotometricInterpretation = 262; + public const int PlanarConfiguration = 284; + public const int ResolutionUnit = 296; + public const int RowsPerStrip = 278; + public const int SamplesPerPixel = 277; + public const int Software = 305; + public const int StripByteCounts = 279; + public const int StripOffsets = 273; + public const int SubfileType = 255; + public const int Threshholding = 263; + public const int XResolution = 282; + public const int YResolution = 283; + + // Section 11: CCITT Bilevel Encodings + + public const int T4Options = 292; + public const int T6Options = 293; + + // Section 12: Document Storage and Retrieval + + public const int DocumentName = 269; + public const int PageName = 285; + public const int PageNumber = 297; + public const int XPosition = 286; + public const int YPosition = 287; + + // Section 14: Differencing Predictor + + public const int Predictor = 317; + + // Section 15: Tiled Images + + public const int TileWidth = 322; + public const int TileLength = 323; + public const int TileOffsets = 324; + public const int TileByteCounts = 325; + + // Section 16: CMYK Images + + public const int InkSet = 332; + public const int NumberOfInks = 334; + public const int InkNames = 333; + public const int DotRange = 336; + public const int TargetPrinter = 337; + + // Section 17: Halftone Hints + + public const int HalftoneHints = 321; + + // Section 19: Data Sample Format + + public const int SampleFormat = 339; + public const int SMinSampleValue = 340; + public const int SMaxSampleValue = 341; + + // Section 20: RGB Image Colorimetry + + public const int WhitePoint = 318; + public const int PrimaryChromaticities = 319; + public const int TransferFunction = 301; + public const int TransferRange = 342; + public const int ReferenceBlackWhite = 532; + + // Section 21: YCbCr Images + + public const int YCbCrCoefficients = 529; + public const int YCbCrSubSampling = 530; + public const int YCbCrPositioning = 531; + + // Section 22: JPEG Compression + + public const int JpegProc = 512; + public const int JpegInterchangeFormat = 513; + public const int JpegInterchangeFormatLength = 514; + public const int JpegRestartInterval = 515; + public const int JpegLosslessPredictors = 517; + public const int JpegPointTransforms = 518; + public const int JpegQTables = 519; + public const int JpegDCTables = 520; + public const int JpegACTables = 521; + + // TIFF Supplement 1: Adobe Pagemaker 6.0 + + public const int SubIFDs = 330; + public const int ClipPath = 343; + public const int XClipPathUnits = 344; + public const int YClipPathUnits = 345; + public const int Indexed = 346; + public const int ImageID = 32781; + public const int OpiProxy = 351; + + // TIFF Supplement 2: Adobe Photoshop + + public const int ImageSourceData = 37724; + + // TIFF/EP Specification: Additional Tags + + public const int JPEGTables = 0x015B; + public const int CFARepeatPatternDim = 0x828D; + public const int BatteryLevel = 0x828F; + public const int Interlace = 0x8829; + public const int TimeZoneOffset = 0x882A; + public const int SelfTimerMode = 0x882B; + public const int Noise = 0x920D; + public const int ImageNumber = 0x9211; + public const int SecurityClassification = 0x9212; + public const int ImageHistory = 0x9213; + public const int TiffEPStandardID = 0x9216; + + // TIFF-F/FX Specification (http://www.ietf.org/rfc/rfc2301.txt) + + public const int BadFaxLines = 326; + public const int CleanFaxData = 327; + public const int ConsecutiveBadFaxLines = 328; + public const int GlobalParametersIFD = 400; + public const int ProfileType = 401; + public const int FaxProfile = 402; + public const int CodingMethod = 403; + public const int VersionYear = 404; + public const int ModeNumber = 405; + public const int Decode = 433; + public const int DefaultImageColor = 434; + public const int StripRowCounts = 559; + public const int ImageLayer = 34732; + + // Embedded Metadata + + public const int Xmp = 700; + public const int Iptc = 33723; + public const int Photoshop = 34377; + public const int ExifIFD = 34665; + public const int GpsIFD = 34853; + public const int InteroperabilityIFD = 40965; + + // Other Private TIFF tags (http://www.awaresystems.be/imaging/tiff/tifftags/private.html) + + public const int WangAnnotation = 32932; + public const int MDFileTag = 33445; + public const int MDScalePixel = 33446; + public const int MDColorTable = 33447; + public const int MDLabName = 33448; + public const int MDSampleInfo = 33449; + public const int MDPrepDate = 33450; + public const int MDPrepTime = 33451; + public const int MDFileUnits = 33452; + public const int ModelPixelScaleTag = 33550; + public const int IngrPacketDataTag = 33918; + public const int IngrFlagRegisters = 33919; + public const int IrasBTransformationMatrix = 33920; + public const int ModelTiePointTag = 33922; + public const int ModelTransformationTag = 34264; + public const int IccProfile = 34675; + public const int GeoKeyDirectoryTag = 34735; + public const int GeoDoubleParamsTag = 34736; + public const int GeoAsciiParamsTag = 34737; + public const int HylaFAXFaxRecvParams = 34908; + public const int HylaFAXFaxSubAddress = 34909; + public const int HylaFAXFaxRecvTime = 34910; + public const int GdalMetadata = 42112; + public const int GdalNodata = 42113; + public const int OceScanjobDescription = 50215; + public const int OceApplicationSelector = 50216; + public const int OceIdentificationNumber = 50217; + public const int OceImageLogicCharacteristics = 50218; + public const int AliasLayerMetadata = 50784; + } +} \ No newline at end of file diff --git a/src/ImageSharp.Formats.Tiff/TiffType.cs b/src/ImageSharp.Formats.Tiff/TiffType.cs new file mode 100644 index 000000000..1bb7f6cfb --- /dev/null +++ b/src/ImageSharp.Formats.Tiff/TiffType.cs @@ -0,0 +1,27 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + /// + /// Enumeration representing the data types understood by the Tiff file-format. + /// + public enum TiffType + { + Byte = 1, + Ascii = 2, + Short = 3, + Long = 4, + Rational = 5, + SByte = 6, + Undefined = 7, + SShort = 8, + SLong = 9, + SRational = 10, + Float = 11, + Double = 12, + Ifd = 13 + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/ImageSharp.Formats.Tiff.Tests.csproj b/tests/ImageSharp.Formats.Tiff.Tests/ImageSharp.Formats.Tiff.Tests.csproj new file mode 100644 index 000000000..a20f1fd99 --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/ImageSharp.Formats.Tiff.Tests.csproj @@ -0,0 +1,18 @@ + + + + Exe + netcoreapp1.0 + + + + + + + + + + + + + diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/ByteArrayUtility.cs b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/ByteArrayUtility.cs new file mode 100644 index 000000000..8021f5330 --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/ByteArrayUtility.cs @@ -0,0 +1,28 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System; + using System.Collections.Generic; + + public static class ByteArrayUtility + { + public static byte[] WithByteOrder(this byte[] bytes, bool isLittleEndian) + { + if (BitConverter.IsLittleEndian != isLittleEndian) + { + byte[] reversedBytes = new byte[bytes.Length]; + Array.Copy(bytes, reversedBytes, bytes.Length); + Array.Reverse(reversedBytes); + return reversedBytes; + } + else + { + return bytes; + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/ByteBuffer.cs b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/ByteBuffer.cs new file mode 100644 index 000000000..290c942f8 --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/ByteBuffer.cs @@ -0,0 +1,41 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System; + using System.Collections.Generic; + + public class ByteBuffer + { + List bytes = new List(); + bool isLittleEndian; + + public ByteBuffer(bool isLittleEndian) + { + this.isLittleEndian = isLittleEndian; + } + + public void AddByte(byte value) + { + bytes.Add(value); + } + + public void AddUInt16(ushort value) + { + bytes.AddRange(BitConverter.GetBytes(value).WithByteOrder(isLittleEndian)); + } + + public void AddUInt32(uint value) + { + bytes.AddRange(BitConverter.GetBytes(value).WithByteOrder(isLittleEndian)); + } + + public byte[] ToArray() + { + return bytes.ToArray(); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/ITiffGenDataSource.cs b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/ITiffGenDataSource.cs new file mode 100644 index 000000000..33bf99924 --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/ITiffGenDataSource.cs @@ -0,0 +1,18 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System; + using System.Collections.Generic; + + /// + /// An interface for any class within the Tiff generator that produces data to be included in the file. + /// + public interface ITiffGenDataSource + { + IEnumerable GetData(bool isLittleEndian); + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenDataBlock.cs b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenDataBlock.cs new file mode 100644 index 000000000..bbce12054 --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenDataBlock.cs @@ -0,0 +1,31 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System; + using System.Collections.Generic; + + /// + /// A utility data structure to represent an independent block of data in a Tiff file. + /// These may be located in any order within a Tiff file. + /// + public class TiffGenDataBlock + { + public TiffGenDataBlock(byte[] bytes) + { + this.Bytes = bytes; + this.References = new List(); + } + + public byte[] Bytes { get; } + public IList References { get; } + + public void AddReference(byte[] bytes, int offset) + { + References.Add(new TiffGenDataReference(bytes, offset)); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenDataReference.cs b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenDataReference.cs new file mode 100644 index 000000000..ec4c0c5df --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenDataReference.cs @@ -0,0 +1,25 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System; + using System.Collections.Generic; + + /// + /// A utility data structure to represent a reference from one block of data to another in a Tiff file. + /// + public class TiffGenDataReference + { + public TiffGenDataReference(byte[] bytes, int offset) + { + this.Bytes = bytes; + this.Offset = offset; + } + + public byte[] Bytes { get; } + public int Offset { get; } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs new file mode 100644 index 000000000..4f0ff868b --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs @@ -0,0 +1,102 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using ImageSharp.Formats; + + /// + /// A utility data structure to represent Tiff IFD entries in unit tests. + /// + public abstract class TiffGenEntry : ITiffGenDataSource + { + private TiffGenEntry(ushort tag, TiffType type) + { + this.Tag = tag; + this.Type = type; + } + + public ushort Tag { get; } + public TiffType Type { get; } + + public abstract IEnumerable GetData(bool isLittleEndian); + + public static TiffGenEntry Ascii(ushort tag, string value) + { + return new TiffGenEntryAscii(tag, value); + } + + public static TiffGenEntry Integer(ushort tag, TiffType type, int value) + { + return TiffGenEntry.Integer(tag, type, new int[] {value}); + } + + public static TiffGenEntry Integer(ushort tag, TiffType type, int[] value) + { + if (type != TiffType.Byte && type != TiffType.Short && type != TiffType.Long && + type != TiffType.SByte && type != TiffType.SShort && type != TiffType.SLong) + throw new ArgumentException(nameof(type), "The specified type is not an integer type."); + + return new TiffGenEntryInteger(tag, type, value); + } + + private class TiffGenEntryAscii : TiffGenEntry + { + public TiffGenEntryAscii(ushort tag, string value) : base(tag, TiffType.Ascii) + { + this.Value = value; + } + + public string Value { get; } + + public override IEnumerable GetData(bool isLittleEndian) + { + byte[] bytes = Encoding.ASCII.GetBytes($"{Value}\0"); + return new[] { new TiffGenDataBlock(bytes) }; + } + } + + private class TiffGenEntryInteger : TiffGenEntry + { + public TiffGenEntryInteger(ushort tag, TiffType type, int[] value) : base(tag, type) + { + this.Value = value; + } + + public int[] Value { get; } + + public override IEnumerable GetData(bool isLittleEndian) + { + byte[] bytes = GetBytes().SelectMany(b => b.WithByteOrder(isLittleEndian)).ToArray(); + return new[] { new TiffGenDataBlock(bytes) }; + } + + private IEnumerable GetBytes() + { + switch (Type) + { + case TiffType.Byte: + return Value.Select(i => new byte[] { (byte)i }); + case TiffType.Short: + return Value.Select(i => BitConverter.GetBytes((ushort)i)); + case TiffType.Long: + return Value.Select(i => BitConverter.GetBytes((uint)i)); + case TiffType.SByte: + return Value.Select(i => BitConverter.GetBytes((sbyte)i)); + case TiffType.SShort: + return Value.Select(i => BitConverter.GetBytes((short)i)); + case TiffType.SLong: + return Value.Select(i => BitConverter.GetBytes((int)i)); + default: + throw new InvalidOperationException(); + } + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenExtensions.cs b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenExtensions.cs new file mode 100644 index 000000000..87ba62121 --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenExtensions.cs @@ -0,0 +1,47 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System; + using System.IO; + using System.Linq; + + /// + /// A utility class for generating in-memory Tiff files for use in unit tests. + /// + public static class TiffGenExtensions + { + public static byte[] ToBytes(this ITiffGenDataSource dataSource, bool isLittleEndian) + { + var dataBlocks = dataSource.GetData(isLittleEndian); + + int offset = 0; + + foreach (var dataBlock in dataBlocks) + { + byte[] offsetBytes = BitConverter.GetBytes(offset).WithByteOrder(isLittleEndian); + + foreach (var reference in dataBlock.References) + { + reference.Bytes[reference.Offset + 0] = offsetBytes[0]; + reference.Bytes[reference.Offset + 1] = offsetBytes[1]; + reference.Bytes[reference.Offset + 2] = offsetBytes[2]; + reference.Bytes[reference.Offset + 3] = offsetBytes[3]; + } + + offset += dataBlock.Bytes.Length; + } + + return dataBlocks.SelectMany(b => b.Bytes).ToArray(); + } + + public static Stream ToStream(this ITiffGenDataSource dataSource, bool isLittleEndian) + { + var bytes = dataSource.ToBytes(isLittleEndian); + return new MemoryStream(bytes); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenHeader.cs b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenHeader.cs new file mode 100644 index 000000000..b270ff208 --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenHeader.cs @@ -0,0 +1,42 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System; + using System.Collections.Generic; + using System.Linq; + + /// + /// A utility data structure to represent a Tiff file-header. + /// + public class TiffGenHeader : ITiffGenDataSource + { + public TiffGenHeader() + { + this.MagicNumber = 42; + } + + public ushort? ByteOrderMarker { get; set; } + public ushort MagicNumber { get; set; } + public TiffGenIfd FirstIfd { get; set; } + + public IEnumerable GetData(bool isLittleEndian) + { + ByteBuffer bytes = new ByteBuffer(isLittleEndian); + + bytes.AddUInt16(ByteOrderMarker ?? (isLittleEndian ? (ushort)0x4949 : (ushort)0x4D4D)); + bytes.AddUInt16(MagicNumber); + bytes.AddUInt32(0); + + var headerData = new TiffGenDataBlock(bytes.ToArray()); + var firstIfdData = FirstIfd.GetData(isLittleEndian); + + firstIfdData.First().AddReference(headerData.Bytes, 4); + + return new [] { headerData }.Concat(firstIfdData); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfd.cs b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfd.cs new file mode 100644 index 000000000..2e745df36 --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfd.cs @@ -0,0 +1,91 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System; + using System.Collections.Generic; + using System.Linq; + + /// + /// A utility data structure to represent Tiff IFDs in unit tests. + /// + public class TiffGenIfd : ITiffGenDataSource + { + public TiffGenIfd() + { + this.Entries = new List(); + } + + public List Entries { get; } + public TiffGenIfd NextIfd { get; set; } + + public IEnumerable GetData(bool isLittleEndian) + { + ByteBuffer bytes = new ByteBuffer(isLittleEndian); + List dataBlocks = new List(); + List> entryReferences = new List>(); + + // Add the entry count + + bytes.AddUInt16((ushort)Entries.Count); + + // Add all IFD entries + + int entryOffset = 2; + + foreach (var entry in Entries) + { + var entryData = entry.GetData(isLittleEndian); + var entryBytes = entryData.First().Bytes; + var entryCount = entryBytes.Length; + + bytes.AddUInt16(entry.Tag); + bytes.AddUInt16((ushort)entry.Type); + bytes.AddUInt32((uint)entryCount); + + if (entryCount <=4) + { + bytes.AddByte(entryCount > 0 ? entryBytes[0] : (byte)0); + bytes.AddByte(entryCount > 1 ? entryBytes[1] : (byte)0); + bytes.AddByte(entryCount > 2 ? entryBytes[2] : (byte)0); + bytes.AddByte(entryCount > 3 ? entryBytes[3] : (byte)0); + + dataBlocks.AddRange(entryData.Skip(1)); + } + else + { + bytes.AddUInt32(0); + dataBlocks.AddRange(entryData); + entryReferences.Add(Tuple.Create(entryData.First(), entryOffset + 8)); + } + + entryOffset += 12; + } + + // Add reference to next IFD + + bytes.AddUInt32(0); + + // Build the data + + var ifdData = new TiffGenDataBlock(bytes.ToArray()); + + foreach (var entryReference in entryReferences) + { + entryReference.Item1.AddReference(ifdData.Bytes, entryReference.Item2); + } + + IEnumerable nextIfdData = new TiffGenDataBlock[0]; + if (NextIfd != null) + { + nextIfdData = NextIfd.GetData(isLittleEndian); + nextIfdData.First().AddReference(ifdData.Bytes, ifdData.Bytes.Length - 4); + } + + return new [] { ifdData }.Concat(dataBlocks).Concat(nextIfdData); + } + } +} \ No newline at end of file From 64f791d8d855dc2736dcab22db5d6b220587c779 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Tue, 28 Feb 2017 19:21:28 +0000 Subject: [PATCH 002/275] Add Tiff implementation of IImageFormat --- src/ImageSharp.Formats.Tiff/TiffConstants.cs | 30 +++++++ src/ImageSharp.Formats.Tiff/TiffFormat.cs | 41 +++++++++ .../Formats/Tiff/TiffFormatTests.cs | 88 +++++++++++++++++++ 3 files changed, 159 insertions(+) create mode 100644 src/ImageSharp.Formats.Tiff/TiffConstants.cs create mode 100644 src/ImageSharp.Formats.Tiff/TiffFormat.cs create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffFormatTests.cs diff --git a/src/ImageSharp.Formats.Tiff/TiffConstants.cs b/src/ImageSharp.Formats.Tiff/TiffConstants.cs new file mode 100644 index 000000000..e5d2df044 --- /dev/null +++ b/src/ImageSharp.Formats.Tiff/TiffConstants.cs @@ -0,0 +1,30 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + using System.Text; + + /// + /// Defines constants defined in the TIFF specification. + /// + internal static class GifConstants + { + /// + /// Byte order markers for indicating little endian encoding. + /// + public const ushort ByteOrderLittleEndian = 0x4949; + + /// + /// Byte order markers for indicating big endian encoding. + /// + public const ushort ByteOrderBigEndian = 0x4D4D; + + /// + /// Magic number used within the image file header to identify a TIFF format file. + /// + public const ushort HeaderMagicNumber = 42; + } +} diff --git a/src/ImageSharp.Formats.Tiff/TiffFormat.cs b/src/ImageSharp.Formats.Tiff/TiffFormat.cs new file mode 100644 index 000000000..010c54f0a --- /dev/null +++ b/src/ImageSharp.Formats.Tiff/TiffFormat.cs @@ -0,0 +1,41 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + using System.Collections.Generic; + + /// + /// Encapsulates the means to encode and decode Tiff images. + /// + public class TiffFormat //: IImageFormat + { + /// + public string MimeType => "image/tiff"; + + /// + public string Extension => "tif"; + + /// + public IEnumerable SupportedExtensions => new string[] { "tif", "tiff" }; + + /// + //public IImageDecoder Decoder => new TiffDecoder(); + + /// + //public IImageEncoder Encoder => throw new System.NotImplementedException(); + + /// + public int HeaderSize => 4; + + /// + public bool IsSupportedFileFormat(byte[] header) + { + return header.Length >= this.HeaderSize && + ((header[0] == 0x49 && header[1] == 0x49 && header[2] == 0x2A && header[3] == 0x00) || // Little-endian + (header[0] == 0x4D && header[1] == 0x4D && header[2] == 0x00 && header[3] == 0x2A)); // Big-endian + } + } +} diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffFormatTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffFormatTests.cs new file mode 100644 index 000000000..313b9c950 --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffFormatTests.cs @@ -0,0 +1,88 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System.Linq; + using Xunit; + + using ImageSharp.Formats; + + public class TiffFormatTests + { + public static object[][] IsLittleEndianValues = new[] { new object[] { false }, + new object[] { true } }; + + [Theory] + [MemberData(nameof(IsLittleEndianValues))] + public void IsSupportedFileFormat_ReturnsTrue_ForValidFile(bool isLittleEndian) + { + byte[] bytes = new TiffGenHeader() + { + FirstIfd = new TiffGenIfd() + } + .ToBytes(isLittleEndian); + + TiffFormat tiffFormat = new TiffFormat(); + byte[] headerBytes = bytes.Take(tiffFormat.HeaderSize).ToArray(); + bool isSupported = tiffFormat.IsSupportedFileFormat(headerBytes); + + Assert.True(isSupported); + } + + [Theory] + [MemberData(nameof(IsLittleEndianValues))] + public void IsSupportedFileFormat_ReturnsFalse_WithInvalidByteOrderMarkers(bool isLittleEndian) + { + byte[] bytes = new TiffGenHeader() + { + FirstIfd = new TiffGenIfd(), + ByteOrderMarker = 0x1234 + } + .ToBytes(isLittleEndian); + + TiffFormat tiffFormat = new TiffFormat(); + byte[] headerBytes = bytes.Take(tiffFormat.HeaderSize).ToArray(); + bool isSupported = tiffFormat.IsSupportedFileFormat(headerBytes); + + Assert.False(isSupported); + } + + [Theory] + [MemberData(nameof(IsLittleEndianValues))] + public void IsSupportedFileFormat_ReturnsFalse_WithIncorrectMagicNumber(bool isLittleEndian) + { + byte[] bytes = new TiffGenHeader() + { + FirstIfd = new TiffGenIfd(), + MagicNumber = 32 + } + .ToBytes(isLittleEndian); + + TiffFormat tiffFormat = new TiffFormat(); + byte[] headerBytes = bytes.Take(tiffFormat.HeaderSize).ToArray(); + bool isSupported = tiffFormat.IsSupportedFileFormat(headerBytes); + + Assert.False(isSupported); + } + + [Theory] + [MemberData(nameof(IsLittleEndianValues))] + public void IsSupportedFileFormat_ReturnsFalse_WithShortHeader(bool isLittleEndian) + { + byte[] bytes = new TiffGenHeader() + { + FirstIfd = new TiffGenIfd() + } + .ToBytes(isLittleEndian); + + TiffFormat tiffFormat = new TiffFormat(); + byte[] headerBytes = bytes.Take(tiffFormat.HeaderSize - 1).ToArray(); + bool isSupported = tiffFormat.IsSupportedFileFormat(headerBytes); + + Assert.False(isSupported); + } + } +} From b120e7b359ed8dbffe3d1e25970994ed30056272 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Thu, 2 Mar 2017 21:57:08 +0000 Subject: [PATCH 003/275] Reference ImageSharp (temporary code sharing) --- .../ImageSharp.Formats.Tiff.csproj | 12 ++++++++++ src/ImageSharp.Formats.Tiff/TiffFormat.cs | 6 ++--- .../Formats/Tiff/TiffFormatTests.cs | 22 +++++++++++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp.Formats.Tiff/ImageSharp.Formats.Tiff.csproj b/src/ImageSharp.Formats.Tiff/ImageSharp.Formats.Tiff.csproj index 23103c903..2df493b7d 100644 --- a/src/ImageSharp.Formats.Tiff/ImageSharp.Formats.Tiff.csproj +++ b/src/ImageSharp.Formats.Tiff/ImageSharp.Formats.Tiff.csproj @@ -2,6 +2,18 @@ netstandard1.1 + true + + + + + + + + + + + diff --git a/src/ImageSharp.Formats.Tiff/TiffFormat.cs b/src/ImageSharp.Formats.Tiff/TiffFormat.cs index 010c54f0a..805eef87b 100644 --- a/src/ImageSharp.Formats.Tiff/TiffFormat.cs +++ b/src/ImageSharp.Formats.Tiff/TiffFormat.cs @@ -10,7 +10,7 @@ namespace ImageSharp.Formats /// /// Encapsulates the means to encode and decode Tiff images. /// - public class TiffFormat //: IImageFormat + public class TiffFormat : IImageFormat { /// public string MimeType => "image/tiff"; @@ -22,10 +22,10 @@ namespace ImageSharp.Formats public IEnumerable SupportedExtensions => new string[] { "tif", "tiff" }; /// - //public IImageDecoder Decoder => new TiffDecoder(); + public IImageDecoder Decoder => new TiffDecoder(); /// - //public IImageEncoder Encoder => throw new System.NotImplementedException(); + public IImageEncoder Encoder => throw new System.NotImplementedException(); /// public int HeaderSize => 4; diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffFormatTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffFormatTests.cs index 313b9c950..e0f8fd41b 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffFormatTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffFormatTests.cs @@ -15,6 +15,17 @@ namespace ImageSharp.Tests public static object[][] IsLittleEndianValues = new[] { new object[] { false }, new object[] { true } }; + [Fact] + public void FormatProperties_AreAsExpected() + { + TiffFormat tiffFormat = new TiffFormat(); + + Assert.Equal("image/tiff", tiffFormat.MimeType); + Assert.Equal("tif", tiffFormat.Extension); + Assert.Contains("tif", tiffFormat.SupportedExtensions); + Assert.Contains("tiff", tiffFormat.SupportedExtensions); + } + [Theory] [MemberData(nameof(IsLittleEndianValues))] public void IsSupportedFileFormat_ReturnsTrue_ForValidFile(bool isLittleEndian) @@ -84,5 +95,16 @@ namespace ImageSharp.Tests Assert.False(isSupported); } + + [Fact] + public void Decoder_ReturnsTiffDecoder() + { + TiffFormat tiffFormat = new TiffFormat(); + + var decoder = tiffFormat.Decoder; + + Assert.NotNull(decoder); + Assert.IsType(decoder); + } } } From 9848245df3883755700d556e0b2859b44ed1ce69 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Thu, 2 Mar 2017 22:29:00 +0000 Subject: [PATCH 004/275] Add stub Tiff encoders/decoders --- .../ITiffEncoderOptions.cs | 14 +++++ src/ImageSharp.Formats.Tiff/TiffDecoder.cs | 29 ++++++++++ .../TiffDecoderCore.cs | 58 +++++++++++++++++++ src/ImageSharp.Formats.Tiff/TiffEncoder.cs | 40 +++++++++++++ .../TiffEncoderOptions.cs | 40 +++++++++++++ src/ImageSharp.Formats.Tiff/TiffFormat.cs | 2 +- .../Formats/Tiff/TiffFormatTests.cs | 11 ++++ 7 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 src/ImageSharp.Formats.Tiff/ITiffEncoderOptions.cs create mode 100644 src/ImageSharp.Formats.Tiff/TiffDecoder.cs create mode 100644 src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs create mode 100644 src/ImageSharp.Formats.Tiff/TiffEncoder.cs create mode 100644 src/ImageSharp.Formats.Tiff/TiffEncoderOptions.cs diff --git a/src/ImageSharp.Formats.Tiff/ITiffEncoderOptions.cs b/src/ImageSharp.Formats.Tiff/ITiffEncoderOptions.cs new file mode 100644 index 000000000..df2ad7770 --- /dev/null +++ b/src/ImageSharp.Formats.Tiff/ITiffEncoderOptions.cs @@ -0,0 +1,14 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + /// + /// Encapsulates the options for the . + /// + public interface ITiffEncoderOptions : IEncoderOptions + { + } +} diff --git a/src/ImageSharp.Formats.Tiff/TiffDecoder.cs b/src/ImageSharp.Formats.Tiff/TiffDecoder.cs new file mode 100644 index 000000000..4b99c1cb6 --- /dev/null +++ b/src/ImageSharp.Formats.Tiff/TiffDecoder.cs @@ -0,0 +1,29 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + using System; + using System.IO; + + /// + /// Image decoder for generating an image out of a TIFF stream. + /// + public class TiffDecoder : IImageDecoder + { + /// + public void Decode(Image image, Stream stream, IDecoderOptions options) + where TColor : struct, IPixel + { + Guard.NotNull(image, "image"); + Guard.NotNull(stream, "stream"); + + using (TiffDecoderCore decoder = new TiffDecoderCore(options)) + { + decoder.Decode(image, stream, false); + } + } + } +} diff --git a/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs b/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs new file mode 100644 index 000000000..e9bbb650b --- /dev/null +++ b/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs @@ -0,0 +1,58 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + using System; + using System.IO; + using System.Runtime.CompilerServices; + using System.Threading.Tasks; + + /// + /// Performs the tiff decoding operation. + /// + internal class TiffDecoderCore : IDisposable + { + /// + /// The decoder options. + /// + private readonly IDecoderOptions options; + + /// + /// Initializes a new instance of the class. + /// + /// The decoder options. + public TiffDecoderCore(IDecoderOptions options) + { + this.options = options ?? new DecoderOptions(); + } + + /// + /// Gets the input stream. + /// + public Stream InputStream { get; private set; } + + /// + /// Decodes the image from the specified and sets + /// the data to image. + /// + /// The pixel format. + /// The image, where the data should be set to. + /// The stream, where the image should be. + /// Whether to decode metadata only. + public void Decode(Image image, Stream stream, bool metadataOnly) + where TColor : struct, IPixel + { + this.InputStream = stream; + } + + /// + /// Dispose + /// + public void Dispose() + { + } + } +} diff --git a/src/ImageSharp.Formats.Tiff/TiffEncoder.cs b/src/ImageSharp.Formats.Tiff/TiffEncoder.cs new file mode 100644 index 000000000..7193c1a76 --- /dev/null +++ b/src/ImageSharp.Formats.Tiff/TiffEncoder.cs @@ -0,0 +1,40 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + using System; + using System.IO; + + /// + /// Encoder for writing the data image to a stream in TIFF format. + /// + public class TiffEncoder : IImageEncoder + { + /// + public void Encode(Image image, Stream stream, IEncoderOptions options) + where TColor : struct, IPixel + { + ITiffEncoderOptions tiffOptions = TiffEncoderOptions.Create(options); + + this.Encode(image, stream, tiffOptions); + } + + /// + /// Encodes the image to the specified stream from the . + /// + /// The pixel format. + /// The to encode from. + /// The to encode the image data to. + /// The options for the encoder. + public void Encode(Image image, Stream stream, ITiffEncoderOptions options) + where TColor : struct, IPixel + { + throw new NotImplementedException(); + // TiffEncoderCore encode = new TiffEncoderCore(options); + // encode.Encode(image, stream); + } + } +} diff --git a/src/ImageSharp.Formats.Tiff/TiffEncoderOptions.cs b/src/ImageSharp.Formats.Tiff/TiffEncoderOptions.cs new file mode 100644 index 000000000..ed72bbb92 --- /dev/null +++ b/src/ImageSharp.Formats.Tiff/TiffEncoderOptions.cs @@ -0,0 +1,40 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + /// + /// Encapsulates the options for the . + /// + public sealed class TiffEncoderOptions : EncoderOptions, ITiffEncoderOptions + { + /// + /// Initializes a new instance of the class. + /// + public TiffEncoderOptions() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The options for the encoder. + private TiffEncoderOptions(IEncoderOptions options) + : base(options) + { + } + + /// + /// Converts the options to a instance with a + /// cast or by creating a new instance with the specfied options. + /// + /// The options for the encoder. + /// The options for the . + internal static ITiffEncoderOptions Create(IEncoderOptions options) + { + return options as ITiffEncoderOptions ?? new TiffEncoderOptions(options); + } + } +} diff --git a/src/ImageSharp.Formats.Tiff/TiffFormat.cs b/src/ImageSharp.Formats.Tiff/TiffFormat.cs index 805eef87b..6f09d4909 100644 --- a/src/ImageSharp.Formats.Tiff/TiffFormat.cs +++ b/src/ImageSharp.Formats.Tiff/TiffFormat.cs @@ -25,7 +25,7 @@ namespace ImageSharp.Formats public IImageDecoder Decoder => new TiffDecoder(); /// - public IImageEncoder Encoder => throw new System.NotImplementedException(); + public IImageEncoder Encoder => new TiffEncoder(); /// public int HeaderSize => 4; diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffFormatTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffFormatTests.cs index e0f8fd41b..265787546 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffFormatTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffFormatTests.cs @@ -106,5 +106,16 @@ namespace ImageSharp.Tests Assert.NotNull(decoder); Assert.IsType(decoder); } + + [Fact] + public void Encoder_ReturnsTiffEncoder() + { + TiffFormat tiffFormat = new TiffFormat(); + + var encoder = tiffFormat.Encoder; + + Assert.NotNull(encoder); + Assert.IsType(encoder); + } } } From 1c9f39918f8dbf4fc65ac1999ebfd55e4d71c5ce Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Sat, 4 Mar 2017 11:50:00 +0000 Subject: [PATCH 005/275] Read and validate the TIFF file header. --- src/ImageSharp.Formats.Tiff/TiffConstants.cs | 6 +- .../TiffDecoderCore.cs | 71 +++++++++++++++++ .../Formats/Tiff/TiffDecoderHeaderTests.cs | 79 +++++++++++++++++++ .../TestUtilities/Tiff/TiffGenHeader.cs | 14 +++- 4 files changed, 163 insertions(+), 7 deletions(-) create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs diff --git a/src/ImageSharp.Formats.Tiff/TiffConstants.cs b/src/ImageSharp.Formats.Tiff/TiffConstants.cs index e5d2df044..0f9145208 100644 --- a/src/ImageSharp.Formats.Tiff/TiffConstants.cs +++ b/src/ImageSharp.Formats.Tiff/TiffConstants.cs @@ -10,17 +10,17 @@ namespace ImageSharp.Formats /// /// Defines constants defined in the TIFF specification. /// - internal static class GifConstants + internal static class TiffConstants { /// /// Byte order markers for indicating little endian encoding. /// - public const ushort ByteOrderLittleEndian = 0x4949; + public const byte ByteOrderLittleEndian = 0x49; /// /// Byte order markers for indicating big endian encoding. /// - public const ushort ByteOrderBigEndian = 0x4D4D; + public const byte ByteOrderBigEndian = 0x4D; /// /// Magic number used within the image file header to identify a TIFF format file. diff --git a/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs b/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs index e9bbb650b..18a5c3f5e 100644 --- a/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs @@ -20,6 +20,11 @@ namespace ImageSharp.Formats /// private readonly IDecoderOptions options; + /// + /// A flag indicating if the file is encoded in little-endian or big-endian format. + /// + private bool isLittleEndian; + /// /// Initializes a new instance of the class. /// @@ -46,6 +51,8 @@ namespace ImageSharp.Formats where TColor : struct, IPixel { this.InputStream = stream; + + uint firstIfdOffset = ReadHeader(); } /// @@ -54,5 +61,69 @@ namespace ImageSharp.Formats public void Dispose() { } + + private uint ReadHeader() + { + byte[] headerBytes = new byte[8]; + ReadBytes(headerBytes, 8); + + if (headerBytes[0] == TiffConstants.ByteOrderLittleEndian && headerBytes[1] == TiffConstants.ByteOrderLittleEndian) + isLittleEndian = true; + else if (headerBytes[0] != TiffConstants.ByteOrderBigEndian && headerBytes[1] != TiffConstants.ByteOrderBigEndian) + throw new ImageFormatException("Invalid TIFF file header."); + + if (ToUInt16(headerBytes, 2) != TiffConstants.HeaderMagicNumber) + throw new ImageFormatException("Invalid TIFF file header."); + + uint firstIfdOffset = ToUInt32(headerBytes, 4); + if (firstIfdOffset == 0) + throw new ImageFormatException("Invalid TIFF file header."); + + return firstIfdOffset; + } + + private byte[] ReadBytes(byte[] buffer, int count) + { + int offset = 0; + + while (count > 0) + { + int bytesRead = InputStream.Read(buffer, offset, count); + + if (bytesRead == 0) + break; + + offset += bytesRead; + count -= bytesRead; + } + + return buffer; + } + + private Int16 ToInt16(byte[] bytes, int offset) + { + if (isLittleEndian) + return (short)(bytes[offset + 0] | (bytes[offset + 1] << 8)); + else + return (short)((bytes[offset + 0] << 8) | bytes[offset + 1]); + } + + private Int32 ToInt32(byte[] bytes, int offset) + { + if (isLittleEndian) + return bytes[offset + 0] | (bytes[offset + 1] << 8) | (bytes[offset + 2] << 16) | (bytes[offset + 3] << 24); + else + return (bytes[offset + 0] << 24) | (bytes[offset + 1] << 16) | (bytes[offset + 2] << 8) | bytes[offset + 3]; + } + + private UInt32 ToUInt32(byte[] bytes, int offset) + { + return (uint)ToInt32(bytes, offset); + } + + private UInt16 ToUInt16(byte[] bytes, int offset) + { + return (ushort)ToInt16(bytes, offset); + } } } diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs new file mode 100644 index 000000000..00b826ef0 --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs @@ -0,0 +1,79 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System.IO; + using System.Linq; + using Xunit; + + using ImageSharp.Formats; + + public class TiffDecoderHeaderTests + { + public static object[][] IsLittleEndianValues = new[] { new object[] { false }, + new object[] { true } }; + + [Theory] + [MemberData(nameof(IsLittleEndianValues))] + public void Decode_ThrowsException_WithInvalidByteOrderMarkers(bool isLittleEndian) + { + Stream stream = new TiffGenHeader() + { + FirstIfd = new TiffGenIfd(), + ByteOrderMarker = 0x1234 + } + .ToStream(isLittleEndian); + + TiffDecoder decoder = new TiffDecoder(); + + ImageFormatException e = Assert.Throws(() => { TestDecode(decoder, stream); }); + + Assert.Equal("Invalid TIFF file header.", e.Message); + } + + [Theory] + [MemberData(nameof(IsLittleEndianValues))] + public void Decode_ThrowsException_WithIncorrectMagicNumber(bool isLittleEndian) + { + Stream stream = new TiffGenHeader() + { + FirstIfd = new TiffGenIfd(), + MagicNumber = 32 + } + .ToStream(isLittleEndian); + + TiffDecoder decoder = new TiffDecoder(); + + ImageFormatException e = Assert.Throws(() => { TestDecode(decoder, stream); }); + + Assert.Equal("Invalid TIFF file header.", e.Message); + } + + [Theory] + [MemberData(nameof(IsLittleEndianValues))] + public void Decode_ThrowsException_WithNoIfdZero(bool isLittleEndian) + { + Stream stream = new TiffGenHeader() + { + FirstIfd = null + } + .ToStream(isLittleEndian); + + TiffDecoder decoder = new TiffDecoder(); + + ImageFormatException e = Assert.Throws(() => { TestDecode(decoder, stream); }); + + Assert.Equal("Invalid TIFF file header.", e.Message); + } + + private void TestDecode(TiffDecoder decoder, Stream stream) + { + Configuration.Default.AddImageFormat(new TiffFormat()); + Image image = new Image(1,1); + decoder.Decode(image, stream, null); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenHeader.cs b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenHeader.cs index b270ff208..95322dc66 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenHeader.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenHeader.cs @@ -32,11 +32,17 @@ namespace ImageSharp.Tests bytes.AddUInt32(0); var headerData = new TiffGenDataBlock(bytes.ToArray()); - var firstIfdData = FirstIfd.GetData(isLittleEndian); - firstIfdData.First().AddReference(headerData.Bytes, 4); - - return new [] { headerData }.Concat(firstIfdData); + if (FirstIfd != null) + { + var firstIfdData = FirstIfd.GetData(isLittleEndian); + firstIfdData.First().AddReference(headerData.Bytes, 4); + return new [] { headerData }.Concat(firstIfdData); + } + else + { + return new [] { headerData }; + } } } } \ No newline at end of file From 300a4bd03cbeb646af762371ffa71e9241c1302a Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Sat, 4 Mar 2017 12:05:14 +0000 Subject: [PATCH 006/275] Make Tiff implementation details internal --- src/ImageSharp.Formats.Tiff/AssemblyInfo.cs | 11 +++++++++++ src/ImageSharp.Formats.Tiff/TiffTags.cs | 2 +- src/ImageSharp.Formats.Tiff/TiffType.cs | 2 +- .../TestUtilities/Tiff/ITiffGenDataSource.cs | 2 +- .../TestUtilities/Tiff/TiffGenDataBlock.cs | 2 +- .../TestUtilities/Tiff/TiffGenDataReference.cs | 2 +- .../TestUtilities/Tiff/TiffGenEntry.cs | 2 +- .../TestUtilities/Tiff/TiffGenExtensions.cs | 2 +- .../TestUtilities/Tiff/TiffGenHeader.cs | 2 +- .../TestUtilities/Tiff/TiffGenIfd.cs | 2 +- 10 files changed, 20 insertions(+), 9 deletions(-) create mode 100644 src/ImageSharp.Formats.Tiff/AssemblyInfo.cs diff --git a/src/ImageSharp.Formats.Tiff/AssemblyInfo.cs b/src/ImageSharp.Formats.Tiff/AssemblyInfo.cs new file mode 100644 index 000000000..4d1cbfe55 --- /dev/null +++ b/src/ImageSharp.Formats.Tiff/AssemblyInfo.cs @@ -0,0 +1,11 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; + +// Ensure the internals can be tested. +[assembly: InternalsVisibleTo("ImageSharp.Formats.Tiff.Tests")] \ No newline at end of file diff --git a/src/ImageSharp.Formats.Tiff/TiffTags.cs b/src/ImageSharp.Formats.Tiff/TiffTags.cs index db4087d58..41721fb1d 100644 --- a/src/ImageSharp.Formats.Tiff/TiffTags.cs +++ b/src/ImageSharp.Formats.Tiff/TiffTags.cs @@ -8,7 +8,7 @@ namespace ImageSharp.Formats /// /// Constants representing tag IDs in the Tiff file-format. /// - public class TiffTags + internal class TiffTags { // Section 8: Baseline Fields diff --git a/src/ImageSharp.Formats.Tiff/TiffType.cs b/src/ImageSharp.Formats.Tiff/TiffType.cs index 1bb7f6cfb..b98236c0f 100644 --- a/src/ImageSharp.Formats.Tiff/TiffType.cs +++ b/src/ImageSharp.Formats.Tiff/TiffType.cs @@ -8,7 +8,7 @@ namespace ImageSharp.Formats /// /// Enumeration representing the data types understood by the Tiff file-format. /// - public enum TiffType + internal enum TiffType { Byte = 1, Ascii = 2, diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/ITiffGenDataSource.cs b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/ITiffGenDataSource.cs index 33bf99924..1c8a485b9 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/ITiffGenDataSource.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/ITiffGenDataSource.cs @@ -11,7 +11,7 @@ namespace ImageSharp.Tests /// /// An interface for any class within the Tiff generator that produces data to be included in the file. /// - public interface ITiffGenDataSource + internal interface ITiffGenDataSource { IEnumerable GetData(bool isLittleEndian); } diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenDataBlock.cs b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenDataBlock.cs index bbce12054..6a91dbbcc 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenDataBlock.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenDataBlock.cs @@ -12,7 +12,7 @@ namespace ImageSharp.Tests /// A utility data structure to represent an independent block of data in a Tiff file. /// These may be located in any order within a Tiff file. /// - public class TiffGenDataBlock + internal class TiffGenDataBlock { public TiffGenDataBlock(byte[] bytes) { diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenDataReference.cs b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenDataReference.cs index ec4c0c5df..90cacb23d 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenDataReference.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenDataReference.cs @@ -11,7 +11,7 @@ namespace ImageSharp.Tests /// /// A utility data structure to represent a reference from one block of data to another in a Tiff file. /// - public class TiffGenDataReference + internal class TiffGenDataReference { public TiffGenDataReference(byte[] bytes, int offset) { diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs index 4f0ff868b..df61b4d8a 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs @@ -14,7 +14,7 @@ namespace ImageSharp.Tests /// /// A utility data structure to represent Tiff IFD entries in unit tests. /// - public abstract class TiffGenEntry : ITiffGenDataSource + internal abstract class TiffGenEntry : ITiffGenDataSource { private TiffGenEntry(ushort tag, TiffType type) { diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenExtensions.cs b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenExtensions.cs index 87ba62121..21c3b0844 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenExtensions.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenExtensions.cs @@ -12,7 +12,7 @@ namespace ImageSharp.Tests /// /// A utility class for generating in-memory Tiff files for use in unit tests. /// - public static class TiffGenExtensions + internal static class TiffGenExtensions { public static byte[] ToBytes(this ITiffGenDataSource dataSource, bool isLittleEndian) { diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenHeader.cs b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenHeader.cs index 95322dc66..b28ceedc2 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenHeader.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenHeader.cs @@ -12,7 +12,7 @@ namespace ImageSharp.Tests /// /// A utility data structure to represent a Tiff file-header. /// - public class TiffGenHeader : ITiffGenDataSource + internal class TiffGenHeader : ITiffGenDataSource { public TiffGenHeader() { diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfd.cs b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfd.cs index 2e745df36..c26a0f199 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfd.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfd.cs @@ -12,7 +12,7 @@ namespace ImageSharp.Tests /// /// A utility data structure to represent Tiff IFDs in unit tests. /// - public class TiffGenIfd : ITiffGenDataSource + internal class TiffGenIfd : ITiffGenDataSource { public TiffGenIfd() { From f0237696f92007385affdacc3147a71d8f81dab0 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Sun, 5 Mar 2017 11:26:41 +0000 Subject: [PATCH 007/275] Make TiffDecoderCore more unit-testable --- .../TiffDecoderCore.cs | 24 ++++++++----- .../Formats/Tiff/TiffDecoderHeaderTests.cs | 34 +++++++++++++++++++ 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs b/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs index 18a5c3f5e..aee57b1ea 100644 --- a/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs @@ -20,11 +20,6 @@ namespace ImageSharp.Formats /// private readonly IDecoderOptions options; - /// - /// A flag indicating if the file is encoded in little-endian or big-endian format. - /// - private bool isLittleEndian; - /// /// Initializes a new instance of the class. /// @@ -34,11 +29,22 @@ namespace ImageSharp.Formats this.options = options ?? new DecoderOptions(); } + public TiffDecoderCore(Stream stream, bool isLittleEndian, IDecoderOptions options) : this(options) + { + this.InputStream = stream; + this.IsLittleEndian = isLittleEndian; + } + /// /// Gets the input stream. /// public Stream InputStream { get; private set; } + /// + /// A flag indicating if the file is encoded in little-endian or big-endian format. + /// + public bool IsLittleEndian; + /// /// Decodes the image from the specified and sets /// the data to image. @@ -62,13 +68,13 @@ namespace ImageSharp.Formats { } - private uint ReadHeader() + public uint ReadHeader() { byte[] headerBytes = new byte[8]; ReadBytes(headerBytes, 8); if (headerBytes[0] == TiffConstants.ByteOrderLittleEndian && headerBytes[1] == TiffConstants.ByteOrderLittleEndian) - isLittleEndian = true; + IsLittleEndian = true; else if (headerBytes[0] != TiffConstants.ByteOrderBigEndian && headerBytes[1] != TiffConstants.ByteOrderBigEndian) throw new ImageFormatException("Invalid TIFF file header."); @@ -102,7 +108,7 @@ namespace ImageSharp.Formats private Int16 ToInt16(byte[] bytes, int offset) { - if (isLittleEndian) + if (IsLittleEndian) return (short)(bytes[offset + 0] | (bytes[offset + 1] << 8)); else return (short)((bytes[offset + 0] << 8) | bytes[offset + 1]); @@ -110,7 +116,7 @@ namespace ImageSharp.Formats private Int32 ToInt32(byte[] bytes, int offset) { - if (isLittleEndian) + if (IsLittleEndian) return bytes[offset + 0] | (bytes[offset + 1] << 8) | (bytes[offset + 2] << 16) | (bytes[offset + 3] << 24); else return (bytes[offset + 0] << 24) | (bytes[offset + 1] << 16) | (bytes[offset + 2] << 8) | bytes[offset + 3]; diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs index 00b826ef0..9394519e3 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs @@ -16,6 +16,40 @@ namespace ImageSharp.Tests public static object[][] IsLittleEndianValues = new[] { new object[] { false }, new object[] { true } }; + [Theory] + [MemberData(nameof(IsLittleEndianValues))] + public void ReadHeader_ReadsEndianness(bool isLittleEndian) + { + Stream stream = new TiffGenHeader() + { + FirstIfd = new TiffGenIfd() + } + .ToStream(isLittleEndian); + + TiffDecoderCore decoder = new TiffDecoderCore(stream, false, null); + + decoder.ReadHeader(); + + Assert.Equal(isLittleEndian, decoder.IsLittleEndian); + } + + [Theory] + [MemberData(nameof(IsLittleEndianValues))] + public void ReadHeader_ReadsFirstIfdOffset(bool isLittleEndian) + { + Stream stream = new TiffGenHeader() + { + FirstIfd = new TiffGenIfd() + } + .ToStream(isLittleEndian); + + TiffDecoderCore decoder = new TiffDecoderCore(stream, false, null); + + uint firstIfdOffset = decoder.ReadHeader(); + + Assert.Equal(8u, firstIfdOffset); + } + [Theory] [MemberData(nameof(IsLittleEndianValues))] public void Decode_ThrowsException_WithInvalidByteOrderMarkers(bool isLittleEndian) From dff3ca43cf96996b5b10074890f0855f063a82e0 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Sun, 5 Mar 2017 12:11:15 +0000 Subject: [PATCH 008/275] Read raw data from TIFF IFDs --- src/ImageSharp.Formats.Tiff/TiffConstants.cs | 10 ++ .../TiffDecoderCore.cs | 37 +++++- .../TiffIfd/TiffIfd.cs | 22 ++++ .../TiffIfd/TiffIfdEntry.cs | 26 ++++ .../Formats/Tiff/TiffDecoderIfdTests.cs | 111 ++++++++++++++++++ 5 files changed, 201 insertions(+), 5 deletions(-) create mode 100644 src/ImageSharp.Formats.Tiff/TiffIfd/TiffIfd.cs create mode 100644 src/ImageSharp.Formats.Tiff/TiffIfd/TiffIfdEntry.cs create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdTests.cs diff --git a/src/ImageSharp.Formats.Tiff/TiffConstants.cs b/src/ImageSharp.Formats.Tiff/TiffConstants.cs index 0f9145208..73508b34a 100644 --- a/src/ImageSharp.Formats.Tiff/TiffConstants.cs +++ b/src/ImageSharp.Formats.Tiff/TiffConstants.cs @@ -26,5 +26,15 @@ namespace ImageSharp.Formats /// Magic number used within the image file header to identify a TIFF format file. /// public const ushort HeaderMagicNumber = 42; + + /// + /// Size (in bytes) of the TIFF file header. + /// + public const int SizeOfTiffHeader = 8; + + /// + /// Size (in bytes) of each individual TIFF IFD entry + /// + public const int SizeOfIfdEntry = 12; } } diff --git a/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs b/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs index aee57b1ea..e8c0db788 100644 --- a/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs @@ -59,6 +59,7 @@ namespace ImageSharp.Formats this.InputStream = stream; uint firstIfdOffset = ReadHeader(); + TiffIfd firstIfd = ReadIfd(firstIfdOffset); } /// @@ -70,8 +71,8 @@ namespace ImageSharp.Formats public uint ReadHeader() { - byte[] headerBytes = new byte[8]; - ReadBytes(headerBytes, 8); + byte[] headerBytes = new byte[TiffConstants.SizeOfTiffHeader]; + ReadBytes(headerBytes, TiffConstants.SizeOfTiffHeader); if (headerBytes[0] == TiffConstants.ByteOrderLittleEndian && headerBytes[1] == TiffConstants.ByteOrderLittleEndian) IsLittleEndian = true; @@ -88,7 +89,35 @@ namespace ImageSharp.Formats return firstIfdOffset; } - private byte[] ReadBytes(byte[] buffer, int count) + public TiffIfd ReadIfd(uint offset) + { + InputStream.Seek(offset, SeekOrigin.Begin); + + byte[] buffer = new byte[TiffConstants.SizeOfIfdEntry]; + + ReadBytes(buffer, 2); + ushort entryCount = ToUInt16(buffer, 0); + + TiffIfdEntry[] entries = new TiffIfdEntry[entryCount]; + for (int i = 0 ; i +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + /// + /// Data structure for holding details of each TIFF IFD + /// + internal struct TiffIfd + { + public TiffIfdEntry[] Entries; + public uint NextIfdOffset; + + public TiffIfd(TiffIfdEntry[] entries, uint nextIfdOffset) + { + this.Entries = entries; + this.NextIfdOffset = nextIfdOffset; + } + } +} diff --git a/src/ImageSharp.Formats.Tiff/TiffIfd/TiffIfdEntry.cs b/src/ImageSharp.Formats.Tiff/TiffIfd/TiffIfdEntry.cs new file mode 100644 index 000000000..04686a4da --- /dev/null +++ b/src/ImageSharp.Formats.Tiff/TiffIfd/TiffIfdEntry.cs @@ -0,0 +1,26 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + /// + /// Data structure for holding details of each TIFF IFD entry + /// + internal struct TiffIfdEntry + { + public ushort Tag; + public TiffType Type; + public uint Count; + public byte[] Value; + + public TiffIfdEntry(ushort tag, TiffType type, uint count, byte[] value) + { + this.Tag = tag; + this.Type = type; + this.Count = count; + this.Value = value; + } + } +} diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdTests.cs new file mode 100644 index 000000000..af0e0b93f --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdTests.cs @@ -0,0 +1,111 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System.IO; + using System.Linq; + using Xunit; + + using ImageSharp.Formats; + + public class TiffDecoderIfdTests + { + public static object[][] IsLittleEndianValues = new[] { new object[] { false }, + new object[] { true } }; + + [Theory] + [MemberData(nameof(IsLittleEndianValues))] + public void ReadIfd_ReadsNextIfdOffset_IfPresent(bool isLittleEndian) + { + Stream stream = new TiffGenIfd() + { + Entries = + { + TiffGenEntry.Integer(TiffTags.ImageWidth, TiffType.Long, 150) + }, + NextIfd = new TiffGenIfd() + } + .ToStream(isLittleEndian); + + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); + TiffIfd ifd = decoder.ReadIfd(0); + + Assert.Equal(18u, ifd.NextIfdOffset); + } + + [Theory] + [MemberData(nameof(IsLittleEndianValues))] + public void ReadIfd_ReadsNextIfdOffset_ZeroIfLastIfd(bool isLittleEndian) + { + Stream stream = new TiffGenIfd() + { + Entries = + { + TiffGenEntry.Integer(TiffTags.ImageWidth, TiffType.Long, 150) + } + } + .ToStream(isLittleEndian); + + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); + TiffIfd ifd = decoder.ReadIfd(0); + + Assert.Equal(0u, ifd.NextIfdOffset); + } + + [Theory] + [MemberData(nameof(IsLittleEndianValues))] + public void ReadIfd_ReturnsCorrectNumberOfEntries(bool isLittleEndian) + { + Stream stream = new TiffGenIfd() + { + Entries = + { + TiffGenEntry.Integer(TiffTags.ImageWidth, TiffType.Long, 150), + TiffGenEntry.Integer(TiffTags.ImageLength, TiffType.Long, 210), + TiffGenEntry.Integer(TiffTags.Orientation, TiffType.Short, 1), + TiffGenEntry.Ascii(TiffTags.Artist, "Image Artist Name"), + TiffGenEntry.Ascii(TiffTags.HostComputer, "Host Computer Name") + }, + NextIfd = new TiffGenIfd() + } + .ToStream(isLittleEndian); + + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); + TiffIfd ifd = decoder.ReadIfd(0); + + Assert.NotNull(ifd.Entries); + Assert.Equal(5, ifd.Entries.Length); + } + + [Theory] + [MemberData(nameof(IsLittleEndianValues))] + public void ReadIfd_ReadsRawTiffEntryData(bool isLittleEndian) + { + Stream stream = new TiffGenIfd() + { + Entries = + { + TiffGenEntry.Integer(TiffTags.ImageWidth, TiffType.Long, 150), + TiffGenEntry.Integer(TiffTags.ImageLength, TiffType.Long, 210), + TiffGenEntry.Integer(TiffTags.Orientation, TiffType.Short, 1) + }, + NextIfd = new TiffGenIfd() + } + .ToStream(isLittleEndian); + + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); + TiffIfd ifd = decoder.ReadIfd(0); + TiffIfdEntry entry = ifd.Entries[1]; + + byte[] expectedData = isLittleEndian ? new byte[] {210,0,0,0} : new byte[] {0,0,0,210}; + Assert.NotNull(entry); + Assert.Equal(TiffTags.ImageLength, entry.Tag); + Assert.Equal(TiffType.Long, entry.Type); + Assert.Equal(4u, entry.Count); + Assert.Equal(expectedData, entry.Value); + } + } +} \ No newline at end of file From cd9df139f23e795140be250e4ed072d7d46b9415 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Sun, 5 Mar 2017 12:14:24 +0000 Subject: [PATCH 009/275] More comprehensive testing of header checks --- .../Formats/Tiff/TiffDecoderHeaderTests.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs index 9394519e3..470b49e9f 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs @@ -51,15 +51,21 @@ namespace ImageSharp.Tests } [Theory] - [MemberData(nameof(IsLittleEndianValues))] - public void Decode_ThrowsException_WithInvalidByteOrderMarkers(bool isLittleEndian) + [InlineData(0x1234)] + [InlineData(0x4912)] + [InlineData(0x1249)] + [InlineData(0x4D12)] + [InlineData(0x124D)] + [InlineData(0x494D)] + [InlineData(0x4D49)] + public void Decode_ThrowsException_WithInvalidByteOrderMarkers(ushort byteOrderMarker) { Stream stream = new TiffGenHeader() { FirstIfd = new TiffGenIfd(), - ByteOrderMarker = 0x1234 + ByteOrderMarker = byteOrderMarker } - .ToStream(isLittleEndian); + .ToStream(true); TiffDecoder decoder = new TiffDecoder(); From 2a89db1a067630ca00f5aa3f42de9b3353247ff5 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Mon, 6 Mar 2017 22:17:02 +0000 Subject: [PATCH 010/275] Read raw bytes for IFD entries --- .../TiffDecoderCore.cs | 45 ++++++ .../Formats/Tiff/TiffDecoderIfdEntryTests.cs | 135 ++++++++++++++++++ .../Formats/Tiff/TiffDecoderIfdTests.cs | 4 +- .../ImageSharp.Formats.Tiff.Tests.csproj | 1 + .../TestUtilities/Tiff/TiffGenEntry.cs | 35 ++++- .../TestUtilities/Tiff/TiffGenIfd.cs | 13 +- 6 files changed, 220 insertions(+), 13 deletions(-) create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs diff --git a/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs b/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs index e8c0db788..37aad7320 100644 --- a/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs @@ -133,6 +133,23 @@ namespace ImageSharp.Formats } } + public byte[] ReadBytes(ref TiffIfdEntry entry) + { + uint byteLength = GetSizeOfData(entry); + + if (entry.Value.Length < byteLength) + { + uint offset = ToUInt32(entry.Value, 0); + InputStream.Seek(offset, SeekOrigin.Begin); + + byte[] data = new byte[byteLength]; + ReadBytes(data, (int)byteLength); + entry.Value = data; + } + + return entry.Value; + } + private Int16 ToInt16(byte[] bytes, int offset) { if (IsLittleEndian) @@ -158,5 +175,33 @@ namespace ImageSharp.Formats { return (ushort)ToInt16(bytes, offset); } + + public static uint GetSizeOfData(TiffIfdEntry entry) => SizeOfDataType(entry.Type) * entry.Count; + + private static uint SizeOfDataType(TiffType type) + { + switch (type) + { + case TiffType.Byte: + case TiffType.Ascii: + case TiffType.SByte: + case TiffType.Undefined: + return 1u; + case TiffType.Short: + case TiffType.SShort: + return 2u; + case TiffType.Long: + case TiffType.SLong: + case TiffType.Float: + case TiffType.Ifd: + return 4u; + case TiffType.Rational: + case TiffType.SRational: + case TiffType.Double: + return 8u; + default: + return 0u; + } + } } } diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs new file mode 100644 index 000000000..1d987de9a --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs @@ -0,0 +1,135 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System.IO; + using System.Linq; + using Xunit; + + using ImageSharp.Formats; + + public class TiffDecoderIfdEntryTests + { + [Theory] + [InlineDataAttribute(TiffType.Byte, 1u, 1u)] + [InlineDataAttribute(TiffType.Ascii, 1u, 1u)] + [InlineDataAttribute(TiffType.Short, 1u, 2u)] + [InlineDataAttribute(TiffType.Long, 1u, 4u)] + [InlineDataAttribute(TiffType.Rational, 1u, 8u)] + [InlineDataAttribute(TiffType.SByte, 1u, 1u)] + [InlineDataAttribute(TiffType.Undefined, 1u, 1u)] + [InlineDataAttribute(TiffType.SShort, 1u, 2u)] + [InlineDataAttribute(TiffType.SLong, 1u, 4u)] + [InlineDataAttribute(TiffType.SRational, 1u, 8u)] + [InlineDataAttribute(TiffType.Float, 1u, 4u)] + [InlineDataAttribute(TiffType.Double, 1u, 8u)] + [InlineDataAttribute(TiffType.Ifd, 1u, 4u)] + [InlineDataAttribute((TiffType)999, 1u, 0u)] + public void GetSizeOfData_SingleItem_ReturnsCorrectSize(ushort type, uint count, uint expectedSize) + { + TiffIfdEntry entry = new TiffIfdEntry(TiffTags.ImageWidth, (TiffType)type, count, new byte[4]); + uint size = TiffDecoderCore.GetSizeOfData(entry); + Assert.Equal(expectedSize, size); + } + + [Theory] + [InlineDataAttribute(TiffType.Byte, 15u, 15u)] + [InlineDataAttribute(TiffType.Ascii, 20u, 20u)] + [InlineDataAttribute(TiffType.Short, 18u, 36u)] + [InlineDataAttribute(TiffType.Long, 4u, 16u)] + [InlineDataAttribute(TiffType.Rational, 9u, 72u)] + [InlineDataAttribute(TiffType.SByte, 5u, 5u)] + [InlineDataAttribute(TiffType.Undefined, 136u, 136u)] + [InlineDataAttribute(TiffType.SShort, 12u, 24u)] + [InlineDataAttribute(TiffType.SLong, 15u, 60u)] + [InlineDataAttribute(TiffType.SRational, 10u, 80u)] + [InlineDataAttribute(TiffType.Float, 2u, 8u)] + [InlineDataAttribute(TiffType.Double, 2u, 16u)] + [InlineDataAttribute(TiffType.Ifd, 10u, 40u)] + [InlineDataAttribute((TiffType)999, 1050u, 0u)] + public void GetSizeOfData_Array_ReturnsCorrectSize(ushort type, uint count, uint expectedSize) + { + TiffIfdEntry entry = new TiffIfdEntry(TiffTags.ImageWidth, (TiffType)type, count, new byte[4]); + uint size = TiffDecoderCore.GetSizeOfData(entry); + Assert.Equal(expectedSize, size); + } + + [Theory] + [InlineDataAttribute(TiffType.Byte, 1u, new byte[] { 17 }, false)] + [InlineDataAttribute(TiffType.Byte, 1u, new byte[] { 17 }, true)] + [InlineDataAttribute(TiffType.Byte, 2u, new byte[] { 17, 28 }, false)] + [InlineDataAttribute(TiffType.Byte, 2u, new byte[] { 17, 28 }, true)] + [InlineDataAttribute(TiffType.Byte, 4u, new byte[] { 17, 28, 2, 9 }, false)] + [InlineDataAttribute(TiffType.Byte, 4u, new byte[] { 17, 28, 2, 9 }, true)] + [InlineDataAttribute(TiffType.Byte, 5u, new byte[] { 17, 28, 2, 9, 13 }, false)] + [InlineDataAttribute(TiffType.Byte, 5u, new byte[] { 17, 28, 2, 9, 13 }, true)] + [InlineDataAttribute(TiffType.Byte, 10u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2, 127, 86 }, false)] + [InlineDataAttribute(TiffType.Byte, 10u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2, 127, 86 }, true)] + [InlineDataAttribute(TiffType.Short, 1u, new byte[] { 17, 28 }, false)] + [InlineDataAttribute(TiffType.Short, 1u, new byte[] { 17, 28 }, true)] + [InlineDataAttribute(TiffType.Short, 2u, new byte[] { 17, 28, 2, 9 }, false)] + [InlineDataAttribute(TiffType.Short, 2u, new byte[] { 17, 28, 2, 9 }, true)] + [InlineDataAttribute(TiffType.Short, 3u, new byte[] { 17, 28, 2, 9, 13, 37 }, false)] + [InlineDataAttribute(TiffType.Short, 3u, new byte[] { 17, 28, 2, 9, 13, 37 }, true)] + [InlineDataAttribute(TiffType.Long, 1u, new byte[] { 17, 28, 2, 9 }, false)] + [InlineDataAttribute(TiffType.Long, 1u, new byte[] { 17, 28, 2, 9 }, true)] + [InlineDataAttribute(TiffType.Long, 2u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2 }, false)] + [InlineDataAttribute(TiffType.Long, 2u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2 }, true)] + [InlineDataAttribute(TiffType.Rational, 1u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2 }, false)] + [InlineDataAttribute(TiffType.Rational, 1u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2 }, true)] + public void ReadBytes_ReturnsExpectedData(ushort type, uint count, byte[] bytes, bool isLittleEndian) + { + (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, count, bytes), isLittleEndian); + + byte[] result = decoder.ReadBytes(ref entry); + + if (bytes.Length < 4) + result = result.Take(bytes.Length).ToArray(); + + Assert.Equal(bytes, result); + } + + [Theory] + [InlineDataAttribute(TiffType.Byte, 5u, new byte[] { 17, 28, 2, 9, 13 }, false)] + [InlineDataAttribute(TiffType.Byte, 5u, new byte[] { 17, 28, 2, 9, 13 }, true)] + [InlineDataAttribute(TiffType.Byte, 10u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2, 127, 86 }, false)] + [InlineDataAttribute(TiffType.Byte, 10u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2, 127, 86 }, true)] + [InlineDataAttribute(TiffType.Short, 3u, new byte[] { 17, 28, 2, 9, 13, 37 }, false)] + [InlineDataAttribute(TiffType.Short, 3u, new byte[] { 17, 28, 2, 9, 13, 37 }, true)] + [InlineDataAttribute(TiffType.Long, 2u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2 }, false)] + [InlineDataAttribute(TiffType.Long, 2u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2 }, true)] + [InlineDataAttribute(TiffType.Rational, 1u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2 }, false)] + [InlineDataAttribute(TiffType.Rational, 1u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2 }, true)] + public void ReadBytes_CachesDataLongerThanFourBytes(ushort type, uint count, byte[] bytes, bool isLittleEndian) + { + (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, count, bytes), isLittleEndian); + + Assert.Equal(4, entry.Value.Length); + + byte[] result = decoder.ReadBytes(ref entry); + + Assert.Equal(bytes.Length, entry.Value.Length); + Assert.Equal(bytes, entry.Value); + } + + private (TiffDecoderCore, TiffIfdEntry) GenerateTestIfdEntry(TiffGenEntry entry, bool isLittleEndian) + { + Stream stream = new TiffGenIfd() + { + Entries = + { + entry + } + } + .ToStream(isLittleEndian); + + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); + TiffIfdEntry ifdEntry = decoder.ReadIfd(0).Entries[0]; + + return (decoder, ifdEntry); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdTests.cs index af0e0b93f..606e024a1 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdTests.cs @@ -80,7 +80,7 @@ namespace ImageSharp.Tests Assert.Equal(5, ifd.Entries.Length); } - [Theory] + [Theory] [MemberData(nameof(IsLittleEndianValues))] public void ReadIfd_ReadsRawTiffEntryData(bool isLittleEndian) { @@ -104,7 +104,7 @@ namespace ImageSharp.Tests Assert.NotNull(entry); Assert.Equal(TiffTags.ImageLength, entry.Tag); Assert.Equal(TiffType.Long, entry.Type); - Assert.Equal(4u, entry.Count); + Assert.Equal(1u, entry.Count); Assert.Equal(expectedData, entry.Value); } } diff --git a/tests/ImageSharp.Formats.Tiff.Tests/ImageSharp.Formats.Tiff.Tests.csproj b/tests/ImageSharp.Formats.Tiff.Tests/ImageSharp.Formats.Tiff.Tests.csproj index a20f1fd99..30ec6b75f 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/ImageSharp.Formats.Tiff.Tests.csproj +++ b/tests/ImageSharp.Formats.Tiff.Tests/ImageSharp.Formats.Tiff.Tests.csproj @@ -11,6 +11,7 @@ + diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs index df61b4d8a..757da63b4 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs @@ -16,12 +16,14 @@ namespace ImageSharp.Tests /// internal abstract class TiffGenEntry : ITiffGenDataSource { - private TiffGenEntry(ushort tag, TiffType type) + private TiffGenEntry(ushort tag, TiffType type, uint count) { this.Tag = tag; this.Type = type; + this.Count = count; } + public uint Count { get; } public ushort Tag { get; } public TiffType Type { get; } @@ -32,6 +34,11 @@ namespace ImageSharp.Tests return new TiffGenEntryAscii(tag, value); } + public static TiffGenEntry Bytes(ushort tag, TiffType type, uint count, byte[] value) + { + return new TiffGenEntryBytes(tag, type, count, value); + } + public static TiffGenEntry Integer(ushort tag, TiffType type, int value) { return TiffGenEntry.Integer(tag, type, new int[] {value}); @@ -48,7 +55,7 @@ namespace ImageSharp.Tests private class TiffGenEntryAscii : TiffGenEntry { - public TiffGenEntryAscii(ushort tag, string value) : base(tag, TiffType.Ascii) + public TiffGenEntryAscii(ushort tag, string value) : base(tag, TiffType.Ascii, (uint)GetBytes(value).Length) { this.Value = value; } @@ -57,14 +64,34 @@ namespace ImageSharp.Tests public override IEnumerable GetData(bool isLittleEndian) { - byte[] bytes = Encoding.ASCII.GetBytes($"{Value}\0"); + byte[] bytes = GetBytes(Value); return new[] { new TiffGenDataBlock(bytes) }; } + + private static byte[] GetBytes(string value) + { + return Encoding.ASCII.GetBytes($"{value}\0"); + } + } + + private class TiffGenEntryBytes : TiffGenEntry + { + public TiffGenEntryBytes(ushort tag, TiffType type, uint count, byte[] value) : base(tag, type, count) + { + this.Value = value; + } + + public byte[] Value { get; } + + public override IEnumerable GetData(bool isLittleEndian) + { + return new[] { new TiffGenDataBlock(Value) }; + } } private class TiffGenEntryInteger : TiffGenEntry { - public TiffGenEntryInteger(ushort tag, TiffType type, int[] value) : base(tag, type) + public TiffGenEntryInteger(ushort tag, TiffType type, int[] value) : base(tag, type, (uint)value.Length) { this.Value = value; } diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfd.cs b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfd.cs index c26a0f199..ee560b18f 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfd.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfd.cs @@ -40,18 +40,17 @@ namespace ImageSharp.Tests { var entryData = entry.GetData(isLittleEndian); var entryBytes = entryData.First().Bytes; - var entryCount = entryBytes.Length; bytes.AddUInt16(entry.Tag); bytes.AddUInt16((ushort)entry.Type); - bytes.AddUInt32((uint)entryCount); + bytes.AddUInt32(entry.Count); - if (entryCount <=4) + if (entryBytes.Length <=4) { - bytes.AddByte(entryCount > 0 ? entryBytes[0] : (byte)0); - bytes.AddByte(entryCount > 1 ? entryBytes[1] : (byte)0); - bytes.AddByte(entryCount > 2 ? entryBytes[2] : (byte)0); - bytes.AddByte(entryCount > 3 ? entryBytes[3] : (byte)0); + bytes.AddByte(entryBytes.Length > 0 ? entryBytes[0] : (byte)0); + bytes.AddByte(entryBytes.Length > 1 ? entryBytes[1] : (byte)0); + bytes.AddByte(entryBytes.Length > 2 ? entryBytes[2] : (byte)0); + bytes.AddByte(entryBytes.Length > 3 ? entryBytes[3] : (byte)0); dataBlocks.AddRange(entryData.Skip(1)); } From d82ef72d723f50f80ef71dba6877da11d398894e Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Sun, 12 Mar 2017 15:43:38 +0000 Subject: [PATCH 011/275] Read integer types from TIFF IFDs --- .../TiffDecoderCore.cs | 110 ++++++++ .../Formats/Tiff/TiffDecoderIfdEntryTests.cs | 249 ++++++++++++++++++ .../TestUtilities/Tiff/TiffGenEntry.cs | 51 ++++ 3 files changed, 410 insertions(+) diff --git a/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs b/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs index 37aad7320..9a0920980 100644 --- a/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs @@ -150,6 +150,111 @@ namespace ImageSharp.Formats return entry.Value; } + public uint ReadUnsignedInteger(ref TiffIfdEntry entry) + { + if (entry.Count != 1) + throw new ImageFormatException($"Cannot read a single value from an array of multiple items."); + + switch (entry.Type) + { + case TiffType.Byte: + return (uint)ToByte(entry.Value, 0); + case TiffType.Short: + return (uint)ToUInt16(entry.Value, 0); + case TiffType.Long: + return ToUInt32(entry.Value, 0); + default: + throw new ImageFormatException($"A value of type '{entry.Type}' cannot be converted to an unsigned integer."); + } + } + + public int ReadSignedInteger(ref TiffIfdEntry entry) + { + if (entry.Count != 1) + throw new ImageFormatException($"Cannot read a single value from an array of multiple items."); + + switch (entry.Type) + { + case TiffType.SByte: + return (int)ToSByte(entry.Value, 0); + case TiffType.SShort: + return (int)ToInt16(entry.Value, 0); + case TiffType.SLong: + return ToInt32(entry.Value, 0); + default: + throw new ImageFormatException($"A value of type '{entry.Type}' cannot be converted to a signed integer."); + } + } + + public uint[] ReadUnsignedIntegerArray(ref TiffIfdEntry entry) + { + byte[] bytes = ReadBytes(ref entry); + uint[] result = new uint[entry.Count]; + + switch (entry.Type) + { + case TiffType.Byte: + { + for (int i = 0 ; i < result.Length ; i++) + result[i] = (uint)ToByte(bytes, i); + break; + } + case TiffType.Short: + { + for (int i = 0 ; i < result.Length ; i++) + result[i] = (uint)ToUInt16(bytes, i * 2); + break; + } + case TiffType.Long: + { + for (int i = 0 ; i < result.Length ; i++) + result[i] = ToUInt32(bytes, i * 4); + break; + } + default: + throw new ImageFormatException($"A value of type '{entry.Type}' cannot be converted to an unsigned integer."); + } + + return result; + } + + public int[] ReadSignedIntegerArray(ref TiffIfdEntry entry) + { + byte[] bytes = ReadBytes(ref entry); + int[] result = new int[entry.Count]; + + switch (entry.Type) + { + case TiffType.SByte: + { + for (int i = 0 ; i < result.Length ; i++) + result[i] = (int)ToSByte(bytes, i); + break; + } + case TiffType.SShort: + { + for (int i = 0 ; i < result.Length ; i++) + result[i] = (int)ToInt16(bytes, i * 2); + break; + } + case TiffType.SLong: + { + for (int i = 0 ; i < result.Length ; i++) + result[i] = ToInt32(bytes, i * 4); + break; + } + default: + throw new ImageFormatException($"A value of type '{entry.Type}' cannot be converted to a signed integer."); + } + + return result; + } + + private SByte ToSByte(byte[] bytes, int offset) + { + return (sbyte)bytes[offset]; + } + private Int16 ToInt16(byte[] bytes, int offset) { if (IsLittleEndian) @@ -166,6 +271,11 @@ namespace ImageSharp.Formats return (bytes[offset + 0] << 24) | (bytes[offset + 1] << 16) | (bytes[offset + 2] << 8) | bytes[offset + 3]; } + private Byte ToByte(byte[] bytes, int offset) + { + return bytes[offset]; + } + private UInt32 ToUInt32(byte[] bytes, int offset) { return (uint)ToInt32(bytes, offset); diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs index 1d987de9a..16581e367 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs @@ -5,6 +5,7 @@ namespace ImageSharp.Tests { + using System; using System.IO; using System.Linq; using Xunit; @@ -115,6 +116,254 @@ namespace ImageSharp.Tests Assert.Equal(bytes, entry.Value); } + [Theory] + [InlineDataAttribute(TiffType.Byte, true, new byte[] { 0, 1, 2, 3 }, 0)] + [InlineDataAttribute(TiffType.Byte, true, new byte[] { 1, 2, 3, 4 }, 1)] + [InlineDataAttribute(TiffType.Byte, true, new byte[] { 255, 2, 3, 4 }, 255)] + [InlineDataAttribute(TiffType.Byte, false, new byte[] { 0, 1, 2, 3 }, 0)] + [InlineDataAttribute(TiffType.Byte, false, new byte[] { 1, 2, 3, 4 }, 1)] + [InlineDataAttribute(TiffType.Byte, false, new byte[] { 255, 2, 3, 4 }, 255)] + [InlineDataAttribute(TiffType.Short, true, new byte[] { 0, 0, 2, 3 }, 0)] + [InlineDataAttribute(TiffType.Short, true, new byte[] { 1, 0, 2, 3 }, 1)] + [InlineDataAttribute(TiffType.Short, true, new byte[] { 0, 1, 2, 3 }, 256)] + [InlineDataAttribute(TiffType.Short, true, new byte[] { 2, 1, 2, 3 }, 258)] + [InlineDataAttribute(TiffType.Short, true, new byte[] { 255, 255, 2, 3 }, UInt16.MaxValue)] + [InlineDataAttribute(TiffType.Short, false, new byte[] { 0, 0, 2, 3 }, 0)] + [InlineDataAttribute(TiffType.Short, false, new byte[] { 0, 1, 2, 3 }, 1)] + [InlineDataAttribute(TiffType.Short, false, new byte[] { 1, 0, 2, 3 }, 256)] + [InlineDataAttribute(TiffType.Short, false, new byte[] { 1, 2, 2, 3 }, 258)] + [InlineDataAttribute(TiffType.Short, false, new byte[] { 255, 255, 2, 3 }, UInt16.MaxValue)] + [InlineDataAttribute(TiffType.Long, true, new byte[] { 0, 0, 0, 0 }, 0)] + [InlineDataAttribute(TiffType.Long, true, new byte[] { 1, 0, 0, 0 }, 1)] + [InlineDataAttribute(TiffType.Long, true, new byte[] { 0, 1, 0, 0 }, 256)] + [InlineDataAttribute(TiffType.Long, true, new byte[] { 0, 0, 1, 0 }, 256 * 256)] + [InlineDataAttribute(TiffType.Long, true, new byte[] { 0, 0, 0, 1 }, 256 * 256 * 256)] + [InlineDataAttribute(TiffType.Long, true, new byte[] { 1, 2, 3, 4 }, 67305985)] + [InlineDataAttribute(TiffType.Long, true, new byte[] { 255, 255, 255, 255 }, UInt32.MaxValue)] + [InlineDataAttribute(TiffType.Long, false, new byte[] { 0, 0, 0, 0 }, 0)] + [InlineDataAttribute(TiffType.Long, false, new byte[] { 0, 0, 0, 1 }, 1)] + [InlineDataAttribute(TiffType.Long, false, new byte[] { 0, 0, 1, 0 }, 256)] + [InlineDataAttribute(TiffType.Long, false, new byte[] { 0, 1, 0, 0 }, 256 * 256)] + [InlineDataAttribute(TiffType.Long, false, new byte[] { 1, 0, 0, 0 }, 256 * 256 * 256)] + [InlineDataAttribute(TiffType.Long, false, new byte[] { 4, 3, 2, 1 }, 67305985)] + [InlineDataAttribute(TiffType.Long, false, new byte[] { 255, 255, 255, 255 }, UInt32.MaxValue)] + public void ReadUnsignedInteger_ReturnsValue(ushort type, bool isLittleEndian, byte[] bytes, uint expectedValue) + { + (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, 1, bytes), isLittleEndian); + + uint result = decoder.ReadUnsignedInteger(ref entry); + + Assert.Equal(expectedValue, result); + } + + [Theory] + [InlineDataAttribute(TiffType.Ascii)] + [InlineDataAttribute(TiffType.Rational)] + [InlineDataAttribute(TiffType.SByte)] + [InlineDataAttribute(TiffType.Undefined)] + [InlineDataAttribute(TiffType.SShort)] + [InlineDataAttribute(TiffType.SLong)] + [InlineDataAttribute(TiffType.SRational)] + [InlineDataAttribute(TiffType.Float)] + [InlineDataAttribute(TiffType.Double)] + [InlineDataAttribute(TiffType.Ifd)] + [InlineDataAttribute((TiffType)99)] + public void ReadUnsignedInteger_ThrowsExceptionIfInvalidType(ushort type) + { + (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, 1, new byte[4]), true); + + var e = Assert.Throws(() => decoder.ReadUnsignedInteger(ref entry)); + + Assert.Equal($"A value of type '{(TiffType)type}' cannot be converted to an unsigned integer.", e.Message); + } + + [Theory] + [InlineDataAttribute(TiffType.Byte, true)] + [InlineDataAttribute(TiffType.Short, true)] + [InlineDataAttribute(TiffType.Long, true)] + [InlineDataAttribute(TiffType.Byte, false)] + [InlineDataAttribute(TiffType.Short, false)] + [InlineDataAttribute(TiffType.Long, false)] + public void ReadUnsignedInteger_ThrowsExceptionIfCountIsNotOne(ushort type, bool isLittleEndian) + { + (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, 2, new byte[4]), isLittleEndian); + + var e = Assert.Throws(() => decoder.ReadUnsignedInteger(ref entry)); + + Assert.Equal($"Cannot read a single value from an array of multiple items.", e.Message); + } + + [Theory] + [InlineDataAttribute(TiffType.SByte, true, new byte[] { 0, 1, 2, 3 }, 0)] + [InlineDataAttribute(TiffType.SByte, true, new byte[] { 1, 2, 3, 4 }, 1)] + [InlineDataAttribute(TiffType.SByte, true, new byte[] { 255, 2, 3, 4 }, -1)] + [InlineDataAttribute(TiffType.SByte, false, new byte[] { 0, 1, 2, 3 }, 0)] + [InlineDataAttribute(TiffType.SByte, false, new byte[] { 1, 2, 3, 4 }, 1)] + [InlineDataAttribute(TiffType.SByte, false, new byte[] { 255, 2, 3, 4 }, -1)] + [InlineDataAttribute(TiffType.SShort, true, new byte[] { 0, 0, 2, 3 }, 0)] + [InlineDataAttribute(TiffType.SShort, true, new byte[] { 1, 0, 2, 3 }, 1)] + [InlineDataAttribute(TiffType.SShort, true, new byte[] { 0, 1, 2, 3 }, 256)] + [InlineDataAttribute(TiffType.SShort, true, new byte[] { 2, 1, 2, 3 }, 258)] + [InlineDataAttribute(TiffType.SShort, true, new byte[] { 255, 255, 2, 3 }, -1)] + [InlineDataAttribute(TiffType.SShort, false, new byte[] { 0, 0, 2, 3 }, 0)] + [InlineDataAttribute(TiffType.SShort, false, new byte[] { 0, 1, 2, 3 }, 1)] + [InlineDataAttribute(TiffType.SShort, false, new byte[] { 1, 0, 2, 3 }, 256)] + [InlineDataAttribute(TiffType.SShort, false, new byte[] { 1, 2, 2, 3 }, 258)] + [InlineDataAttribute(TiffType.SShort, false, new byte[] { 255, 255, 2, 3 }, -1)] + [InlineDataAttribute(TiffType.SLong, true, new byte[] { 0, 0, 0, 0 }, 0)] + [InlineDataAttribute(TiffType.SLong, true, new byte[] { 1, 0, 0, 0 }, 1)] + [InlineDataAttribute(TiffType.SLong, true, new byte[] { 0, 1, 0, 0 }, 256)] + [InlineDataAttribute(TiffType.SLong, true, new byte[] { 0, 0, 1, 0 }, 256 * 256)] + [InlineDataAttribute(TiffType.SLong, true, new byte[] { 0, 0, 0, 1 }, 256 * 256 * 256)] + [InlineDataAttribute(TiffType.SLong, true, new byte[] { 1, 2, 3, 4 }, 67305985)] + [InlineDataAttribute(TiffType.SLong, true, new byte[] { 255, 255, 255, 255 }, -1)] + [InlineDataAttribute(TiffType.SLong, false, new byte[] { 0, 0, 0, 0 }, 0)] + [InlineDataAttribute(TiffType.SLong, false, new byte[] { 0, 0, 0, 1 }, 1)] + [InlineDataAttribute(TiffType.SLong, false, new byte[] { 0, 0, 1, 0 }, 256)] + [InlineDataAttribute(TiffType.SLong, false, new byte[] { 0, 1, 0, 0 }, 256 * 256)] + [InlineDataAttribute(TiffType.SLong, false, new byte[] { 1, 0, 0, 0 }, 256 * 256 * 256)] + [InlineDataAttribute(TiffType.SLong, false, new byte[] { 4, 3, 2, 1 }, 67305985)] + [InlineDataAttribute(TiffType.SLong, false, new byte[] { 255, 255, 255, 255 }, -1)] + public void ReadSignedInteger_ReturnsValue(ushort type, bool isLittleEndian, byte[] bytes, int expectedValue) + { + (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, 1, bytes), isLittleEndian); + + int result = decoder.ReadSignedInteger(ref entry); + + Assert.Equal(expectedValue, result); + } + + [Theory] + [InlineDataAttribute(TiffType.Byte)] + [InlineDataAttribute(TiffType.Ascii)] + [InlineDataAttribute(TiffType.Short)] + [InlineDataAttribute(TiffType.Long)] + [InlineDataAttribute(TiffType.Rational)] + [InlineDataAttribute(TiffType.Undefined)] + [InlineDataAttribute(TiffType.SRational)] + [InlineDataAttribute(TiffType.Float)] + [InlineDataAttribute(TiffType.Double)] + [InlineDataAttribute(TiffType.Ifd)] + [InlineDataAttribute((TiffType)99)] + public void ReadSignedInteger_ThrowsExceptionIfInvalidType(ushort type) + { + (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, 1, new byte[4]), true); + + var e = Assert.Throws(() => decoder.ReadSignedInteger(ref entry)); + + Assert.Equal($"A value of type '{(TiffType)type}' cannot be converted to a signed integer.", e.Message); + } + + [Theory] + [InlineDataAttribute(TiffType.SByte, true)] + [InlineDataAttribute(TiffType.SShort, true)] + [InlineDataAttribute(TiffType.SLong, true)] + [InlineDataAttribute(TiffType.SByte, false)] + [InlineDataAttribute(TiffType.SShort, false)] + [InlineDataAttribute(TiffType.SLong, false)] + public void ReadSignedInteger_ThrowsExceptionIfCountIsNotOne(ushort type, bool isLittleEndian) + { + (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, 2, new byte[4]), isLittleEndian); + + var e = Assert.Throws(() => decoder.ReadSignedInteger(ref entry)); + + Assert.Equal($"Cannot read a single value from an array of multiple items.", e.Message); + } + + [Theory] + [InlineDataAttribute(TiffType.Byte, 1, true, new byte[] { 0, 1, 2, 3 }, new uint[] { 0 })] + [InlineDataAttribute(TiffType.Byte, 3, true, new byte[] { 0, 1, 2, 3 }, new uint[] { 0, 1, 2 })] + [InlineDataAttribute(TiffType.Byte, 7, true, new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8 }, new uint[] { 0, 1, 2, 3, 4, 5, 6 })] + [InlineDataAttribute(TiffType.Byte, 1, false, new byte[] { 0, 1, 2, 3 }, new uint[] { 0 })] + [InlineDataAttribute(TiffType.Byte, 3, false, new byte[] { 0, 1, 2, 3 }, new uint[] { 0, 1, 2 })] + [InlineDataAttribute(TiffType.Byte, 7, false, new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8 }, new uint[] { 0, 1, 2, 3, 4, 5, 6 })] + [InlineDataAttribute(TiffType.Short, 1, true, new byte[] { 1, 0, 3, 2 }, new uint[] { 1 })] + [InlineDataAttribute(TiffType.Short, 2, true, new byte[] { 1, 0, 3, 2 }, new uint[] { 1, 515 })] + [InlineDataAttribute(TiffType.Short, 3, true, new byte[] { 1, 0, 3, 2, 5, 4, 6, 7, 8 }, new uint[] { 1, 515, 1029 })] + [InlineDataAttribute(TiffType.Short, 1, false, new byte[] { 0, 1, 2, 3 }, new uint[] { 1 })] + [InlineDataAttribute(TiffType.Short, 2, false, new byte[] { 0, 1, 2, 3 }, new uint[] { 1, 515 })] + [InlineDataAttribute(TiffType.Short, 3, false, new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8 }, new uint[] { 1, 515, 1029 })] + [InlineDataAttribute(TiffType.Long, 1, true, new byte[] { 4, 3, 2, 1 }, new uint[] { 0x01020304 })] + [InlineDataAttribute(TiffType.Long, 2, true, new byte[] { 4, 3, 2, 1, 6, 5, 4, 3, 99, 99 }, new uint[] { 0x01020304, 0x03040506 })] + [InlineDataAttribute(TiffType.Long, 1, false, new byte[] { 1, 2, 3, 4 }, new uint[] { 0x01020304 })] + [InlineDataAttribute(TiffType.Long, 2, false, new byte[] { 1, 2, 3, 4, 3, 4, 5, 6, 99, 99 }, new uint[] { 0x01020304, 0x03040506 })] + public void ReadUnsignedIntegerArray_ReturnsValue(ushort type, int count, bool isLittleEndian, byte[] bytes, uint[] expectedValue) + { + (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, (uint)expectedValue.Length, bytes), isLittleEndian); + + uint[] result = decoder.ReadUnsignedIntegerArray(ref entry); + + Assert.Equal(expectedValue, result); + } + + [Theory] + [InlineDataAttribute(TiffType.Ascii)] + [InlineDataAttribute(TiffType.Rational)] + [InlineDataAttribute(TiffType.SByte)] + [InlineDataAttribute(TiffType.Undefined)] + [InlineDataAttribute(TiffType.SShort)] + [InlineDataAttribute(TiffType.SLong)] + [InlineDataAttribute(TiffType.SRational)] + [InlineDataAttribute(TiffType.Float)] + [InlineDataAttribute(TiffType.Double)] + [InlineDataAttribute(TiffType.Ifd)] + [InlineDataAttribute((TiffType)99)] + public void ReadUnsignedIntegerArray_ThrowsExceptionIfInvalidType(ushort type) + { + (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, 1, new byte[4]), true); + + var e = Assert.Throws(() => decoder.ReadUnsignedIntegerArray(ref entry)); + + Assert.Equal($"A value of type '{(TiffType)type}' cannot be converted to an unsigned integer.", e.Message); + } + + [Theory] + [InlineDataAttribute(TiffType.SByte, 1, true, new byte[] { 0, 1, 2, 3 }, new int[] { 0 })] + [InlineDataAttribute(TiffType.SByte, 3, true, new byte[] { 0, 255, 2, 3 }, new int[] { 0, -1, 2 })] + [InlineDataAttribute(TiffType.SByte, 7, true, new byte[] { 0, 255, 2, 3, 4, 5, 6, 7, 8 }, new int[] { 0, -1, 2, 3, 4, 5, 6 })] + [InlineDataAttribute(TiffType.SByte, 1, false, new byte[] { 0, 1, 2, 3 }, new int[] { 0 })] + [InlineDataAttribute(TiffType.SByte, 3, false, new byte[] { 0, 255, 2, 3 }, new int[] { 0, -1, 2 })] + [InlineDataAttribute(TiffType.SByte, 7, false, new byte[] { 0, 255, 2, 3, 4, 5, 6, 7, 8 }, new int[] { 0, -1, 2, 3, 4, 5, 6 })] + [InlineDataAttribute(TiffType.SShort, 1, true, new byte[] { 1, 0, 3, 2 }, new int[] { 1 })] + [InlineDataAttribute(TiffType.SShort, 2, true, new byte[] { 1, 0, 255, 255 }, new int[] { 1, -1 })] + [InlineDataAttribute(TiffType.SShort, 3, true, new byte[] { 1, 0, 255, 255, 5, 4, 6, 7, 8 }, new int[] { 1, -1, 1029 })] + [InlineDataAttribute(TiffType.SShort, 1, false, new byte[] { 0, 1, 2, 3 }, new int[] { 1 })] + [InlineDataAttribute(TiffType.SShort, 2, false, new byte[] { 0, 1, 255, 255 }, new int[] { 1, -1 })] + [InlineDataAttribute(TiffType.SShort, 3, false, new byte[] { 0, 1, 255, 255, 4, 5, 6, 7, 8 }, new int[] { 1, -1, 1029 })] + [InlineDataAttribute(TiffType.SLong, 1, true, new byte[] { 4, 3, 2, 1 }, new int[] { 0x01020304 })] + [InlineDataAttribute(TiffType.SLong, 2, true, new byte[] { 4, 3, 2, 1, 255, 255, 255, 255, 99, 99 }, new int[] { 0x01020304, -1 })] + [InlineDataAttribute(TiffType.SLong, 1, false, new byte[] { 1, 2, 3, 4 }, new int[] { 0x01020304 })] + [InlineDataAttribute(TiffType.SLong, 2, false, new byte[] { 1, 2, 3, 4, 255, 255, 255, 255, 99, 99 }, new int[] { 0x01020304, -1 })] + public void ReadSignedIntegerArray_ReturnsValue(ushort type, int count, bool isLittleEndian, byte[] bytes, int[] expectedValue) + { + (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, (uint)expectedValue.Length, bytes), isLittleEndian); + + int[] result = decoder.ReadSignedIntegerArray(ref entry); + + Assert.Equal(expectedValue, result); + } + + [Theory] + [InlineDataAttribute(TiffType.Byte)] + [InlineDataAttribute(TiffType.Ascii)] + [InlineDataAttribute(TiffType.Short)] + [InlineDataAttribute(TiffType.Long)] + [InlineDataAttribute(TiffType.Rational)] + [InlineDataAttribute(TiffType.Undefined)] + [InlineDataAttribute(TiffType.SRational)] + [InlineDataAttribute(TiffType.Float)] + [InlineDataAttribute(TiffType.Double)] + [InlineDataAttribute(TiffType.Ifd)] + [InlineDataAttribute((TiffType)99)] + public void ReadSignedIntegerArray_ThrowsExceptionIfInvalidType(ushort type) + { + (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, 1, new byte[4]), true); + + var e = Assert.Throws(() => decoder.ReadSignedIntegerArray(ref entry)); + + Assert.Equal($"A value of type '{(TiffType)type}' cannot be converted to a signed integer.", e.Message); + } + private (TiffDecoderCore, TiffIfdEntry) GenerateTestIfdEntry(TiffGenEntry entry, bool isLittleEndian) { Stream stream = new TiffGenIfd() diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs index 757da63b4..c0bb9d78c 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs @@ -53,6 +53,20 @@ namespace ImageSharp.Tests return new TiffGenEntryInteger(tag, type, value); } + public static TiffGenEntry Integer(ushort tag, TiffType type, uint value) + { + return TiffGenEntry.Integer(tag, type, new uint[] {value}); + } + + public static TiffGenEntry Integer(ushort tag, TiffType type, uint[] value) + { + if (type != TiffType.Byte && type != TiffType.Short && type != TiffType.Long && + type != TiffType.SByte && type != TiffType.SShort && type != TiffType.SLong) + throw new ArgumentException(nameof(type), "The specified type is not an integer type."); + + return new TiffGenEntryUnsignedInteger(tag, type, value); + } + private class TiffGenEntryAscii : TiffGenEntry { public TiffGenEntryAscii(ushort tag, string value) : base(tag, TiffType.Ascii, (uint)GetBytes(value).Length) @@ -125,5 +139,42 @@ namespace ImageSharp.Tests } } } + + private class TiffGenEntryUnsignedInteger : TiffGenEntry + { + public TiffGenEntryUnsignedInteger(ushort tag, TiffType type, uint[] value) : base(tag, type, (uint)value.Length) + { + this.Value = value; + } + + public uint[] Value { get; } + + public override IEnumerable GetData(bool isLittleEndian) + { + byte[] bytes = GetBytes().SelectMany(b => b.WithByteOrder(isLittleEndian)).ToArray(); + return new[] { new TiffGenDataBlock(bytes) }; + } + + private IEnumerable GetBytes() + { + switch (Type) + { + case TiffType.Byte: + return Value.Select(i => new byte[] { (byte)i }); + case TiffType.Short: + return Value.Select(i => BitConverter.GetBytes((ushort)i)); + case TiffType.Long: + return Value.Select(i => BitConverter.GetBytes((uint)i)); + case TiffType.SByte: + return Value.Select(i => BitConverter.GetBytes((sbyte)i)); + case TiffType.SShort: + return Value.Select(i => BitConverter.GetBytes((short)i)); + case TiffType.SLong: + return Value.Select(i => BitConverter.GetBytes((int)i)); + default: + throw new InvalidOperationException(); + } + } + } } } \ No newline at end of file From a27964aabe89272b311ebf49afd6e81033a207f7 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Sun, 12 Mar 2017 16:06:26 +0000 Subject: [PATCH 012/275] Read strings from TIFF IFDs --- .../TiffDecoderCore.cs | 14 +++++ .../Formats/Tiff/TiffDecoderIfdEntryTests.cs | 59 +++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs b/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs index 9a0920980..87dbf86da 100644 --- a/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs @@ -8,6 +8,7 @@ namespace ImageSharp.Formats using System; using System.IO; using System.Runtime.CompilerServices; + using System.Text; using System.Threading.Tasks; /// @@ -250,6 +251,19 @@ namespace ImageSharp.Formats return result; } + public string ReadString(ref TiffIfdEntry entry) + { + if (entry.Type != TiffType.Ascii) + throw new ImageFormatException($"A value of type '{entry.Type}' cannot be converted to a string."); + + byte[] bytes = ReadBytes(ref entry); + + if (bytes[entry.Count - 1] != 0) + throw new ImageFormatException("The retrieved string is not null terminated."); + + return Encoding.UTF8.GetString(bytes, 0, (int)entry.Count - 1); + } + private SByte ToSByte(byte[] bytes, int offset) { return (sbyte)bytes[offset]; diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs index 16581e367..ecdb375a5 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs @@ -364,6 +364,65 @@ namespace ImageSharp.Tests Assert.Equal($"A value of type '{(TiffType)type}' cannot be converted to a signed integer.", e.Message); } + [Theory] + [InlineDataAttribute(true, new byte[] { 0 }, "")] + [InlineDataAttribute(true, new byte[] { (byte)'A', (byte)'B', (byte)'C', 0 }, "ABC")] + [InlineDataAttribute(true, new byte[] { (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', 0 }, "ABCDEF")] + [InlineDataAttribute(true, new byte[] { (byte)'A', (byte)'B', (byte)'C', (byte)'D', 0, (byte)'E', (byte)'F', (byte)'G', (byte)'H', 0 }, "ABCD\0EFGH")] + [InlineDataAttribute(false, new byte[] { 0 }, "")] + [InlineDataAttribute(false, new byte[] { (byte)'A', (byte)'B', (byte)'C', 0 }, "ABC")] + [InlineDataAttribute(false, new byte[] { (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', 0 }, "ABCDEF")] + [InlineDataAttribute(false, new byte[] { (byte)'A', (byte)'B', (byte)'C', (byte)'D', 0, (byte)'E', (byte)'F', (byte)'G', (byte)'H', 0 }, "ABCD\0EFGH")] + public void ReadString_ReturnsValue(bool isLittleEndian, byte[] bytes, string expectedValue) + { + (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, TiffType.Ascii, (uint)bytes.Length, bytes), isLittleEndian); + + string result = decoder.ReadString(ref entry); + + Assert.Equal(expectedValue, result); + } + + [Theory] + [InlineDataAttribute(TiffType.Byte)] + [InlineDataAttribute(TiffType.Short)] + [InlineDataAttribute(TiffType.Long)] + [InlineDataAttribute(TiffType.Rational)] + [InlineDataAttribute(TiffType.SByte)] + [InlineDataAttribute(TiffType.Undefined)] + [InlineDataAttribute(TiffType.SShort)] + [InlineDataAttribute(TiffType.SLong)] + [InlineDataAttribute(TiffType.SRational)] + [InlineDataAttribute(TiffType.Float)] + [InlineDataAttribute(TiffType.Double)] + [InlineDataAttribute(TiffType.Ifd)] + [InlineDataAttribute((TiffType)99)] + public void ReadString_ThrowsExceptionIfInvalidType(ushort type) + { + (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, 1, new byte[4]), true); + + var e = Assert.Throws(() => decoder.ReadString(ref entry)); + + Assert.Equal($"A value of type '{(TiffType)type}' cannot be converted to a string.", e.Message); + } + + [Theory] + [InlineDataAttribute(true, new byte[] { (byte)'A' })] + [InlineDataAttribute(true, new byte[] { (byte)'A', (byte)'B', (byte)'C' })] + [InlineDataAttribute(true, new byte[] { (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F' })] + [InlineDataAttribute(true, new byte[] { (byte)'A', (byte)'B', (byte)'C', (byte)'D', 0, (byte)'E', (byte)'F', (byte)'G', (byte)'H' })] + [InlineDataAttribute(false, new byte[] { (byte)'A' })] + [InlineDataAttribute(false, new byte[] { (byte)'A', (byte)'B', (byte)'C' })] + [InlineDataAttribute(false, new byte[] { (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F' })] + [InlineDataAttribute(false, new byte[] { (byte)'A', (byte)'B', (byte)'C', (byte)'D', 0, (byte)'E', (byte)'F', (byte)'G', (byte)'H' })] + public void ReadString_ThrowsExceptionIfStringIsNotNullTerminated(bool isLittleEndian, byte[] bytes) + { + (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, TiffType.Ascii, (uint)bytes.Length, bytes), isLittleEndian); + + var e = Assert.Throws(() => decoder.ReadString(ref entry)); + + Assert.Equal($"The retrieved string is not null terminated.", e.Message); + } + private (TiffDecoderCore, TiffIfdEntry) GenerateTestIfdEntry(TiffGenEntry entry, bool isLittleEndian) { Stream stream = new TiffGenIfd() From 6c9e894141499d10b8b1a200e99c5cdb6309ef8c Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Mon, 13 Mar 2017 20:05:43 +0000 Subject: [PATCH 013/275] Read rational numbers from TIFF IFDs --- .../TiffDecoderCore.cs | 52 +++++ .../Formats/Tiff/TiffDecoderIfdEntryTests.cs | 184 ++++++++++++++++++ 2 files changed, 236 insertions(+) diff --git a/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs b/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs index 87dbf86da..309050f41 100644 --- a/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs @@ -264,6 +264,58 @@ namespace ImageSharp.Formats return Encoding.UTF8.GetString(bytes, 0, (int)entry.Count - 1); } + public Rational ReadUnsignedRational(ref TiffIfdEntry entry) + { + if (entry.Count != 1) + throw new ImageFormatException($"Cannot read a single value from an array of multiple items."); + + return ReadUnsignedRationalArray(ref entry)[0]; + } + + public SignedRational ReadSignedRational(ref TiffIfdEntry entry) + { + if (entry.Count != 1) + throw new ImageFormatException($"Cannot read a single value from an array of multiple items."); + + return ReadSignedRationalArray(ref entry)[0]; + } + + public Rational[] ReadUnsignedRationalArray(ref TiffIfdEntry entry) + { + if (entry.Type != TiffType.Rational) + throw new ImageFormatException($"A value of type '{entry.Type}' cannot be converted to a Rational."); + + byte[] bytes = ReadBytes(ref entry); + Rational[] result = new Rational[entry.Count]; + + for (int i = 0 ; i < result.Length ; i++) + { + uint numerator = ToUInt32(bytes, i * 8); + uint denominator = ToUInt32(bytes, i * 8 + 4); + result[i] = new Rational(numerator, denominator); + } + + return result; + } + + public SignedRational[] ReadSignedRationalArray(ref TiffIfdEntry entry) + { + if (entry.Type != TiffType.SRational) + throw new ImageFormatException($"A value of type '{entry.Type}' cannot be converted to a SignedRational."); + + byte[] bytes = ReadBytes(ref entry); + SignedRational[] result = new SignedRational[entry.Count]; + + for (int i = 0 ; i < result.Length ; i++) + { + int numerator = ToInt32(bytes, i * 8); + int denominator = ToInt32(bytes, i * 8 + 4); + result[i] = new SignedRational(numerator, denominator); + } + + return result; + } + private SByte ToSByte(byte[] bytes, int offset) { return (sbyte)bytes[offset]; diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs index ecdb375a5..b810182c9 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs @@ -423,6 +423,190 @@ namespace ImageSharp.Tests Assert.Equal($"The retrieved string is not null terminated.", e.Message); } + [Theory] + [InlineDataAttribute(true, new byte[] { 0, 0, 0, 0, 2, 0, 0, 0 }, 0, 2)] + [InlineDataAttribute(true, new byte[] { 1, 0, 0, 0, 2, 0, 0, 0 }, 1, 2)] + [InlineDataAttribute(false, new byte[] { 0, 0, 0, 0, 0, 0, 0, 2 }, 0, 2)] + [InlineDataAttribute(false, new byte[] { 0, 0, 0, 1, 0, 0, 0, 2 }, 1, 2)] + public void ReadUnsignedRational_ReturnsValue(bool isLittleEndian, byte[] bytes, uint expectedNumerator, uint expectedDenominator) + { + (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, TiffType.Rational, 1, bytes), isLittleEndian); + + Rational result = decoder.ReadUnsignedRational(ref entry); + Rational expectedValue = new Rational(expectedNumerator, expectedDenominator); + + Assert.Equal(expectedValue, result); + } + + [Theory] + [InlineDataAttribute(true, new byte[] { 0, 0, 0, 0, 2, 0, 0, 0 }, 0, 2)] + [InlineDataAttribute(true, new byte[] { 1, 0, 0, 0, 2, 0, 0, 0 }, 1, 2)] + [InlineDataAttribute(true, new byte[] { 255, 255, 255, 255, 2, 0, 0, 0 }, -1, 2)] + [InlineDataAttribute(false, new byte[] { 0, 0, 0, 0, 0, 0, 0, 2 }, 0, 2)] + [InlineDataAttribute(false, new byte[] { 0, 0, 0, 1, 0, 0, 0, 2 }, 1, 2)] + [InlineDataAttribute(false, new byte[] { 255, 255, 255, 255, 0, 0, 0, 2 }, -1, 2)] + public void ReadSignedRational_ReturnsValue(bool isLittleEndian, byte[] bytes, int expectedNumerator, int expectedDenominator) + { + (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, TiffType.SRational, 1, bytes), isLittleEndian); + + SignedRational result = decoder.ReadSignedRational(ref entry); + SignedRational expectedValue = new SignedRational(expectedNumerator, expectedDenominator); + + Assert.Equal(expectedValue, result); + } + + [Theory] + [InlineDataAttribute(true, new byte[] { 0, 0, 0, 0, 2, 0, 0, 0 }, new uint[] { 0 }, new uint[] { 2 })] + [InlineDataAttribute(true, new byte[] { 1, 0, 0, 0, 2, 0, 0, 0 }, new uint[] { 1 }, new uint[] { 2 })] + [InlineDataAttribute(true, new byte[] { 1, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0 }, new uint[] { 1, 2 }, new uint[] { 2, 3 })] + [InlineDataAttribute(false, new byte[] { 0, 0, 0, 0, 0, 0, 0, 2 }, new uint[] { 0 }, new uint[] { 2 })] + [InlineDataAttribute(false, new byte[] { 0, 0, 0, 1, 0, 0, 0, 2 }, new uint[] { 1 }, new uint[] { 2 })] + [InlineDataAttribute(false, new byte[] { 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 3 }, new uint[] { 1, 2 }, new uint[] { 2, 3 })] + public void ReadUnsignedRationalArray_ReturnsValue(bool isLittleEndian, byte[] bytes, uint[] expectedNumerators, uint[] expectedDenominators) + { + (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, TiffType.Rational, (uint)expectedNumerators.Length, bytes), isLittleEndian); + + Rational[] result = decoder.ReadUnsignedRationalArray(ref entry); + Rational[] expectedValue = Enumerable.Range(0, expectedNumerators.Length).Select(i => new Rational(expectedNumerators[i], expectedDenominators[i])).ToArray(); + + Assert.Equal(expectedValue, result); + } + + [Theory] + [InlineDataAttribute(true, new byte[] { 0, 0, 0, 0, 2, 0, 0, 0 }, new int[] { 0 }, new int[] { 2 })] + [InlineDataAttribute(true, new byte[] { 1, 0, 0, 0, 2, 0, 0, 0 }, new int[] { 1 }, new int[] { 2 })] + [InlineDataAttribute(true, new byte[] { 255, 255, 255, 255, 2, 0, 0, 0 }, new int[] { -1 }, new int[] { 2 })] + [InlineDataAttribute(true, new byte[] { 255, 255, 255, 255, 2, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0 }, new int[] { -1, 2 }, new int[] { 2, 3 })] + [InlineDataAttribute(false, new byte[] { 0, 0, 0, 0, 0, 0, 0, 2 }, new int[] { 0 }, new int[] { 2 })] + [InlineDataAttribute(false, new byte[] { 0, 0, 0, 1, 0, 0, 0, 2 }, new int[] { 1 }, new int[] { 2 })] + [InlineDataAttribute(false, new byte[] { 255, 255, 255, 255, 0, 0, 0, 2 }, new int[] { -1 }, new int[] { 2 })] + [InlineDataAttribute(false, new byte[] { 255, 255, 255, 255, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 3 }, new int[] { -1, 2 }, new int[] { 2, 3 })] + public void ReadSignedRationalArray_ReturnsValue(bool isLittleEndian, byte[] bytes, int[] expectedNumerators, int[] expectedDenominators) + { + (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, TiffType.SRational, (uint)expectedNumerators.Length, bytes), isLittleEndian); + + SignedRational[] result = decoder.ReadSignedRationalArray(ref entry); + SignedRational[] expectedValue = Enumerable.Range(0, expectedNumerators.Length).Select(i => new SignedRational(expectedNumerators[i], expectedDenominators[i])).ToArray(); + + Assert.Equal(expectedValue, result); + } + + [Theory] + [InlineDataAttribute(TiffType.Byte)] + [InlineDataAttribute(TiffType.Ascii)] + [InlineDataAttribute(TiffType.Short)] + [InlineDataAttribute(TiffType.Long)] + [InlineDataAttribute(TiffType.SByte)] + [InlineDataAttribute(TiffType.Undefined)] + [InlineDataAttribute(TiffType.SShort)] + [InlineDataAttribute(TiffType.SLong)] + [InlineDataAttribute(TiffType.SRational)] + [InlineDataAttribute(TiffType.Float)] + [InlineDataAttribute(TiffType.Double)] + [InlineDataAttribute(TiffType.Ifd)] + [InlineDataAttribute((TiffType)99)] + public void ReadUnsignedRational_ThrowsExceptionIfInvalidType(ushort type) + { + (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, 1, new byte[4]), true); + + var e = Assert.Throws(() => decoder.ReadUnsignedRational(ref entry)); + + Assert.Equal($"A value of type '{(TiffType)type}' cannot be converted to a Rational.", e.Message); + } + + [Theory] + [InlineDataAttribute(TiffType.Byte)] + [InlineDataAttribute(TiffType.Ascii)] + [InlineDataAttribute(TiffType.Short)] + [InlineDataAttribute(TiffType.Long)] + [InlineDataAttribute(TiffType.SByte)] + [InlineDataAttribute(TiffType.Rational)] + [InlineDataAttribute(TiffType.Undefined)] + [InlineDataAttribute(TiffType.SShort)] + [InlineDataAttribute(TiffType.SLong)] + [InlineDataAttribute(TiffType.Float)] + [InlineDataAttribute(TiffType.Double)] + [InlineDataAttribute(TiffType.Ifd)] + [InlineDataAttribute((TiffType)99)] + public void ReadSignedRational_ThrowsExceptionIfInvalidType(ushort type) + { + (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, 1, new byte[4]), true); + + var e = Assert.Throws(() => decoder.ReadSignedRational(ref entry)); + + Assert.Equal($"A value of type '{(TiffType)type}' cannot be converted to a SignedRational.", e.Message); + } + + [Theory] + [InlineDataAttribute(TiffType.Byte)] + [InlineDataAttribute(TiffType.Ascii)] + [InlineDataAttribute(TiffType.Short)] + [InlineDataAttribute(TiffType.Long)] + [InlineDataAttribute(TiffType.SByte)] + [InlineDataAttribute(TiffType.Undefined)] + [InlineDataAttribute(TiffType.SShort)] + [InlineDataAttribute(TiffType.SLong)] + [InlineDataAttribute(TiffType.SRational)] + [InlineDataAttribute(TiffType.Float)] + [InlineDataAttribute(TiffType.Double)] + [InlineDataAttribute(TiffType.Ifd)] + [InlineDataAttribute((TiffType)99)] + public void ReadUnsignedRationalArray_ThrowsExceptionIfInvalidType(ushort type) + { + (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, 1, new byte[4]), true); + + var e = Assert.Throws(() => decoder.ReadUnsignedRationalArray(ref entry)); + + Assert.Equal($"A value of type '{(TiffType)type}' cannot be converted to a Rational.", e.Message); + } + + [Theory] + [InlineDataAttribute(TiffType.Byte)] + [InlineDataAttribute(TiffType.Ascii)] + [InlineDataAttribute(TiffType.Short)] + [InlineDataAttribute(TiffType.Long)] + [InlineDataAttribute(TiffType.Rational)] + [InlineDataAttribute(TiffType.SByte)] + [InlineDataAttribute(TiffType.Undefined)] + [InlineDataAttribute(TiffType.SShort)] + [InlineDataAttribute(TiffType.SLong)] + [InlineDataAttribute(TiffType.Float)] + [InlineDataAttribute(TiffType.Double)] + [InlineDataAttribute(TiffType.Ifd)] + [InlineDataAttribute((TiffType)99)] + public void ReadSignedRationalArray_ThrowsExceptionIfInvalidType(ushort type) + { + (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, 1, new byte[4]), true); + + var e = Assert.Throws(() => decoder.ReadSignedRationalArray(ref entry)); + + Assert.Equal($"A value of type '{(TiffType)type}' cannot be converted to a SignedRational.", e.Message); + } + + [Theory] + [InlineDataAttribute(false)] + [InlineDataAttribute(true)] + public void ReadUnsignedRational_ThrowsExceptionIfCountIsNotOne(bool isLittleEndian) + { + (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, TiffType.Rational, 2, new byte[4]), isLittleEndian); + + var e = Assert.Throws(() => decoder.ReadUnsignedRational(ref entry)); + + Assert.Equal($"Cannot read a single value from an array of multiple items.", e.Message); + } + + [Theory] + [InlineDataAttribute(false)] + [InlineDataAttribute(true)] + public void ReadSignedRational_ThrowsExceptionIfCountIsNotOne(bool isLittleEndian) + { + (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, TiffType.SRational, 2, new byte[4]), isLittleEndian); + + var e = Assert.Throws(() => decoder.ReadSignedRational(ref entry)); + + Assert.Equal($"Cannot read a single value from an array of multiple items.", e.Message); + } + private (TiffDecoderCore, TiffIfdEntry) GenerateTestIfdEntry(TiffGenEntry entry, bool isLittleEndian) { Stream stream = new TiffGenIfd() From f56367f6bee7b80f32fb6371ace042c6d8fb6328 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Mon, 13 Mar 2017 20:10:10 +0000 Subject: [PATCH 014/275] Use constants for data type sizes --- src/ImageSharp.Formats.Tiff/TiffConstants.cs | 15 +++++++++++++++ src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs | 16 ++++++++-------- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/ImageSharp.Formats.Tiff/TiffConstants.cs b/src/ImageSharp.Formats.Tiff/TiffConstants.cs index 73508b34a..84b90e69f 100644 --- a/src/ImageSharp.Formats.Tiff/TiffConstants.cs +++ b/src/ImageSharp.Formats.Tiff/TiffConstants.cs @@ -36,5 +36,20 @@ namespace ImageSharp.Formats /// Size (in bytes) of each individual TIFF IFD entry /// public const int SizeOfIfdEntry = 12; + + /// + /// Size (in bytes) of the Short and SShort data types + /// + public const int SizeOfShort = 2; + + /// + /// Size (in bytes) of the Long and SLong data types + /// + public const int SizeOfLong = 4; + + /// + /// Size (in bytes) of the Rational and SRational data types + /// + public const int SizeOfRational = 8; } } diff --git a/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs b/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs index 309050f41..7d5bcf519 100644 --- a/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs @@ -203,13 +203,13 @@ namespace ImageSharp.Formats case TiffType.Short: { for (int i = 0 ; i < result.Length ; i++) - result[i] = (uint)ToUInt16(bytes, i * 2); + result[i] = (uint)ToUInt16(bytes, i * TiffConstants.SizeOfShort); break; } case TiffType.Long: { for (int i = 0 ; i < result.Length ; i++) - result[i] = ToUInt32(bytes, i * 4); + result[i] = ToUInt32(bytes, i * TiffConstants.SizeOfLong); break; } default: @@ -235,13 +235,13 @@ namespace ImageSharp.Formats case TiffType.SShort: { for (int i = 0 ; i < result.Length ; i++) - result[i] = (int)ToInt16(bytes, i * 2); + result[i] = (int)ToInt16(bytes, i * TiffConstants.SizeOfShort); break; } case TiffType.SLong: { for (int i = 0 ; i < result.Length ; i++) - result[i] = ToInt32(bytes, i * 4); + result[i] = ToInt32(bytes, i * TiffConstants.SizeOfLong); break; } default: @@ -290,8 +290,8 @@ namespace ImageSharp.Formats for (int i = 0 ; i < result.Length ; i++) { - uint numerator = ToUInt32(bytes, i * 8); - uint denominator = ToUInt32(bytes, i * 8 + 4); + uint numerator = ToUInt32(bytes, i * TiffConstants.SizeOfRational); + uint denominator = ToUInt32(bytes, i * TiffConstants.SizeOfRational + TiffConstants.SizeOfLong); result[i] = new Rational(numerator, denominator); } @@ -308,8 +308,8 @@ namespace ImageSharp.Formats for (int i = 0 ; i < result.Length ; i++) { - int numerator = ToInt32(bytes, i * 8); - int denominator = ToInt32(bytes, i * 8 + 4); + int numerator = ToInt32(bytes, i * TiffConstants.SizeOfRational); + int denominator = ToInt32(bytes, i * TiffConstants.SizeOfRational + TiffConstants.SizeOfLong); result[i] = new SignedRational(numerator, denominator); } From 4cbb28ba62e115ec274ee16c4c6c85266e404c40 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Mon, 13 Mar 2017 20:32:22 +0000 Subject: [PATCH 015/275] Read floating-point numbers from TIFF IFDs --- src/ImageSharp.Formats.Tiff/TiffConstants.cs | 10 + .../TiffDecoderCore.cs | 69 ++++++ .../Formats/Tiff/TiffDecoderIfdEntryTests.cs | 217 ++++++++++++++++++ 3 files changed, 296 insertions(+) diff --git a/src/ImageSharp.Formats.Tiff/TiffConstants.cs b/src/ImageSharp.Formats.Tiff/TiffConstants.cs index 84b90e69f..858a1ca4b 100644 --- a/src/ImageSharp.Formats.Tiff/TiffConstants.cs +++ b/src/ImageSharp.Formats.Tiff/TiffConstants.cs @@ -51,5 +51,15 @@ namespace ImageSharp.Formats /// Size (in bytes) of the Rational and SRational data types /// public const int SizeOfRational = 8; + + /// + /// Size (in bytes) of the Float data type + /// + public const int SizeOfFloat = 4; + + /// + /// Size (in bytes) of the Double data type + /// + public const int SizeOfDouble = 8; } } diff --git a/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs b/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs index 7d5bcf519..1c2703ed5 100644 --- a/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs @@ -316,6 +316,53 @@ namespace ImageSharp.Formats return result; } + public float ReadFloat(ref TiffIfdEntry entry) + { + if (entry.Count != 1) + throw new ImageFormatException($"Cannot read a single value from an array of multiple items."); + + if (entry.Type != TiffType.Float) + throw new ImageFormatException($"A value of type '{entry.Type}' cannot be converted to a float."); + + return ToSingle(entry.Value, 0); + } + + public double ReadDouble(ref TiffIfdEntry entry) + { + if (entry.Count != 1) + throw new ImageFormatException($"Cannot read a single value from an array of multiple items."); + + return ReadDoubleArray(ref entry)[0]; + } + + public float[] ReadFloatArray(ref TiffIfdEntry entry) + { + if (entry.Type != TiffType.Float) + throw new ImageFormatException($"A value of type '{entry.Type}' cannot be converted to a float."); + + byte[] bytes = ReadBytes(ref entry); + float[] result = new float[entry.Count]; + + for (int i = 0 ; i < result.Length ; i++) + result[i] = ToSingle(bytes, i * TiffConstants.SizeOfFloat); + + return result; + } + + public double[] ReadDoubleArray(ref TiffIfdEntry entry) + { + if (entry.Type != TiffType.Double) + throw new ImageFormatException($"A value of type '{entry.Type}' cannot be converted to a double."); + + byte[] bytes = ReadBytes(ref entry); + double[] result = new double[entry.Count]; + + for (int i = 0 ; i < result.Length ; i++) + result[i] = ToDouble(bytes, i * TiffConstants.SizeOfDouble); + + return result; + } + private SByte ToSByte(byte[] bytes, int offset) { return (sbyte)bytes[offset]; @@ -352,6 +399,28 @@ namespace ImageSharp.Formats return (ushort)ToInt16(bytes, offset); } + private Single ToSingle(byte[] bytes, int offset) + { + byte[] buffer = new byte[4]; + Array.Copy(bytes, offset, buffer, 0, 4); + + if (BitConverter.IsLittleEndian != IsLittleEndian) + Array.Reverse(buffer); + + return BitConverter.ToSingle(buffer, 0); + } + + private Double ToDouble(byte[] bytes, int offset) + { + byte[] buffer = new byte[8]; + Array.Copy(bytes, offset, buffer, 0, 8); + + if (BitConverter.IsLittleEndian != IsLittleEndian) + Array.Reverse(buffer); + + return BitConverter.ToDouble(buffer, 0); + } + public static uint GetSizeOfData(TiffIfdEntry entry) => SizeOfDataType(entry.Type) * entry.Count; private static uint SizeOfDataType(TiffType type) diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs index b810182c9..369fc61de 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs @@ -607,6 +607,223 @@ namespace ImageSharp.Tests Assert.Equal($"Cannot read a single value from an array of multiple items.", e.Message); } + [Theory] + [InlineDataAttribute(false, new byte[] { 0x00, 0x00, 0x00, 0x00 }, 0.0F)] + [InlineDataAttribute(false, new byte[] { 0x3F, 0x80, 0x00, 0x00 }, 1.0F)] + [InlineDataAttribute(false, new byte[] { 0xC0, 0x00, 0x00, 0x00 }, -2.0F)] + [InlineDataAttribute(false, new byte[] { 0x7F, 0x7F, 0xFF, 0xFF }, float.MaxValue)] + [InlineDataAttribute(false, new byte[] { 0x7F, 0x80, 0x00, 0x00 }, float.PositiveInfinity)] + [InlineDataAttribute(false, new byte[] { 0xFF, 0x80, 0x00, 0x00 }, float.NegativeInfinity)] + [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x00, 0x00 }, 0.0F)] + [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x80, 0x3F }, 1.0F)] + [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x00, 0xC0 }, -2.0F)] + [InlineDataAttribute(true, new byte[] { 0xFF, 0xFF, 0x7F, 0x7F }, float.MaxValue)] + [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x80, 0x7F }, float.PositiveInfinity)] + [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x80, 0xFF }, float.NegativeInfinity)] + public void ReadFloat_ReturnsValue(bool isLittleEndian, byte[] bytes, float expectedValue) + { + (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, TiffType.Float, 1, bytes), isLittleEndian); + + float result = decoder.ReadFloat(ref entry); + + Assert.Equal(expectedValue, result); + } + + [Theory] + [InlineDataAttribute(TiffType.Byte)] + [InlineDataAttribute(TiffType.Ascii)] + [InlineDataAttribute(TiffType.Short)] + [InlineDataAttribute(TiffType.Long)] + [InlineDataAttribute(TiffType.Rational)] + [InlineDataAttribute(TiffType.SByte)] + [InlineDataAttribute(TiffType.Undefined)] + [InlineDataAttribute(TiffType.SShort)] + [InlineDataAttribute(TiffType.SLong)] + [InlineDataAttribute(TiffType.SRational)] + [InlineDataAttribute(TiffType.Double)] + [InlineDataAttribute(TiffType.Ifd)] + [InlineDataAttribute((TiffType)99)] + public void ReadFloat_ThrowsExceptionIfInvalidType(ushort type) + { + (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, 1, new byte[4]), true); + + var e = Assert.Throws(() => decoder.ReadFloat(ref entry)); + + Assert.Equal($"A value of type '{(TiffType)type}' cannot be converted to a float.", e.Message); + } + + [Theory] + [InlineDataAttribute(false)] + [InlineDataAttribute(true)] + public void ReadFloat_ThrowsExceptionIfCountIsNotOne(bool isLittleEndian) + { + (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, TiffType.Float, 2, new byte[4]), isLittleEndian); + + var e = Assert.Throws(() => decoder.ReadFloat(ref entry)); + + Assert.Equal($"Cannot read a single value from an array of multiple items.", e.Message); + } + + [Theory] + [InlineDataAttribute(false, new byte[] { 0x00, 0x00, 0x00, 0x00 }, new float[] { 0.0F })] + [InlineDataAttribute(false, new byte[] { 0x3F, 0x80, 0x00, 0x00 }, new float[] { 1.0F })] + [InlineDataAttribute(false, new byte[] { 0xC0, 0x00, 0x00, 0x00 }, new float[] { -2.0F })] + [InlineDataAttribute(false, new byte[] { 0x7F, 0x7F, 0xFF, 0xFF }, new float[] { float.MaxValue })] + [InlineDataAttribute(false, new byte[] { 0x7F, 0x80, 0x00, 0x00 }, new float[] { float.PositiveInfinity })] + [InlineDataAttribute(false, new byte[] { 0xFF, 0x80, 0x00, 0x00 }, new float[] { float.NegativeInfinity })] + [InlineDataAttribute(false, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00 }, new float[] { 0.0F, 1.0F, -2.0F })] + [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x00, 0x00 }, new float[] { 0.0F })] + [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x80, 0x3F }, new float[] { 1.0F })] + [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x00, 0xC0 }, new float[] { -2.0F })] + [InlineDataAttribute(true, new byte[] { 0xFF, 0xFF, 0x7F, 0x7F }, new float[] { float.MaxValue })] + [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x80, 0x7F }, new float[] { float.PositiveInfinity })] + [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x80, 0xFF }, new float[] { float.NegativeInfinity })] + [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0xC0 }, new float[] { 0.0F, 1.0F, -2.0F })] + + public void ReadFloatArray_ReturnsValue(bool isLittleEndian, byte[] bytes, float[] expectedValue) + { + (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, TiffType.Float, (uint)expectedValue.Length, bytes), isLittleEndian); + + float[] result = decoder.ReadFloatArray(ref entry); + + Assert.Equal(expectedValue, result); + } + + [Theory] + [InlineDataAttribute(TiffType.Byte)] + [InlineDataAttribute(TiffType.Ascii)] + [InlineDataAttribute(TiffType.Short)] + [InlineDataAttribute(TiffType.Long)] + [InlineDataAttribute(TiffType.Rational)] + [InlineDataAttribute(TiffType.SByte)] + [InlineDataAttribute(TiffType.Undefined)] + [InlineDataAttribute(TiffType.SShort)] + [InlineDataAttribute(TiffType.SLong)] + [InlineDataAttribute(TiffType.SRational)] + [InlineDataAttribute(TiffType.Double)] + [InlineDataAttribute(TiffType.Ifd)] + [InlineDataAttribute((TiffType)99)] + public void ReadFloatArray_ThrowsExceptionIfInvalidType(ushort type) + { + (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, 1, new byte[4]), true); + + var e = Assert.Throws(() => decoder.ReadFloatArray(ref entry)); + + Assert.Equal($"A value of type '{(TiffType)type}' cannot be converted to a float.", e.Message); + } + + [Theory] + [InlineDataAttribute(false, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0.0)] + [InlineDataAttribute(false, new byte[] { 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 1.0)] + [InlineDataAttribute(false, new byte[] { 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 2.0)] + [InlineDataAttribute(false, new byte[] { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, -2.0)] + [InlineDataAttribute(false, new byte[] { 0x7F, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, double.MaxValue)] + [InlineDataAttribute(false, new byte[] { 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, double.PositiveInfinity)] + [InlineDataAttribute(false, new byte[] { 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, double.NegativeInfinity)] + [InlineDataAttribute(false, new byte[] { 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, double.NaN)] + [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0.0)] + [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F }, 1.0)] + [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40 }, 2.0)] + [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0 }, -2.0)] + [InlineDataAttribute(true, new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0x7F }, double.MaxValue)] + [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x7F }, double.PositiveInfinity)] + [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF }, double.NegativeInfinity)] + [InlineDataAttribute(true, new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F }, double.NaN)] + public void ReadDouble_ReturnsValue(bool isLittleEndian, byte[] bytes, double expectedValue) + { + (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, TiffType.Double, 1, bytes), isLittleEndian); + + double result = decoder.ReadDouble(ref entry); + + Assert.Equal(expectedValue, result); + } + + [Theory] + [InlineDataAttribute(TiffType.Byte)] + [InlineDataAttribute(TiffType.Ascii)] + [InlineDataAttribute(TiffType.Short)] + [InlineDataAttribute(TiffType.Long)] + [InlineDataAttribute(TiffType.Rational)] + [InlineDataAttribute(TiffType.SByte)] + [InlineDataAttribute(TiffType.Undefined)] + [InlineDataAttribute(TiffType.SShort)] + [InlineDataAttribute(TiffType.SLong)] + [InlineDataAttribute(TiffType.SRational)] + [InlineDataAttribute(TiffType.Float)] + [InlineDataAttribute(TiffType.Ifd)] + [InlineDataAttribute((TiffType)99)] + public void ReadDouble_ThrowsExceptionIfInvalidType(ushort type) + { + (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, 1, new byte[4]), true); + + var e = Assert.Throws(() => decoder.ReadDouble(ref entry)); + + Assert.Equal($"A value of type '{(TiffType)type}' cannot be converted to a double.", e.Message); + } + + [Theory] + [InlineDataAttribute(false)] + [InlineDataAttribute(true)] + public void ReadDouble_ThrowsExceptionIfCountIsNotOne(bool isLittleEndian) + { + (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, TiffType.Double, 2, new byte[4]), isLittleEndian); + + var e = Assert.Throws(() => decoder.ReadDouble(ref entry)); + + Assert.Equal($"Cannot read a single value from an array of multiple items.", e.Message); + } + + [Theory] + [InlineDataAttribute(false, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, new double[] { 0.0 })] + [InlineDataAttribute(false, new byte[] { 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, new double[] { 1.0 })] + [InlineDataAttribute(false, new byte[] { 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, new double[] { 2.0 })] + [InlineDataAttribute(false, new byte[] { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, new double[] { -2.0 })] + [InlineDataAttribute(false, new byte[] { 0x7F, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, new double[] { double.MaxValue })] + [InlineDataAttribute(false, new byte[] { 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, new double[] { double.PositiveInfinity })] + [InlineDataAttribute(false, new byte[] { 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, new double[] { double.NegativeInfinity })] + [InlineDataAttribute(false, new byte[] { 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, new double[] { double.NaN })] + [InlineDataAttribute(false, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, new double[] { 0.0, 1.0, -2.0 })] + [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, new double[] { 0.0 })] + [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F }, new double[] { 1.0 })] + [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40 }, new double[] { 2.0 })] + [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0 }, new double[] { -2.0 })] + [InlineDataAttribute(true, new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0x7F }, new double[] { double.MaxValue })] + [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x7F }, new double[] { double.PositiveInfinity })] + [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF }, new double[] { double.NegativeInfinity })] + [InlineDataAttribute(true, new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F }, new double[] { double.NaN })] + [InlineDataAttribute(true, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0 }, new double[] { 0.0, 1.0, -2.0 })] + public void ReadDoubleArray_ReturnsValue(bool isLittleEndian, byte[] bytes, double[] expectedValue) + { + (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, TiffType.Double, (uint)expectedValue.Length, bytes), isLittleEndian); + + double[] result = decoder.ReadDoubleArray(ref entry); + + Assert.Equal(expectedValue, result); + } + + [Theory] + [InlineDataAttribute(TiffType.Byte)] + [InlineDataAttribute(TiffType.Ascii)] + [InlineDataAttribute(TiffType.Short)] + [InlineDataAttribute(TiffType.Long)] + [InlineDataAttribute(TiffType.Rational)] + [InlineDataAttribute(TiffType.SByte)] + [InlineDataAttribute(TiffType.Undefined)] + [InlineDataAttribute(TiffType.SShort)] + [InlineDataAttribute(TiffType.SLong)] + [InlineDataAttribute(TiffType.SRational)] + [InlineDataAttribute(TiffType.Float)] + [InlineDataAttribute(TiffType.Ifd)] + [InlineDataAttribute((TiffType)99)] + public void ReadDoubleArray_ThrowsExceptionIfInvalidType(ushort type) + { + (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, 1, new byte[4]), true); + + var e = Assert.Throws(() => decoder.ReadDoubleArray(ref entry)); + + Assert.Equal($"A value of type '{(TiffType)type}' cannot be converted to a double.", e.Message); + } + private (TiffDecoderCore, TiffIfdEntry) GenerateTestIfdEntry(TiffGenEntry entry, bool isLittleEndian) { Stream stream = new TiffGenIfd() From a073a9a2d415692ee96df248f4d1282499835141 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Tue, 14 Mar 2017 20:59:31 +0000 Subject: [PATCH 016/275] Add a number of TIFF specific constants/enums --- .../Constants/TiffCompression.cs | 37 +++++++++++++++++ .../{ => Constants}/TiffConstants.cs | 0 .../Constants/TiffExtraSamples.cs | 19 +++++++++ .../Constants/TiffFillOrder.cs | 18 +++++++++ .../Constants/TiffNewSubfileType.cs | 31 ++++++++++++++ .../Constants/TiffOrientation.cs | 24 +++++++++++ .../TiffPhotometricInterpretation.cs | 40 +++++++++++++++++++ .../Constants/TiffPlanarConfiguration.cs | 18 +++++++++ .../Constants/TiffResolutionUnit.cs | 19 +++++++++ .../Constants/TiffSubfileType.cs | 19 +++++++++ .../{ => Constants}/TiffTags.cs | 0 .../Constants/TiffThreshholding.cs | 19 +++++++++ .../{ => Constants}/TiffType.cs | 0 13 files changed, 244 insertions(+) create mode 100644 src/ImageSharp.Formats.Tiff/Constants/TiffCompression.cs rename src/ImageSharp.Formats.Tiff/{ => Constants}/TiffConstants.cs (100%) create mode 100644 src/ImageSharp.Formats.Tiff/Constants/TiffExtraSamples.cs create mode 100644 src/ImageSharp.Formats.Tiff/Constants/TiffFillOrder.cs create mode 100644 src/ImageSharp.Formats.Tiff/Constants/TiffNewSubfileType.cs create mode 100644 src/ImageSharp.Formats.Tiff/Constants/TiffOrientation.cs create mode 100644 src/ImageSharp.Formats.Tiff/Constants/TiffPhotometricInterpretation.cs create mode 100644 src/ImageSharp.Formats.Tiff/Constants/TiffPlanarConfiguration.cs create mode 100644 src/ImageSharp.Formats.Tiff/Constants/TiffResolutionUnit.cs create mode 100644 src/ImageSharp.Formats.Tiff/Constants/TiffSubfileType.cs rename src/ImageSharp.Formats.Tiff/{ => Constants}/TiffTags.cs (100%) create mode 100644 src/ImageSharp.Formats.Tiff/Constants/TiffThreshholding.cs rename src/ImageSharp.Formats.Tiff/{ => Constants}/TiffType.cs (100%) diff --git a/src/ImageSharp.Formats.Tiff/Constants/TiffCompression.cs b/src/ImageSharp.Formats.Tiff/Constants/TiffCompression.cs new file mode 100644 index 000000000..4caa7887d --- /dev/null +++ b/src/ImageSharp.Formats.Tiff/Constants/TiffCompression.cs @@ -0,0 +1,37 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + /// + /// Enumeration representing the compression formats defined by the Tiff file-format. + /// + internal enum TiffCompression + { + // TIFF baseline compression types + + None = 1, + Ccitt1D = 2, + PackBits = 32773, + + // TIFF Extension compression types + + CcittGroup3Fax = 3, + CcittGroup4Fax = 4, + Lzw = 5, + OldJpeg = 6, + + // Technote 2 + + Jpeg = 7, + Deflate = 8, + OldDeflate = 32946, + + // TIFF-F/FX Extension + + ItuTRecT82 = 9, + ItuTRecT43 = 10 + } +} \ No newline at end of file diff --git a/src/ImageSharp.Formats.Tiff/TiffConstants.cs b/src/ImageSharp.Formats.Tiff/Constants/TiffConstants.cs similarity index 100% rename from src/ImageSharp.Formats.Tiff/TiffConstants.cs rename to src/ImageSharp.Formats.Tiff/Constants/TiffConstants.cs diff --git a/src/ImageSharp.Formats.Tiff/Constants/TiffExtraSamples.cs b/src/ImageSharp.Formats.Tiff/Constants/TiffExtraSamples.cs new file mode 100644 index 000000000..4fa153fc5 --- /dev/null +++ b/src/ImageSharp.Formats.Tiff/Constants/TiffExtraSamples.cs @@ -0,0 +1,19 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + /// + /// Enumeration representing the possible uses of extra-samples in TIFF format files. + /// + internal enum TiffExtraSamples + { + // TIFF baseline ExtraSample values + + Unspecified = 0, + AssociatedAlpha = 1, + UnassociatedAlpha = 2 + } +} \ No newline at end of file diff --git a/src/ImageSharp.Formats.Tiff/Constants/TiffFillOrder.cs b/src/ImageSharp.Formats.Tiff/Constants/TiffFillOrder.cs new file mode 100644 index 000000000..e520599b4 --- /dev/null +++ b/src/ImageSharp.Formats.Tiff/Constants/TiffFillOrder.cs @@ -0,0 +1,18 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + /// + /// Enumeration representing the fill orders defined by the Tiff file-format. + /// + internal enum TiffFillOrder + { + // TIFF baseline FillOrder values + + MostSignificantBitFirst = 1, + LeastSignificantBitFirst = 2 + } +} \ No newline at end of file diff --git a/src/ImageSharp.Formats.Tiff/Constants/TiffNewSubfileType.cs b/src/ImageSharp.Formats.Tiff/Constants/TiffNewSubfileType.cs new file mode 100644 index 000000000..cf66d6d58 --- /dev/null +++ b/src/ImageSharp.Formats.Tiff/Constants/TiffNewSubfileType.cs @@ -0,0 +1,31 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + using System; + + /// + /// Enumeration representing the sub-file types defined by the Tiff file-format. + /// + [Flags] + internal enum TiffNewSubfileType + { + // TIFF baseline subfile types + + FullImage = 0x0000, + Preview = 0x0001, + SinglePage = 0x0002, + TransparencyMask = 0x0004, + + // DNG Specification subfile types + + AlternativePreview = 0x10000, + + // TIFF-F/FX Specification subfile types + + MixedRasterContent = 0x0008 + } +} \ No newline at end of file diff --git a/src/ImageSharp.Formats.Tiff/Constants/TiffOrientation.cs b/src/ImageSharp.Formats.Tiff/Constants/TiffOrientation.cs new file mode 100644 index 000000000..4938a3f7f --- /dev/null +++ b/src/ImageSharp.Formats.Tiff/Constants/TiffOrientation.cs @@ -0,0 +1,24 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + /// + /// Enumeration representing the image orientations defined by the Tiff file-format. + /// + internal enum TiffOrientation + { + /// TIFF baseline Orientation values + + TopLeft = 1, + TopRight = 2, + BottomRight = 3, + BottomLeft = 4, + LeftTop = 5, + RightTop = 6, + RightBottom = 7, + LeftBottom = 8 + } +} \ No newline at end of file diff --git a/src/ImageSharp.Formats.Tiff/Constants/TiffPhotometricInterpretation.cs b/src/ImageSharp.Formats.Tiff/Constants/TiffPhotometricInterpretation.cs new file mode 100644 index 000000000..0894c2dad --- /dev/null +++ b/src/ImageSharp.Formats.Tiff/Constants/TiffPhotometricInterpretation.cs @@ -0,0 +1,40 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + /// + /// Enumeration representing the photometric interpretation formats defined by the Tiff file-format. + /// + internal enum TiffPhotometricInterpretation + { + // TIFF baseline color spaces + + WhiteIsZero = 0, + BlackIsZero = 1, + Rgb = 2, + PaletteColor = 3, + TransparencyMask = 4, + + // TIFF Extension color spaces + + Separated = 5, + YCbCr = 6, + CieLab = 8, + + // TIFF TechNote 1 + + IccLab = 9, + + // TIFF-F/FX Specification + + ItuLab = 10, + + // DNG Specification + + ColorFilterArray = 32803, + LinearRaw = 34892 + } +} \ No newline at end of file diff --git a/src/ImageSharp.Formats.Tiff/Constants/TiffPlanarConfiguration.cs b/src/ImageSharp.Formats.Tiff/Constants/TiffPlanarConfiguration.cs new file mode 100644 index 000000000..0c9e08302 --- /dev/null +++ b/src/ImageSharp.Formats.Tiff/Constants/TiffPlanarConfiguration.cs @@ -0,0 +1,18 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + /// + /// Enumeration representing the planar configuration types defined by the Tiff file-format. + /// + internal enum TiffPlanarConfiguration + { + // TIFF baseline PlanarConfiguration values + + Chunky = 1, + Planar = 2 + } +} \ No newline at end of file diff --git a/src/ImageSharp.Formats.Tiff/Constants/TiffResolutionUnit.cs b/src/ImageSharp.Formats.Tiff/Constants/TiffResolutionUnit.cs new file mode 100644 index 000000000..9275a79fb --- /dev/null +++ b/src/ImageSharp.Formats.Tiff/Constants/TiffResolutionUnit.cs @@ -0,0 +1,19 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + /// + /// Enumeration representing the resolution units defined by the Tiff file-format. + /// + internal enum TiffResolutionUnit + { + // TIFF baseline ResolutionUnit values + + None = 1, + Inch = 2, + Centimeter = 2 + } +} \ No newline at end of file diff --git a/src/ImageSharp.Formats.Tiff/Constants/TiffSubfileType.cs b/src/ImageSharp.Formats.Tiff/Constants/TiffSubfileType.cs new file mode 100644 index 000000000..0b256f031 --- /dev/null +++ b/src/ImageSharp.Formats.Tiff/Constants/TiffSubfileType.cs @@ -0,0 +1,19 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + /// + /// Enumeration representing the sub-file types defined by the Tiff file-format. + /// + internal enum TiffSubfileType + { + // TIFF baseline subfile types + + FullImage = 1, + Preview = 2, + SinglePage = 3 + } +} \ No newline at end of file diff --git a/src/ImageSharp.Formats.Tiff/TiffTags.cs b/src/ImageSharp.Formats.Tiff/Constants/TiffTags.cs similarity index 100% rename from src/ImageSharp.Formats.Tiff/TiffTags.cs rename to src/ImageSharp.Formats.Tiff/Constants/TiffTags.cs diff --git a/src/ImageSharp.Formats.Tiff/Constants/TiffThreshholding.cs b/src/ImageSharp.Formats.Tiff/Constants/TiffThreshholding.cs new file mode 100644 index 000000000..237b8419b --- /dev/null +++ b/src/ImageSharp.Formats.Tiff/Constants/TiffThreshholding.cs @@ -0,0 +1,19 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + /// + /// Enumeration representing the threshholding types defined by the Tiff file-format. + /// + internal enum TiffThreshholding + { + /// TIFF baseline Threshholding values + + None = 1, + Ordered = 2, + Random = 3 + } +} \ No newline at end of file diff --git a/src/ImageSharp.Formats.Tiff/TiffType.cs b/src/ImageSharp.Formats.Tiff/Constants/TiffType.cs similarity index 100% rename from src/ImageSharp.Formats.Tiff/TiffType.cs rename to src/ImageSharp.Formats.Tiff/Constants/TiffType.cs From 254e70ad99a87c568cc84931857b6374295f4d4d Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Tue, 14 Mar 2017 21:45:44 +0000 Subject: [PATCH 017/275] Incorporate Tiff codec into new project structure --- ImageSharp.sln | 17 ++++++++++++++++- src/ImageSharp.Formats.Tiff/AssemblyInfo.cs | 11 ----------- .../ImageSharp.Formats.Tiff.csproj | 19 ------------------- .../Tiff}/Constants/TiffCompression.cs | 0 .../Formats/Tiff}/Constants/TiffConstants.cs | 0 .../Tiff}/Constants/TiffExtraSamples.cs | 0 .../Formats/Tiff}/Constants/TiffFillOrder.cs | 0 .../Tiff}/Constants/TiffNewSubfileType.cs | 0 .../Tiff}/Constants/TiffOrientation.cs | 0 .../TiffPhotometricInterpretation.cs | 0 .../Constants/TiffPlanarConfiguration.cs | 0 .../Tiff}/Constants/TiffResolutionUnit.cs | 0 .../Tiff}/Constants/TiffSubfileType.cs | 0 .../Formats/Tiff}/Constants/TiffTags.cs | 0 .../Tiff}/Constants/TiffThreshholding.cs | 0 .../Formats/Tiff}/Constants/TiffType.cs | 0 .../Formats/Tiff}/ITiffEncoderOptions.cs | 0 .../Formats/Tiff}/TiffDecoder.cs | 0 .../Formats/Tiff}/TiffDecoderCore.cs | 0 .../Formats/Tiff}/TiffEncoder.cs | 0 .../Formats/Tiff}/TiffEncoderOptions.cs | 0 .../Formats/Tiff}/TiffFormat.cs | 0 .../Formats/Tiff}/TiffIfd/TiffIfd.cs | 0 .../Formats/Tiff}/TiffIfd/TiffIfdEntry.cs | 0 src/Shared/AssemblyInfo.Common.cs | 3 ++- .../ImageSharp.Formats.Tiff.Tests.csproj | 12 ++++++------ 26 files changed, 24 insertions(+), 38 deletions(-) delete mode 100644 src/ImageSharp.Formats.Tiff/AssemblyInfo.cs delete mode 100644 src/ImageSharp.Formats.Tiff/ImageSharp.Formats.Tiff.csproj rename src/{ImageSharp.Formats.Tiff => ImageSharp/Formats/Tiff}/Constants/TiffCompression.cs (100%) rename src/{ImageSharp.Formats.Tiff => ImageSharp/Formats/Tiff}/Constants/TiffConstants.cs (100%) rename src/{ImageSharp.Formats.Tiff => ImageSharp/Formats/Tiff}/Constants/TiffExtraSamples.cs (100%) rename src/{ImageSharp.Formats.Tiff => ImageSharp/Formats/Tiff}/Constants/TiffFillOrder.cs (100%) rename src/{ImageSharp.Formats.Tiff => ImageSharp/Formats/Tiff}/Constants/TiffNewSubfileType.cs (100%) rename src/{ImageSharp.Formats.Tiff => ImageSharp/Formats/Tiff}/Constants/TiffOrientation.cs (100%) rename src/{ImageSharp.Formats.Tiff => ImageSharp/Formats/Tiff}/Constants/TiffPhotometricInterpretation.cs (100%) rename src/{ImageSharp.Formats.Tiff => ImageSharp/Formats/Tiff}/Constants/TiffPlanarConfiguration.cs (100%) rename src/{ImageSharp.Formats.Tiff => ImageSharp/Formats/Tiff}/Constants/TiffResolutionUnit.cs (100%) rename src/{ImageSharp.Formats.Tiff => ImageSharp/Formats/Tiff}/Constants/TiffSubfileType.cs (100%) rename src/{ImageSharp.Formats.Tiff => ImageSharp/Formats/Tiff}/Constants/TiffTags.cs (100%) rename src/{ImageSharp.Formats.Tiff => ImageSharp/Formats/Tiff}/Constants/TiffThreshholding.cs (100%) rename src/{ImageSharp.Formats.Tiff => ImageSharp/Formats/Tiff}/Constants/TiffType.cs (100%) rename src/{ImageSharp.Formats.Tiff => ImageSharp/Formats/Tiff}/ITiffEncoderOptions.cs (100%) rename src/{ImageSharp.Formats.Tiff => ImageSharp/Formats/Tiff}/TiffDecoder.cs (100%) rename src/{ImageSharp.Formats.Tiff => ImageSharp/Formats/Tiff}/TiffDecoderCore.cs (100%) rename src/{ImageSharp.Formats.Tiff => ImageSharp/Formats/Tiff}/TiffEncoder.cs (100%) rename src/{ImageSharp.Formats.Tiff => ImageSharp/Formats/Tiff}/TiffEncoderOptions.cs (100%) rename src/{ImageSharp.Formats.Tiff => ImageSharp/Formats/Tiff}/TiffFormat.cs (100%) rename src/{ImageSharp.Formats.Tiff => ImageSharp/Formats/Tiff}/TiffIfd/TiffIfd.cs (100%) rename src/{ImageSharp.Formats.Tiff => ImageSharp/Formats/Tiff}/TiffIfd/TiffIfdEntry.cs (100%) diff --git a/ImageSharp.sln b/ImageSharp.sln index 9c729493b..223d7c3c1 100644 --- a/ImageSharp.sln +++ b/ImageSharp.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26228.4 @@ -45,6 +45,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Benchmarks", "te EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageSharp.Sandbox46", "tests\ImageSharp.Sandbox46\ImageSharp.Sandbox46.csproj", "{96188137-5FA6-4924-AB6E-4EFF79C6E0BB}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageSharp.Formats.Tiff.Tests", "tests\ImageSharp.Formats.Tiff.Tests\ImageSharp.Formats.Tiff.Tests.csproj", "{F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -127,6 +129,18 @@ Global {96188137-5FA6-4924-AB6E-4EFF79C6E0BB}.Release|x64.Build.0 = Release|Any CPU {96188137-5FA6-4924-AB6E-4EFF79C6E0BB}.Release|x86.ActiveCfg = Release|Any CPU {96188137-5FA6-4924-AB6E-4EFF79C6E0BB}.Release|x86.Build.0 = Release|Any CPU + {F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC}.Debug|x64.ActiveCfg = Debug|x64 + {F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC}.Debug|x64.Build.0 = Debug|x64 + {F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC}.Debug|x86.ActiveCfg = Debug|x86 + {F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC}.Debug|x86.Build.0 = Debug|x86 + {F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC}.Release|Any CPU.Build.0 = Release|Any CPU + {F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC}.Release|x64.ActiveCfg = Release|x64 + {F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC}.Release|x64.Build.0 = Release|x64 + {F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC}.Release|x86.ActiveCfg = Release|x86 + {F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -139,5 +153,6 @@ Global {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} {2BF743D8-2A06-412D-96D7-F448F00C5EA5} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} {96188137-5FA6-4924-AB6E-4EFF79C6E0BB} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} + {F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} EndGlobalSection EndGlobal diff --git a/src/ImageSharp.Formats.Tiff/AssemblyInfo.cs b/src/ImageSharp.Formats.Tiff/AssemblyInfo.cs deleted file mode 100644 index 4d1cbfe55..000000000 --- a/src/ImageSharp.Formats.Tiff/AssemblyInfo.cs +++ /dev/null @@ -1,11 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -using System.Reflection; -using System.Resources; -using System.Runtime.CompilerServices; - -// Ensure the internals can be tested. -[assembly: InternalsVisibleTo("ImageSharp.Formats.Tiff.Tests")] \ No newline at end of file diff --git a/src/ImageSharp.Formats.Tiff/ImageSharp.Formats.Tiff.csproj b/src/ImageSharp.Formats.Tiff/ImageSharp.Formats.Tiff.csproj deleted file mode 100644 index 2df493b7d..000000000 --- a/src/ImageSharp.Formats.Tiff/ImageSharp.Formats.Tiff.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - netstandard1.1 - true - - - - - - - - - - - - - - diff --git a/src/ImageSharp.Formats.Tiff/Constants/TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs similarity index 100% rename from src/ImageSharp.Formats.Tiff/Constants/TiffCompression.cs rename to src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs diff --git a/src/ImageSharp.Formats.Tiff/Constants/TiffConstants.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs similarity index 100% rename from src/ImageSharp.Formats.Tiff/Constants/TiffConstants.cs rename to src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs diff --git a/src/ImageSharp.Formats.Tiff/Constants/TiffExtraSamples.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffExtraSamples.cs similarity index 100% rename from src/ImageSharp.Formats.Tiff/Constants/TiffExtraSamples.cs rename to src/ImageSharp/Formats/Tiff/Constants/TiffExtraSamples.cs diff --git a/src/ImageSharp.Formats.Tiff/Constants/TiffFillOrder.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffFillOrder.cs similarity index 100% rename from src/ImageSharp.Formats.Tiff/Constants/TiffFillOrder.cs rename to src/ImageSharp/Formats/Tiff/Constants/TiffFillOrder.cs diff --git a/src/ImageSharp.Formats.Tiff/Constants/TiffNewSubfileType.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs similarity index 100% rename from src/ImageSharp.Formats.Tiff/Constants/TiffNewSubfileType.cs rename to src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs diff --git a/src/ImageSharp.Formats.Tiff/Constants/TiffOrientation.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffOrientation.cs similarity index 100% rename from src/ImageSharp.Formats.Tiff/Constants/TiffOrientation.cs rename to src/ImageSharp/Formats/Tiff/Constants/TiffOrientation.cs diff --git a/src/ImageSharp.Formats.Tiff/Constants/TiffPhotometricInterpretation.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs similarity index 100% rename from src/ImageSharp.Formats.Tiff/Constants/TiffPhotometricInterpretation.cs rename to src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs diff --git a/src/ImageSharp.Formats.Tiff/Constants/TiffPlanarConfiguration.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffPlanarConfiguration.cs similarity index 100% rename from src/ImageSharp.Formats.Tiff/Constants/TiffPlanarConfiguration.cs rename to src/ImageSharp/Formats/Tiff/Constants/TiffPlanarConfiguration.cs diff --git a/src/ImageSharp.Formats.Tiff/Constants/TiffResolutionUnit.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs similarity index 100% rename from src/ImageSharp.Formats.Tiff/Constants/TiffResolutionUnit.cs rename to src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs diff --git a/src/ImageSharp.Formats.Tiff/Constants/TiffSubfileType.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffSubfileType.cs similarity index 100% rename from src/ImageSharp.Formats.Tiff/Constants/TiffSubfileType.cs rename to src/ImageSharp/Formats/Tiff/Constants/TiffSubfileType.cs diff --git a/src/ImageSharp.Formats.Tiff/Constants/TiffTags.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffTags.cs similarity index 100% rename from src/ImageSharp.Formats.Tiff/Constants/TiffTags.cs rename to src/ImageSharp/Formats/Tiff/Constants/TiffTags.cs diff --git a/src/ImageSharp.Formats.Tiff/Constants/TiffThreshholding.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffThreshholding.cs similarity index 100% rename from src/ImageSharp.Formats.Tiff/Constants/TiffThreshholding.cs rename to src/ImageSharp/Formats/Tiff/Constants/TiffThreshholding.cs diff --git a/src/ImageSharp.Formats.Tiff/Constants/TiffType.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffType.cs similarity index 100% rename from src/ImageSharp.Formats.Tiff/Constants/TiffType.cs rename to src/ImageSharp/Formats/Tiff/Constants/TiffType.cs diff --git a/src/ImageSharp.Formats.Tiff/ITiffEncoderOptions.cs b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs similarity index 100% rename from src/ImageSharp.Formats.Tiff/ITiffEncoderOptions.cs rename to src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs diff --git a/src/ImageSharp.Formats.Tiff/TiffDecoder.cs b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs similarity index 100% rename from src/ImageSharp.Formats.Tiff/TiffDecoder.cs rename to src/ImageSharp/Formats/Tiff/TiffDecoder.cs diff --git a/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs similarity index 100% rename from src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs rename to src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs diff --git a/src/ImageSharp.Formats.Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs similarity index 100% rename from src/ImageSharp.Formats.Tiff/TiffEncoder.cs rename to src/ImageSharp/Formats/Tiff/TiffEncoder.cs diff --git a/src/ImageSharp.Formats.Tiff/TiffEncoderOptions.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderOptions.cs similarity index 100% rename from src/ImageSharp.Formats.Tiff/TiffEncoderOptions.cs rename to src/ImageSharp/Formats/Tiff/TiffEncoderOptions.cs diff --git a/src/ImageSharp.Formats.Tiff/TiffFormat.cs b/src/ImageSharp/Formats/Tiff/TiffFormat.cs similarity index 100% rename from src/ImageSharp.Formats.Tiff/TiffFormat.cs rename to src/ImageSharp/Formats/Tiff/TiffFormat.cs diff --git a/src/ImageSharp.Formats.Tiff/TiffIfd/TiffIfd.cs b/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfd.cs similarity index 100% rename from src/ImageSharp.Formats.Tiff/TiffIfd/TiffIfd.cs rename to src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfd.cs diff --git a/src/ImageSharp.Formats.Tiff/TiffIfd/TiffIfdEntry.cs b/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntry.cs similarity index 100% rename from src/ImageSharp.Formats.Tiff/TiffIfd/TiffIfdEntry.cs rename to src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntry.cs diff --git a/src/Shared/AssemblyInfo.Common.cs b/src/Shared/AssemblyInfo.Common.cs index 252ef3eae..46ed22e4f 100644 --- a/src/Shared/AssemblyInfo.Common.cs +++ b/src/Shared/AssemblyInfo.Common.cs @@ -37,4 +37,5 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("ImageSharp.Drawing")] [assembly: InternalsVisibleTo("ImageSharp.Benchmarks")] [assembly: InternalsVisibleTo("ImageSharp.Tests")] -[assembly: InternalsVisibleTo("ImageSharp.Sandbox46")] \ No newline at end of file +[assembly: InternalsVisibleTo("ImageSharp.Formats.Tiff.Tests")] +[assembly: InternalsVisibleTo("ImageSharp.Sandbox46")] diff --git a/tests/ImageSharp.Formats.Tiff.Tests/ImageSharp.Formats.Tiff.Tests.csproj b/tests/ImageSharp.Formats.Tiff.Tests/ImageSharp.Formats.Tiff.Tests.csproj index 30ec6b75f..dae9c9db3 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/ImageSharp.Formats.Tiff.Tests.csproj +++ b/tests/ImageSharp.Formats.Tiff.Tests/ImageSharp.Formats.Tiff.Tests.csproj @@ -2,18 +2,18 @@ Exe - netcoreapp1.0 + netcoreapp1.1 - + + + + - - - - + From ef2302dfee6311c424df856ebfee4b093ca3bd7d Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Tue, 14 Mar 2017 22:13:25 +0000 Subject: [PATCH 018/275] Remove unneeded using statements --- .vscode/launch.json | 23 +++++++++++++++++++ .../Formats/Tiff/Constants/TiffConstants.cs | 2 -- src/ImageSharp/Formats/Tiff/TiffDecoder.cs | 1 - .../Formats/Tiff/TiffDecoderCore.cs | 2 -- .../Formats/Tiff/TiffEncoderOptions.cs | 2 +- .../Formats/Tiff/TiffDecoderHeaderTests.cs | 1 - .../Formats/Tiff/TiffDecoderIfdTests.cs | 1 - .../TestUtilities/ByteArrayUtility.cs | 1 - .../TestUtilities/Tiff/ITiffGenDataSource.cs | 1 - .../TestUtilities/Tiff/TiffGenDataBlock.cs | 1 - .../Tiff/TiffGenDataReference.cs | 3 --- .../TestUtilities/Tiff/TiffGenHeader.cs | 5 ++-- 12 files changed, 26 insertions(+), 17 deletions(-) create mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..8ab214d41 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,23 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": ".NET Core Launch (console)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + "program": "${workspaceRoot}\\build\\bin\\Debug\\netcoreapp1.1\\build.dll", + "args": [], + "cwd": "${workspaceRoot}\\build", + "console": "internalConsole", + "stopAtEntry": false, + "internalConsoleOptions": "openOnSessionStart" + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach", + "processId": "${command:pickProcess}" + } + ] +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs index 858a1ca4b..f6242aa43 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs @@ -5,8 +5,6 @@ namespace ImageSharp.Formats { - using System.Text; - /// /// Defines constants defined in the TIFF specification. /// diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs index 4b99c1cb6..794fb4f1f 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs @@ -5,7 +5,6 @@ namespace ImageSharp.Formats { - using System; using System.IO; /// diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 1c2703ed5..08d42b973 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -7,9 +7,7 @@ namespace ImageSharp.Formats { using System; using System.IO; - using System.Runtime.CompilerServices; using System.Text; - using System.Threading.Tasks; /// /// Performs the tiff decoding operation. diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderOptions.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderOptions.cs index ed72bbb92..3a9ae8aa2 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderOptions.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderOptions.cs @@ -6,7 +6,7 @@ namespace ImageSharp.Formats { /// - /// Encapsulates the options for the . + /// Encapsulates the options for the . /// public sealed class TiffEncoderOptions : EncoderOptions, ITiffEncoderOptions { diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs index 470b49e9f..11c999a0f 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs @@ -6,7 +6,6 @@ namespace ImageSharp.Tests { using System.IO; - using System.Linq; using Xunit; using ImageSharp.Formats; diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdTests.cs index 606e024a1..d5400279f 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdTests.cs @@ -6,7 +6,6 @@ namespace ImageSharp.Tests { using System.IO; - using System.Linq; using Xunit; using ImageSharp.Formats; diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/ByteArrayUtility.cs b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/ByteArrayUtility.cs index 8021f5330..0a3c9fc34 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/ByteArrayUtility.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/ByteArrayUtility.cs @@ -6,7 +6,6 @@ namespace ImageSharp.Tests { using System; - using System.Collections.Generic; public static class ByteArrayUtility { diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/ITiffGenDataSource.cs b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/ITiffGenDataSource.cs index 1c8a485b9..75025f3e7 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/ITiffGenDataSource.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/ITiffGenDataSource.cs @@ -5,7 +5,6 @@ namespace ImageSharp.Tests { - using System; using System.Collections.Generic; /// diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenDataBlock.cs b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenDataBlock.cs index 6a91dbbcc..0b412f7fe 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenDataBlock.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenDataBlock.cs @@ -5,7 +5,6 @@ namespace ImageSharp.Tests { - using System; using System.Collections.Generic; /// diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenDataReference.cs b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenDataReference.cs index 90cacb23d..24d03bece 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenDataReference.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenDataReference.cs @@ -5,9 +5,6 @@ namespace ImageSharp.Tests { - using System; - using System.Collections.Generic; - /// /// A utility data structure to represent a reference from one block of data to another in a Tiff file. /// diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenHeader.cs b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenHeader.cs index b28ceedc2..946faedf9 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenHeader.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenHeader.cs @@ -5,7 +5,6 @@ namespace ImageSharp.Tests { - using System; using System.Collections.Generic; using System.Linq; @@ -37,11 +36,11 @@ namespace ImageSharp.Tests { var firstIfdData = FirstIfd.GetData(isLittleEndian); firstIfdData.First().AddReference(headerData.Bytes, 4); - return new [] { headerData }.Concat(firstIfdData); + return new[] { headerData }.Concat(firstIfdData); } else { - return new [] { headerData }; + return new[] { headerData }; } } } From 11d7aed07c9cfaf469a64367b9cc1c91f2ebd15c Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Tue, 14 Mar 2017 22:44:20 +0000 Subject: [PATCH 019/275] Fix many StyleCop warnings! --- .../Tiff/Constants/TiffNewSubfileType.cs | 2 +- .../Formats/Tiff/Constants/TiffOrientation.cs | 2 +- .../Tiff/Constants/TiffThreshholding.cs | 2 +- .../Formats/Tiff/TiffDecoderCore.cs | 267 +++++++++++------- src/ImageSharp/Formats/Tiff/TiffFormat.cs | 4 +- 5 files changed, 177 insertions(+), 100 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs index cf66d6d58..9a6fa497e 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs @@ -25,7 +25,7 @@ namespace ImageSharp.Formats AlternativePreview = 0x10000, // TIFF-F/FX Specification subfile types - + MixedRasterContent = 0x0008 } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffOrientation.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffOrientation.cs index 4938a3f7f..6dbdec484 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffOrientation.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffOrientation.cs @@ -10,7 +10,7 @@ namespace ImageSharp.Formats /// internal enum TiffOrientation { - /// TIFF baseline Orientation values + // TIFF baseline Orientation values TopLeft = 1, TopRight = 2, diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffThreshholding.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffThreshholding.cs index 237b8419b..72efa9222 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffThreshholding.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffThreshholding.cs @@ -10,7 +10,7 @@ namespace ImageSharp.Formats /// internal enum TiffThreshholding { - /// TIFF baseline Threshholding values + // TIFF baseline Threshholding values None = 1, Ordered = 2, diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 08d42b973..8e0a42515 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -28,7 +28,8 @@ namespace ImageSharp.Formats this.options = options ?? new DecoderOptions(); } - public TiffDecoderCore(Stream stream, bool isLittleEndian, IDecoderOptions options) : this(options) + public TiffDecoderCore(Stream stream, bool isLittleEndian, IDecoderOptions options) + : this(options) { this.InputStream = stream; this.IsLittleEndian = isLittleEndian; @@ -42,7 +43,7 @@ namespace ImageSharp.Formats /// /// A flag indicating if the file is encoded in little-endian or big-endian format. /// - public bool IsLittleEndian; + public bool IsLittleEndian { get; private set; } /// /// Decodes the image from the specified and sets @@ -57,8 +58,8 @@ namespace ImageSharp.Formats { this.InputStream = stream; - uint firstIfdOffset = ReadHeader(); - TiffIfd firstIfd = ReadIfd(firstIfdOffset); + uint firstIfdOffset = this.ReadHeader(); + TiffIfd firstIfd = this.ReadIfd(firstIfdOffset); } /// @@ -71,47 +72,55 @@ namespace ImageSharp.Formats public uint ReadHeader() { byte[] headerBytes = new byte[TiffConstants.SizeOfTiffHeader]; - ReadBytes(headerBytes, TiffConstants.SizeOfTiffHeader); + this.ReadBytes(headerBytes, TiffConstants.SizeOfTiffHeader); if (headerBytes[0] == TiffConstants.ByteOrderLittleEndian && headerBytes[1] == TiffConstants.ByteOrderLittleEndian) - IsLittleEndian = true; + { + this.IsLittleEndian = true; + } else if (headerBytes[0] != TiffConstants.ByteOrderBigEndian && headerBytes[1] != TiffConstants.ByteOrderBigEndian) + { throw new ImageFormatException("Invalid TIFF file header."); + } - if (ToUInt16(headerBytes, 2) != TiffConstants.HeaderMagicNumber) + if (this.ToUInt16(headerBytes, 2) != TiffConstants.HeaderMagicNumber) + { throw new ImageFormatException("Invalid TIFF file header."); + } - uint firstIfdOffset = ToUInt32(headerBytes, 4); + uint firstIfdOffset = this.ToUInt32(headerBytes, 4); if (firstIfdOffset == 0) + { throw new ImageFormatException("Invalid TIFF file header."); + } return firstIfdOffset; } public TiffIfd ReadIfd(uint offset) { - InputStream.Seek(offset, SeekOrigin.Begin); - + this.InputStream.Seek(offset, SeekOrigin.Begin); + byte[] buffer = new byte[TiffConstants.SizeOfIfdEntry]; - ReadBytes(buffer, 2); - ushort entryCount = ToUInt16(buffer, 0); + this.ReadBytes(buffer, 2); + ushort entryCount = this.ToUInt16(buffer, 0); TiffIfdEntry[] entries = new TiffIfdEntry[entryCount]; - for (int i = 0 ; i 0) { - int bytesRead = InputStream.Read(buffer, offset, count); + int bytesRead = this.InputStream.Read(buffer, offset, count); if (bytesRead == 0) + { break; + } offset += bytesRead; count -= bytesRead; @@ -138,11 +149,11 @@ namespace ImageSharp.Formats if (entry.Value.Length < byteLength) { - uint offset = ToUInt32(entry.Value, 0); - InputStream.Seek(offset, SeekOrigin.Begin); + uint offset = this.ToUInt32(entry.Value, 0); + this.InputStream.Seek(offset, SeekOrigin.Begin); byte[] data = new byte[byteLength]; - ReadBytes(data, (int)byteLength); + this.ReadBytes(data, (int)byteLength); entry.Value = data; } @@ -152,16 +163,18 @@ namespace ImageSharp.Formats public uint ReadUnsignedInteger(ref TiffIfdEntry entry) { if (entry.Count != 1) + { throw new ImageFormatException($"Cannot read a single value from an array of multiple items."); + } switch (entry.Type) { case TiffType.Byte: - return (uint)ToByte(entry.Value, 0); + return (uint)this.ToByte(entry.Value, 0); case TiffType.Short: - return (uint)ToUInt16(entry.Value, 0); + return (uint)this.ToUInt16(entry.Value, 0); case TiffType.Long: - return ToUInt32(entry.Value, 0); + return this.ToUInt32(entry.Value, 0); default: throw new ImageFormatException($"A value of type '{entry.Type}' cannot be converted to an unsigned integer."); } @@ -170,16 +183,18 @@ namespace ImageSharp.Formats public int ReadSignedInteger(ref TiffIfdEntry entry) { if (entry.Count != 1) + { throw new ImageFormatException($"Cannot read a single value from an array of multiple items."); + } switch (entry.Type) { case TiffType.SByte: - return (int)ToSByte(entry.Value, 0); + return (int)this.ToSByte(entry.Value, 0); case TiffType.SShort: - return (int)ToInt16(entry.Value, 0); + return (int)this.ToInt16(entry.Value, 0); case TiffType.SLong: - return ToInt32(entry.Value, 0); + return this.ToInt32(entry.Value, 0); default: throw new ImageFormatException($"A value of type '{entry.Type}' cannot be converted to a signed integer."); } @@ -187,29 +202,41 @@ namespace ImageSharp.Formats public uint[] ReadUnsignedIntegerArray(ref TiffIfdEntry entry) { - byte[] bytes = ReadBytes(ref entry); + byte[] bytes = this.ReadBytes(ref entry); uint[] result = new uint[entry.Count]; switch (entry.Type) { case TiffType.Byte: - { - for (int i = 0 ; i < result.Length ; i++) - result[i] = (uint)ToByte(bytes, i); - break; - } + { + for (int i = 0; i < result.Length; i++) + { + result[i] = (uint)this.ToByte(bytes, i); + } + + break; + } + case TiffType.Short: - { - for (int i = 0 ; i < result.Length ; i++) - result[i] = (uint)ToUInt16(bytes, i * TiffConstants.SizeOfShort); - break; - } + { + for (int i = 0; i < result.Length; i++) + { + result[i] = (uint)this.ToUInt16(bytes, i * TiffConstants.SizeOfShort); + } + + break; + } + case TiffType.Long: - { - for (int i = 0 ; i < result.Length ; i++) - result[i] = ToUInt32(bytes, i * TiffConstants.SizeOfLong); - break; - } + { + for (int i = 0; i < result.Length; i++) + { + result[i] = this.ToUInt32(bytes, i * TiffConstants.SizeOfLong); + } + + break; + } + default: throw new ImageFormatException($"A value of type '{entry.Type}' cannot be converted to an unsigned integer."); } @@ -219,29 +246,41 @@ namespace ImageSharp.Formats public int[] ReadSignedIntegerArray(ref TiffIfdEntry entry) { - byte[] bytes = ReadBytes(ref entry); + byte[] bytes = this.ReadBytes(ref entry); int[] result = new int[entry.Count]; switch (entry.Type) { case TiffType.SByte: - { - for (int i = 0 ; i < result.Length ; i++) - result[i] = (int)ToSByte(bytes, i); - break; - } + { + for (int i = 0; i < result.Length; i++) + { + result[i] = (int)this.ToSByte(bytes, i); + } + + break; + } + case TiffType.SShort: - { - for (int i = 0 ; i < result.Length ; i++) - result[i] = (int)ToInt16(bytes, i * TiffConstants.SizeOfShort); - break; - } + { + for (int i = 0; i < result.Length; i++) + { + result[i] = (int)this.ToInt16(bytes, i * TiffConstants.SizeOfShort); + } + + break; + } + case TiffType.SLong: - { - for (int i = 0 ; i < result.Length ; i++) - result[i] = ToInt32(bytes, i * TiffConstants.SizeOfLong); - break; - } + { + for (int i = 0; i < result.Length; i++) + { + result[i] = this.ToInt32(bytes, i * TiffConstants.SizeOfLong); + } + + break; + } + default: throw new ImageFormatException($"A value of type '{entry.Type}' cannot be converted to a signed integer."); } @@ -252,12 +291,16 @@ namespace ImageSharp.Formats public string ReadString(ref TiffIfdEntry entry) { if (entry.Type != TiffType.Ascii) + { throw new ImageFormatException($"A value of type '{entry.Type}' cannot be converted to a string."); + } + + byte[] bytes = this.ReadBytes(ref entry); - byte[] bytes = ReadBytes(ref entry); - if (bytes[entry.Count - 1] != 0) + { throw new ImageFormatException("The retrieved string is not null terminated."); + } return Encoding.UTF8.GetString(bytes, 0, (int)entry.Count - 1); } @@ -265,31 +308,37 @@ namespace ImageSharp.Formats public Rational ReadUnsignedRational(ref TiffIfdEntry entry) { if (entry.Count != 1) + { throw new ImageFormatException($"Cannot read a single value from an array of multiple items."); + } - return ReadUnsignedRationalArray(ref entry)[0]; + return this.ReadUnsignedRationalArray(ref entry)[0]; } public SignedRational ReadSignedRational(ref TiffIfdEntry entry) { if (entry.Count != 1) + { throw new ImageFormatException($"Cannot read a single value from an array of multiple items."); + } - return ReadSignedRationalArray(ref entry)[0]; + return this.ReadSignedRationalArray(ref entry)[0]; } public Rational[] ReadUnsignedRationalArray(ref TiffIfdEntry entry) { if (entry.Type != TiffType.Rational) + { throw new ImageFormatException($"A value of type '{entry.Type}' cannot be converted to a Rational."); + } - byte[] bytes = ReadBytes(ref entry); + byte[] bytes = this.ReadBytes(ref entry); Rational[] result = new Rational[entry.Count]; - for (int i = 0 ; i < result.Length ; i++) + for (int i = 0; i < result.Length; i++) { - uint numerator = ToUInt32(bytes, i * TiffConstants.SizeOfRational); - uint denominator = ToUInt32(bytes, i * TiffConstants.SizeOfRational + TiffConstants.SizeOfLong); + uint numerator = this.ToUInt32(bytes, i * TiffConstants.SizeOfRational); + uint denominator = this.ToUInt32(bytes, i * TiffConstants.SizeOfRational + TiffConstants.SizeOfLong); result[i] = new Rational(numerator, denominator); } @@ -299,15 +348,17 @@ namespace ImageSharp.Formats public SignedRational[] ReadSignedRationalArray(ref TiffIfdEntry entry) { if (entry.Type != TiffType.SRational) + { throw new ImageFormatException($"A value of type '{entry.Type}' cannot be converted to a SignedRational."); + } - byte[] bytes = ReadBytes(ref entry); + byte[] bytes = this.ReadBytes(ref entry); SignedRational[] result = new SignedRational[entry.Count]; - for (int i = 0 ; i < result.Length ; i++) + for (int i = 0; i < result.Length; i++) { - int numerator = ToInt32(bytes, i * TiffConstants.SizeOfRational); - int denominator = ToInt32(bytes, i * TiffConstants.SizeOfRational + TiffConstants.SizeOfLong); + int numerator = this.ToInt32(bytes, i * TiffConstants.SizeOfRational); + int denominator = this.ToInt32(bytes, i * TiffConstants.SizeOfRational + TiffConstants.SizeOfLong); result[i] = new SignedRational(numerator, denominator); } @@ -317,32 +368,42 @@ namespace ImageSharp.Formats public float ReadFloat(ref TiffIfdEntry entry) { if (entry.Count != 1) + { throw new ImageFormatException($"Cannot read a single value from an array of multiple items."); + } if (entry.Type != TiffType.Float) + { throw new ImageFormatException($"A value of type '{entry.Type}' cannot be converted to a float."); + } - return ToSingle(entry.Value, 0); + return this.ToSingle(entry.Value, 0); } public double ReadDouble(ref TiffIfdEntry entry) { if (entry.Count != 1) + { throw new ImageFormatException($"Cannot read a single value from an array of multiple items."); + } - return ReadDoubleArray(ref entry)[0]; + return this.ReadDoubleArray(ref entry)[0]; } public float[] ReadFloatArray(ref TiffIfdEntry entry) { if (entry.Type != TiffType.Float) + { throw new ImageFormatException($"A value of type '{entry.Type}' cannot be converted to a float."); + } - byte[] bytes = ReadBytes(ref entry); + byte[] bytes = this.ReadBytes(ref entry); float[] result = new float[entry.Count]; - for (int i = 0 ; i < result.Length ; i++) - result[i] = ToSingle(bytes, i * TiffConstants.SizeOfFloat); + for (int i = 0; i < result.Length; i++) + { + result[i] = this.ToSingle(bytes, i * TiffConstants.SizeOfFloat); + } return result; } @@ -350,71 +411,87 @@ namespace ImageSharp.Formats public double[] ReadDoubleArray(ref TiffIfdEntry entry) { if (entry.Type != TiffType.Double) + { throw new ImageFormatException($"A value of type '{entry.Type}' cannot be converted to a double."); + } - byte[] bytes = ReadBytes(ref entry); + byte[] bytes = this.ReadBytes(ref entry); double[] result = new double[entry.Count]; - for (int i = 0 ; i < result.Length ; i++) - result[i] = ToDouble(bytes, i * TiffConstants.SizeOfDouble); + for (int i = 0; i < result.Length; i++) + { + result[i] = this.ToDouble(bytes, i * TiffConstants.SizeOfDouble); + } return result; } - private SByte ToSByte(byte[] bytes, int offset) + private sbyte ToSByte(byte[] bytes, int offset) { return (sbyte)bytes[offset]; } - private Int16 ToInt16(byte[] bytes, int offset) + private short ToInt16(byte[] bytes, int offset) { - if (IsLittleEndian) + if (this.IsLittleEndian) + { return (short)(bytes[offset + 0] | (bytes[offset + 1] << 8)); + } else + { return (short)((bytes[offset + 0] << 8) | bytes[offset + 1]); + } } - private Int32 ToInt32(byte[] bytes, int offset) + private int ToInt32(byte[] bytes, int offset) { - if (IsLittleEndian) + if (this.IsLittleEndian) + { return bytes[offset + 0] | (bytes[offset + 1] << 8) | (bytes[offset + 2] << 16) | (bytes[offset + 3] << 24); + } else + { return (bytes[offset + 0] << 24) | (bytes[offset + 1] << 16) | (bytes[offset + 2] << 8) | bytes[offset + 3]; + } } - private Byte ToByte(byte[] bytes, int offset) + private byte ToByte(byte[] bytes, int offset) { return bytes[offset]; } - private UInt32 ToUInt32(byte[] bytes, int offset) + private uint ToUInt32(byte[] bytes, int offset) { - return (uint)ToInt32(bytes, offset); + return (uint)this.ToInt32(bytes, offset); } - private UInt16 ToUInt16(byte[] bytes, int offset) + private ushort ToUInt16(byte[] bytes, int offset) { - return (ushort)ToInt16(bytes, offset); + return (ushort)this.ToInt16(bytes, offset); } - private Single ToSingle(byte[] bytes, int offset) + private float ToSingle(byte[] bytes, int offset) { byte[] buffer = new byte[4]; Array.Copy(bytes, offset, buffer, 0, 4); - if (BitConverter.IsLittleEndian != IsLittleEndian) + if (this.IsLittleEndian != BitConverter.IsLittleEndian) + { Array.Reverse(buffer); + } return BitConverter.ToSingle(buffer, 0); } - private Double ToDouble(byte[] bytes, int offset) + private double ToDouble(byte[] bytes, int offset) { byte[] buffer = new byte[8]; Array.Copy(bytes, offset, buffer, 0, 8); - if (BitConverter.IsLittleEndian != IsLittleEndian) + if (this.IsLittleEndian != BitConverter.IsLittleEndian) + { Array.Reverse(buffer); + } return BitConverter.ToDouble(buffer, 0); } diff --git a/src/ImageSharp/Formats/Tiff/TiffFormat.cs b/src/ImageSharp/Formats/Tiff/TiffFormat.cs index 6f09d4909..20090a289 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFormat.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFormat.cs @@ -34,8 +34,8 @@ namespace ImageSharp.Formats public bool IsSupportedFileFormat(byte[] header) { return header.Length >= this.HeaderSize && - ((header[0] == 0x49 && header[1] == 0x49 && header[2] == 0x2A && header[3] == 0x00) || // Little-endian - (header[0] == 0x4D && header[1] == 0x4D && header[2] == 0x00 && header[3] == 0x2A)); // Big-endian + ((header[0] == 0x49 && header[1] == 0x49 && header[2] == 0x2A && header[3] == 0x00) || // Little-endian + (header[0] == 0x4D && header[1] == 0x4D && header[2] == 0x00 && header[3] == 0x2A)); // Big-endian } } } From fdcd483e58d580fc82904b9e38c07147e260af3c Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Sat, 18 Mar 2017 15:24:58 +0000 Subject: [PATCH 020/275] Add documentation to all elements. --- .../Formats/Tiff/Constants/TiffCompression.cs | 52 +- .../Formats/Tiff/Constants/TiffConstants.cs | 2 +- .../Tiff/Constants/TiffExtraSamples.cs | 15 +- .../Formats/Tiff/Constants/TiffFillOrder.cs | 9 +- .../Tiff/Constants/TiffNewSubfileType.cs | 27 +- .../Formats/Tiff/Constants/TiffOrientation.cs | 33 +- .../TiffPhotometricInterpretation.cs | 53 +- .../Tiff/Constants/TiffPlanarConfiguration.cs | 11 +- .../Tiff/Constants/TiffResolutionUnit.cs | 13 +- .../Formats/Tiff/Constants/TiffSubfileType.cs | 13 +- .../Formats/Tiff/Constants/TiffTags.cs | 581 +++++++++++++++++- .../Tiff/Constants/TiffThreshholding.cs | 15 +- .../Formats/Tiff/Constants/TiffType.cs | 51 ++ .../Formats/Tiff/TiffDecoderCore.cs | 292 +++++++-- src/ImageSharp/Formats/Tiff/TiffEncoder.cs | 2 - .../Formats/Tiff/TiffIfd/TiffIfd.cs | 14 +- .../Formats/Tiff/TiffIfd/TiffIfdEntry.cs | 24 +- 17 files changed, 1079 insertions(+), 128 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs index 4caa7887d..7880f683e 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs @@ -10,28 +10,64 @@ namespace ImageSharp.Formats /// internal enum TiffCompression { - // TIFF baseline compression types - + /// + /// No compression. + /// None = 1, + + /// + /// CCITT Group 3 1-Dimensional Modified Huffman run-length encoding. + /// Ccitt1D = 2, - PackBits = 32773, - // TIFF Extension compression types + /// + /// PackBits compression + /// + PackBits = 32773, + /// + /// T4-encoding: CCITT T.4 bi-level encoding (see Section 11 of the TIFF 6.0 specification). + /// CcittGroup3Fax = 3, + + /// + /// T6-encoding: CCITT T.6 bi-level encoding (see Section 11 of the TIFF 6.0 specification). + /// CcittGroup4Fax = 4, + + /// + /// LZW compression (see Section 13 of the TIFF 6.0 specification). + /// Lzw = 5, - OldJpeg = 6, - // Technote 2 + /// + /// JPEG compression - obsolete (see Section 22 of the TIFF 6.0 specification). + /// + OldJpeg = 6, + /// + /// JPEG compression (see TIFF Specification, supplement 2). + /// Jpeg = 7, + + /// + /// Deflate compression, using zlib data format (see TIFF Specification, supplement 2). + /// Deflate = 8, - OldDeflate = 32946, - // TIFF-F/FX Extension + /// + /// Deflate compression - old. + /// + OldDeflate = 32946, + /// + /// ITU-T Rec. T.82 coding, applying ITU-T Rec. T.85 (JBIG) (see RFC2301). + /// ItuTRecT82 = 9, + + /// + /// ITU-T Rec. T.43 representation, using ITU-T Rec. T.82 (JBIG) (see RFC2301). + /// ItuTRecT43 = 10 } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs index f6242aa43..77cf5e0bd 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs @@ -39,7 +39,7 @@ namespace ImageSharp.Formats /// Size (in bytes) of the Short and SShort data types /// public const int SizeOfShort = 2; - + /// /// Size (in bytes) of the Long and SLong data types /// diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffExtraSamples.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffExtraSamples.cs index 4fa153fc5..d15d312f1 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffExtraSamples.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffExtraSamples.cs @@ -6,14 +6,23 @@ namespace ImageSharp.Formats { /// - /// Enumeration representing the possible uses of extra-samples in TIFF format files. + /// Enumeration representing the possible uses of extra components in TIFF format files. /// internal enum TiffExtraSamples { - // TIFF baseline ExtraSample values - + /// + /// Unspecified data. + /// Unspecified = 0, + + /// + /// Associated alpha data (with pre-multiplied color). + /// AssociatedAlpha = 1, + + /// + /// Unassociated alpha data. + /// UnassociatedAlpha = 2 } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffFillOrder.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffFillOrder.cs index e520599b4..99d88e90e 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffFillOrder.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffFillOrder.cs @@ -10,9 +10,14 @@ namespace ImageSharp.Formats /// internal enum TiffFillOrder { - // TIFF baseline FillOrder values - + /// + /// Pixels with lower column values are stored in the higher-order bits of the byte. + /// MostSignificantBitFirst = 1, + + /// + /// Pixels with lower column values are stored in the lower-order bits of the byte. + /// LeastSignificantBitFirst = 2 } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs index 9a6fa497e..3d1885377 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs @@ -13,19 +13,34 @@ namespace ImageSharp.Formats [Flags] internal enum TiffNewSubfileType { - // TIFF baseline subfile types - + /// + /// A full-resolution image. + /// FullImage = 0x0000, + + /// + /// Reduced-resolution version of another image in this TIFF file. + /// Preview = 0x0001, + + /// + /// A single page of a multi-page image. + /// SinglePage = 0x0002, - TransparencyMask = 0x0004, - // DNG Specification subfile types + /// + /// A transparency mask for another image in this TIFF file. + /// + TransparencyMask = 0x0004, + /// + /// Alternative reduced-resolution version of another image in this TIFF file (see DNG specification). + /// AlternativePreview = 0x10000, - // TIFF-F/FX Specification subfile types - + /// + /// Mixed raster content (see RFC2301). + /// MixedRasterContent = 0x0008 } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffOrientation.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffOrientation.cs index 6dbdec484..8f1469354 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffOrientation.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffOrientation.cs @@ -10,15 +10,44 @@ namespace ImageSharp.Formats /// internal enum TiffOrientation { - // TIFF baseline Orientation values - + /// + /// The 0th row and 0th column represent the visual top and left-hand side of the image respectively. + /// TopLeft = 1, + + /// + /// The 0th row and 0th column represent the visual top and right-hand side of the image respectively. + /// TopRight = 2, + + /// + /// The 0th row and 0th column represent the visual bottom and right-hand side of the image respectively. + /// BottomRight = 3, + + /// + /// The 0th row and 0th column represent the visual bottom and left-hand side of the image respectively. + /// BottomLeft = 4, + + /// + /// The 0th row and 0th column represent the visual left-hand side and top of the image respectively. + /// LeftTop = 5, + + /// + /// The 0th row and 0th column represent the visual right-hand side and top of the image respectively. + /// RightTop = 6, + + /// + /// The 0th row and 0th column represent the visual right-hand side and bottom of the image respectively. + /// RightBottom = 7, + + /// + /// The 0th row and 0th column represent the visual left-hand side and bottom of the image respectively. + /// LeftBottom = 8 } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs index 0894c2dad..21f1b56e8 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs @@ -10,31 +10,64 @@ namespace ImageSharp.Formats /// internal enum TiffPhotometricInterpretation { - // TIFF baseline color spaces - + /// + /// Bilevel and grayscale: 0 is imaged as white. The maximum value is imaged as black. + /// WhiteIsZero = 0, + + /// + /// Bilevel and grayscale: 0 is imaged as black. The maximum value is imaged as white. + /// BlackIsZero = 1, + + /// + /// RGB + /// Rgb = 2, + + /// + /// Palette Color + /// PaletteColor = 3, - TransparencyMask = 4, - // TIFF Extension color spaces + /// + /// A transparency mask + /// + TransparencyMask = 4, + /// + /// Separated: usually CMYK (see Section 16 of the TIFF 6.0 specification). + /// Separated = 5, + + /// + /// YCbCr (see Section 21 of the TIFF 6.0 specification). + /// YCbCr = 6, - CieLab = 8, - // TIFF TechNote 1 + /// + /// 1976 CIE L*a*b* (see Section 23 of the TIFF 6.0 specification). + /// + CieLab = 8, + /// + /// ICC L*a*b* (see TIFF Specification, supplement 1). + /// IccLab = 9, - // TIFF-F/FX Specification - + /// + /// ITU L*a*b* (see RFC2301). + /// ItuLab = 10, - // DNG Specification - + /// + /// Color Filter Array (see the DNG specification). + /// ColorFilterArray = 32803, + + /// + /// Linear Raw (see the DNG specification). + /// LinearRaw = 34892 } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffPlanarConfiguration.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffPlanarConfiguration.cs index 0c9e08302..e3c40adfd 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffPlanarConfiguration.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffPlanarConfiguration.cs @@ -6,13 +6,18 @@ namespace ImageSharp.Formats { /// - /// Enumeration representing the planar configuration types defined by the Tiff file-format. + /// Enumeration representing how the components of each pixel are stored the Tiff file-format. /// internal enum TiffPlanarConfiguration { - // TIFF baseline PlanarConfiguration values - + /// + /// Chunky format. + /// Chunky = 1, + + /// + /// Planar format. + /// Planar = 2 } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs index 9275a79fb..582c47644 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs @@ -10,10 +10,19 @@ namespace ImageSharp.Formats /// internal enum TiffResolutionUnit { - // TIFF baseline ResolutionUnit values - + /// + /// No absolute unit of measurement. + /// None = 1, + + /// + /// Inch. + /// Inch = 2, + + /// + /// Centimeter. + /// Centimeter = 2 } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffSubfileType.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffSubfileType.cs index 0b256f031..6a86f3b30 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffSubfileType.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffSubfileType.cs @@ -10,10 +10,19 @@ namespace ImageSharp.Formats /// internal enum TiffSubfileType { - // TIFF baseline subfile types - + /// + /// Full-resolution image data. + /// FullImage = 1, + + /// + /// Reduced-resolution image data. + /// Preview = 2, + + /// + /// A single page of a multi-page image. + /// SinglePage = 3 } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffTags.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffTags.cs index 41721fb1d..56fc71461 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffTags.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffTags.cs @@ -10,196 +10,709 @@ namespace ImageSharp.Formats /// internal class TiffTags { - // Section 8: Baseline Fields - + /// + /// Artist (see Section 8: Baseline Fields). + /// public const int Artist = 315; + + /// + /// BitsPerSample (see Section 8: Baseline Fields). + /// public const int BitsPerSample = 258; + + /// + /// CellLength (see Section 8: Baseline Fields). + /// public const int CellLength = 265; + + /// + /// CellWidth (see Section 8: Baseline Fields). + /// public const int CellWidth = 264; + + /// + /// ColorMap (see Section 8: Baseline Fields). + /// public const int ColorMap = 320; + + /// + /// Compression (see Section 8: Baseline Fields). + /// public const int Compression = 259; + + /// + /// Copyright (see Section 8: Baseline Fields). + /// public const int Copyright = 33432; + + /// + /// DateTime (see Section 8: Baseline Fields). + /// public const int DateTime = 306; + + /// + /// ExtraSamples (see Section 8: Baseline Fields). + /// public const int ExtraSamples = 338; + + /// + /// FillOrder (see Section 8: Baseline Fields). + /// public const int FillOrder = 266; + + /// + /// FreeByteCounts (see Section 8: Baseline Fields). + /// public const int FreeByteCounts = 289; + + /// + /// FreeOffsets (see Section 8: Baseline Fields). + /// public const int FreeOffsets = 288; + + /// + /// GrayResponseCurve (see Section 8: Baseline Fields). + /// public const int GrayResponseCurve = 291; + + /// + /// GrayResponseUnit (see Section 8: Baseline Fields). + /// public const int GrayResponseUnit = 290; + + /// + /// HostComputer (see Section 8: Baseline Fields). + /// public const int HostComputer = 316; + + /// + /// ImageDescription (see Section 8: Baseline Fields). + /// public const int ImageDescription = 270; + + /// + /// ImageLength (see Section 8: Baseline Fields). + /// public const int ImageLength = 257; + + /// + /// ImageWidth (see Section 8: Baseline Fields). + /// public const int ImageWidth = 256; + + /// + /// Make (see Section 8: Baseline Fields). + /// public const int Make = 271; + + /// + /// MaxSampleValue (see Section 8: Baseline Fields). + /// public const int MaxSampleValue = 281; + + /// + /// MinSampleValue (see Section 8: Baseline Fields). + /// public const int MinSampleValue = 280; + + /// + /// Model (see Section 8: Baseline Fields). + /// public const int Model = 272; + + /// + /// NewSubfileType (see Section 8: Baseline Fields). + /// public const int NewSubfileType = 254; + + /// + /// Orientation (see Section 8: Baseline Fields). + /// public const int Orientation = 274; + + /// + /// PhotometricInterpretation (see Section 8: Baseline Fields). + /// public const int PhotometricInterpretation = 262; + + /// + /// PlanarConfiguration (see Section 8: Baseline Fields). + /// public const int PlanarConfiguration = 284; + + /// + /// ResolutionUnit (see Section 8: Baseline Fields). + /// public const int ResolutionUnit = 296; + + /// + /// RowsPerStrip (see Section 8: Baseline Fields). + /// public const int RowsPerStrip = 278; + + /// + /// SamplesPerPixel (see Section 8: Baseline Fields). + /// public const int SamplesPerPixel = 277; + + /// + /// Software (see Section 8: Baseline Fields). + /// public const int Software = 305; + + /// + /// StripByteCounts (see Section 8: Baseline Fields). + /// public const int StripByteCounts = 279; + + /// + /// StripOffsets (see Section 8: Baseline Fields). + /// public const int StripOffsets = 273; + + /// + /// SubfileType (see Section 8: Baseline Fields). + /// public const int SubfileType = 255; + + /// + /// Threshholding (see Section 8: Baseline Fields). + /// public const int Threshholding = 263; + + /// + /// XResolution (see Section 8: Baseline Fields). + /// public const int XResolution = 282; - public const int YResolution = 283; - // Section 11: CCITT Bilevel Encodings + /// + /// YResolution (see Section 8: Baseline Fields). + /// + public const int YResolution = 283; + /// + /// T4Options (see Section 11: CCITT Bilevel Encodings). + /// public const int T4Options = 292; - public const int T6Options = 293; - // Section 12: Document Storage and Retrieval + /// + /// T6Options (see Section 11: CCITT Bilevel Encodings). + /// + public const int T6Options = 293; + /// + /// DocumentName (see Section 12: Document Storage and Retrieval). + /// public const int DocumentName = 269; + + /// + /// PageName (see Section 12: Document Storage and Retrieval). + /// public const int PageName = 285; + + /// + /// PageNumber (see Section 12: Document Storage and Retrieval). + /// public const int PageNumber = 297; + + /// + /// XPosition (see Section 12: Document Storage and Retrieval). + /// public const int XPosition = 286; - public const int YPosition = 287; - // Section 14: Differencing Predictor + /// + /// YPosition (see Section 12: Document Storage and Retrieval). + /// + public const int YPosition = 287; + /// + /// Predictor (see Section 14: Differencing Predictor). + /// public const int Predictor = 317; - // Section 15: Tiled Images - + /// + /// TileWidth (see Section 15: Tiled Images). + /// public const int TileWidth = 322; + + /// + /// TileLength (see Section 15: Tiled Images). + /// public const int TileLength = 323; + + /// + /// TileOffsets (see Section 15: Tiled Images). + /// public const int TileOffsets = 324; - public const int TileByteCounts = 325; - // Section 16: CMYK Images + /// + /// TileByteCounts (see Section 15: Tiled Images). + /// + public const int TileByteCounts = 325; + /// + /// InkSet (see Section 16: CMYK Images). + /// public const int InkSet = 332; + + /// + /// NumberOfInks (see Section 16: CMYK Images). + /// public const int NumberOfInks = 334; + + /// + /// InkNames (see Section 16: CMYK Images). + /// public const int InkNames = 333; + + /// + /// DotRange (see Section 16: CMYK Images). + /// public const int DotRange = 336; - public const int TargetPrinter = 337; - // Section 17: Halftone Hints + /// + /// TargetPrinter (see Section 16: CMYK Images). + /// + public const int TargetPrinter = 337; + /// + /// HalftoneHints (see Section 17: Halftone Hints). + /// public const int HalftoneHints = 321; - // Section 19: Data Sample Format - + /// + /// SampleFormat (see Section 19: Data Sample Format). + /// public const int SampleFormat = 339; + + /// + /// SMinSampleValue (see Section 19: Data Sample Format). + /// public const int SMinSampleValue = 340; - public const int SMaxSampleValue = 341; - // Section 20: RGB Image Colorimetry + /// + /// SMaxSampleValue (see Section 19: Data Sample Format). + /// + public const int SMaxSampleValue = 341; + /// + /// WhitePoint (see Section 20: RGB Image Colorimetry). + /// public const int WhitePoint = 318; + + /// + /// PrimaryChromaticities (see Section 20: RGB Image Colorimetry). + /// public const int PrimaryChromaticities = 319; + + /// + /// TransferFunction (see Section 20: RGB Image Colorimetry). + /// public const int TransferFunction = 301; + + /// + /// TransferRange (see Section 20: RGB Image Colorimetry). + /// public const int TransferRange = 342; - public const int ReferenceBlackWhite = 532; - // Section 21: YCbCr Images + /// + /// ReferenceBlackWhite (see Section 20: RGB Image Colorimetry). + /// + public const int ReferenceBlackWhite = 532; + /// + /// YCbCrCoefficients (see Section 21: YCbCr Images). + /// public const int YCbCrCoefficients = 529; + + /// + /// YCbCrSubSampling (see Section 21: YCbCr Images). + /// public const int YCbCrSubSampling = 530; - public const int YCbCrPositioning = 531; - // Section 22: JPEG Compression + /// + /// YCbCrPositioning (see Section 21: YCbCr Images). + /// + public const int YCbCrPositioning = 531; + /// + /// JpegProc (see Section 22: JPEG Compression). + /// public const int JpegProc = 512; + + /// + /// JpegInterchangeFormat (see Section 22: JPEG Compression). + /// public const int JpegInterchangeFormat = 513; + + /// + /// JpegInterchangeFormatLength (see Section 22: JPEG Compression). + /// public const int JpegInterchangeFormatLength = 514; + + /// + /// JpegRestartInterval (see Section 22: JPEG Compression). + /// public const int JpegRestartInterval = 515; + + /// + /// JpegLosslessPredictors (see Section 22: JPEG Compression). + /// public const int JpegLosslessPredictors = 517; + + /// + /// JpegPointTransforms (see Section 22: JPEG Compression). + /// public const int JpegPointTransforms = 518; + + /// + /// JpegQTables (see Section 22: JPEG Compression). + /// public const int JpegQTables = 519; + + /// + /// JpegDCTables (see Section 22: JPEG Compression). + /// public const int JpegDCTables = 520; - public const int JpegACTables = 521; - // TIFF Supplement 1: Adobe Pagemaker 6.0 + /// + /// JpegACTables (see Section 22: JPEG Compression). + /// + public const int JpegACTables = 521; + /// + /// SubIFDs (see TIFF Supplement 1: Adobe Pagemaker 6.0). + /// public const int SubIFDs = 330; + + /// + /// ClipPath (see TIFF Supplement 1: Adobe Pagemaker 6.0). + /// public const int ClipPath = 343; + + /// + /// XClipPathUnits (see TIFF Supplement 1: Adobe Pagemaker 6.0). + /// public const int XClipPathUnits = 344; + + /// + /// YClipPathUnits (see TIFF Supplement 1: Adobe Pagemaker 6.0). + /// public const int YClipPathUnits = 345; + + /// + /// Indexed (see TIFF Supplement 1: Adobe Pagemaker 6.0). + /// public const int Indexed = 346; + + /// + /// ImageID (see TIFF Supplement 1: Adobe Pagemaker 6.0). + /// public const int ImageID = 32781; - public const int OpiProxy = 351; - // TIFF Supplement 2: Adobe Photoshop + /// + /// OpiProxy (see TIFF Supplement 1: Adobe Pagemaker 6.0). + /// + public const int OpiProxy = 351; + /// + /// ImageSourceData (see TIFF Supplement 2: Adobe Photoshop). + /// public const int ImageSourceData = 37724; - // TIFF/EP Specification: Additional Tags - + /// + /// JPEGTables (see TIFF/EP Specification: Additional Tags). + /// public const int JPEGTables = 0x015B; + + /// + /// CFARepeatPatternDim (see TIFF/EP Specification: Additional Tags). + /// public const int CFARepeatPatternDim = 0x828D; + + /// + /// BatteryLevel (see TIFF/EP Specification: Additional Tags). + /// public const int BatteryLevel = 0x828F; + + /// + /// Interlace (see TIFF/EP Specification: Additional Tags). + /// public const int Interlace = 0x8829; + + /// + /// TimeZoneOffset (see TIFF/EP Specification: Additional Tags). + /// public const int TimeZoneOffset = 0x882A; + + /// + /// SelfTimerMode (see TIFF/EP Specification: Additional Tags). + /// public const int SelfTimerMode = 0x882B; + + /// + /// Noise (see TIFF/EP Specification: Additional Tags). + /// public const int Noise = 0x920D; + + /// + /// ImageNumber (see TIFF/EP Specification: Additional Tags). + /// public const int ImageNumber = 0x9211; + + /// + /// SecurityClassification (see TIFF/EP Specification: Additional Tags). + /// public const int SecurityClassification = 0x9212; + + /// + /// ImageHistory (see TIFF/EP Specification: Additional Tags). + /// public const int ImageHistory = 0x9213; - public const int TiffEPStandardID = 0x9216; - // TIFF-F/FX Specification (http://www.ietf.org/rfc/rfc2301.txt) + /// + /// TiffEPStandardID (see TIFF/EP Specification: Additional Tags). + /// + public const int TiffEPStandardID = 0x9216; + /// + /// BadFaxLines (see RFC2301: TIFF-F/FX Specification). + /// public const int BadFaxLines = 326; + + /// + /// CleanFaxData (see RFC2301: TIFF-F/FX Specification). + /// public const int CleanFaxData = 327; + + /// + /// ConsecutiveBadFaxLines (see RFC2301: TIFF-F/FX Specification). + /// public const int ConsecutiveBadFaxLines = 328; + + /// + /// GlobalParametersIFD (see RFC2301: TIFF-F/FX Specification). + /// public const int GlobalParametersIFD = 400; + + /// + /// ProfileType (see RFC2301: TIFF-F/FX Specification). + /// public const int ProfileType = 401; + + /// + /// FaxProfile (see RFC2301: TIFF-F/FX Specification). + /// public const int FaxProfile = 402; + + /// + /// CodingMethod (see RFC2301: TIFF-F/FX Specification). + /// public const int CodingMethod = 403; + + /// + /// VersionYear (see RFC2301: TIFF-F/FX Specification). + /// public const int VersionYear = 404; + + /// + /// ModeNumber (see RFC2301: TIFF-F/FX Specification). + /// public const int ModeNumber = 405; + + /// + /// Decode (see RFC2301: TIFF-F/FX Specification). + /// public const int Decode = 433; + + /// + /// DefaultImageColor (see RFC2301: TIFF-F/FX Specification). + /// public const int DefaultImageColor = 434; + + /// + /// StripRowCounts (see RFC2301: TIFF-F/FX Specification). + /// public const int StripRowCounts = 559; - public const int ImageLayer = 34732; - // Embedded Metadata + /// + /// ImageLayer (see RFC2301: TIFF-F/FX Specification). + /// + public const int ImageLayer = 34732; + /// + /// Xmp (Embedded Metadata). + /// public const int Xmp = 700; + + /// + /// Iptc (Embedded Metadata). + /// public const int Iptc = 33723; + + /// + /// Photoshop (Embedded Metadata). + /// public const int Photoshop = 34377; + + /// + /// ExifIFD (Embedded Metadata). + /// public const int ExifIFD = 34665; + + /// + /// GpsIFD (Embedded Metadata). + /// public const int GpsIFD = 34853; - public const int InteroperabilityIFD = 40965; - // Other Private TIFF tags (http://www.awaresystems.be/imaging/tiff/tifftags/private.html) + /// + /// InteroperabilityIFD (Embedded Metadata). + /// + public const int InteroperabilityIFD = 40965; + /// + /// WangAnnotation (Other Private TIFF tags : see http://www.awaresystems.be/imaging/tiff/tifftags/private.html). + /// public const int WangAnnotation = 32932; + + /// + /// MDFileTag (Other Private TIFF tags : see http://www.awaresystems.be/imaging/tiff/tifftags/private.html). + /// public const int MDFileTag = 33445; + + /// + /// MDScalePixel (Other Private TIFF tags : see http://www.awaresystems.be/imaging/tiff/tifftags/private.html). + /// public const int MDScalePixel = 33446; + + /// + /// MDColorTable (Other Private TIFF tags : see http://www.awaresystems.be/imaging/tiff/tifftags/private.html). + /// public const int MDColorTable = 33447; + + /// + /// MDLabName (Other Private TIFF tags : see http://www.awaresystems.be/imaging/tiff/tifftags/private.html). + /// public const int MDLabName = 33448; + + /// + /// MDSampleInfo (Other Private TIFF tags : see http://www.awaresystems.be/imaging/tiff/tifftags/private.html). + /// public const int MDSampleInfo = 33449; + + /// + /// MDPrepDate (Other Private TIFF tags : see http://www.awaresystems.be/imaging/tiff/tifftags/private.html). + /// public const int MDPrepDate = 33450; + + /// + /// MDPrepTime (Other Private TIFF tags : see http://www.awaresystems.be/imaging/tiff/tifftags/private.html). + /// public const int MDPrepTime = 33451; + + /// + /// MDFileUnits (Other Private TIFF tags : see http://www.awaresystems.be/imaging/tiff/tifftags/private.html). + /// public const int MDFileUnits = 33452; + + /// + /// ModelPixelScaleTag (Other Private TIFF tags : see http://www.awaresystems.be/imaging/tiff/tifftags/private.html). + /// public const int ModelPixelScaleTag = 33550; + + /// + /// IngrPacketDataTag (Other Private TIFF tags : see http://www.awaresystems.be/imaging/tiff/tifftags/private.html). + /// public const int IngrPacketDataTag = 33918; + + /// + /// IngrFlagRegisters (Other Private TIFF tags : see http://www.awaresystems.be/imaging/tiff/tifftags/private.html). + /// public const int IngrFlagRegisters = 33919; + + /// + /// IrasBTransformationMatrix (Other Private TIFF tags : see http://www.awaresystems.be/imaging/tiff/tifftags/private.html). + /// public const int IrasBTransformationMatrix = 33920; + + /// + /// ModelTiePointTag (Other Private TIFF tags : see http://www.awaresystems.be/imaging/tiff/tifftags/private.html). + /// public const int ModelTiePointTag = 33922; + + /// + /// ModelTransformationTag (Other Private TIFF tags : see http://www.awaresystems.be/imaging/tiff/tifftags/private.html). + /// public const int ModelTransformationTag = 34264; + + /// + /// IccProfile (Other Private TIFF tags : see http://www.awaresystems.be/imaging/tiff/tifftags/private.html). + /// public const int IccProfile = 34675; + + /// + /// GeoKeyDirectoryTag (Other Private TIFF tags : see http://www.awaresystems.be/imaging/tiff/tifftags/private.html). + /// public const int GeoKeyDirectoryTag = 34735; + + /// + /// GeoDoubleParamsTag (Other Private TIFF tags : see http://www.awaresystems.be/imaging/tiff/tifftags/private.html). + /// public const int GeoDoubleParamsTag = 34736; + + /// + /// GeoAsciiParamsTag (Other Private TIFF tags : see http://www.awaresystems.be/imaging/tiff/tifftags/private.html). + /// public const int GeoAsciiParamsTag = 34737; + + /// + /// HylaFAXFaxRecvParams (Other Private TIFF tags : see http://www.awaresystems.be/imaging/tiff/tifftags/private.html). + /// public const int HylaFAXFaxRecvParams = 34908; + + /// + /// HylaFAXFaxSubAddress (Other Private TIFF tags : see http://www.awaresystems.be/imaging/tiff/tifftags/private.html). + /// public const int HylaFAXFaxSubAddress = 34909; + + /// + /// HylaFAXFaxRecvTime (Other Private TIFF tags : see http://www.awaresystems.be/imaging/tiff/tifftags/private.html). + /// public const int HylaFAXFaxRecvTime = 34910; + + /// + /// GdalMetadata (Other Private TIFF tags : see http://www.awaresystems.be/imaging/tiff/tifftags/private.html). + /// public const int GdalMetadata = 42112; + + /// + /// GdalNodata (Other Private TIFF tags : see http://www.awaresystems.be/imaging/tiff/tifftags/private.html). + /// public const int GdalNodata = 42113; + + /// + /// OceScanjobDescription (Other Private TIFF tags : see http://www.awaresystems.be/imaging/tiff/tifftags/private.html). + /// public const int OceScanjobDescription = 50215; + + /// + /// OceApplicationSelector (Other Private TIFF tags : see http://www.awaresystems.be/imaging/tiff/tifftags/private.html). + /// public const int OceApplicationSelector = 50216; + + /// + /// OceIdentificationNumber (Other Private TIFF tags : see http://www.awaresystems.be/imaging/tiff/tifftags/private.html). + /// public const int OceIdentificationNumber = 50217; + + /// + /// OceImageLogicCharacteristics (Other Private TIFF tags : see http://www.awaresystems.be/imaging/tiff/tifftags/private.html). + /// public const int OceImageLogicCharacteristics = 50218; + + /// + /// AliasLayerMetadata (Other Private TIFF tags : see http://www.awaresystems.be/imaging/tiff/tifftags/private.html). + /// public const int AliasLayerMetadata = 50784; } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffThreshholding.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffThreshholding.cs index 72efa9222..eff57cd90 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffThreshholding.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffThreshholding.cs @@ -6,14 +6,23 @@ namespace ImageSharp.Formats { /// - /// Enumeration representing the threshholding types defined by the Tiff file-format. + /// Enumeration representing the threshholding applied to image data defined by the Tiff file-format. /// internal enum TiffThreshholding { - // TIFF baseline Threshholding values - + /// + /// No dithering or halftoning. + /// None = 1, + + /// + /// An ordered dither or halftone technique. + /// Ordered = 2, + + /// + /// A randomized process such as error diffusion. + /// Random = 3 } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffType.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffType.cs index b98236c0f..0b342d06a 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffType.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffType.cs @@ -10,18 +10,69 @@ namespace ImageSharp.Formats /// internal enum TiffType { + /// + /// Unsigned 8-bit integer. + /// Byte = 1, + + /// + /// ASCII formatted text. + /// Ascii = 2, + + /// + /// Unsigned 16-bit integer. + /// Short = 3, + + /// + /// Unsigned 32-bit integer. + /// Long = 4, + + /// + /// Unsigned rational number. + /// Rational = 5, + + /// + /// Signed 8-bit integer. + /// SByte = 6, + + /// + /// Undefined data type. + /// Undefined = 7, + + /// + /// Signed 16-bit integer. + /// SShort = 8, + + /// + /// Signed 32-bit integer. + /// SLong = 9, + + /// + /// Signed rational number. + /// SRational = 10, + + /// + /// Single precision (4-byte) IEEE format. + /// Float = 11, + + /// + /// Double precision (8-byte) IEEE format. + /// Double = 12, + + /// + /// Reference to an IFD. + /// Ifd = 13 } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 8e0a42515..e24a1aa39 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -28,6 +28,12 @@ namespace ImageSharp.Formats this.options = options ?? new DecoderOptions(); } + /// + /// Initializes a new instance of the class. + /// + /// The input stream. + /// A flag indicating if the file is encoded in little-endian or big-endian format. + /// The decoder options. public TiffDecoderCore(Stream stream, bool isLittleEndian, IDecoderOptions options) : this(options) { @@ -45,6 +51,13 @@ namespace ImageSharp.Formats /// public bool IsLittleEndian { get; private set; } + /// + /// Calculates the size (in bytes) of the data contained within an IFD entry. + /// + /// The IFD entry to calculate the size for. + /// The size of the data (in bytes). + public static uint GetSizeOfData(TiffIfdEntry entry) => SizeOfDataType(entry.Type) * entry.Count; + /// /// Decodes the image from the specified and sets /// the data to image. @@ -69,6 +82,13 @@ namespace ImageSharp.Formats { } + /// + /// Reads the TIFF header from the input stream. + /// + /// The byte offset to the first IFD in the file. + /// + /// Thrown if the TIFF file header is invalid. + /// public uint ReadHeader() { byte[] headerBytes = new byte[TiffConstants.SizeOfTiffHeader]; @@ -97,6 +117,11 @@ namespace ImageSharp.Formats return firstIfdOffset; } + /// + /// Reads a from the input stream. + /// + /// The byte offset within the file to find the IFD. + /// A containing the retrieved data. public TiffIfd ReadIfd(uint offset) { this.InputStream.Seek(offset, SeekOrigin.Begin); @@ -125,24 +150,11 @@ namespace ImageSharp.Formats return new TiffIfd(entries, nextIfdOffset); } - private void ReadBytes(byte[] buffer, int count) - { - int offset = 0; - - while (count > 0) - { - int bytesRead = this.InputStream.Read(buffer, offset, count); - - if (bytesRead == 0) - { - break; - } - - offset += bytesRead; - count -= bytesRead; - } - } - + /// + /// Reads the data from a as an array of bytes. + /// + /// The to read. + /// The data. public byte[] ReadBytes(ref TiffIfdEntry entry) { uint byteLength = GetSizeOfData(entry); @@ -160,6 +172,15 @@ namespace ImageSharp.Formats return entry.Value; } + /// + /// Reads the data from a as an unsigned integer value. + /// + /// The to read. + /// The data. + /// + /// Thrown if the data-type specified by the file cannot be converted to a , or if + /// there is an array of items. + /// public uint ReadUnsignedInteger(ref TiffIfdEntry entry) { if (entry.Count != 1) @@ -180,6 +201,15 @@ namespace ImageSharp.Formats } } + /// + /// Reads the data from a as a signed integer value. + /// + /// The to read. + /// The data. + /// + /// Thrown if the data-type specified by the file cannot be converted to an , or if + /// there is an array of items. + /// public int ReadSignedInteger(ref TiffIfdEntry entry) { if (entry.Count != 1) @@ -200,6 +230,14 @@ namespace ImageSharp.Formats } } + /// + /// Reads the data from a as an array of unsigned integer values. + /// + /// The to read. + /// The data. + /// + /// Thrown if the data-type specified by the file cannot be converted to a . + /// public uint[] ReadUnsignedIntegerArray(ref TiffIfdEntry entry) { byte[] bytes = this.ReadBytes(ref entry); @@ -244,6 +282,14 @@ namespace ImageSharp.Formats return result; } + /// + /// Reads the data from a as an array of signed integer values. + /// + /// The to read. + /// The data. + /// + /// Thrown if the data-type specified by the file cannot be converted to an . + /// public int[] ReadSignedIntegerArray(ref TiffIfdEntry entry) { byte[] bytes = this.ReadBytes(ref entry); @@ -288,6 +334,14 @@ namespace ImageSharp.Formats return result; } + /// + /// Reads the data from a as a value. + /// + /// The to read. + /// The data. + /// + /// Thrown if the data-type specified by the file cannot be converted to a . + /// public string ReadString(ref TiffIfdEntry entry) { if (entry.Type != TiffType.Ascii) @@ -305,6 +359,15 @@ namespace ImageSharp.Formats return Encoding.UTF8.GetString(bytes, 0, (int)entry.Count - 1); } + /// + /// Reads the data from a as a value. + /// + /// The to read. + /// The data. + /// + /// Thrown if the data-type specified by the file cannot be converted to a , or if + /// there is an array of items. + /// public Rational ReadUnsignedRational(ref TiffIfdEntry entry) { if (entry.Count != 1) @@ -315,6 +378,15 @@ namespace ImageSharp.Formats return this.ReadUnsignedRationalArray(ref entry)[0]; } + /// + /// Reads the data from a as a value. + /// + /// The to read. + /// The data. + /// + /// Thrown if the data-type specified by the file cannot be converted to a , or if + /// there is an array of items. + /// public SignedRational ReadSignedRational(ref TiffIfdEntry entry) { if (entry.Count != 1) @@ -325,6 +397,14 @@ namespace ImageSharp.Formats return this.ReadSignedRationalArray(ref entry)[0]; } + /// + /// Reads the data from a as an array of values. + /// + /// The to read. + /// The data. + /// + /// Thrown if the data-type specified by the file cannot be converted to a . + /// public Rational[] ReadUnsignedRationalArray(ref TiffIfdEntry entry) { if (entry.Type != TiffType.Rational) @@ -338,13 +418,21 @@ namespace ImageSharp.Formats for (int i = 0; i < result.Length; i++) { uint numerator = this.ToUInt32(bytes, i * TiffConstants.SizeOfRational); - uint denominator = this.ToUInt32(bytes, i * TiffConstants.SizeOfRational + TiffConstants.SizeOfLong); + uint denominator = this.ToUInt32(bytes, (i * TiffConstants.SizeOfRational) + TiffConstants.SizeOfLong); result[i] = new Rational(numerator, denominator); } return result; } + /// + /// Reads the data from a as an array of values. + /// + /// The to read. + /// The data. + /// + /// Thrown if the data-type specified by the file cannot be converted to a . + /// public SignedRational[] ReadSignedRationalArray(ref TiffIfdEntry entry) { if (entry.Type != TiffType.SRational) @@ -358,13 +446,22 @@ namespace ImageSharp.Formats for (int i = 0; i < result.Length; i++) { int numerator = this.ToInt32(bytes, i * TiffConstants.SizeOfRational); - int denominator = this.ToInt32(bytes, i * TiffConstants.SizeOfRational + TiffConstants.SizeOfLong); + int denominator = this.ToInt32(bytes, (i * TiffConstants.SizeOfRational) + TiffConstants.SizeOfLong); result[i] = new SignedRational(numerator, denominator); } return result; } + /// + /// Reads the data from a as a value. + /// + /// The to read. + /// The data. + /// + /// Thrown if the data-type specified by the file cannot be converted to a , or if + /// there is an array of items. + /// public float ReadFloat(ref TiffIfdEntry entry) { if (entry.Count != 1) @@ -380,6 +477,15 @@ namespace ImageSharp.Formats return this.ToSingle(entry.Value, 0); } + /// + /// Reads the data from a as a value. + /// + /// The to read. + /// The data. + /// + /// Thrown if the data-type specified by the file cannot be converted to a , or if + /// there is an array of items. + /// public double ReadDouble(ref TiffIfdEntry entry) { if (entry.Count != 1) @@ -390,6 +496,14 @@ namespace ImageSharp.Formats return this.ReadDoubleArray(ref entry)[0]; } + /// + /// Reads the data from a as an array of values. + /// + /// The to read. + /// The data. + /// + /// Thrown if the data-type specified by the file cannot be converted to a . + /// public float[] ReadFloatArray(ref TiffIfdEntry entry) { if (entry.Type != TiffType.Float) @@ -408,6 +522,14 @@ namespace ImageSharp.Formats return result; } + /// + /// Reads the data from a as an array of values. + /// + /// The to read. + /// The data. + /// + /// Thrown if the data-type specified by the file cannot be converted to a . + /// public double[] ReadDoubleArray(ref TiffIfdEntry entry) { if (entry.Type != TiffType.Double) @@ -426,11 +548,77 @@ namespace ImageSharp.Formats return result; } + /// + /// Calculates the size (in bytes) for the specified TIFF data-type. + /// + /// The data-type to calculate the size for. + /// The size of the data-type (in bytes). + private static uint SizeOfDataType(TiffType type) + { + switch (type) + { + case TiffType.Byte: + case TiffType.Ascii: + case TiffType.SByte: + case TiffType.Undefined: + return 1u; + case TiffType.Short: + case TiffType.SShort: + return 2u; + case TiffType.Long: + case TiffType.SLong: + case TiffType.Float: + case TiffType.Ifd: + return 4u; + case TiffType.Rational: + case TiffType.SRational: + case TiffType.Double: + return 8u; + default: + return 0u; + } + } + + /// + /// Reads a sequence of bytes from the input stream into a buffer. + /// + /// A buffer to store the retrieved data. + /// The number of bytes to read. + private void ReadBytes(byte[] buffer, int count) + { + int offset = 0; + + while (count > 0) + { + int bytesRead = this.InputStream.Read(buffer, offset, count); + + if (bytesRead == 0) + { + break; + } + + offset += bytesRead; + count -= bytesRead; + } + } + + /// + /// Converts buffer data into an using the correct endianness. + /// + /// The buffer. + /// The byte offset within the buffer. + /// The converted value. private sbyte ToSByte(byte[] bytes, int offset) { return (sbyte)bytes[offset]; } + /// + /// Converts buffer data into an using the correct endianness. + /// + /// The buffer. + /// The byte offset within the buffer. + /// The converted value. private short ToInt16(byte[] bytes, int offset) { if (this.IsLittleEndian) @@ -443,6 +631,12 @@ namespace ImageSharp.Formats } } + /// + /// Converts buffer data into an using the correct endianness. + /// + /// The buffer. + /// The byte offset within the buffer. + /// The converted value. private int ToInt32(byte[] bytes, int offset) { if (this.IsLittleEndian) @@ -455,21 +649,45 @@ namespace ImageSharp.Formats } } + /// + /// Converts buffer data into a using the correct endianness. + /// + /// The buffer. + /// The byte offset within the buffer. + /// The converted value. private byte ToByte(byte[] bytes, int offset) { return bytes[offset]; } + /// + /// Converts buffer data into a using the correct endianness. + /// + /// The buffer. + /// The byte offset within the buffer. + /// The converted value. private uint ToUInt32(byte[] bytes, int offset) { return (uint)this.ToInt32(bytes, offset); } + /// + /// Converts buffer data into a using the correct endianness. + /// + /// The buffer. + /// The byte offset within the buffer. + /// The converted value. private ushort ToUInt16(byte[] bytes, int offset) { return (ushort)this.ToInt16(bytes, offset); } + /// + /// Converts buffer data into a using the correct endianness. + /// + /// The buffer. + /// The byte offset within the buffer. + /// The converted value. private float ToSingle(byte[] bytes, int offset) { byte[] buffer = new byte[4]; @@ -483,6 +701,12 @@ namespace ImageSharp.Formats return BitConverter.ToSingle(buffer, 0); } + /// + /// Converts buffer data into a using the correct endianness. + /// + /// The buffer. + /// The byte offset within the buffer. + /// The converted value. private double ToDouble(byte[] bytes, int offset) { byte[] buffer = new byte[8]; @@ -495,33 +719,5 @@ namespace ImageSharp.Formats return BitConverter.ToDouble(buffer, 0); } - - public static uint GetSizeOfData(TiffIfdEntry entry) => SizeOfDataType(entry.Type) * entry.Count; - - private static uint SizeOfDataType(TiffType type) - { - switch (type) - { - case TiffType.Byte: - case TiffType.Ascii: - case TiffType.SByte: - case TiffType.Undefined: - return 1u; - case TiffType.Short: - case TiffType.SShort: - return 2u; - case TiffType.Long: - case TiffType.SLong: - case TiffType.Float: - case TiffType.Ifd: - return 4u; - case TiffType.Rational: - case TiffType.SRational: - case TiffType.Double: - return 8u; - default: - return 0u; - } - } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs index 7193c1a76..c54a43ede 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs @@ -33,8 +33,6 @@ namespace ImageSharp.Formats where TColor : struct, IPixel { throw new NotImplementedException(); - // TiffEncoderCore encode = new TiffEncoderCore(options); - // encode.Encode(image, stream); } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfd.cs b/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfd.cs index 40848c4d8..477182c1e 100644 --- a/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfd.cs +++ b/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfd.cs @@ -6,13 +6,25 @@ namespace ImageSharp.Formats { /// - /// Data structure for holding details of each TIFF IFD + /// Data structure for holding details of each TIFF IFD. /// internal struct TiffIfd { + /// + /// An array of the entries within this IFD. + /// public TiffIfdEntry[] Entries; + + /// + /// Offset (in bytes) to the next IFD, or zero if this is the last IFD. + /// public uint NextIfdOffset; + /// + /// Initializes a new instance of the class. + /// + /// An array of the entries within the IFD. + /// Offset (in bytes) to the next IFD, or zero if this is the last IFD. public TiffIfd(TiffIfdEntry[] entries, uint nextIfdOffset) { this.Entries = entries; diff --git a/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntry.cs b/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntry.cs index 04686a4da..b2983eaad 100644 --- a/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntry.cs +++ b/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntry.cs @@ -6,15 +6,37 @@ namespace ImageSharp.Formats { /// - /// Data structure for holding details of each TIFF IFD entry + /// Data structure for holding details of each TIFF IFD entry. /// internal struct TiffIfdEntry { + /// + /// The Tag ID for this entry. See for typical values. + /// public ushort Tag; + + /// + /// The data-type of this entry. + /// public TiffType Type; + + /// + /// The number of array items in this entry, or one if only a single value. + /// public uint Count; + + /// + /// The raw byte data for this entry. + /// public byte[] Value; + /// + /// Initializes a new instance of the class. + /// + /// The Tag ID for this entry. + /// The data-type of this entry. + /// The number of array items in this entry. + /// The raw byte data for this entry. public TiffIfdEntry(ushort tag, TiffType type, uint count, byte[] value) { this.Tag = tag; From 2840de50e45cbf6982e085341dd6554b703f349b Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Sat, 18 Mar 2017 15:59:38 +0000 Subject: [PATCH 021/275] Run TIFF unit tests in CI --- .travis.yml | 1 + tests/CodeCoverage/CodeCoverage.cmd | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index af8d4ad9d..8eb24a84a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,6 +21,7 @@ branches: script: - dotnet restore - dotnet test tests/ImageSharp.Tests/ImageSharp.Tests.csproj -c Release -f "netcoreapp1.1" + - dotnet test tests/ImageSharp.Formats.Tiff.Tests/ImageSharp.Formats.Tiff.Tests.csproj -c Release -f "netcoreapp1.1" env: global: diff --git a/tests/CodeCoverage/CodeCoverage.cmd b/tests/CodeCoverage/CodeCoverage.cmd index 1e16d5c14..417662d19 100644 --- a/tests/CodeCoverage/CodeCoverage.cmd +++ b/tests/CodeCoverage/CodeCoverage.cmd @@ -12,9 +12,12 @@ dotnet restore ImageSharp.sln dotnet build ImageSharp.sln --no-incremental -c release /p:codecov=true rem The -threshold options prevents this taking ages... tests\CodeCoverage\OpenCover.4.6.519\tools\OpenCover.Console.exe -target:"dotnet.exe" -targetargs:"test tests\ImageSharp.Tests\ImageSharp.Tests.csproj --no-build -c release /p:codecov=true" -register:user -threshold:10 -oldStyle -safemode:off -output:.\ImageSharp.Coverage.xml -hideskipped:All -returntargetcode -filter:"+[ImageSharp*]*" +tests\CodeCoverage\OpenCover.4.6.519\tools\OpenCover.Console.exe -target:"dotnet.exe" -targetargs:"test tests\ImageSharp.Tests\ImageSharp.Formats.Tiff.Tests.csproj --no-build -c release /p:codecov=true" -register:user -threshold:10 -oldStyle -safemode:off -output:.\ImageSharp.Formats.Tiff.Coverage.xml -hideskipped:All -returntargetcode -filter:"+[ImageSharp*]*" + if %errorlevel% neq 0 exit /b %errorlevel% SET PATH=C:\\Python34;C:\\Python34\\Scripts;%PATH% pip install codecov -codecov -f "ImageSharp.Coverage.xml" \ No newline at end of file +codecov -f "ImageSharp.Coverage.xml" +codecov -f "ImageSharp.Formats.Tiff.Coverage.xml" \ No newline at end of file From 910cf8b836a4ca72300bf15080b3d1f470a983ec Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Sat, 18 Mar 2017 16:12:27 +0000 Subject: [PATCH 022/275] Fix project path for CI coverage script --- tests/CodeCoverage/CodeCoverage.cmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/CodeCoverage/CodeCoverage.cmd b/tests/CodeCoverage/CodeCoverage.cmd index 417662d19..2a88a6e4f 100644 --- a/tests/CodeCoverage/CodeCoverage.cmd +++ b/tests/CodeCoverage/CodeCoverage.cmd @@ -12,7 +12,7 @@ dotnet restore ImageSharp.sln dotnet build ImageSharp.sln --no-incremental -c release /p:codecov=true rem The -threshold options prevents this taking ages... tests\CodeCoverage\OpenCover.4.6.519\tools\OpenCover.Console.exe -target:"dotnet.exe" -targetargs:"test tests\ImageSharp.Tests\ImageSharp.Tests.csproj --no-build -c release /p:codecov=true" -register:user -threshold:10 -oldStyle -safemode:off -output:.\ImageSharp.Coverage.xml -hideskipped:All -returntargetcode -filter:"+[ImageSharp*]*" -tests\CodeCoverage\OpenCover.4.6.519\tools\OpenCover.Console.exe -target:"dotnet.exe" -targetargs:"test tests\ImageSharp.Tests\ImageSharp.Formats.Tiff.Tests.csproj --no-build -c release /p:codecov=true" -register:user -threshold:10 -oldStyle -safemode:off -output:.\ImageSharp.Formats.Tiff.Coverage.xml -hideskipped:All -returntargetcode -filter:"+[ImageSharp*]*" +tests\CodeCoverage\OpenCover.4.6.519\tools\OpenCover.Console.exe -target:"dotnet.exe" -targetargs:"test tests\ImageSharp.Formats.Tiff.Tests\ImageSharp.Formats.Tiff.Tests.csproj --no-build -c release /p:codecov=true" -register:user -threshold:10 -oldStyle -safemode:off -output:.\ImageSharp.Formats.Tiff.Coverage.xml -hideskipped:All -returntargetcode -filter:"+[ImageSharp*]*" if %errorlevel% neq 0 exit /b %errorlevel% From b8815687215fe0bb90c173ca4d64220388495b23 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Mon, 20 Mar 2017 14:33:22 +0000 Subject: [PATCH 023/275] Alternatively... Just merge in the TIFF tests --- .travis.yml | 1 - tests/CodeCoverage/CodeCoverage.cmd | 5 +---- tests/ImageSharp.Tests/ImageSharp.Tests.csproj | 4 ++++ 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8eb24a84a..af8d4ad9d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,7 +21,6 @@ branches: script: - dotnet restore - dotnet test tests/ImageSharp.Tests/ImageSharp.Tests.csproj -c Release -f "netcoreapp1.1" - - dotnet test tests/ImageSharp.Formats.Tiff.Tests/ImageSharp.Formats.Tiff.Tests.csproj -c Release -f "netcoreapp1.1" env: global: diff --git a/tests/CodeCoverage/CodeCoverage.cmd b/tests/CodeCoverage/CodeCoverage.cmd index 2a88a6e4f..1e16d5c14 100644 --- a/tests/CodeCoverage/CodeCoverage.cmd +++ b/tests/CodeCoverage/CodeCoverage.cmd @@ -12,12 +12,9 @@ dotnet restore ImageSharp.sln dotnet build ImageSharp.sln --no-incremental -c release /p:codecov=true rem The -threshold options prevents this taking ages... tests\CodeCoverage\OpenCover.4.6.519\tools\OpenCover.Console.exe -target:"dotnet.exe" -targetargs:"test tests\ImageSharp.Tests\ImageSharp.Tests.csproj --no-build -c release /p:codecov=true" -register:user -threshold:10 -oldStyle -safemode:off -output:.\ImageSharp.Coverage.xml -hideskipped:All -returntargetcode -filter:"+[ImageSharp*]*" -tests\CodeCoverage\OpenCover.4.6.519\tools\OpenCover.Console.exe -target:"dotnet.exe" -targetargs:"test tests\ImageSharp.Formats.Tiff.Tests\ImageSharp.Formats.Tiff.Tests.csproj --no-build -c release /p:codecov=true" -register:user -threshold:10 -oldStyle -safemode:off -output:.\ImageSharp.Formats.Tiff.Coverage.xml -hideskipped:All -returntargetcode -filter:"+[ImageSharp*]*" - if %errorlevel% neq 0 exit /b %errorlevel% SET PATH=C:\\Python34;C:\\Python34\\Scripts;%PATH% pip install codecov -codecov -f "ImageSharp.Coverage.xml" -codecov -f "ImageSharp.Formats.Tiff.Coverage.xml" \ No newline at end of file +codecov -f "ImageSharp.Coverage.xml" \ No newline at end of file diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index c6f916e00..438a9213e 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -6,10 +6,14 @@ portable True + + + + From 1e9165a16b6b99f8d77000584434e973657acb35 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Mon, 20 Mar 2017 15:51:25 +0000 Subject: [PATCH 024/275] Decode TIFF image dimensions. --- .../Formats/Tiff/TiffDecoderCore.cs | 22 +++++ .../Formats/Tiff/TiffIfd/TiffIfd.cs | 21 +++++ .../Formats/Tiff/TiffDecoderImageTests.cs | 84 +++++++++++++++++++ .../Formats/Tiff/TiffIfd/TiffIfdEntryTests.cs | 26 ++++++ .../Formats/Tiff/TiffIfd/TiffIfdTests.cs | 61 ++++++++++++++ .../Tiff/TiffGenIfdExtensions.cs | 24 ++++++ 6 files changed, 238 insertions(+) create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryTests.cs create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfdExtensions.cs diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index e24a1aa39..eb4c6b1c8 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -73,6 +73,7 @@ namespace ImageSharp.Formats uint firstIfdOffset = this.ReadHeader(); TiffIfd firstIfd = this.ReadIfd(firstIfdOffset); + this.DecodeImage(firstIfd, image); } /// @@ -150,6 +151,27 @@ namespace ImageSharp.Formats return new TiffIfd(entries, nextIfdOffset); } + /// + /// Decodes the image data from a specified IFD. + /// + /// The pixel format. + /// The IFD to read the image from. + /// The image, where the data should be set to. + public void DecodeImage(TiffIfd ifd, Image image) + where TColor : struct, IPixel + { + if (!ifd.TryGetIfdEntry(TiffTags.ImageLength, out TiffIfdEntry imageLengthEntry) + || !ifd.TryGetIfdEntry(TiffTags.ImageWidth, out TiffIfdEntry imageWidthEntry)) + { + throw new ImageFormatException("The TIFF IFD does not specify the image dimensions."); + } + + int width = (int)this.ReadUnsignedInteger(ref imageWidthEntry); + int height = (int)this.ReadUnsignedInteger(ref imageLengthEntry); + + image.InitPixels(width, height); + } + /// /// Reads the data from a as an array of bytes. /// diff --git a/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfd.cs b/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfd.cs index 477182c1e..2206e97f3 100644 --- a/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfd.cs +++ b/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfd.cs @@ -30,5 +30,26 @@ namespace ImageSharp.Formats this.Entries = entries; this.NextIfdOffset = nextIfdOffset; } + + /// + /// Gets the child with the specified tag ID. + /// + /// The tag ID to search for. + /// The resulting , if it exists. + /// A flag indicating whether the requested entry exists + public bool TryGetIfdEntry(ushort tag, out TiffIfdEntry entry) + { + for (int i = 0; i < this.Entries.Length; i++) + { + if (this.Entries[i].Tag == tag) + { + entry = this.Entries[i]; + return true; + } + } + + entry = default(TiffIfdEntry); + return false; + } } } diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs new file mode 100644 index 000000000..824bbc3b5 --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs @@ -0,0 +1,84 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System.IO; + using Xunit; + + using ImageSharp.Formats; + + public class TiffDecoderImageTests + { + public const int ImageWidth = 200; + public const int ImageHeight = 150; + + public static object[][] IsLittleEndianValues = new[] { new object[] { false }, + new object[] { true } }; + + [Theory] + [MemberData(nameof(IsLittleEndianValues))] + public void DecodeImage_SetsImageDimensions(bool isLittleEndian) + { + Stream stream = CreateTiffGenIfd() + .ToStream(isLittleEndian); + + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); + TiffIfd ifd = decoder.ReadIfd(0); + Image image = new Image(1,1); + + decoder.DecodeImage(ifd, image); + + Assert.Equal(ImageWidth, image.Width); + Assert.Equal(ImageHeight, image.Height); + } + + [Theory] + [MemberData(nameof(IsLittleEndianValues))] + public void DecodeImage_ThrowsException_WithMissingImageWidth(bool isLittleEndian) + { + Stream stream = CreateTiffGenIfd() + .WithoutEntry(TiffTags.ImageWidth) + .ToStream(isLittleEndian); + + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); + TiffIfd ifd = decoder.ReadIfd(0); + Image image = new Image(1,1); + + var e = Assert.Throws(() => decoder.DecodeImage(ifd, image)); + + Assert.Equal("The TIFF IFD does not specify the image dimensions.", e.Message); + } + + [Theory] + [MemberData(nameof(IsLittleEndianValues))] + public void DecodeImage_ThrowsException_WithMissingImageLength(bool isLittleEndian) + { + Stream stream = CreateTiffGenIfd() + .WithoutEntry(TiffTags.ImageLength) + .ToStream(isLittleEndian); + + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); + TiffIfd ifd = decoder.ReadIfd(0); + Image image = new Image(1,1); + + var e = Assert.Throws(() => decoder.DecodeImage(ifd, image)); + + Assert.Equal("The TIFF IFD does not specify the image dimensions.", e.Message); + } + + private TiffGenIfd CreateTiffGenIfd() + { + return new TiffGenIfd() + { + Entries = + { + TiffGenEntry.Integer(TiffTags.ImageWidth, TiffType.Long, ImageWidth), + TiffGenEntry.Integer(TiffTags.ImageLength, TiffType.Long, ImageHeight), + } + }; + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryTests.cs new file mode 100644 index 000000000..08b7dc8eb --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryTests.cs @@ -0,0 +1,26 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System.IO; + using Xunit; + + using ImageSharp.Formats; + + public class TiffIfdEntryTests + { + [Fact] + public void Constructor_SetsProperties() + { + var entry = new TiffIfdEntry((ushort)10u, TiffType.Short, 20u, new byte[] { 2, 4, 6, 8 }); + + Assert.Equal(10u, entry.Tag); + Assert.Equal(TiffType.Short, entry.Type); + Assert.Equal(20u, entry.Count); + Assert.Equal(new byte[] { 2, 4, 6, 8 }, entry.Value); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs new file mode 100644 index 000000000..d9f8425cb --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs @@ -0,0 +1,61 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System.IO; + using Xunit; + + using ImageSharp.Formats; + + public class TiffIfdTests + { + [Fact] + public void Constructor_SetsProperties() + { + var entries = new TiffIfdEntry[10]; + var ifd = new TiffIfd(entries, 1234u); + + Assert.Equal(entries, ifd.Entries); + Assert.Equal(1234u, ifd.NextIfdOffset); + } + + [Fact] + public void TryGetIfdEntry_ReturnsIfdIfExists() + { + var entries = new[] + { + new TiffIfdEntry(10, TiffType.Short, 20, new byte[4]), + new TiffIfdEntry(20, TiffType.Short, 20, new byte[4]), + new TiffIfdEntry(30, TiffType.Short, 20, new byte[4]), + new TiffIfdEntry(40, TiffType.Short, 20, new byte[4]) + }; + var ifd = new TiffIfd(entries, 1234u); + + bool success = ifd.TryGetIfdEntry(30, out var entry); + + Assert.Equal(true, success); + Assert.Equal(30, entry.Tag); + } + + [Fact] + public void TryGetIfdEntry_ReturnsFalseOtherwise() + { + var entries = new[] + { + new TiffIfdEntry(10, TiffType.Short, 20, new byte[4]), + new TiffIfdEntry(20, TiffType.Short, 20, new byte[4]), + new TiffIfdEntry(30, TiffType.Short, 20, new byte[4]), + new TiffIfdEntry(40, TiffType.Short, 20, new byte[4]) + }; + var ifd = new TiffIfd(entries, 1234u); + + bool success = ifd.TryGetIfdEntry(25, out var entry); + + Assert.Equal(false, success); + Assert.Equal(0, entry.Tag); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfdExtensions.cs b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfdExtensions.cs new file mode 100644 index 000000000..84ea0f1ac --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfdExtensions.cs @@ -0,0 +1,24 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System; + using System.IO; + using System.Linq; + + /// + /// A utility class for manipulating in-memory Tiff files for use in unit tests. + /// + internal static class TiffGenIfdExtensions + { + public static TiffGenIfd WithoutEntry(this TiffGenIfd ifd, ushort tag) + { + TiffGenEntry entry = ifd.Entries.First(e => e.Tag == tag); + ifd.Entries.Remove(entry); + return ifd; + } + } +} \ No newline at end of file From d5e85a621f9b1db62264ed9229bd97a35419737c Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Wed, 29 Mar 2017 20:44:20 +0100 Subject: [PATCH 025/275] Decode TIFF image resolution --- .../Tiff/Constants/TiffResolutionUnit.cs | 2 +- .../Formats/Tiff/TiffDecoderCore.cs | 20 +++++ .../Formats/Tiff/TiffIfd/TiffIfd.cs | 24 ++++-- .../Formats/Tiff/TiffDecoderImageTests.cs | 76 ++++++++++++++++--- .../Formats/Tiff/TiffIfd/TiffIfdTests.cs | 35 +++++++++ .../TestUtilities/Tiff/TiffGenEntry.cs | 30 +++++++- .../Tiff/TiffGenIfdExtensions.cs | 15 +++- 7 files changed, 180 insertions(+), 22 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs index 582c47644..307f9b9d2 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs @@ -23,6 +23,6 @@ namespace ImageSharp.Formats /// /// Centimeter. /// - Centimeter = 2 + Centimeter = 3 } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index eb4c6b1c8..28d45a6e1 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -170,6 +170,26 @@ namespace ImageSharp.Formats int height = (int)this.ReadUnsignedInteger(ref imageLengthEntry); image.InitPixels(width, height); + + TiffResolutionUnit resolutionUnit = TiffResolutionUnit.Inch; + if (ifd.TryGetIfdEntry(TiffTags.ResolutionUnit, out TiffIfdEntry resolutionUnitEntry)) + { + resolutionUnit = (TiffResolutionUnit)this.ReadUnsignedInteger(ref resolutionUnitEntry); + } + + double resolutionUnitFactor = resolutionUnit == TiffResolutionUnit.Centimeter ? 1.0 / 2.54 : 1.0; + + if (ifd.TryGetIfdEntry(TiffTags.XResolution, out TiffIfdEntry xResolutionEntry)) + { + Rational xResolution = this.ReadUnsignedRational(ref xResolutionEntry); + image.MetaData.HorizontalResolution = xResolution.ToDouble(); + } + + if (ifd.TryGetIfdEntry(TiffTags.YResolution, out TiffIfdEntry yResolutionEntry)) + { + Rational yResolution = this.ReadUnsignedRational(ref yResolutionEntry); + image.MetaData.VerticalResolution = yResolution.ToDouble(); + } } /// diff --git a/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfd.cs b/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfd.cs index 2206e97f3..d2071aff9 100644 --- a/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfd.cs +++ b/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfd.cs @@ -35,21 +35,31 @@ namespace ImageSharp.Formats /// Gets the child with the specified tag ID. /// /// The tag ID to search for. - /// The resulting , if it exists. - /// A flag indicating whether the requested entry exists - public bool TryGetIfdEntry(ushort tag, out TiffIfdEntry entry) + /// The resulting , or null if it does not exists. + public TiffIfdEntry? GetIfdEntry(ushort tag) { for (int i = 0; i < this.Entries.Length; i++) { if (this.Entries[i].Tag == tag) { - entry = this.Entries[i]; - return true; + return this.Entries[i]; } } - entry = default(TiffIfdEntry); - return false; + return null; + } + + /// + /// Gets the child with the specified tag ID. + /// + /// The tag ID to search for. + /// The resulting , if it exists. + /// A flag indicating whether the requested entry exists. + public bool TryGetIfdEntry(ushort tag, out TiffIfdEntry entry) + { + TiffIfdEntry? nullableEntry = this.GetIfdEntry(tag); + entry = nullableEntry ?? default(TiffIfdEntry); + return nullableEntry.HasValue; } } } diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs index 824bbc3b5..b0a97102f 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs @@ -14,6 +14,8 @@ namespace ImageSharp.Tests { public const int ImageWidth = 200; public const int ImageHeight = 150; + public const int XResolution = 100; + public const int YResolution = 200; public static object[][] IsLittleEndianValues = new[] { new object[] { false }, new object[] { true } }; @@ -27,14 +29,67 @@ namespace ImageSharp.Tests TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); TiffIfd ifd = decoder.ReadIfd(0); - Image image = new Image(1,1); + Image image = new Image(1, 1); decoder.DecodeImage(ifd, image); - + Assert.Equal(ImageWidth, image.Width); Assert.Equal(ImageHeight, image.Height); } + [Theory] + [InlineData(false, 150u, 1u, 200u, 1u, 2u /* Inch */, 150.0, 200.0)] + [InlineData(false, 150u, 1u, 200u, 1u, 3u /* Cm */, 150.0 / 2.54, 200.0 / 2.54)] + [InlineData(false, 150u, 1u, 200u, 1u, 1u /* None */, 96.0, 96.0)] + [InlineData(false, 150u, 1u, 200u, 1u, null /* Inch */, 150.0, 200.0)] + [InlineData(false, 5u, 2u, 9u, 4u, 2u /* Inch */, 2.5, 2.25)] + [InlineData(false, null, null, null, null, null /* Inch */, 96.0, 96.0)] + [InlineData(false, 150u, 1u, null, null, 2u /* Inch */, 150.0, 96.0)] + [InlineData(false, null, null, 200u, 1u, 2u /* Inch */, 96.0, 200.0)] + [InlineData(true, 150u, 1u, 200u, 1u, 2u /* Inch */, 150.0, 200.0)] + [InlineData(true, 150u, 1u, 200u, 1u, 3u /* Cm */, 150.0 / 2.54, 200.0 / 2.54)] + [InlineData(true, 150u, 1u, 200u, 1u, 1u /* None */, 96.0, 96.0)] + [InlineData(false, 5u, 2u, 9u, 4u, 2u /* Inch */, 2.5, 2.25)] + [InlineData(true, 150u, 1u, 200u, 1u, null /* Inch */, 150.0, 200.0)] + [InlineData(true, null, null, null, null, null /* Inch */, 96.0, 96.0)] + [InlineData(true, 150u, 1u, null, null, 2u /* Inch */, 150.0, 96.0)] + [InlineData(true, null, null, 200u, 1u, 2u /* Inch */, 96.0, 200.0)] + public void DecodeImage_SetsImageResolution(bool isLittleEndian, uint? xResolutionNumerator, uint? xResolutionDenominator, + uint? yResolutionNumerator, uint? yResolutionDenominator, uint? resolutionUnit, + double expectedHorizonalResolution, double expectedVerticalResolution) + { + TiffGenIfd ifdGen = CreateTiffGenIfd() + .WithoutEntry(TiffTags.XResolution) + .WithoutEntry(TiffTags.YResolution) + .WithoutEntry(TiffTags.ResolutionUnit); + + if (xResolutionNumerator != null) + { + ifdGen.WithEntry(TiffGenEntry.Rational(TiffTags.XResolution, xResolutionNumerator.Value, xResolutionDenominator.Value)); + } + + if (yResolutionNumerator != null) + { + ifdGen.WithEntry(TiffGenEntry.Rational(TiffTags.YResolution, yResolutionNumerator.Value, yResolutionDenominator.Value)); + } + + if (resolutionUnit != null) + { + ifdGen.WithEntry(TiffGenEntry.Integer(TiffTags.ResolutionUnit, TiffType.Short, resolutionUnit.Value)); + } + + Stream stream = ifdGen.ToStream(isLittleEndian); + + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); + TiffIfd ifd = decoder.ReadIfd(0); + Image image = new Image(1, 1); + + decoder.DecodeImage(ifd, image); + + Assert.Equal(expectedHorizonalResolution, image.MetaData.HorizontalResolution); + Assert.Equal(expectedVerticalResolution, image.MetaData.VerticalResolution); + } + [Theory] [MemberData(nameof(IsLittleEndianValues))] public void DecodeImage_ThrowsException_WithMissingImageWidth(bool isLittleEndian) @@ -45,8 +100,8 @@ namespace ImageSharp.Tests TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); TiffIfd ifd = decoder.ReadIfd(0); - Image image = new Image(1,1); - + Image image = new Image(1, 1); + var e = Assert.Throws(() => decoder.DecodeImage(ifd, image)); Assert.Equal("The TIFF IFD does not specify the image dimensions.", e.Message); @@ -62,8 +117,8 @@ namespace ImageSharp.Tests TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); TiffIfd ifd = decoder.ReadIfd(0); - Image image = new Image(1,1); - + Image image = new Image(1, 1); + var e = Assert.Throws(() => decoder.DecodeImage(ifd, image)); Assert.Equal("The TIFF IFD does not specify the image dimensions.", e.Message); @@ -72,13 +127,16 @@ namespace ImageSharp.Tests private TiffGenIfd CreateTiffGenIfd() { return new TiffGenIfd() - { - Entries = + { + Entries = { TiffGenEntry.Integer(TiffTags.ImageWidth, TiffType.Long, ImageWidth), TiffGenEntry.Integer(TiffTags.ImageLength, TiffType.Long, ImageHeight), + TiffGenEntry.Rational(TiffTags.XResolution, XResolution, 1), + TiffGenEntry.Rational(TiffTags.YResolution, YResolution, 1), + TiffGenEntry.Integer(TiffTags.ResolutionUnit, TiffType.Short, 2) } - }; + }; } } } \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs index d9f8425cb..c68047539 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs @@ -22,6 +22,41 @@ namespace ImageSharp.Tests Assert.Equal(1234u, ifd.NextIfdOffset); } + [Fact] + public void GetIfdEntry_ReturnsIfdIfExists() + { + var entries = new[] + { + new TiffIfdEntry(10, TiffType.Short, 20, new byte[4]), + new TiffIfdEntry(20, TiffType.Short, 20, new byte[4]), + new TiffIfdEntry(30, TiffType.Short, 20, new byte[4]), + new TiffIfdEntry(40, TiffType.Short, 20, new byte[4]) + }; + var ifd = new TiffIfd(entries, 1234u); + + TiffIfdEntry? entry = ifd.GetIfdEntry(30); + + Assert.Equal(true, entry.HasValue); + Assert.Equal(30, entry.Value.Tag); + } + + [Fact] + public void GetIfdEntry_ReturnsNullOtherwise() + { + var entries = new[] + { + new TiffIfdEntry(10, TiffType.Short, 20, new byte[4]), + new TiffIfdEntry(20, TiffType.Short, 20, new byte[4]), + new TiffIfdEntry(30, TiffType.Short, 20, new byte[4]), + new TiffIfdEntry(40, TiffType.Short, 20, new byte[4]) + }; + var ifd = new TiffIfd(entries, 1234u); + + TiffIfdEntry? entry = ifd.GetIfdEntry(25); + + Assert.Equal(false, entry.HasValue); + } + [Fact] public void TryGetIfdEntry_ReturnsIfdIfExists() { diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs index c0bb9d78c..2065e8501 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs @@ -41,7 +41,7 @@ namespace ImageSharp.Tests public static TiffGenEntry Integer(ushort tag, TiffType type, int value) { - return TiffGenEntry.Integer(tag, type, new int[] {value}); + return TiffGenEntry.Integer(tag, type, new int[] { value }); } public static TiffGenEntry Integer(ushort tag, TiffType type, int[] value) @@ -55,7 +55,7 @@ namespace ImageSharp.Tests public static TiffGenEntry Integer(ushort tag, TiffType type, uint value) { - return TiffGenEntry.Integer(tag, type, new uint[] {value}); + return TiffGenEntry.Integer(tag, type, new uint[] { value }); } public static TiffGenEntry Integer(ushort tag, TiffType type, uint[] value) @@ -67,6 +67,11 @@ namespace ImageSharp.Tests return new TiffGenEntryUnsignedInteger(tag, type, value); } + public static TiffGenEntry Rational(ushort tag, uint numerator, uint denominator) + { + return new TiffGenEntryRational(tag, numerator, denominator); + } + private class TiffGenEntryAscii : TiffGenEntry { public TiffGenEntryAscii(ushort tag, string value) : base(tag, TiffType.Ascii, (uint)GetBytes(value).Length) @@ -176,5 +181,26 @@ namespace ImageSharp.Tests } } } + + private class TiffGenEntryRational : TiffGenEntry + { + public TiffGenEntryRational(ushort tag, uint numerator, uint denominator) : base(tag, TiffType.Rational, 1u) + { + this.Numerator = numerator; + this.Denominator = denominator; + } + + public uint Numerator { get; } + + public uint Denominator { get; } + + public override IEnumerable GetData(bool isLittleEndian) + { + byte[] numeratorBytes = BitConverter.GetBytes(Numerator).WithByteOrder(isLittleEndian); + byte[] denominatorBytes = BitConverter.GetBytes(Denominator).WithByteOrder(isLittleEndian); + byte[] bytes = Enumerable.Concat(numeratorBytes, denominatorBytes).ToArray(); + return new[] { new TiffGenDataBlock(bytes) }; + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfdExtensions.cs b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfdExtensions.cs index 84ea0f1ac..c44291640 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfdExtensions.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfdExtensions.cs @@ -5,8 +5,6 @@ namespace ImageSharp.Tests { - using System; - using System.IO; using System.Linq; /// @@ -17,7 +15,18 @@ namespace ImageSharp.Tests public static TiffGenIfd WithoutEntry(this TiffGenIfd ifd, ushort tag) { TiffGenEntry entry = ifd.Entries.First(e => e.Tag == tag); - ifd.Entries.Remove(entry); + if (entry != null) + { + ifd.Entries.Remove(entry); + } + return ifd; + } + + public static TiffGenIfd WithEntry(this TiffGenIfd ifd, TiffGenEntry entry) + { + ifd.WithoutEntry(entry.Tag); + ifd.Entries.Add(entry); + return ifd; } } From 738f62bf9cc32c25d48876b3f629e624eb019dbe Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Sat, 1 Apr 2017 15:58:48 +0100 Subject: [PATCH 026/275] Implement image decoding for the most basic TIFF image format. --- .../Tiff/Compression/NoneTiffCompression.cs | 28 +++ .../Tiff/Compression/TiffCompressionType.cs | 18 ++ .../TiffColorType.cs | 18 ++ .../WhiteIsZero8TiffColor.cs | 45 ++++ .../Formats/Tiff/TiffDecoderCore.cs | 226 +++++++++++++++--- .../Formats/Tiff/Utils/TiffUtils.cs | 38 +++ .../Compression/NoneTiffCompressionTests.cs | 28 +++ .../PhotometricInterpretationTestBase.cs | 59 +++++ .../WhiteIsZero8TiffColorTests.cs | 51 ++++ .../Formats/Tiff/TiffDecoderImageTests.cs | 190 ++++++++++++++- .../Tiff/TiffGenIfdExtensions.cs | 2 +- 11 files changed, 660 insertions(+), 43 deletions(-) create mode 100644 src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs create mode 100644 src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs create mode 100644 src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs create mode 100644 src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs create mode 100644 src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColorTests.cs diff --git a/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs new file mode 100644 index 000000000..c538cf473 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs @@ -0,0 +1,28 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + using System.IO; + using System.Runtime.CompilerServices; + + /// + /// Class to handle cases where TIFF image data is not compressed. + /// + internal static class NoneTiffCompression + { + /// + /// Decompresses image data into the supplied buffer. + /// + /// The to read image data from. + /// The number of bytes to read from the input stream. + /// The output buffer for uncompressed data. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Decompress(Stream stream, int byteCount, byte[] buffer) + { + stream.ReadFull(buffer, byteCount); + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs new file mode 100644 index 000000000..5b6368bf9 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs @@ -0,0 +1,18 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + /// + /// Provides enumeration of the various TIFF compression types. + /// + internal enum TiffCompressionType + { + /// + /// Image data is stored uncompressed in the TIFF file. + /// + None = 0 + } +} diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs new file mode 100644 index 000000000..bca27e4b2 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs @@ -0,0 +1,18 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + /// + /// Provides enumeration of the various TIFF photometric interpretation implementation types. + /// + internal enum TiffColorType + { + /// + /// Grayscale: 0 is imaged as white. The maximum value is imaged as black. Optimised implementation for 8-bit images. + /// + WhiteIsZero8 + } +} diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs new file mode 100644 index 000000000..295db5e18 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs @@ -0,0 +1,45 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + using System.Runtime.CompilerServices; + using ImageSharp; + + /// + /// Implements the 'WhiteIsZero' photometric interpretation (optimised for 8-bit grayscale images). + /// + internal static class WhiteIsZero8TiffColor + { + /// + /// Decodes pixel data using the current photometric interpretation. + /// + /// The pixel format. + /// The buffer to read image data from. + /// The image buffer to write pixels to. + /// The x-coordinate of the left-hand side of the image block. + /// The y-coordinate of the top of the image block. + /// The width of the image block. + /// The height of the image block. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Decode(byte[] data, PixelAccessor pixels, int left, int top, int width, int height) + where TColor : struct, IPixel + { + TColor color = default(TColor); + + uint offset = 0; + + for (int y = top; y < top + height; y++) + { + for (int x = left; x < left + width; x++) + { + byte intensity = (byte)(255 - data[offset++]); + color.PackFromBytes(intensity, intensity, intensity, 255); + pixels[x, y] = color; + } + } + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 28d45a6e1..f186e33ee 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -6,6 +6,7 @@ namespace ImageSharp.Formats { using System; + using System.Buffers; using System.IO; using System.Text; @@ -41,6 +42,16 @@ namespace ImageSharp.Formats this.IsLittleEndian = isLittleEndian; } + /// + /// Gets or sets the photometric interpretation implementation to use when decoding the image. + /// + public TiffColorType ColorType { get; set; } + + /// + /// Gets or sets the compression implementation to use when decoding the image. + /// + public TiffCompressionType CompressionType { get; set; } + /// /// Gets the input stream. /// @@ -93,7 +104,7 @@ namespace ImageSharp.Formats public uint ReadHeader() { byte[] headerBytes = new byte[TiffConstants.SizeOfTiffHeader]; - this.ReadBytes(headerBytes, TiffConstants.SizeOfTiffHeader); + this.InputStream.ReadFull(headerBytes, TiffConstants.SizeOfTiffHeader); if (headerBytes[0] == TiffConstants.ByteOrderLittleEndian && headerBytes[1] == TiffConstants.ByteOrderLittleEndian) { @@ -129,13 +140,13 @@ namespace ImageSharp.Formats byte[] buffer = new byte[TiffConstants.SizeOfIfdEntry]; - this.ReadBytes(buffer, 2); + this.InputStream.ReadFull(buffer, 2); ushort entryCount = this.ToUInt16(buffer, 0); TiffIfdEntry[] entries = new TiffIfdEntry[entryCount]; for (int i = 0; i < entryCount; i++) { - this.ReadBytes(buffer, TiffConstants.SizeOfIfdEntry); + this.InputStream.ReadFull(buffer, TiffConstants.SizeOfIfdEntry); ushort tag = this.ToUInt16(buffer, 0); TiffType type = (TiffType)this.ToUInt16(buffer, 2); @@ -145,7 +156,7 @@ namespace ImageSharp.Formats entries[i] = new TiffIfdEntry(tag, type, count, value); } - this.ReadBytes(buffer, 4); + this.InputStream.ReadFull(buffer, 4); uint nextIfdOffset = this.ToUInt32(buffer, 0); return new TiffIfd(entries, nextIfdOffset); @@ -177,18 +188,184 @@ namespace ImageSharp.Formats resolutionUnit = (TiffResolutionUnit)this.ReadUnsignedInteger(ref resolutionUnitEntry); } - double resolutionUnitFactor = resolutionUnit == TiffResolutionUnit.Centimeter ? 1.0 / 2.54 : 1.0; + if (resolutionUnit != TiffResolutionUnit.None) + { + double resolutionUnitFactor = resolutionUnit == TiffResolutionUnit.Centimeter ? 2.54 : 1.0; + + if (ifd.TryGetIfdEntry(TiffTags.XResolution, out TiffIfdEntry xResolutionEntry)) + { + Rational xResolution = this.ReadUnsignedRational(ref xResolutionEntry); + image.MetaData.HorizontalResolution = xResolution.ToDouble() * resolutionUnitFactor; + } + + if (ifd.TryGetIfdEntry(TiffTags.YResolution, out TiffIfdEntry yResolutionEntry)) + { + Rational yResolution = this.ReadUnsignedRational(ref yResolutionEntry); + image.MetaData.VerticalResolution = yResolution.ToDouble() * resolutionUnitFactor; + } + } + + this.ReadImageFormat(ifd); + + if (ifd.TryGetIfdEntry(TiffTags.RowsPerStrip, out TiffIfdEntry rowsPerStripEntry) + && ifd.TryGetIfdEntry(TiffTags.StripOffsets, out TiffIfdEntry stripOffsetsEntry) + && ifd.TryGetIfdEntry(TiffTags.StripByteCounts, out TiffIfdEntry stripByteCountsEntry)) + { + int rowsPerStrip = (int)this.ReadUnsignedInteger(ref rowsPerStripEntry); + uint[] stripOffsets = this.ReadUnsignedIntegerArray(ref stripOffsetsEntry); + uint[] stripByteCounts = this.ReadUnsignedIntegerArray(ref stripByteCountsEntry); + + int uncompressedStripSize = this.CalculateImageBufferSize(width, rowsPerStrip); + + using (PixelAccessor pixels = image.Lock()) + { + byte[] stripBytes = ArrayPool.Shared.Rent(uncompressedStripSize); + + try + { + this.DecompressImageBlock(stripOffsets[0], stripByteCounts[0], stripBytes); + this.ProcessImageBlock(stripBytes, pixels, 0, 0, width, rowsPerStrip); + } + finally + { + ArrayPool.Shared.Return(stripBytes); + } + } + } + } + + /// + /// Determines the TIFF compression and color types, and reads any associated parameters. + /// + /// The IFD to read the image format information for. + public void ReadImageFormat(TiffIfd ifd) + { + TiffCompression compression = TiffCompression.None; - if (ifd.TryGetIfdEntry(TiffTags.XResolution, out TiffIfdEntry xResolutionEntry)) + if (ifd.TryGetIfdEntry(TiffTags.Compression, out TiffIfdEntry compressionEntry)) { - Rational xResolution = this.ReadUnsignedRational(ref xResolutionEntry); - image.MetaData.HorizontalResolution = xResolution.ToDouble(); + compression = (TiffCompression)this.ReadUnsignedInteger(ref compressionEntry); } - - if (ifd.TryGetIfdEntry(TiffTags.YResolution, out TiffIfdEntry yResolutionEntry)) + + switch (compression) { - Rational yResolution = this.ReadUnsignedRational(ref yResolutionEntry); - image.MetaData.VerticalResolution = yResolution.ToDouble(); + case TiffCompression.None: + { + this.CompressionType = TiffCompressionType.None; + break; + } + + default: + { + throw new NotSupportedException("The specified TIFF compression format is not supported."); + } + } + + TiffPhotometricInterpretation photometricInterpretation; + + if (ifd.TryGetIfdEntry(TiffTags.PhotometricInterpretation, out TiffIfdEntry photometricInterpretationEntry)) + { + photometricInterpretation = (TiffPhotometricInterpretation)this.ReadUnsignedInteger(ref photometricInterpretationEntry); + } + else + { + if (compression == TiffCompression.Ccitt1D) + { + photometricInterpretation = TiffPhotometricInterpretation.WhiteIsZero; + } + else + { + throw new ImageFormatException("The TIFF photometric interpretation entry is missing."); + } + } + + switch (photometricInterpretation) + { + case TiffPhotometricInterpretation.WhiteIsZero: + { + if (ifd.TryGetIfdEntry(TiffTags.BitsPerSample, out TiffIfdEntry bitsPerSampleEntry)) + { + uint[] bitsPerSample = this.ReadUnsignedIntegerArray(ref bitsPerSampleEntry); + + if (bitsPerSample.Length == 1 && bitsPerSample[0] == 8) + { + this.ColorType = TiffColorType.WhiteIsZero8; + } + else + { + throw new NotSupportedException("The specified TIFF bit-depth is not supported."); + } + } + else + { + throw new NotSupportedException("TIFF bilevel images are not supported."); + } + + break; + } + + default: + throw new NotSupportedException("The specified TIFF photometric interpretation is not supported."); + } + } + + /// + /// Calculates the size (in bytes) for a pixel buffer using the determined color format. + /// + /// The width for the desired pixel buffer. + /// The height for the desired pixel buffer. + /// The size (in bytes) of the required pixel buffer. + public int CalculateImageBufferSize(int width, int height) + { + switch (this.ColorType) + { + case TiffColorType.WhiteIsZero8: + return width * height; + default: + throw new InvalidOperationException(); + } + } + + /// + /// Decompresses an image block from the input stream into the specified buffer. + /// + /// The offset within the file of the image block. + /// The size (in bytes) of the compressed data. + /// The buffer to write the uncompressed data. + public void DecompressImageBlock(uint offset, uint byteCount, byte[] buffer) + { + this.InputStream.Seek(offset, SeekOrigin.Begin); + + switch (this.CompressionType) + { + case TiffCompressionType.None: + NoneTiffCompression.Decompress(this.InputStream, (int)byteCount, buffer); + break; + default: + throw new InvalidOperationException(); + } + } + + /// + /// Decodes pixel data using the current photometric interpretation. + /// + /// The pixel format. + /// The buffer to read image data from. + /// The image buffer to write pixels to. + /// The x-coordinate of the left-hand side of the image block. + /// The y-coordinate of the top of the image block. + /// The width of the image block. + /// The height of the image block. + public void ProcessImageBlock(byte[] data, PixelAccessor pixels, int left, int top, int width, int height) + where TColor : struct, IPixel + { + switch (this.ColorType) + { + case TiffColorType.WhiteIsZero8: + WhiteIsZero8TiffColor.Decode(data, pixels, left, top, width, height); + break; + default: + throw new InvalidOperationException(); } } @@ -207,7 +384,7 @@ namespace ImageSharp.Formats this.InputStream.Seek(offset, SeekOrigin.Begin); byte[] data = new byte[byteLength]; - this.ReadBytes(data, (int)byteLength); + this.InputStream.ReadFull(data, (int)byteLength); entry.Value = data; } @@ -621,29 +798,6 @@ namespace ImageSharp.Formats } } - /// - /// Reads a sequence of bytes from the input stream into a buffer. - /// - /// A buffer to store the retrieved data. - /// The number of bytes to read. - private void ReadBytes(byte[] buffer, int count) - { - int offset = 0; - - while (count > 0) - { - int bytesRead = this.InputStream.Read(buffer, offset, count); - - if (bytesRead == 0) - { - break; - } - - offset += bytesRead; - count -= bytesRead; - } - } - /// /// Converts buffer data into an using the correct endianness. /// diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs new file mode 100644 index 000000000..e4049cf0f --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs @@ -0,0 +1,38 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// +namespace ImageSharp.Formats +{ + using System.IO; + + /// + /// TIFF specific utilities and extension methods. + /// + internal static class TiffUtils + { + /// + /// Reads a sequence of bytes from the input stream into a buffer. + /// + /// The stream to read from. + /// A buffer to store the retrieved data. + /// The number of bytes to read. + public static void ReadFull(this Stream stream, byte[] buffer, int count) + { + int offset = 0; + + while (count > 0) + { + int bytesRead = stream.Read(buffer, offset, count); + + if (bytesRead == 0) + { + break; + } + + offset += bytesRead; + count -= bytesRead; + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs new file mode 100644 index 000000000..e3277eb96 --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs @@ -0,0 +1,28 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System.IO; + using Xunit; + + using ImageSharp.Formats; + + public class NoneTiffCompressionTests + { + [Theory] + [InlineData(new byte[] { 10, 15, 20, 25, 30, 35, 40, 45 }, 8, new byte[] { 10, 15, 20, 25, 30, 35, 40, 45 })] + [InlineData(new byte[] { 10, 15, 20, 25, 30, 35, 40, 45 }, 5, new byte[] { 10, 15, 20, 25, 30 })] + public void Decompress_ReadsData(byte[] inputData, int byteCount, byte[] expectedResult) + { + Stream stream = new MemoryStream(inputData); + byte[] buffer = new byte[expectedResult.Length]; + + NoneTiffCompression.Decompress(stream, byteCount, buffer); + + Assert.Equal(expectedResult, buffer); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs new file mode 100644 index 000000000..7fdb12177 --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs @@ -0,0 +1,59 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System; + using Xunit; + + public abstract class PhotometricInterpretationTestBase + { + public static Color[][] Offset(Color[][] input, int xOffset, int yOffset, int width, int height) + { + int inputHeight = input.Length; + int inputWidth = input[0].Length; + + Color[][] output = new Color[height][]; + + for (int y = 0; y < output.Length; y++) + { + output[y] = new Color[width]; + } + + for (int y = 0; y < inputHeight; y++) + { + for (int x = 0; x < inputWidth; x++) + { + output[y + yOffset][x + xOffset] = input[y][x]; + } + } + + return output; + } + + public static void AssertDecode(Color[][] expectedResult, Action> decodeAction) + { + int resultWidth = expectedResult[0].Length; + int resultHeight = expectedResult.Length; + Image image = new Image(resultWidth, resultHeight); + + using (PixelAccessor pixels = image.Lock()) + { + decodeAction(pixels); + } + + using (PixelAccessor pixels = image.Lock()) + { + for (int y = 0; y < resultHeight; y++) + { + for (int x = 0; x < resultWidth; x++) + { + Assert.Equal(expectedResult[y][x], pixels[x, y]); + } + } + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColorTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColorTests.cs new file mode 100644 index 000000000..075881f61 --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColorTests.cs @@ -0,0 +1,51 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System.Collections.Generic; + using Xunit; + + using ImageSharp.Formats; + + public class WhiteIsZero8TiffColorTests : PhotometricInterpretationTestBase + { + private static Color Gray000 = new Color(255, 255, 255, 255); + private static Color Gray128 = new Color(127, 127, 127, 255); + private static Color Gray255 = new Color(0, 0, 0, 255); + + private static byte[] GrayscaleBytes4x4 = new byte[] { 128, 255, 000, 255, + 255, 255, 255, 255, + 000, 128, 128, 255, + 255, 000, 255, 128 }; + + private static Color[][] GrayscaleResult4x4 = new[] { new[] { Gray128, Gray255, Gray000, Gray255 }, + new[] { Gray255, Gray255, Gray255, Gray255 }, + new[] { Gray000, Gray128, Gray128, Gray255 }, + new[] { Gray255, Gray000, Gray255, Gray128 }}; + + public static IEnumerable DecodeData + { + get + { + yield return new object[] { GrayscaleBytes4x4, 0, 0, 4, 4, GrayscaleResult4x4 }; + yield return new object[] { GrayscaleBytes4x4, 0, 0, 4, 4, Offset(GrayscaleResult4x4, 0, 0, 6, 6) }; + yield return new object[] { GrayscaleBytes4x4, 1, 0, 4, 4, Offset(GrayscaleResult4x4, 1, 0, 6, 6) }; + yield return new object[] { GrayscaleBytes4x4, 0, 1, 4, 4, Offset(GrayscaleResult4x4, 0, 1, 6, 6) }; + yield return new object[] { GrayscaleBytes4x4, 1, 1, 4, 4, Offset(GrayscaleResult4x4, 1, 1, 6, 6) }; + } + } + + [Theory] + [MemberData(nameof(DecodeData))] + public void Decode_WritesPixelData(byte[] inputData, int left, int top, int width, int height, Color[][] expectedResult) + { + AssertDecode(expectedResult, pixels => + { + WhiteIsZero8TiffColor.Decode(inputData, pixels, left, top, width, height); + }); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs index b0a97102f..34a0c2e4c 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs @@ -5,6 +5,7 @@ namespace ImageSharp.Tests { + using System; using System.IO; using Xunit; @@ -39,7 +40,7 @@ namespace ImageSharp.Tests [Theory] [InlineData(false, 150u, 1u, 200u, 1u, 2u /* Inch */, 150.0, 200.0)] - [InlineData(false, 150u, 1u, 200u, 1u, 3u /* Cm */, 150.0 / 2.54, 200.0 / 2.54)] + [InlineData(false, 150u, 1u, 200u, 1u, 3u /* Cm */, 150.0 * 2.54, 200.0 * 2.54)] [InlineData(false, 150u, 1u, 200u, 1u, 1u /* None */, 96.0, 96.0)] [InlineData(false, 150u, 1u, 200u, 1u, null /* Inch */, 150.0, 200.0)] [InlineData(false, 5u, 2u, 9u, 4u, 2u /* Inch */, 2.5, 2.25)] @@ -47,9 +48,9 @@ namespace ImageSharp.Tests [InlineData(false, 150u, 1u, null, null, 2u /* Inch */, 150.0, 96.0)] [InlineData(false, null, null, 200u, 1u, 2u /* Inch */, 96.0, 200.0)] [InlineData(true, 150u, 1u, 200u, 1u, 2u /* Inch */, 150.0, 200.0)] - [InlineData(true, 150u, 1u, 200u, 1u, 3u /* Cm */, 150.0 / 2.54, 200.0 / 2.54)] + [InlineData(true, 150u, 1u, 200u, 1u, 3u /* Cm */, 150.0 * 2.54, 200.0 * 2.54)] [InlineData(true, 150u, 1u, 200u, 1u, 1u /* None */, 96.0, 96.0)] - [InlineData(false, 5u, 2u, 9u, 4u, 2u /* Inch */, 2.5, 2.25)] + [InlineData(true, 5u, 2u, 9u, 4u, 2u /* Inch */, 2.5, 2.25)] [InlineData(true, 150u, 1u, 200u, 1u, null /* Inch */, 150.0, 200.0)] [InlineData(true, null, null, null, null, null /* Inch */, 96.0, 96.0)] [InlineData(true, 150u, 1u, null, null, 2u /* Inch */, 150.0, 96.0)] @@ -86,8 +87,8 @@ namespace ImageSharp.Tests decoder.DecodeImage(ifd, image); - Assert.Equal(expectedHorizonalResolution, image.MetaData.HorizontalResolution); - Assert.Equal(expectedVerticalResolution, image.MetaData.VerticalResolution); + Assert.Equal(expectedHorizonalResolution, image.MetaData.HorizontalResolution, 10); + Assert.Equal(expectedVerticalResolution, image.MetaData.VerticalResolution, 10); } [Theory] @@ -124,6 +125,180 @@ namespace ImageSharp.Tests Assert.Equal("The TIFF IFD does not specify the image dimensions.", e.Message); } + [Theory] + [InlineData(false, TiffCompression.None, TiffCompressionType.None)] + [InlineData(true, TiffCompression.None, TiffCompressionType.None)] + public void ReadImageFormat_DeterminesCorrectCompressionImplementation(bool isLittleEndian, ushort compression, int compressionType) + { + Stream stream = CreateTiffGenIfd() + .WithEntry(TiffGenEntry.Integer(TiffTags.Compression, TiffType.Short, compression)) + .ToStream(isLittleEndian); + + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); + TiffIfd ifd = decoder.ReadIfd(0); + decoder.ReadImageFormat(ifd); + + Assert.Equal((TiffCompressionType)compressionType, decoder.CompressionType); + } + + [Theory] + [InlineData(false, TiffCompression.Ccitt1D)] + [InlineData(false, TiffCompression.CcittGroup3Fax)] + [InlineData(false, TiffCompression.CcittGroup4Fax)] + [InlineData(false, TiffCompression.Deflate)] + [InlineData(false, TiffCompression.ItuTRecT43)] + [InlineData(false, TiffCompression.ItuTRecT82)] + [InlineData(false, TiffCompression.Jpeg)] + [InlineData(false, TiffCompression.Lzw)] + [InlineData(false, TiffCompression.OldDeflate)] + [InlineData(false, TiffCompression.OldJpeg)] + [InlineData(false, TiffCompression.PackBits)] + [InlineData(false, 999)] + [InlineData(true, TiffCompression.Ccitt1D)] + [InlineData(true, TiffCompression.CcittGroup3Fax)] + [InlineData(true, TiffCompression.CcittGroup4Fax)] + [InlineData(true, TiffCompression.Deflate)] + [InlineData(true, TiffCompression.ItuTRecT43)] + [InlineData(true, TiffCompression.ItuTRecT82)] + [InlineData(true, TiffCompression.Jpeg)] + [InlineData(true, TiffCompression.Lzw)] + [InlineData(true, TiffCompression.OldDeflate)] + [InlineData(true, TiffCompression.OldJpeg)] + [InlineData(true, TiffCompression.PackBits)] + [InlineData(true, 999)] + public void ReadImageFormat_ThrowsExceptionForUnsupportedCompression(bool isLittleEndian, ushort compression) + { + Stream stream = CreateTiffGenIfd() + .WithEntry(TiffGenEntry.Integer(TiffTags.Compression, TiffType.Short, compression)) + .ToStream(isLittleEndian); + + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); + TiffIfd ifd = decoder.ReadIfd(0); + + var e = Assert.Throws(() => decoder.ReadImageFormat(ifd)); + + Assert.Equal("The specified TIFF compression format is not supported.", e.Message); + } + + [Theory] + [InlineData(false, TiffPhotometricInterpretation.WhiteIsZero, new[] { 8 }, TiffColorType.WhiteIsZero8)] + [InlineData(true, TiffPhotometricInterpretation.WhiteIsZero, new[] { 8 }, TiffColorType.WhiteIsZero8)] + public void ReadImageFormat_DeterminesCorrectColorImplementation(bool isLittleEndian, ushort photometricInterpretation, int[] bitsPerSample, int colorType) + { + Stream stream = CreateTiffGenIfd() + .WithEntry(TiffGenEntry.Integer(TiffTags.PhotometricInterpretation, TiffType.Short, photometricInterpretation)) + .WithEntry(TiffGenEntry.Integer(TiffTags.BitsPerSample, TiffType.Short, bitsPerSample)) + .ToStream(isLittleEndian); + + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); + TiffIfd ifd = decoder.ReadIfd(0); + decoder.ReadImageFormat(ifd); + + Assert.Equal((TiffColorType)colorType, decoder.ColorType); + } + + // [Theory] + // [InlineData(false, new[] { 8 }, TiffColorType.WhiteIsZero8)] + // [InlineData(true, new[] { 8 }, TiffColorType.WhiteIsZero8)] + // public void ReadImageFormat_UsesDefaultColorImplementationForCcitt1D(bool isLittleEndian, int[] bitsPerSample, int colorType) + // { + // Stream stream = CreateTiffGenIfd() + // .WithEntry(TiffGenEntry.Integer(TiffTags.Compression, TiffType.Short, (int)TiffCompression.Ccitt1D)) + // .WithEntry(TiffGenEntry.Integer(TiffTags.BitsPerSample, TiffType.Short, bitsPerSample)) + // .WithoutEntry(TiffTags.PhotometricInterpretation) + // .ToStream(isLittleEndian); + + // TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); + // TiffIfd ifd = decoder.ReadIfd(0); + // decoder.ReadImageFormat(ifd); + + // Assert.Equal((TiffColorType)colorType, decoder.ColorType); + // } + + [Theory] + [MemberData(nameof(IsLittleEndianValues))] + public void ReadImageFormat_ThrowsExceptionForMissingPhotometricInterpretation(bool isLittleEndian) + { + Stream stream = CreateTiffGenIfd() + .WithoutEntry(TiffTags.PhotometricInterpretation) + .ToStream(isLittleEndian); + + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); + TiffIfd ifd = decoder.ReadIfd(0); + + var e = Assert.Throws(() => decoder.ReadImageFormat(ifd)); + + Assert.Equal("The TIFF photometric interpretation entry is missing.", e.Message); + } + + [Theory] + [InlineData(false, TiffPhotometricInterpretation.BlackIsZero)] + [InlineData(false, TiffPhotometricInterpretation.CieLab)] + [InlineData(false, TiffPhotometricInterpretation.ColorFilterArray)] + [InlineData(false, TiffPhotometricInterpretation.IccLab)] + [InlineData(false, TiffPhotometricInterpretation.ItuLab)] + [InlineData(false, TiffPhotometricInterpretation.LinearRaw)] + [InlineData(false, TiffPhotometricInterpretation.PaletteColor)] + [InlineData(false, TiffPhotometricInterpretation.Rgb)] + [InlineData(false, TiffPhotometricInterpretation.Separated)] + [InlineData(false, TiffPhotometricInterpretation.TransparencyMask)] + [InlineData(false, TiffPhotometricInterpretation.YCbCr)] + [InlineData(false, 999)] + [InlineData(true, TiffPhotometricInterpretation.BlackIsZero)] + [InlineData(true, TiffPhotometricInterpretation.CieLab)] + [InlineData(true, TiffPhotometricInterpretation.ColorFilterArray)] + [InlineData(true, TiffPhotometricInterpretation.IccLab)] + [InlineData(true, TiffPhotometricInterpretation.ItuLab)] + [InlineData(true, TiffPhotometricInterpretation.LinearRaw)] + [InlineData(true, TiffPhotometricInterpretation.PaletteColor)] + [InlineData(true, TiffPhotometricInterpretation.Rgb)] + [InlineData(true, TiffPhotometricInterpretation.Separated)] + [InlineData(true, TiffPhotometricInterpretation.TransparencyMask)] + [InlineData(true, TiffPhotometricInterpretation.YCbCr)] + [InlineData(true, 999)] + public void ReadImageFormat_ThrowsExceptionForUnsupportedPhotometricInterpretation(bool isLittleEndian, ushort photometricInterpretation) + { + Stream stream = CreateTiffGenIfd() + .WithEntry(TiffGenEntry.Integer(TiffTags.PhotometricInterpretation, TiffType.Short, photometricInterpretation)) + .ToStream(isLittleEndian); + + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); + TiffIfd ifd = decoder.ReadIfd(0); + + var e = Assert.Throws(() => decoder.ReadImageFormat(ifd)); + + Assert.Equal("The specified TIFF photometric interpretation is not supported.", e.Message); + } + + [Theory] + [InlineData(false, TiffPhotometricInterpretation.WhiteIsZero, new[] { 3 })] + [InlineData(true, TiffPhotometricInterpretation.WhiteIsZero, new[] { 3 })] + public void ReadImageFormat_ThrowsExceptionForUnsupportedBitDepth(bool isLittleEndian, ushort photometricInterpretation, int[] bitsPerSample) + { + Stream stream = CreateTiffGenIfd() + .WithEntry(TiffGenEntry.Integer(TiffTags.PhotometricInterpretation, TiffType.Short, photometricInterpretation)) + .WithEntry(TiffGenEntry.Integer(TiffTags.BitsPerSample, TiffType.Short, bitsPerSample)) + .ToStream(isLittleEndian); + + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); + TiffIfd ifd = decoder.ReadIfd(0); + + var e = Assert.Throws(() => decoder.ReadImageFormat(ifd)); + + Assert.Equal("The specified TIFF bit-depth is not supported.", e.Message); + } + + [Theory] + [InlineData(TiffColorType.WhiteIsZero8, 100, 80, 100 * 80)] + public void CalculateImageBufferSize_ReturnsCorrectSize(ushort colorType, int width, int height, int expectedResult) + { + TiffDecoderCore decoder = new TiffDecoderCore(null); + + int bufferSize = decoder.CalculateImageBufferSize(width, height); + + Assert.Equal(expectedResult, bufferSize); + } + private TiffGenIfd CreateTiffGenIfd() { return new TiffGenIfd() @@ -134,7 +309,10 @@ namespace ImageSharp.Tests TiffGenEntry.Integer(TiffTags.ImageLength, TiffType.Long, ImageHeight), TiffGenEntry.Rational(TiffTags.XResolution, XResolution, 1), TiffGenEntry.Rational(TiffTags.YResolution, YResolution, 1), - TiffGenEntry.Integer(TiffTags.ResolutionUnit, TiffType.Short, 2) + TiffGenEntry.Integer(TiffTags.ResolutionUnit, TiffType.Short, 2), + TiffGenEntry.Integer(TiffTags.PhotometricInterpretation, TiffType.Short, (int)TiffPhotometricInterpretation.WhiteIsZero), + TiffGenEntry.Integer(TiffTags.BitsPerSample, TiffType.Short, new int[] { 8 }), + TiffGenEntry.Integer(TiffTags.Compression, TiffType.Short, (int)TiffCompression.None) } }; } diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfdExtensions.cs b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfdExtensions.cs index c44291640..4b62b9803 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfdExtensions.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfdExtensions.cs @@ -14,7 +14,7 @@ namespace ImageSharp.Tests { public static TiffGenIfd WithoutEntry(this TiffGenIfd ifd, ushort tag) { - TiffGenEntry entry = ifd.Entries.First(e => e.Tag == tag); + TiffGenEntry entry = ifd.Entries.FirstOrDefault(e => e.Tag == tag); if (entry != null) { ifd.Entries.Remove(entry); From 5c79b5d7556e6ee14ead62d314351b03e6ada406 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Sat, 1 Apr 2017 16:01:40 +0100 Subject: [PATCH 027/275] Add Image extensions to save TIFF format files --- .../Formats/Tiff/ImageExtensions.cs | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 src/ImageSharp/Formats/Tiff/ImageExtensions.cs diff --git a/src/ImageSharp/Formats/Tiff/ImageExtensions.cs b/src/ImageSharp/Formats/Tiff/ImageExtensions.cs new file mode 100644 index 000000000..01384b827 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/ImageExtensions.cs @@ -0,0 +1,53 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System.IO; + + using Formats; + + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Saves the image to the given stream with the tiff format. + /// + /// The pixel format. + /// The image this method extends. + /// The stream to save the image to. + /// Thrown if the stream is null. + /// + /// The . + /// + public static Image SaveAsTiff(this Image source, Stream stream) + where TColor : struct, IPixel + { + return SaveAsTiff(source, stream, null); + } + + /// + /// Saves the image to the given stream with the tiff format. + /// + /// The pixel format. + /// The image this method extends. + /// The stream to save the image to. + /// The options for the encoder. + /// Thrown if the stream is null. + /// + /// The . + /// + public static Image SaveAsTiff(this Image source, Stream stream, ITiffEncoderOptions options) + where TColor : struct, IPixel + { + TiffEncoder encoder = new TiffEncoder(); + encoder.Encode(source, stream, options); + + return source; + } + } +} From 8af80459baa107038abe27985de10be1b9a25552 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Tue, 4 Apr 2017 20:14:16 +0100 Subject: [PATCH 028/275] Move TIFF internals into own namespace --- .../Tiff/Compression/NoneTiffCompression.cs | 2 +- .../Tiff/Compression/TiffCompressionType.cs | 2 +- .../Formats/Tiff/Constants/TiffCompression.cs | 2 +- .../Formats/Tiff/Constants/TiffConstants.cs | 2 +- .../Tiff/Constants/TiffExtraSamples.cs | 2 +- .../Formats/Tiff/Constants/TiffFillOrder.cs | 2 +- .../Tiff/Constants/TiffNewSubfileType.cs | 2 +- .../Formats/Tiff/Constants/TiffOrientation.cs | 2 +- .../TiffPhotometricInterpretation.cs | 2 +- .../Tiff/Constants/TiffPlanarConfiguration.cs | 2 +- .../Tiff/Constants/TiffResolutionUnit.cs | 2 +- .../Formats/Tiff/Constants/TiffSubfileType.cs | 2 +- .../Formats/Tiff/Constants/TiffTags.cs | 2 +- .../Tiff/Constants/TiffThreshholding.cs | 2 +- .../Formats/Tiff/Constants/TiffType.cs | 2 +- .../TiffColorType.cs | 2 +- .../WhiteIsZero8TiffColor.cs | 2 +- .../Formats/Tiff/TiffDecoderCore.cs | 1 + .../Formats/Tiff/TiffIfd/TiffIfd.cs | 2 +- .../Formats/Tiff/TiffIfd/TiffIfdEntry.cs | 2 +- .../Formats/Tiff/Utils/TiffUtils.cs | 2 +- .../Compression/NoneTiffCompressionTests.cs | 2 +- .../WhiteIsZero8TiffColorTests.cs | 2 +- .../Formats/Tiff/TiffDecoderIfdEntryTests.cs | 1 + .../Formats/Tiff/TiffDecoderIfdTests.cs | 33 ++++++++++--------- .../Formats/Tiff/TiffDecoderImageTests.cs | 1 + .../Formats/Tiff/TiffIfd/TiffIfdEntryTests.cs | 3 +- .../Formats/Tiff/TiffIfd/TiffIfdTests.cs | 2 +- .../TestUtilities/Tiff/TiffGenEntry.cs | 2 +- 29 files changed, 45 insertions(+), 42 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs index c538cf473..6bc8a308f 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Formats +namespace ImageSharp.Formats.Tiff { using System.IO; using System.Runtime.CompilerServices; diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs index 5b6368bf9..c661ea894 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Formats +namespace ImageSharp.Formats.Tiff { /// /// Provides enumeration of the various TIFF compression types. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs index 7880f683e..acb0685db 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Formats +namespace ImageSharp.Formats.Tiff { /// /// Enumeration representing the compression formats defined by the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs index 77cf5e0bd..1858d49b8 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Formats +namespace ImageSharp.Formats.Tiff { /// /// Defines constants defined in the TIFF specification. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffExtraSamples.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffExtraSamples.cs index d15d312f1..545ae4c39 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffExtraSamples.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffExtraSamples.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Formats +namespace ImageSharp.Formats.Tiff { /// /// Enumeration representing the possible uses of extra components in TIFF format files. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffFillOrder.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffFillOrder.cs index 99d88e90e..7edf0eeaa 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffFillOrder.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffFillOrder.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Formats +namespace ImageSharp.Formats.Tiff { /// /// Enumeration representing the fill orders defined by the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs index 3d1885377..20bf16c63 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Formats +namespace ImageSharp.Formats.Tiff { using System; diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffOrientation.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffOrientation.cs index 8f1469354..9ffa5cf81 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffOrientation.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffOrientation.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Formats +namespace ImageSharp.Formats.Tiff { /// /// Enumeration representing the image orientations defined by the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs index 21f1b56e8..35d1a291c 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Formats +namespace ImageSharp.Formats.Tiff { /// /// Enumeration representing the photometric interpretation formats defined by the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffPlanarConfiguration.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffPlanarConfiguration.cs index e3c40adfd..ef0b72236 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffPlanarConfiguration.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffPlanarConfiguration.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Formats +namespace ImageSharp.Formats.Tiff { /// /// Enumeration representing how the components of each pixel are stored the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs index 307f9b9d2..4bb7c15ba 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Formats +namespace ImageSharp.Formats.Tiff { /// /// Enumeration representing the resolution units defined by the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffSubfileType.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffSubfileType.cs index 6a86f3b30..050af238c 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffSubfileType.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffSubfileType.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Formats +namespace ImageSharp.Formats.Tiff { /// /// Enumeration representing the sub-file types defined by the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffTags.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffTags.cs index 56fc71461..7d9343515 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffTags.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffTags.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Formats +namespace ImageSharp.Formats.Tiff { /// /// Constants representing tag IDs in the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffThreshholding.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffThreshholding.cs index eff57cd90..0e9444302 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffThreshholding.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffThreshholding.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Formats +namespace ImageSharp.Formats.Tiff { /// /// Enumeration representing the threshholding applied to image data defined by the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffType.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffType.cs index 0b342d06a..1a1fc3108 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffType.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffType.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Formats +namespace ImageSharp.Formats.Tiff { /// /// Enumeration representing the data types understood by the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs index bca27e4b2..da7d6af26 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Formats +namespace ImageSharp.Formats.Tiff { /// /// Provides enumeration of the various TIFF photometric interpretation implementation types. diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs index 295db5e18..97d7edaca 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Formats +namespace ImageSharp.Formats.Tiff { using System.Runtime.CompilerServices; using ImageSharp; diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index f186e33ee..d5d7d06b8 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -9,6 +9,7 @@ namespace ImageSharp.Formats using System.Buffers; using System.IO; using System.Text; + using ImageSharp.Formats.Tiff; /// /// Performs the tiff decoding operation. diff --git a/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfd.cs b/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfd.cs index d2071aff9..f666f371d 100644 --- a/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfd.cs +++ b/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfd.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Formats +namespace ImageSharp.Formats.Tiff { /// /// Data structure for holding details of each TIFF IFD. diff --git a/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntry.cs b/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntry.cs index b2983eaad..d9c1722c8 100644 --- a/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntry.cs +++ b/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntry.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Formats +namespace ImageSharp.Formats.Tiff { /// /// Data structure for holding details of each TIFF IFD entry. diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs index e4049cf0f..64e352745 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs @@ -2,7 +2,7 @@ // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Formats +namespace ImageSharp.Formats.Tiff { using System.IO; diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs index e3277eb96..40348cfed 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs @@ -8,7 +8,7 @@ namespace ImageSharp.Tests using System.IO; using Xunit; - using ImageSharp.Formats; + using ImageSharp.Formats.Tiff; public class NoneTiffCompressionTests { diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColorTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColorTests.cs index 075881f61..7b2513ce5 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColorTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColorTests.cs @@ -8,7 +8,7 @@ namespace ImageSharp.Tests using System.Collections.Generic; using Xunit; - using ImageSharp.Formats; + using ImageSharp.Formats.Tiff; public class WhiteIsZero8TiffColorTests : PhotometricInterpretationTestBase { diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs index 369fc61de..f8dcfed53 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs @@ -11,6 +11,7 @@ namespace ImageSharp.Tests using Xunit; using ImageSharp.Formats; + using ImageSharp.Formats.Tiff; public class TiffDecoderIfdEntryTests { diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdTests.cs index d5400279f..7aa60af82 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdTests.cs @@ -9,6 +9,7 @@ namespace ImageSharp.Tests using Xunit; using ImageSharp.Formats; + using ImageSharp.Formats.Tiff; public class TiffDecoderIfdTests { @@ -20,13 +21,13 @@ namespace ImageSharp.Tests public void ReadIfd_ReadsNextIfdOffset_IfPresent(bool isLittleEndian) { Stream stream = new TiffGenIfd() - { - Entries = + { + Entries = { TiffGenEntry.Integer(TiffTags.ImageWidth, TiffType.Long, 150) }, - NextIfd = new TiffGenIfd() - } + NextIfd = new TiffGenIfd() + } .ToStream(isLittleEndian); TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); @@ -40,12 +41,12 @@ namespace ImageSharp.Tests public void ReadIfd_ReadsNextIfdOffset_ZeroIfLastIfd(bool isLittleEndian) { Stream stream = new TiffGenIfd() - { - Entries = + { + Entries = { TiffGenEntry.Integer(TiffTags.ImageWidth, TiffType.Long, 150) } - } + } .ToStream(isLittleEndian); TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); @@ -59,8 +60,8 @@ namespace ImageSharp.Tests public void ReadIfd_ReturnsCorrectNumberOfEntries(bool isLittleEndian) { Stream stream = new TiffGenIfd() - { - Entries = + { + Entries = { TiffGenEntry.Integer(TiffTags.ImageWidth, TiffType.Long, 150), TiffGenEntry.Integer(TiffTags.ImageLength, TiffType.Long, 210), @@ -68,8 +69,8 @@ namespace ImageSharp.Tests TiffGenEntry.Ascii(TiffTags.Artist, "Image Artist Name"), TiffGenEntry.Ascii(TiffTags.HostComputer, "Host Computer Name") }, - NextIfd = new TiffGenIfd() - } + NextIfd = new TiffGenIfd() + } .ToStream(isLittleEndian); TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); @@ -84,22 +85,22 @@ namespace ImageSharp.Tests public void ReadIfd_ReadsRawTiffEntryData(bool isLittleEndian) { Stream stream = new TiffGenIfd() - { - Entries = + { + Entries = { TiffGenEntry.Integer(TiffTags.ImageWidth, TiffType.Long, 150), TiffGenEntry.Integer(TiffTags.ImageLength, TiffType.Long, 210), TiffGenEntry.Integer(TiffTags.Orientation, TiffType.Short, 1) }, - NextIfd = new TiffGenIfd() - } + NextIfd = new TiffGenIfd() + } .ToStream(isLittleEndian); TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); TiffIfd ifd = decoder.ReadIfd(0); TiffIfdEntry entry = ifd.Entries[1]; - byte[] expectedData = isLittleEndian ? new byte[] {210,0,0,0} : new byte[] {0,0,0,210}; + byte[] expectedData = isLittleEndian ? new byte[] { 210, 0, 0, 0 } : new byte[] { 0, 0, 0, 210 }; Assert.NotNull(entry); Assert.Equal(TiffTags.ImageLength, entry.Tag); Assert.Equal(TiffType.Long, entry.Type); diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs index 34a0c2e4c..4b5c77e1b 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs @@ -10,6 +10,7 @@ namespace ImageSharp.Tests using Xunit; using ImageSharp.Formats; + using ImageSharp.Formats.Tiff; public class TiffDecoderImageTests { diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryTests.cs index 08b7dc8eb..efca357f9 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryTests.cs @@ -5,10 +5,9 @@ namespace ImageSharp.Tests { - using System.IO; using Xunit; - using ImageSharp.Formats; + using ImageSharp.Formats.Tiff; public class TiffIfdEntryTests { diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs index c68047539..b9eea06b3 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs @@ -8,7 +8,7 @@ namespace ImageSharp.Tests using System.IO; using Xunit; - using ImageSharp.Formats; + using ImageSharp.Formats.Tiff; public class TiffIfdTests { diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs index 2065e8501..0cdfac5cb 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs @@ -9,7 +9,7 @@ namespace ImageSharp.Tests using System.Collections.Generic; using System.Linq; using System.Text; - using ImageSharp.Formats; + using ImageSharp.Formats.Tiff; /// /// A utility data structure to represent Tiff IFD entries in unit tests. From 91e14b713425613bb00e8c6b5c0d84bc25f88431 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Tue, 4 Apr 2017 21:26:57 +0100 Subject: [PATCH 029/275] Update TIFF codec to new IImageDecoder signature --- src/ImageSharp/Formats/Tiff/TiffDecoder.cs | 7 ++- .../Formats/Tiff/TiffDecoderCore.cs | 31 +++++++---- .../Formats/Tiff/TiffDecoderHeaderTests.cs | 53 +++++++++---------- .../Formats/Tiff/TiffDecoderIfdEntryTests.cs | 2 +- .../Formats/Tiff/TiffDecoderIfdTests.cs | 8 +-- .../Formats/Tiff/TiffDecoderImageTests.cs | 38 ++++++------- .../Formats/Tiff/TiffIfd/TiffIfdTests.cs | 1 - 7 files changed, 71 insertions(+), 69 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs index 794fb4f1f..333c707e3 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs @@ -13,15 +13,14 @@ namespace ImageSharp.Formats public class TiffDecoder : IImageDecoder { /// - public void Decode(Image image, Stream stream, IDecoderOptions options) + public Image Decode(Configuration configuration, Stream stream, IDecoderOptions options) where TColor : struct, IPixel { - Guard.NotNull(image, "image"); Guard.NotNull(stream, "stream"); - using (TiffDecoderCore decoder = new TiffDecoderCore(options)) + using (TiffDecoderCore decoder = new TiffDecoderCore(options, configuration)) { - decoder.Decode(image, stream, false); + return decoder.Decode(stream); } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index d5d7d06b8..d9086c95a 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -21,12 +21,19 @@ namespace ImageSharp.Formats /// private readonly IDecoderOptions options; + /// + /// The global configuration + /// + private readonly Configuration configuration; + /// /// Initializes a new instance of the class. /// /// The decoder options. - public TiffDecoderCore(IDecoderOptions options) + /// The configuration. + public TiffDecoderCore(IDecoderOptions options, Configuration configuration) { + this.configuration = configuration ?? Configuration.Default; this.options = options ?? new DecoderOptions(); } @@ -36,8 +43,9 @@ namespace ImageSharp.Formats /// The input stream. /// A flag indicating if the file is encoded in little-endian or big-endian format. /// The decoder options. - public TiffDecoderCore(Stream stream, bool isLittleEndian, IDecoderOptions options) - : this(options) + /// The configuration. + public TiffDecoderCore(Stream stream, bool isLittleEndian, IDecoderOptions options, Configuration configuration) + : this(options, configuration) { this.InputStream = stream; this.IsLittleEndian = isLittleEndian; @@ -75,17 +83,18 @@ namespace ImageSharp.Formats /// the data to image. /// /// The pixel format. - /// The image, where the data should be set to. /// The stream, where the image should be. - /// Whether to decode metadata only. - public void Decode(Image image, Stream stream, bool metadataOnly) + /// The decoded image. + public Image Decode(Stream stream) where TColor : struct, IPixel { this.InputStream = stream; uint firstIfdOffset = this.ReadHeader(); TiffIfd firstIfd = this.ReadIfd(firstIfdOffset); - this.DecodeImage(firstIfd, image); + Image image = this.DecodeImage(firstIfd); + + return image; } /// @@ -168,8 +177,8 @@ namespace ImageSharp.Formats /// /// The pixel format. /// The IFD to read the image from. - /// The image, where the data should be set to. - public void DecodeImage(TiffIfd ifd, Image image) + /// The decoded image. + public Image DecodeImage(TiffIfd ifd) where TColor : struct, IPixel { if (!ifd.TryGetIfdEntry(TiffTags.ImageLength, out TiffIfdEntry imageLengthEntry) @@ -181,7 +190,7 @@ namespace ImageSharp.Formats int width = (int)this.ReadUnsignedInteger(ref imageWidthEntry); int height = (int)this.ReadUnsignedInteger(ref imageLengthEntry); - image.InitPixels(width, height); + Image image = Image.Create(width, height, this.configuration); TiffResolutionUnit resolutionUnit = TiffResolutionUnit.Inch; if (ifd.TryGetIfdEntry(TiffTags.ResolutionUnit, out TiffIfdEntry resolutionUnitEntry)) @@ -233,6 +242,8 @@ namespace ImageSharp.Formats } } } + + return image; } /// diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs index 11c999a0f..48d64b71c 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs @@ -20,12 +20,12 @@ namespace ImageSharp.Tests public void ReadHeader_ReadsEndianness(bool isLittleEndian) { Stream stream = new TiffGenHeader() - { - FirstIfd = new TiffGenIfd() - } + { + FirstIfd = new TiffGenIfd() + } .ToStream(isLittleEndian); - TiffDecoderCore decoder = new TiffDecoderCore(stream, false, null); + TiffDecoderCore decoder = new TiffDecoderCore(stream, false, null, null); decoder.ReadHeader(); @@ -37,12 +37,12 @@ namespace ImageSharp.Tests public void ReadHeader_ReadsFirstIfdOffset(bool isLittleEndian) { Stream stream = new TiffGenHeader() - { - FirstIfd = new TiffGenIfd() - } + { + FirstIfd = new TiffGenIfd() + } .ToStream(isLittleEndian); - TiffDecoderCore decoder = new TiffDecoderCore(stream, false, null); + TiffDecoderCore decoder = new TiffDecoderCore(stream, false, null, null); uint firstIfdOffset = decoder.ReadHeader(); @@ -60,16 +60,16 @@ namespace ImageSharp.Tests public void Decode_ThrowsException_WithInvalidByteOrderMarkers(ushort byteOrderMarker) { Stream stream = new TiffGenHeader() - { - FirstIfd = new TiffGenIfd(), - ByteOrderMarker = byteOrderMarker - } + { + FirstIfd = new TiffGenIfd(), + ByteOrderMarker = byteOrderMarker + } .ToStream(true); TiffDecoder decoder = new TiffDecoder(); - + ImageFormatException e = Assert.Throws(() => { TestDecode(decoder, stream); }); - + Assert.Equal("Invalid TIFF file header.", e.Message); } @@ -78,16 +78,16 @@ namespace ImageSharp.Tests public void Decode_ThrowsException_WithIncorrectMagicNumber(bool isLittleEndian) { Stream stream = new TiffGenHeader() - { - FirstIfd = new TiffGenIfd(), - MagicNumber = 32 - } + { + FirstIfd = new TiffGenIfd(), + MagicNumber = 32 + } .ToStream(isLittleEndian); TiffDecoder decoder = new TiffDecoder(); - + ImageFormatException e = Assert.Throws(() => { TestDecode(decoder, stream); }); - + Assert.Equal("Invalid TIFF file header.", e.Message); } @@ -96,23 +96,22 @@ namespace ImageSharp.Tests public void Decode_ThrowsException_WithNoIfdZero(bool isLittleEndian) { Stream stream = new TiffGenHeader() - { - FirstIfd = null - } + { + FirstIfd = null + } .ToStream(isLittleEndian); TiffDecoder decoder = new TiffDecoder(); - + ImageFormatException e = Assert.Throws(() => { TestDecode(decoder, stream); }); - + Assert.Equal("Invalid TIFF file header.", e.Message); } private void TestDecode(TiffDecoder decoder, Stream stream) { Configuration.Default.AddImageFormat(new TiffFormat()); - Image image = new Image(1,1); - decoder.Decode(image, stream, null); + Image image = decoder.Decode(Configuration.Default, stream, null); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs index f8dcfed53..846495f67 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs @@ -836,7 +836,7 @@ namespace ImageSharp.Tests } .ToStream(isLittleEndian); - TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null, null); TiffIfdEntry ifdEntry = decoder.ReadIfd(0).Entries[0]; return (decoder, ifdEntry); diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdTests.cs index 7aa60af82..a8d01cf27 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdTests.cs @@ -30,7 +30,7 @@ namespace ImageSharp.Tests } .ToStream(isLittleEndian); - TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null, null); TiffIfd ifd = decoder.ReadIfd(0); Assert.Equal(18u, ifd.NextIfdOffset); @@ -49,7 +49,7 @@ namespace ImageSharp.Tests } .ToStream(isLittleEndian); - TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null, null); TiffIfd ifd = decoder.ReadIfd(0); Assert.Equal(0u, ifd.NextIfdOffset); @@ -73,7 +73,7 @@ namespace ImageSharp.Tests } .ToStream(isLittleEndian); - TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null, null); TiffIfd ifd = decoder.ReadIfd(0); Assert.NotNull(ifd.Entries); @@ -96,7 +96,7 @@ namespace ImageSharp.Tests } .ToStream(isLittleEndian); - TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null, null); TiffIfd ifd = decoder.ReadIfd(0); TiffIfdEntry entry = ifd.Entries[1]; diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs index 4b5c77e1b..b949318d9 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs @@ -29,11 +29,9 @@ namespace ImageSharp.Tests Stream stream = CreateTiffGenIfd() .ToStream(isLittleEndian); - TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null, null); TiffIfd ifd = decoder.ReadIfd(0); - Image image = new Image(1, 1); - - decoder.DecodeImage(ifd, image); + Image image = decoder.DecodeImage(ifd); Assert.Equal(ImageWidth, image.Width); Assert.Equal(ImageHeight, image.Height); @@ -82,11 +80,9 @@ namespace ImageSharp.Tests Stream stream = ifdGen.ToStream(isLittleEndian); - TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null, null); TiffIfd ifd = decoder.ReadIfd(0); - Image image = new Image(1, 1); - - decoder.DecodeImage(ifd, image); + Image image = decoder.DecodeImage(ifd); Assert.Equal(expectedHorizonalResolution, image.MetaData.HorizontalResolution, 10); Assert.Equal(expectedVerticalResolution, image.MetaData.VerticalResolution, 10); @@ -100,11 +96,10 @@ namespace ImageSharp.Tests .WithoutEntry(TiffTags.ImageWidth) .ToStream(isLittleEndian); - TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null, null); TiffIfd ifd = decoder.ReadIfd(0); - Image image = new Image(1, 1); - var e = Assert.Throws(() => decoder.DecodeImage(ifd, image)); + var e = Assert.Throws(() => decoder.DecodeImage(ifd)); Assert.Equal("The TIFF IFD does not specify the image dimensions.", e.Message); } @@ -117,11 +112,10 @@ namespace ImageSharp.Tests .WithoutEntry(TiffTags.ImageLength) .ToStream(isLittleEndian); - TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null, null); TiffIfd ifd = decoder.ReadIfd(0); - Image image = new Image(1, 1); - var e = Assert.Throws(() => decoder.DecodeImage(ifd, image)); + var e = Assert.Throws(() => decoder.DecodeImage(ifd)); Assert.Equal("The TIFF IFD does not specify the image dimensions.", e.Message); } @@ -135,7 +129,7 @@ namespace ImageSharp.Tests .WithEntry(TiffGenEntry.Integer(TiffTags.Compression, TiffType.Short, compression)) .ToStream(isLittleEndian); - TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null, null); TiffIfd ifd = decoder.ReadIfd(0); decoder.ReadImageFormat(ifd); @@ -173,7 +167,7 @@ namespace ImageSharp.Tests .WithEntry(TiffGenEntry.Integer(TiffTags.Compression, TiffType.Short, compression)) .ToStream(isLittleEndian); - TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null, null); TiffIfd ifd = decoder.ReadIfd(0); var e = Assert.Throws(() => decoder.ReadImageFormat(ifd)); @@ -191,7 +185,7 @@ namespace ImageSharp.Tests .WithEntry(TiffGenEntry.Integer(TiffTags.BitsPerSample, TiffType.Short, bitsPerSample)) .ToStream(isLittleEndian); - TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null, null); TiffIfd ifd = decoder.ReadIfd(0); decoder.ReadImageFormat(ifd); @@ -209,7 +203,7 @@ namespace ImageSharp.Tests // .WithoutEntry(TiffTags.PhotometricInterpretation) // .ToStream(isLittleEndian); - // TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); + // TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null, null); // TiffIfd ifd = decoder.ReadIfd(0); // decoder.ReadImageFormat(ifd); @@ -224,7 +218,7 @@ namespace ImageSharp.Tests .WithoutEntry(TiffTags.PhotometricInterpretation) .ToStream(isLittleEndian); - TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null, null); TiffIfd ifd = decoder.ReadIfd(0); var e = Assert.Throws(() => decoder.ReadImageFormat(ifd)); @@ -263,7 +257,7 @@ namespace ImageSharp.Tests .WithEntry(TiffGenEntry.Integer(TiffTags.PhotometricInterpretation, TiffType.Short, photometricInterpretation)) .ToStream(isLittleEndian); - TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null, null); TiffIfd ifd = decoder.ReadIfd(0); var e = Assert.Throws(() => decoder.ReadImageFormat(ifd)); @@ -281,7 +275,7 @@ namespace ImageSharp.Tests .WithEntry(TiffGenEntry.Integer(TiffTags.BitsPerSample, TiffType.Short, bitsPerSample)) .ToStream(isLittleEndian); - TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null); + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null, null); TiffIfd ifd = decoder.ReadIfd(0); var e = Assert.Throws(() => decoder.ReadImageFormat(ifd)); @@ -293,7 +287,7 @@ namespace ImageSharp.Tests [InlineData(TiffColorType.WhiteIsZero8, 100, 80, 100 * 80)] public void CalculateImageBufferSize_ReturnsCorrectSize(ushort colorType, int width, int height, int expectedResult) { - TiffDecoderCore decoder = new TiffDecoderCore(null); + TiffDecoderCore decoder = new TiffDecoderCore(null, null); int bufferSize = decoder.CalculateImageBufferSize(width, height); diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs index b9eea06b3..97e46ef4e 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs @@ -5,7 +5,6 @@ namespace ImageSharp.Tests { - using System.IO; using Xunit; using ImageSharp.Formats.Tiff; From 05f093765206f636b0a631f0f22bd0dadda7d3f2 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Fri, 14 Apr 2017 19:32:12 +0100 Subject: [PATCH 030/275] Add support for WhiteIsZero bilevel & 4-bit images --- .../TiffColorType.cs | 10 ++ .../WhiteIsZero1TiffColor.cs | 53 ++++++ .../WhiteIsZero4TiffColor.cs | 61 +++++++ .../WhiteIsZero8TiffColor.cs | 2 +- .../Formats/Tiff/TiffDecoderCore.cs | 44 ++++- .../PhotometricInterpretationTestBase.cs | 3 +- .../WhiteIsZero8TiffColorTests.cs | 51 ------ .../WhiteIsZeroTiffColorTests.cs | 152 ++++++++++++++++++ .../Formats/Tiff/TiffDecoderImageTests.cs | 26 +++ 9 files changed, 342 insertions(+), 60 deletions(-) create mode 100644 src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs create mode 100644 src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs delete mode 100644 tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColorTests.cs create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs index da7d6af26..be9e14df4 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs @@ -10,6 +10,16 @@ namespace ImageSharp.Formats.Tiff /// internal enum TiffColorType { + /// + /// Grayscale: 0 is imaged as white. The maximum value is imaged as black. Optimised implementation for bilevel images. + /// + WhiteIsZero1, + + /// + /// Grayscale: 0 is imaged as white. The maximum value is imaged as black. Optimised implementation for 4-bit images. + /// + WhiteIsZero4, + /// /// Grayscale: 0 is imaged as white. The maximum value is imaged as black. Optimised implementation for 8-bit images. /// diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs new file mode 100644 index 000000000..5e486c7fe --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs @@ -0,0 +1,53 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats.Tiff +{ + using System; + using System.Runtime.CompilerServices; + using ImageSharp; + + /// + /// Implements the 'WhiteIsZero' photometric interpretation (optimised for bilevel images). + /// + internal static class WhiteIsZero1TiffColor + { + /// + /// Decodes pixel data using the current photometric interpretation. + /// + /// The pixel format. + /// The buffer to read image data from. + /// The image buffer to write pixels to. + /// The x-coordinate of the left-hand side of the image block. + /// The y-coordinate of the top of the image block. + /// The width of the image block. + /// The height of the image block. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Decode(byte[] data, PixelAccessor pixels, int left, int top, int width, int height) + where TColor : struct, IPixel + { + TColor color = default(TColor); + + uint offset = 0; + + for (int y = top; y < top + height; y++) + { + for (int x = left; x < left + width; x += 8) + { + byte b = data[offset++]; + int maxShift = Math.Min(left + width - x, 8); + + for (int shift = 0; shift < maxShift; shift++) + { + int bit = (b >> (7 - shift)) & 1; + byte intensity = (bit == 1) ? (byte)0 : (byte)255; + color.PackFromBytes(intensity, intensity, intensity, 255); + pixels[x + shift, y] = color; + } + } + } + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs new file mode 100644 index 000000000..98f74dca0 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs @@ -0,0 +1,61 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats.Tiff +{ + using System.Runtime.CompilerServices; + using ImageSharp; + + /// + /// Implements the 'WhiteIsZero' photometric interpretation (optimised for 4-bit grayscale images). + /// + internal static class WhiteIsZero4TiffColor + { + /// + /// Decodes pixel data using the current photometric interpretation. + /// + /// The pixel format. + /// The buffer to read image data from. + /// The image buffer to write pixels to. + /// The x-coordinate of the left-hand side of the image block. + /// The y-coordinate of the top of the image block. + /// The width of the image block. + /// The height of the image block. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Decode(byte[] data, PixelAccessor pixels, int left, int top, int width, int height) + where TColor : struct, IPixel + { + TColor color = default(TColor); + + uint offset = 0; + bool isOddWidth = (width & 1) == 1; + + for (int y = top; y < top + height; y++) + { + for (int x = left; x < left + width - 1; x += 2) + { + byte byteData = data[offset++]; + + byte intensity1 = (byte)((15 - ((byteData & 0xF0) >> 4)) * 17); + color.PackFromBytes(intensity1, intensity1, intensity1, 255); + pixels[x, y] = color; + + byte intensity2 = (byte)((15 - (byteData & 0x0F)) * 17); + color.PackFromBytes(intensity2, intensity2, intensity2, 255); + pixels[x + 1, y] = color; + } + + if (isOddWidth) + { + byte byteData = data[offset++]; + + byte intensity1 = (byte)((15 - ((byteData & 0xF0) >> 4)) * 17); + color.PackFromBytes(intensity1, intensity1, intensity1, 255); + pixels[left + width - 1, y] = color; + } + } + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs index 97d7edaca..8ddafd983 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs @@ -9,7 +9,7 @@ namespace ImageSharp.Formats.Tiff using ImageSharp; /// - /// Implements the 'WhiteIsZero' photometric interpretation (optimised for 8-bit grayscale images). + /// Implements the 'WhiteIsZero' photometric interpretation (optimised for 8-bit grayscale images). /// internal static class WhiteIsZero8TiffColor { diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index d9086c95a..cc2d0f8b7 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -299,18 +299,38 @@ namespace ImageSharp.Formats { uint[] bitsPerSample = this.ReadUnsignedIntegerArray(ref bitsPerSampleEntry); - if (bitsPerSample.Length == 1 && bitsPerSample[0] == 8) + if (bitsPerSample.Length == 1) { - this.ColorType = TiffColorType.WhiteIsZero8; - } - else - { - throw new NotSupportedException("The specified TIFF bit-depth is not supported."); + switch (bitsPerSample[0]) + { + case 8: + { + this.ColorType = TiffColorType.WhiteIsZero8; + break; + } + + case 4: + { + this.ColorType = TiffColorType.WhiteIsZero4; + break; + } + + case 1: + { + this.ColorType = TiffColorType.WhiteIsZero1; + break; + } + + default: + { + throw new NotSupportedException("The specified TIFF bit-depth is not supported."); + } + } } } else { - throw new NotSupportedException("TIFF bilevel images are not supported."); + this.ColorType = TiffColorType.WhiteIsZero1; } break; @@ -331,6 +351,10 @@ namespace ImageSharp.Formats { switch (this.ColorType) { + case TiffColorType.WhiteIsZero1: + return ((width + 7) / 8) * height; + case TiffColorType.WhiteIsZero4: + return ((width + 1) / 2) * height; case TiffColorType.WhiteIsZero8: return width * height; default: @@ -373,6 +397,12 @@ namespace ImageSharp.Formats { switch (this.ColorType) { + case TiffColorType.WhiteIsZero1: + WhiteIsZero1TiffColor.Decode(data, pixels, left, top, width, height); + break; + case TiffColorType.WhiteIsZero4: + WhiteIsZero4TiffColor.Decode(data, pixels, left, top, width, height); + break; case TiffColorType.WhiteIsZero8: WhiteIsZero8TiffColor.Decode(data, pixels, left, top, width, height); break; diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs index 7fdb12177..3c245855d 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs @@ -50,7 +50,8 @@ namespace ImageSharp.Tests { for (int x = 0; x < resultWidth; x++) { - Assert.Equal(expectedResult[y][x], pixels[x, y]); + Assert.True(expectedResult[y][x] == pixels[x, y], + $"Pixel ({x}, {y}) should be {expectedResult[y][x]} but was {pixels[x,y]}"); } } } diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColorTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColorTests.cs deleted file mode 100644 index 7b2513ce5..000000000 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColorTests.cs +++ /dev/null @@ -1,51 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using System.Collections.Generic; - using Xunit; - - using ImageSharp.Formats.Tiff; - - public class WhiteIsZero8TiffColorTests : PhotometricInterpretationTestBase - { - private static Color Gray000 = new Color(255, 255, 255, 255); - private static Color Gray128 = new Color(127, 127, 127, 255); - private static Color Gray255 = new Color(0, 0, 0, 255); - - private static byte[] GrayscaleBytes4x4 = new byte[] { 128, 255, 000, 255, - 255, 255, 255, 255, - 000, 128, 128, 255, - 255, 000, 255, 128 }; - - private static Color[][] GrayscaleResult4x4 = new[] { new[] { Gray128, Gray255, Gray000, Gray255 }, - new[] { Gray255, Gray255, Gray255, Gray255 }, - new[] { Gray000, Gray128, Gray128, Gray255 }, - new[] { Gray255, Gray000, Gray255, Gray128 }}; - - public static IEnumerable DecodeData - { - get - { - yield return new object[] { GrayscaleBytes4x4, 0, 0, 4, 4, GrayscaleResult4x4 }; - yield return new object[] { GrayscaleBytes4x4, 0, 0, 4, 4, Offset(GrayscaleResult4x4, 0, 0, 6, 6) }; - yield return new object[] { GrayscaleBytes4x4, 1, 0, 4, 4, Offset(GrayscaleResult4x4, 1, 0, 6, 6) }; - yield return new object[] { GrayscaleBytes4x4, 0, 1, 4, 4, Offset(GrayscaleResult4x4, 0, 1, 6, 6) }; - yield return new object[] { GrayscaleBytes4x4, 1, 1, 4, 4, Offset(GrayscaleResult4x4, 1, 1, 6, 6) }; - } - } - - [Theory] - [MemberData(nameof(DecodeData))] - public void Decode_WritesPixelData(byte[] inputData, int left, int top, int width, int height, Color[][] expectedResult) - { - AssertDecode(expectedResult, pixels => - { - WhiteIsZero8TiffColor.Decode(inputData, pixels, left, top, width, height); - }); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs new file mode 100644 index 000000000..8769d472b --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs @@ -0,0 +1,152 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System.Collections.Generic; + using Xunit; + + using ImageSharp.Formats.Tiff; + + public class WhiteIsZeroTiffColorTests : PhotometricInterpretationTestBase + { + private static Color Gray000 = new Color(255, 255, 255, 255); + private static Color Gray128 = new Color(127, 127, 127, 255); + private static Color Gray255 = new Color(0, 0, 0, 255); + private static Color Gray0 = new Color(255, 255, 255, 255); + private static Color Gray8 = new Color(119, 119, 119, 255); + private static Color GrayF = new Color(0, 0, 0, 255); + private static Color Bit0 = new Color(255, 255, 255, 255); + private static Color Bit1 = new Color(0, 0, 0, 255); + + private static byte[] Bilevel_Bytes4x4 = new byte[] { 0b01010000, + 0b11110000, + 0b01110000, + 0b10010000 }; + + private static Color[][] Bilevel_Result4x4 = new[] { new[] { Bit0, Bit1, Bit0, Bit1 }, + new[] { Bit1, Bit1, Bit1, Bit1 }, + new[] { Bit0, Bit1, Bit1, Bit1 }, + new[] { Bit1, Bit0, Bit0, Bit1 }}; + + private static byte[] Bilevel_Bytes12x4 = new byte[] { 0b01010101, 0b01010000, + 0b11111111, 0b11111111, + 0b01101001, 0b10100000, + 0b10010000, 0b01100000}; + + private static Color[][] Bilevel_Result12x4 = new[] { new[] { Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1 }, + new[] { Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1 }, + new[] { Bit0, Bit1, Bit1, Bit0, Bit1, Bit0, Bit0, Bit1, Bit1, Bit0, Bit1, Bit0 }, + new[] { Bit1, Bit0, Bit0, Bit1, Bit0, Bit0, Bit0, Bit0, Bit0, Bit1, Bit1, Bit0 }}; + + private static byte[] Grayscale4_Bytes4x4 = new byte[] { 0x8F, 0x0F, + 0xFF, 0xFF, + 0x08, 0x8F, + 0xF0, 0xF8 }; + + private static Color[][] Grayscale4_Result4x4 = new[] { new[] { Gray8, GrayF, Gray0, GrayF }, + new[] { GrayF, GrayF, GrayF, GrayF }, + new[] { Gray0, Gray8, Gray8, GrayF }, + new[] { GrayF, Gray0, GrayF, Gray8 }}; + + private static byte[] Grayscale4_Bytes3x4 = new byte[] { 0x8F, 0x00, + 0xFF, 0xF0, + 0x08, 0x80, + 0xF0, 0xF0 }; + + private static Color[][] Grayscale4_Result3x4 = new[] { new[] { Gray8, GrayF, Gray0 }, + new[] { GrayF, GrayF, GrayF }, + new[] { Gray0, Gray8, Gray8 }, + new[] { GrayF, Gray0, GrayF }}; + + private static byte[] Grayscale8_Bytes4x4 = new byte[] { 128, 255, 000, 255, + 255, 255, 255, 255, + 000, 128, 128, 255, + 255, 000, 255, 128 }; + + private static Color[][] Grayscale8_Result4x4 = new[] { new[] { Gray128, Gray255, Gray000, Gray255 }, + new[] { Gray255, Gray255, Gray255, Gray255 }, + new[] { Gray000, Gray128, Gray128, Gray255 }, + new[] { Gray255, Gray000, Gray255, Gray128 }}; + + public static IEnumerable Bilevel_Data + { + get + { + yield return new object[] { Bilevel_Bytes4x4, 1, 0, 0, 4, 4, Bilevel_Result4x4 }; + yield return new object[] { Bilevel_Bytes4x4, 1, 0, 0, 4, 4, Offset(Bilevel_Result4x4, 0, 0, 6, 6) }; + yield return new object[] { Bilevel_Bytes4x4, 1, 1, 0, 4, 4, Offset(Bilevel_Result4x4, 1, 0, 6, 6) }; + yield return new object[] { Bilevel_Bytes4x4, 1, 0, 1, 4, 4, Offset(Bilevel_Result4x4, 0, 1, 6, 6) }; + yield return new object[] { Bilevel_Bytes4x4, 1, 1, 1, 4, 4, Offset(Bilevel_Result4x4, 1, 1, 6, 6) }; + + yield return new object[] { Bilevel_Bytes12x4, 1, 0, 0, 12, 4, Bilevel_Result12x4 }; + yield return new object[] { Bilevel_Bytes12x4, 1, 0, 0, 12, 4, Offset(Bilevel_Result12x4, 0, 0, 18, 6) }; + yield return new object[] { Bilevel_Bytes12x4, 1, 1, 0, 12, 4, Offset(Bilevel_Result12x4, 1, 0, 18, 6) }; + yield return new object[] { Bilevel_Bytes12x4, 1, 0, 1, 12, 4, Offset(Bilevel_Result12x4, 0, 1, 18, 6) }; + yield return new object[] { Bilevel_Bytes12x4, 1, 1, 1, 12, 4, Offset(Bilevel_Result12x4, 1, 1, 18, 6) }; + } + } + + public static IEnumerable Grayscale4_Data + { + get + { + yield return new object[] { Grayscale4_Bytes4x4, 4, 0, 0, 4, 4, Grayscale4_Result4x4 }; + yield return new object[] { Grayscale4_Bytes4x4, 4, 0, 0, 4, 4, Offset(Grayscale4_Result4x4, 0, 0, 6, 6) }; + yield return new object[] { Grayscale4_Bytes4x4, 4, 1, 0, 4, 4, Offset(Grayscale4_Result4x4, 1, 0, 6, 6) }; + yield return new object[] { Grayscale4_Bytes4x4, 4, 0, 1, 4, 4, Offset(Grayscale4_Result4x4, 0, 1, 6, 6) }; + yield return new object[] { Grayscale4_Bytes4x4, 4, 1, 1, 4, 4, Offset(Grayscale4_Result4x4, 1, 1, 6, 6) }; + + yield return new object[] { Grayscale4_Bytes3x4, 4, 0, 0, 3, 4, Grayscale4_Result3x4 }; + yield return new object[] { Grayscale4_Bytes3x4, 4, 0, 0, 3, 4, Offset(Grayscale4_Result3x4, 0, 0, 6, 6) }; + yield return new object[] { Grayscale4_Bytes3x4, 4, 1, 0, 3, 4, Offset(Grayscale4_Result3x4, 1, 0, 6, 6) }; + yield return new object[] { Grayscale4_Bytes3x4, 4, 0, 1, 3, 4, Offset(Grayscale4_Result3x4, 0, 1, 6, 6) }; + yield return new object[] { Grayscale4_Bytes3x4, 4, 1, 1, 3, 4, Offset(Grayscale4_Result3x4, 1, 1, 6, 6) }; + } + } + + public static IEnumerable Grayscale8_Data + { + get + { + yield return new object[] { Grayscale8_Bytes4x4, 8, 0, 0, 4, 4, Grayscale8_Result4x4 }; + yield return new object[] { Grayscale8_Bytes4x4, 8, 0, 0, 4, 4, Offset(Grayscale8_Result4x4, 0, 0, 6, 6) }; + yield return new object[] { Grayscale8_Bytes4x4, 8, 1, 0, 4, 4, Offset(Grayscale8_Result4x4, 1, 0, 6, 6) }; + yield return new object[] { Grayscale8_Bytes4x4, 8, 0, 1, 4, 4, Offset(Grayscale8_Result4x4, 0, 1, 6, 6) }; + yield return new object[] { Grayscale8_Bytes4x4, 8, 1, 1, 4, 4, Offset(Grayscale8_Result4x4, 1, 1, 6, 6) }; + } + } + + [Theory] + [MemberData(nameof(Bilevel_Data))] + public void Decode_WritesPixelData_Bilevel(byte[] inputData, int bitsPerSample, int left, int top, int width, int height, Color[][] expectedResult) + { + AssertDecode(expectedResult, pixels => + { + WhiteIsZero1TiffColor.Decode(inputData, pixels, left, top, width, height); + }); + } + + [Theory] + [MemberData(nameof(Grayscale4_Data))] + public void Decode_WritesPixelData_4Bit(byte[] inputData, int bitsPerSample, int left, int top, int width, int height, Color[][] expectedResult) + { + AssertDecode(expectedResult, pixels => + { + WhiteIsZero4TiffColor.Decode(inputData, pixels, left, top, width, height); + }); + } + + [Theory] + [MemberData(nameof(Grayscale8_Data))] + public void Decode_WritesPixelData_8Bit(byte[] inputData, int bitsPerSample, int left, int top, int width, int height, Color[][] expectedResult) + { + AssertDecode(expectedResult, pixels => + { + WhiteIsZero8TiffColor.Decode(inputData, pixels, left, top, width, height); + }); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs index b949318d9..f302c17b3 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs @@ -178,6 +178,10 @@ namespace ImageSharp.Tests [Theory] [InlineData(false, TiffPhotometricInterpretation.WhiteIsZero, new[] { 8 }, TiffColorType.WhiteIsZero8)] [InlineData(true, TiffPhotometricInterpretation.WhiteIsZero, new[] { 8 }, TiffColorType.WhiteIsZero8)] + [InlineData(false, TiffPhotometricInterpretation.WhiteIsZero, new[] { 4 }, TiffColorType.WhiteIsZero4)] + [InlineData(true, TiffPhotometricInterpretation.WhiteIsZero, new[] { 4 }, TiffColorType.WhiteIsZero4)] + [InlineData(false, TiffPhotometricInterpretation.WhiteIsZero, new[] { 1 }, TiffColorType.WhiteIsZero1)] + [InlineData(true, TiffPhotometricInterpretation.WhiteIsZero, new[] { 1 }, TiffColorType.WhiteIsZero1)] public void ReadImageFormat_DeterminesCorrectColorImplementation(bool isLittleEndian, ushort photometricInterpretation, int[] bitsPerSample, int colorType) { Stream stream = CreateTiffGenIfd() @@ -192,6 +196,23 @@ namespace ImageSharp.Tests Assert.Equal((TiffColorType)colorType, decoder.ColorType); } + [Theory] + [InlineData(false, TiffPhotometricInterpretation.WhiteIsZero, TiffColorType.WhiteIsZero1)] + [InlineData(true, TiffPhotometricInterpretation.WhiteIsZero, TiffColorType.WhiteIsZero1)] + public void ReadImageFormat_DeterminesCorrectColorImplementation_DefaultsToBilevel(bool isLittleEndian, ushort photometricInterpretation, int colorType) + { + Stream stream = CreateTiffGenIfd() + .WithEntry(TiffGenEntry.Integer(TiffTags.PhotometricInterpretation, TiffType.Short, photometricInterpretation)) + .WithoutEntry(TiffTags.BitsPerSample) + .ToStream(isLittleEndian); + + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null, null); + TiffIfd ifd = decoder.ReadIfd(0); + decoder.ReadImageFormat(ifd); + + Assert.Equal((TiffColorType)colorType, decoder.ColorType); + } + // [Theory] // [InlineData(false, new[] { 8 }, TiffColorType.WhiteIsZero8)] // [InlineData(true, new[] { 8 }, TiffColorType.WhiteIsZero8)] @@ -285,9 +306,14 @@ namespace ImageSharp.Tests [Theory] [InlineData(TiffColorType.WhiteIsZero8, 100, 80, 100 * 80)] + [InlineData(TiffColorType.WhiteIsZero4, 100, 80, 50 * 80)] + [InlineData(TiffColorType.WhiteIsZero4, 99, 80, 50 * 80)] + [InlineData(TiffColorType.WhiteIsZero1, 160, 80, 20 * 80)] + [InlineData(TiffColorType.WhiteIsZero1, 153, 80, 20 * 80)] public void CalculateImageBufferSize_ReturnsCorrectSize(ushort colorType, int width, int height, int expectedResult) { TiffDecoderCore decoder = new TiffDecoderCore(null, null); + decoder.ColorType = (TiffColorType)colorType; int bufferSize = decoder.CalculateImageBufferSize(width, height); From c25ba595af31f6e803ed7b36ca5b3d2496ab897e Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Fri, 14 Apr 2017 21:05:52 +0100 Subject: [PATCH 031/275] Add support for PackBits compression --- .../Compression/PackBitsTiffCompression.cs | 79 +++++++++++++++++++ .../Tiff/Compression/TiffCompressionType.cs | 7 +- .../Formats/Tiff/TiffDecoderCore.cs | 9 +++ .../PackBitsTiffCompressionTests.cs | 35 ++++++++ .../Formats/Tiff/TiffDecoderImageTests.cs | 4 +- 5 files changed, 131 insertions(+), 3 deletions(-) create mode 100644 src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs diff --git a/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs new file mode 100644 index 000000000..0ac30e8f1 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs @@ -0,0 +1,79 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats.Tiff +{ + using System; + using System.Buffers; + using System.IO; + using System.Runtime.CompilerServices; + + /// + /// Class to handle cases where TIFF image data is compressed using PackBits compression. + /// + internal static class PackBitsTiffCompression + { + /// + /// Decompresses image data into the supplied buffer. + /// + /// The to read image data from. + /// The number of bytes to read from the input stream. + /// The output buffer for uncompressed data. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Decompress(Stream stream, int byteCount, byte[] buffer) + { + byte[] compressedData = ArrayPool.Shared.Rent(byteCount); + + try + { + stream.ReadFull(compressedData, byteCount); + int compressedOffset = 0; + int decompressedOffset = 0; + + while (compressedOffset < byteCount) + { + byte headerByte = compressedData[compressedOffset]; + + if (headerByte <= (byte)127) + { + int literalOffset = compressedOffset + 1; + int literalLength = compressedData[compressedOffset] + 1; + + Array.Copy(compressedData, literalOffset, buffer, decompressedOffset, literalLength); + + compressedOffset += literalLength + 1; + decompressedOffset += literalLength; + } + else if (headerByte == (byte)0x80) + { + compressedOffset += 1; + } + else + { + byte repeatData = compressedData[compressedOffset + 1]; + int repeatLength = 257 - headerByte; + + ArrayCopyRepeat(repeatData, buffer, decompressedOffset, repeatLength); + + compressedOffset += 2; + decompressedOffset += repeatLength; + } + } + } + finally + { + ArrayPool.Shared.Return(compressedData); + } + } + + private static void ArrayCopyRepeat(byte value, byte[] destinationArray, int destinationIndex, int length) + { + for (int i = 0; i < length; i++) + { + destinationArray[i + destinationIndex] = value; + } + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs index c661ea894..6f9ce8f87 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs @@ -13,6 +13,11 @@ namespace ImageSharp.Formats.Tiff /// /// Image data is stored uncompressed in the TIFF file. /// - None = 0 + None = 0, + + /// + /// Image data is compressed using PackBits compression. + /// + PackBits = 1, } } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index cc2d0f8b7..7c8efad0f 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -267,6 +267,12 @@ namespace ImageSharp.Formats break; } + case TiffCompression.PackBits: + { + this.CompressionType = TiffCompressionType.PackBits; + break; + } + default: { throw new NotSupportedException("The specified TIFF compression format is not supported."); @@ -377,6 +383,9 @@ namespace ImageSharp.Formats case TiffCompressionType.None: NoneTiffCompression.Decompress(this.InputStream, (int)byteCount, buffer); break; + case TiffCompressionType.PackBits: + PackBitsTiffCompression.Decompress(this.InputStream, (int)byteCount, buffer); + break; default: throw new InvalidOperationException(); } diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs new file mode 100644 index 000000000..85a7bd729 --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs @@ -0,0 +1,35 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System.IO; + using Xunit; + + using ImageSharp.Formats.Tiff; + + public class PackBitsTiffCompressionTests + { + [Theory] + [InlineData(new byte[] { }, new byte[] { })] + [InlineData(new byte[] { 0x00, 0x2A }, new byte[] { 0x2A })] // Read one byte + [InlineData(new byte[] { 0x01, 0x15, 0x32 }, new byte[] { 0x15, 0x32 })] // Read two bytes + [InlineData(new byte[] { 0xFF, 0x2A }, new byte[] { 0x2A, 0x2A })] // Repeat two bytes + [InlineData(new byte[] { 0xFE, 0x2A }, new byte[] { 0x2A, 0x2A, 0x2A })] // Repeat three bytes + [InlineData(new byte[] { 0x80 }, new byte[] { })] // Read a 'No operation' byte + [InlineData(new byte[] { 0x01, 0x15, 0x32, 0x80, 0xFF, 0xA2 }, new byte[] { 0x15, 0x32, 0xA2, 0xA2 })] // Read two bytes, nop, repeat two bytes + [InlineData(new byte[] { 0xFE, 0xAA, 0x02, 0x80, 0x00, 0x2A, 0xFD, 0xAA, 0x03, 0x80, 0x00, 0x2A, 0x22, 0xF7, 0xAA }, + new byte[] { 0xAA, 0xAA, 0xAA, 0x80, 0x00, 0x2A, 0xAA, 0xAA, 0xAA, 0xAA, 0x80, 0x00, 0x2A, 0x22, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA })] // Apple PackBits sample + public void Decompress_ReadsData(byte[] inputData, byte[] expectedResult) + { + Stream stream = new MemoryStream(inputData); + byte[] buffer = new byte[expectedResult.Length]; + + PackBitsTiffCompression.Decompress(stream, inputData.Length, buffer); + + Assert.Equal(expectedResult, buffer); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs index f302c17b3..642cc2c0e 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs @@ -123,6 +123,8 @@ namespace ImageSharp.Tests [Theory] [InlineData(false, TiffCompression.None, TiffCompressionType.None)] [InlineData(true, TiffCompression.None, TiffCompressionType.None)] + [InlineData(false, TiffCompression.PackBits, TiffCompressionType.PackBits)] + [InlineData(true, TiffCompression.PackBits, TiffCompressionType.PackBits)] public void ReadImageFormat_DeterminesCorrectCompressionImplementation(bool isLittleEndian, ushort compression, int compressionType) { Stream stream = CreateTiffGenIfd() @@ -147,7 +149,6 @@ namespace ImageSharp.Tests [InlineData(false, TiffCompression.Lzw)] [InlineData(false, TiffCompression.OldDeflate)] [InlineData(false, TiffCompression.OldJpeg)] - [InlineData(false, TiffCompression.PackBits)] [InlineData(false, 999)] [InlineData(true, TiffCompression.Ccitt1D)] [InlineData(true, TiffCompression.CcittGroup3Fax)] @@ -159,7 +160,6 @@ namespace ImageSharp.Tests [InlineData(true, TiffCompression.Lzw)] [InlineData(true, TiffCompression.OldDeflate)] [InlineData(true, TiffCompression.OldJpeg)] - [InlineData(true, TiffCompression.PackBits)] [InlineData(true, 999)] public void ReadImageFormat_ThrowsExceptionForUnsupportedCompression(bool isLittleEndian, ushort compression) { From 12872c06292a27e28206222738c3fb372ad4c2c7 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Fri, 14 Apr 2017 21:55:13 +0100 Subject: [PATCH 032/275] Add support for multi-strip TIFF files --- .../Formats/Tiff/TiffDecoderCore.cs | 44 +++++++++++++------ 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 7c8efad0f..ab84dd31e 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -224,26 +224,44 @@ namespace ImageSharp.Formats int rowsPerStrip = (int)this.ReadUnsignedInteger(ref rowsPerStripEntry); uint[] stripOffsets = this.ReadUnsignedIntegerArray(ref stripOffsetsEntry); uint[] stripByteCounts = this.ReadUnsignedIntegerArray(ref stripByteCountsEntry); + DecodeImageStrips(image, rowsPerStrip, stripOffsets, stripByteCounts); + } - int uncompressedStripSize = this.CalculateImageBufferSize(width, rowsPerStrip); + return image; + } - using (PixelAccessor pixels = image.Lock()) - { - byte[] stripBytes = ArrayPool.Shared.Rent(uncompressedStripSize); + /// + /// Decodes the image data for strip encoded data. + /// + /// The pixel format. + /// The image to decode data into. + /// The number of rows per strip of data. + /// An array of byte offsets to each strip in the image. + /// An array of the size of each strip (in bytes). + private void DecodeImageStrips(Image image, int rowsPerStrip, uint[] stripOffsets, uint[] stripByteCounts) + where TColor : struct, IPixel + { + int uncompressedStripSize = this.CalculateImageBufferSize(image.Width, rowsPerStrip); - try - { - this.DecompressImageBlock(stripOffsets[0], stripByteCounts[0], stripBytes); - this.ProcessImageBlock(stripBytes, pixels, 0, 0, width, rowsPerStrip); - } - finally + using (PixelAccessor pixels = image.Lock()) + { + byte[] stripBytes = ArrayPool.Shared.Rent(uncompressedStripSize); + + try + { + for (int i = 0; i < stripOffsets.Length; i++) { - ArrayPool.Shared.Return(stripBytes); + int stripHeight = i < stripOffsets.Length - 1 || image.Height % rowsPerStrip == 0 ? rowsPerStrip : image.Height % rowsPerStrip; + + this.DecompressImageBlock(stripOffsets[i], stripByteCounts[i], stripBytes); + this.ProcessImageBlock(stripBytes, pixels, 0, rowsPerStrip * i, image.Width, stripHeight); } } + finally + { + ArrayPool.Shared.Return(stripBytes); + } } - - return image; } /// From 4f801ae9af781bc652a1fc825501c01e9b0a6123 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Mon, 15 May 2017 14:24:40 +0100 Subject: [PATCH 033/275] Update namespaces and naming --- .../Formats/Tiff/ImageExtensions.cs | 20 ++-- .../WhiteIsZero1TiffColor.cs | 9 +- .../WhiteIsZero4TiffColor.cs | 9 +- .../WhiteIsZero8TiffColor.cs | 9 +- src/ImageSharp/Formats/Tiff/TiffDecoder.cs | 7 +- .../Formats/Tiff/TiffDecoderCore.cs | 93 ++++++++++--------- src/ImageSharp/Formats/Tiff/TiffEncoder.cs | 15 +-- .../PhotometricInterpretationTestBase.cs | 14 +-- .../WhiteIsZeroTiffColorTests.cs | 32 +++---- .../Formats/Tiff/TiffDecoderHeaderTests.cs | 2 +- .../Formats/Tiff/TiffDecoderImageTests.cs | 8 +- 11 files changed, 112 insertions(+), 106 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/ImageExtensions.cs b/src/ImageSharp/Formats/Tiff/ImageExtensions.cs index 01384b827..db160bd9f 100644 --- a/src/ImageSharp/Formats/Tiff/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Tiff/ImageExtensions.cs @@ -6,26 +6,26 @@ namespace ImageSharp { using System.IO; - using Formats; + using ImageSharp.PixelFormats; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Saves the image to the given stream with the tiff format. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The stream to save the image to. /// Thrown if the stream is null. /// - /// The . + /// The . /// - public static Image SaveAsTiff(this Image source, Stream stream) - where TColor : struct, IPixel + public static Image SaveAsTiff(this Image source, Stream stream) + where TPixel : struct, IPixel { return SaveAsTiff(source, stream, null); } @@ -33,16 +33,16 @@ namespace ImageSharp /// /// Saves the image to the given stream with the tiff format. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The stream to save the image to. /// The options for the encoder. /// Thrown if the stream is null. /// - /// The . + /// The . /// - public static Image SaveAsTiff(this Image source, Stream stream, ITiffEncoderOptions options) - where TColor : struct, IPixel + public static Image SaveAsTiff(this Image source, Stream stream, ITiffEncoderOptions options) + where TPixel : struct, IPixel { TiffEncoder encoder = new TiffEncoder(); encoder.Encode(source, stream, options); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs index 5e486c7fe..34bc5e731 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs @@ -8,6 +8,7 @@ namespace ImageSharp.Formats.Tiff using System; using System.Runtime.CompilerServices; using ImageSharp; + using ImageSharp.PixelFormats; /// /// Implements the 'WhiteIsZero' photometric interpretation (optimised for bilevel images). @@ -17,7 +18,7 @@ namespace ImageSharp.Formats.Tiff /// /// Decodes pixel data using the current photometric interpretation. /// - /// The pixel format. + /// The pixel format. /// The buffer to read image data from. /// The image buffer to write pixels to. /// The x-coordinate of the left-hand side of the image block. @@ -25,10 +26,10 @@ namespace ImageSharp.Formats.Tiff /// The width of the image block. /// The height of the image block. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decode(byte[] data, PixelAccessor pixels, int left, int top, int width, int height) - where TColor : struct, IPixel + public static void Decode(byte[] data, PixelAccessor pixels, int left, int top, int width, int height) + where TPixel : struct, IPixel { - TColor color = default(TColor); + TPixel color = default(TPixel); uint offset = 0; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs index 98f74dca0..00653feb4 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs @@ -7,6 +7,7 @@ namespace ImageSharp.Formats.Tiff { using System.Runtime.CompilerServices; using ImageSharp; + using ImageSharp.PixelFormats; /// /// Implements the 'WhiteIsZero' photometric interpretation (optimised for 4-bit grayscale images). @@ -16,7 +17,7 @@ namespace ImageSharp.Formats.Tiff /// /// Decodes pixel data using the current photometric interpretation. /// - /// The pixel format. + /// The pixel format. /// The buffer to read image data from. /// The image buffer to write pixels to. /// The x-coordinate of the left-hand side of the image block. @@ -24,10 +25,10 @@ namespace ImageSharp.Formats.Tiff /// The width of the image block. /// The height of the image block. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decode(byte[] data, PixelAccessor pixels, int left, int top, int width, int height) - where TColor : struct, IPixel + public static void Decode(byte[] data, PixelAccessor pixels, int left, int top, int width, int height) + where TPixel : struct, IPixel { - TColor color = default(TColor); + TPixel color = default(TPixel); uint offset = 0; bool isOddWidth = (width & 1) == 1; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs index 8ddafd983..8168839ad 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs @@ -7,6 +7,7 @@ namespace ImageSharp.Formats.Tiff { using System.Runtime.CompilerServices; using ImageSharp; + using ImageSharp.PixelFormats; /// /// Implements the 'WhiteIsZero' photometric interpretation (optimised for 8-bit grayscale images). @@ -16,7 +17,7 @@ namespace ImageSharp.Formats.Tiff /// /// Decodes pixel data using the current photometric interpretation. /// - /// The pixel format. + /// The pixel format. /// The buffer to read image data from. /// The image buffer to write pixels to. /// The x-coordinate of the left-hand side of the image block. @@ -24,10 +25,10 @@ namespace ImageSharp.Formats.Tiff /// The width of the image block. /// The height of the image block. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decode(byte[] data, PixelAccessor pixels, int left, int top, int width, int height) - where TColor : struct, IPixel + public static void Decode(byte[] data, PixelAccessor pixels, int left, int top, int width, int height) + where TPixel : struct, IPixel { - TColor color = default(TColor); + TPixel color = default(TPixel); uint offset = 0; diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs index 333c707e3..6a605c878 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs @@ -6,6 +6,7 @@ namespace ImageSharp.Formats { using System.IO; + using ImageSharp.PixelFormats; /// /// Image decoder for generating an image out of a TIFF stream. @@ -13,14 +14,14 @@ namespace ImageSharp.Formats public class TiffDecoder : IImageDecoder { /// - public Image Decode(Configuration configuration, Stream stream, IDecoderOptions options) - where TColor : struct, IPixel + public Image Decode(Configuration configuration, Stream stream, IDecoderOptions options) + where TPixel : struct, IPixel { Guard.NotNull(stream, "stream"); using (TiffDecoderCore decoder = new TiffDecoderCore(options, configuration)) { - return decoder.Decode(stream); + return decoder.Decode(stream); } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index ab84dd31e..225bddb1e 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -10,6 +10,7 @@ namespace ImageSharp.Formats using System.IO; using System.Text; using ImageSharp.Formats.Tiff; + using ImageSharp.PixelFormats; /// /// Performs the tiff decoding operation. @@ -82,17 +83,17 @@ namespace ImageSharp.Formats /// Decodes the image from the specified and sets /// the data to image. /// - /// The pixel format. + /// The pixel format. /// The stream, where the image should be. /// The decoded image. - public Image Decode(Stream stream) - where TColor : struct, IPixel + public Image Decode(Stream stream) + where TPixel : struct, IPixel { this.InputStream = stream; uint firstIfdOffset = this.ReadHeader(); TiffIfd firstIfd = this.ReadIfd(firstIfdOffset); - Image image = this.DecodeImage(firstIfd); + Image image = this.DecodeImage(firstIfd); return image; } @@ -175,11 +176,11 @@ namespace ImageSharp.Formats /// /// Decodes the image data from a specified IFD. /// - /// The pixel format. + /// The pixel format. /// The IFD to read the image from. /// The decoded image. - public Image DecodeImage(TiffIfd ifd) - where TColor : struct, IPixel + public Image DecodeImage(TiffIfd ifd) + where TPixel : struct, IPixel { if (!ifd.TryGetIfdEntry(TiffTags.ImageLength, out TiffIfdEntry imageLengthEntry) || !ifd.TryGetIfdEntry(TiffTags.ImageWidth, out TiffIfdEntry imageWidthEntry)) @@ -190,7 +191,7 @@ namespace ImageSharp.Formats int width = (int)this.ReadUnsignedInteger(ref imageWidthEntry); int height = (int)this.ReadUnsignedInteger(ref imageLengthEntry); - Image image = Image.Create(width, height, this.configuration); + Image image = new Image(this.configuration, width, height); TiffResolutionUnit resolutionUnit = TiffResolutionUnit.Inch; if (ifd.TryGetIfdEntry(TiffTags.ResolutionUnit, out TiffIfdEntry resolutionUnitEntry)) @@ -224,46 +225,12 @@ namespace ImageSharp.Formats int rowsPerStrip = (int)this.ReadUnsignedInteger(ref rowsPerStripEntry); uint[] stripOffsets = this.ReadUnsignedIntegerArray(ref stripOffsetsEntry); uint[] stripByteCounts = this.ReadUnsignedIntegerArray(ref stripByteCountsEntry); - DecodeImageStrips(image, rowsPerStrip, stripOffsets, stripByteCounts); + this.DecodeImageStrips(image, rowsPerStrip, stripOffsets, stripByteCounts); } return image; } - /// - /// Decodes the image data for strip encoded data. - /// - /// The pixel format. - /// The image to decode data into. - /// The number of rows per strip of data. - /// An array of byte offsets to each strip in the image. - /// An array of the size of each strip (in bytes). - private void DecodeImageStrips(Image image, int rowsPerStrip, uint[] stripOffsets, uint[] stripByteCounts) - where TColor : struct, IPixel - { - int uncompressedStripSize = this.CalculateImageBufferSize(image.Width, rowsPerStrip); - - using (PixelAccessor pixels = image.Lock()) - { - byte[] stripBytes = ArrayPool.Shared.Rent(uncompressedStripSize); - - try - { - for (int i = 0; i < stripOffsets.Length; i++) - { - int stripHeight = i < stripOffsets.Length - 1 || image.Height % rowsPerStrip == 0 ? rowsPerStrip : image.Height % rowsPerStrip; - - this.DecompressImageBlock(stripOffsets[i], stripByteCounts[i], stripBytes); - this.ProcessImageBlock(stripBytes, pixels, 0, rowsPerStrip * i, image.Width, stripHeight); - } - } - finally - { - ArrayPool.Shared.Return(stripBytes); - } - } - } - /// /// Determines the TIFF compression and color types, and reads any associated parameters. /// @@ -412,15 +379,15 @@ namespace ImageSharp.Formats /// /// Decodes pixel data using the current photometric interpretation. /// - /// The pixel format. + /// The pixel format. /// The buffer to read image data from. /// The image buffer to write pixels to. /// The x-coordinate of the left-hand side of the image block. /// The y-coordinate of the top of the image block. /// The width of the image block. /// The height of the image block. - public void ProcessImageBlock(byte[] data, PixelAccessor pixels, int left, int top, int width, int height) - where TColor : struct, IPixel + public void ProcessImageBlock(byte[] data, PixelAccessor pixels, int left, int top, int width, int height) + where TPixel : struct, IPixel { switch (this.ColorType) { @@ -984,5 +951,39 @@ namespace ImageSharp.Formats return BitConverter.ToDouble(buffer, 0); } + + /// + /// Decodes the image data for strip encoded data. + /// + /// The pixel format. + /// The image to decode data into. + /// The number of rows per strip of data. + /// An array of byte offsets to each strip in the image. + /// An array of the size of each strip (in bytes). + private void DecodeImageStrips(Image image, int rowsPerStrip, uint[] stripOffsets, uint[] stripByteCounts) + where TPixel : struct, IPixel + { + int uncompressedStripSize = this.CalculateImageBufferSize(image.Width, rowsPerStrip); + + using (PixelAccessor pixels = image.Lock()) + { + byte[] stripBytes = ArrayPool.Shared.Rent(uncompressedStripSize); + + try + { + for (int i = 0; i < stripOffsets.Length; i++) + { + int stripHeight = i < stripOffsets.Length - 1 || image.Height % rowsPerStrip == 0 ? rowsPerStrip : image.Height % rowsPerStrip; + + this.DecompressImageBlock(stripOffsets[i], stripByteCounts[i], stripBytes); + this.ProcessImageBlock(stripBytes, pixels, 0, rowsPerStrip * i, image.Width, stripHeight); + } + } + finally + { + ArrayPool.Shared.Return(stripBytes); + } + } + } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs index c54a43ede..7bcb575db 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs @@ -7,6 +7,7 @@ namespace ImageSharp.Formats { using System; using System.IO; + using ImageSharp.PixelFormats; /// /// Encoder for writing the data image to a stream in TIFF format. @@ -14,8 +15,8 @@ namespace ImageSharp.Formats public class TiffEncoder : IImageEncoder { /// - public void Encode(Image image, Stream stream, IEncoderOptions options) - where TColor : struct, IPixel + public void Encode(Image image, Stream stream, IEncoderOptions options) + where TPixel : struct, IPixel { ITiffEncoderOptions tiffOptions = TiffEncoderOptions.Create(options); @@ -23,14 +24,14 @@ namespace ImageSharp.Formats } /// - /// Encodes the image to the specified stream from the . + /// Encodes the image to the specified stream from the . /// - /// The pixel format. - /// The to encode from. + /// The pixel format. + /// The to encode from. /// The to encode the image data to. /// The options for the encoder. - public void Encode(Image image, Stream stream, ITiffEncoderOptions options) - where TColor : struct, IPixel + public void Encode(Image image, Stream stream, ITiffEncoderOptions options) + where TPixel : struct, IPixel { throw new NotImplementedException(); } diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs index 3c245855d..ab9a89116 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs @@ -10,16 +10,16 @@ namespace ImageSharp.Tests public abstract class PhotometricInterpretationTestBase { - public static Color[][] Offset(Color[][] input, int xOffset, int yOffset, int width, int height) + public static Rgba32[][] Offset(Rgba32[][] input, int xOffset, int yOffset, int width, int height) { int inputHeight = input.Length; int inputWidth = input[0].Length; - Color[][] output = new Color[height][]; + Rgba32[][] output = new Rgba32[height][]; for (int y = 0; y < output.Length; y++) { - output[y] = new Color[width]; + output[y] = new Rgba32[width]; } for (int y = 0; y < inputHeight; y++) @@ -33,18 +33,18 @@ namespace ImageSharp.Tests return output; } - public static void AssertDecode(Color[][] expectedResult, Action> decodeAction) + public static void AssertDecode(Rgba32[][] expectedResult, Action> decodeAction) { int resultWidth = expectedResult[0].Length; int resultHeight = expectedResult.Length; - Image image = new Image(resultWidth, resultHeight); + Image image = new Image(resultWidth, resultHeight); - using (PixelAccessor pixels = image.Lock()) + using (PixelAccessor pixels = image.Lock()) { decodeAction(pixels); } - using (PixelAccessor pixels = image.Lock()) + using (PixelAccessor pixels = image.Lock()) { for (int y = 0; y < resultHeight; y++) { diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs index 8769d472b..ce04e0225 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs @@ -12,21 +12,21 @@ namespace ImageSharp.Tests public class WhiteIsZeroTiffColorTests : PhotometricInterpretationTestBase { - private static Color Gray000 = new Color(255, 255, 255, 255); - private static Color Gray128 = new Color(127, 127, 127, 255); - private static Color Gray255 = new Color(0, 0, 0, 255); - private static Color Gray0 = new Color(255, 255, 255, 255); - private static Color Gray8 = new Color(119, 119, 119, 255); - private static Color GrayF = new Color(0, 0, 0, 255); - private static Color Bit0 = new Color(255, 255, 255, 255); - private static Color Bit1 = new Color(0, 0, 0, 255); + private static Rgba32 Gray000 = new Rgba32(255, 255, 255, 255); + private static Rgba32 Gray128 = new Rgba32(127, 127, 127, 255); + private static Rgba32 Gray255 = new Rgba32(0, 0, 0, 255); + private static Rgba32 Gray0 = new Rgba32(255, 255, 255, 255); + private static Rgba32 Gray8 = new Rgba32(119, 119, 119, 255); + private static Rgba32 GrayF = new Rgba32(0, 0, 0, 255); + private static Rgba32 Bit0 = new Rgba32(255, 255, 255, 255); + private static Rgba32 Bit1 = new Rgba32(0, 0, 0, 255); private static byte[] Bilevel_Bytes4x4 = new byte[] { 0b01010000, 0b11110000, 0b01110000, 0b10010000 }; - private static Color[][] Bilevel_Result4x4 = new[] { new[] { Bit0, Bit1, Bit0, Bit1 }, + private static Rgba32[][] Bilevel_Result4x4 = new[] { new[] { Bit0, Bit1, Bit0, Bit1 }, new[] { Bit1, Bit1, Bit1, Bit1 }, new[] { Bit0, Bit1, Bit1, Bit1 }, new[] { Bit1, Bit0, Bit0, Bit1 }}; @@ -36,7 +36,7 @@ namespace ImageSharp.Tests 0b01101001, 0b10100000, 0b10010000, 0b01100000}; - private static Color[][] Bilevel_Result12x4 = new[] { new[] { Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1 }, + private static Rgba32[][] Bilevel_Result12x4 = new[] { new[] { Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1 }, new[] { Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1 }, new[] { Bit0, Bit1, Bit1, Bit0, Bit1, Bit0, Bit0, Bit1, Bit1, Bit0, Bit1, Bit0 }, new[] { Bit1, Bit0, Bit0, Bit1, Bit0, Bit0, Bit0, Bit0, Bit0, Bit1, Bit1, Bit0 }}; @@ -46,7 +46,7 @@ namespace ImageSharp.Tests 0x08, 0x8F, 0xF0, 0xF8 }; - private static Color[][] Grayscale4_Result4x4 = new[] { new[] { Gray8, GrayF, Gray0, GrayF }, + private static Rgba32[][] Grayscale4_Result4x4 = new[] { new[] { Gray8, GrayF, Gray0, GrayF }, new[] { GrayF, GrayF, GrayF, GrayF }, new[] { Gray0, Gray8, Gray8, GrayF }, new[] { GrayF, Gray0, GrayF, Gray8 }}; @@ -56,7 +56,7 @@ namespace ImageSharp.Tests 0x08, 0x80, 0xF0, 0xF0 }; - private static Color[][] Grayscale4_Result3x4 = new[] { new[] { Gray8, GrayF, Gray0 }, + private static Rgba32[][] Grayscale4_Result3x4 = new[] { new[] { Gray8, GrayF, Gray0 }, new[] { GrayF, GrayF, GrayF }, new[] { Gray0, Gray8, Gray8 }, new[] { GrayF, Gray0, GrayF }}; @@ -66,7 +66,7 @@ namespace ImageSharp.Tests 000, 128, 128, 255, 255, 000, 255, 128 }; - private static Color[][] Grayscale8_Result4x4 = new[] { new[] { Gray128, Gray255, Gray000, Gray255 }, + private static Rgba32[][] Grayscale8_Result4x4 = new[] { new[] { Gray128, Gray255, Gray000, Gray255 }, new[] { Gray255, Gray255, Gray255, Gray255 }, new[] { Gray000, Gray128, Gray128, Gray255 }, new[] { Gray255, Gray000, Gray255, Gray128 }}; @@ -121,7 +121,7 @@ namespace ImageSharp.Tests [Theory] [MemberData(nameof(Bilevel_Data))] - public void Decode_WritesPixelData_Bilevel(byte[] inputData, int bitsPerSample, int left, int top, int width, int height, Color[][] expectedResult) + public void Decode_WritesPixelData_Bilevel(byte[] inputData, int bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult) { AssertDecode(expectedResult, pixels => { @@ -131,7 +131,7 @@ namespace ImageSharp.Tests [Theory] [MemberData(nameof(Grayscale4_Data))] - public void Decode_WritesPixelData_4Bit(byte[] inputData, int bitsPerSample, int left, int top, int width, int height, Color[][] expectedResult) + public void Decode_WritesPixelData_4Bit(byte[] inputData, int bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult) { AssertDecode(expectedResult, pixels => { @@ -141,7 +141,7 @@ namespace ImageSharp.Tests [Theory] [MemberData(nameof(Grayscale8_Data))] - public void Decode_WritesPixelData_8Bit(byte[] inputData, int bitsPerSample, int left, int top, int width, int height, Color[][] expectedResult) + public void Decode_WritesPixelData_8Bit(byte[] inputData, int bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult) { AssertDecode(expectedResult, pixels => { diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs index 48d64b71c..ae581d293 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs @@ -111,7 +111,7 @@ namespace ImageSharp.Tests private void TestDecode(TiffDecoder decoder, Stream stream) { Configuration.Default.AddImageFormat(new TiffFormat()); - Image image = decoder.Decode(Configuration.Default, stream, null); + Image image = decoder.Decode(Configuration.Default, stream, null); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs index 642cc2c0e..2cf638459 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs @@ -31,7 +31,7 @@ namespace ImageSharp.Tests TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null, null); TiffIfd ifd = decoder.ReadIfd(0); - Image image = decoder.DecodeImage(ifd); + Image image = decoder.DecodeImage(ifd); Assert.Equal(ImageWidth, image.Width); Assert.Equal(ImageHeight, image.Height); @@ -82,7 +82,7 @@ namespace ImageSharp.Tests TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null, null); TiffIfd ifd = decoder.ReadIfd(0); - Image image = decoder.DecodeImage(ifd); + Image image = decoder.DecodeImage(ifd); Assert.Equal(expectedHorizonalResolution, image.MetaData.HorizontalResolution, 10); Assert.Equal(expectedVerticalResolution, image.MetaData.VerticalResolution, 10); @@ -99,7 +99,7 @@ namespace ImageSharp.Tests TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null, null); TiffIfd ifd = decoder.ReadIfd(0); - var e = Assert.Throws(() => decoder.DecodeImage(ifd)); + var e = Assert.Throws(() => decoder.DecodeImage(ifd)); Assert.Equal("The TIFF IFD does not specify the image dimensions.", e.Message); } @@ -115,7 +115,7 @@ namespace ImageSharp.Tests TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null, null); TiffIfd ifd = decoder.ReadIfd(0); - var e = Assert.Throws(() => decoder.DecodeImage(ifd)); + var e = Assert.Throws(() => decoder.DecodeImage(ifd)); Assert.Equal("The TIFF IFD does not specify the image dimensions.", e.Message); } From 5e4c4f392d001aea06faf5a1fec17e553e242b5e Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Mon, 15 May 2017 15:49:58 +0100 Subject: [PATCH 034/275] Add default WhiteIsZero implementation --- .../TiffColorType.cs | 5 ++ .../WhiteIsZeroTiffColor.cs | 53 +++++++++++++++ .../Formats/Tiff/TiffDecoderCore.cs | 33 ++++++---- .../Formats/Tiff/Utils/BitReader.cs | 65 +++++++++++++++++++ .../WhiteIsZeroTiffColorTests.cs | 12 ++++ .../Formats/Tiff/TiffDecoderImageTests.cs | 52 +++++++++++---- 6 files changed, 194 insertions(+), 26 deletions(-) create mode 100644 src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs create mode 100644 src/ImageSharp/Formats/Tiff/Utils/BitReader.cs diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs index be9e14df4..b86e17959 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs @@ -10,6 +10,11 @@ namespace ImageSharp.Formats.Tiff /// internal enum TiffColorType { + /// + /// Grayscale: 0 is imaged as white. The maximum value is imaged as black. + /// + WhiteIsZero, + /// /// Grayscale: 0 is imaged as white. The maximum value is imaged as black. Optimised implementation for bilevel images. /// diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs new file mode 100644 index 000000000..876ea8789 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs @@ -0,0 +1,53 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats.Tiff +{ + using System; + using System.Numerics; + using System.Runtime.CompilerServices; + using ImageSharp; + using ImageSharp.PixelFormats; + + /// + /// Implements the 'WhiteIsZero' photometric interpretation (for all bit depths). + /// + internal static class WhiteIsZeroTiffColor + { + /// + /// Decodes pixel data using the current photometric interpretation. + /// + /// The pixel format. + /// The buffer to read image data from. + /// The number of bits per sample for each pixel. + /// The image buffer to write pixels to. + /// The x-coordinate of the left-hand side of the image block. + /// The y-coordinate of the top of the image block. + /// The width of the image block. + /// The height of the image block. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Decode(byte[] data, uint[] bitsPerSample, PixelAccessor pixels, int left, int top, int width, int height) + where TPixel : struct, IPixel + { + TPixel color = default(TPixel); + + BitReader bitReader = new BitReader(data); + float factor = (float)Math.Pow(2, bitsPerSample[0]) - 1.0f; + + for (int y = top; y < top + height; y++) + { + for (int x = left; x < left + width; x++) + { + int value = bitReader.ReadBits(bitsPerSample[0]); + float intensity = 1.0f - (((float)value) / factor); + color.PackFromVector4(new Vector4(intensity, intensity, intensity, 1.0f)); + pixels[x, y] = color; + } + + bitReader.NextRow(); + } + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 225bddb1e..98635eca7 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -52,6 +52,11 @@ namespace ImageSharp.Formats this.IsLittleEndian = isLittleEndian; } + /// + /// Gets or sets the number of bits for each sample of the pixel format used to encode the image. + /// + public uint[] BitsPerSample { get; set; } + /// /// Gets or sets the photometric interpretation implementation to use when decoding the image. /// @@ -288,11 +293,11 @@ namespace ImageSharp.Formats { if (ifd.TryGetIfdEntry(TiffTags.BitsPerSample, out TiffIfdEntry bitsPerSampleEntry)) { - uint[] bitsPerSample = this.ReadUnsignedIntegerArray(ref bitsPerSampleEntry); + this.BitsPerSample = this.ReadUnsignedIntegerArray(ref bitsPerSampleEntry); - if (bitsPerSample.Length == 1) + if (this.BitsPerSample.Length == 1) { - switch (bitsPerSample[0]) + switch (this.BitsPerSample[0]) { case 8: { @@ -314,7 +319,8 @@ namespace ImageSharp.Formats default: { - throw new NotSupportedException("The specified TIFF bit-depth is not supported."); + this.ColorType = TiffColorType.WhiteIsZero; + break; } } } @@ -322,6 +328,7 @@ namespace ImageSharp.Formats else { this.ColorType = TiffColorType.WhiteIsZero1; + this.BitsPerSample = new[] { 1u }; } break; @@ -340,17 +347,14 @@ namespace ImageSharp.Formats /// The size (in bytes) of the required pixel buffer. public int CalculateImageBufferSize(int width, int height) { - switch (this.ColorType) + uint bitsPerPixel = 0; + for (int i = 0; i < this.BitsPerSample.Length; i++) { - case TiffColorType.WhiteIsZero1: - return ((width + 7) / 8) * height; - case TiffColorType.WhiteIsZero4: - return ((width + 1) / 2) * height; - case TiffColorType.WhiteIsZero8: - return width * height; - default: - throw new InvalidOperationException(); + bitsPerPixel += this.BitsPerSample[i]; } + + int bytesPerRow = ((width * (int)bitsPerPixel) + 7) / 8; + return bytesPerRow * height; } /// @@ -391,6 +395,9 @@ namespace ImageSharp.Formats { switch (this.ColorType) { + case TiffColorType.WhiteIsZero: + WhiteIsZeroTiffColor.Decode(data, this.BitsPerSample, pixels, left, top, width, height); + break; case TiffColorType.WhiteIsZero1: WhiteIsZero1TiffColor.Decode(data, pixels, left, top, width, height); break; diff --git a/src/ImageSharp/Formats/Tiff/Utils/BitReader.cs b/src/ImageSharp/Formats/Tiff/Utils/BitReader.cs new file mode 100644 index 000000000..f330690f9 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Utils/BitReader.cs @@ -0,0 +1,65 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// +namespace ImageSharp.Formats.Tiff +{ + using System.IO; + + /// + /// Utility class to read a sequence of bits from an array + /// + internal class BitReader + { + private readonly byte[] array; + private int offset; + private int bitOffset; + + /// + /// Initializes a new instance of the class. + /// + /// The array to read data from. + public BitReader(byte[] array) + { + this.array = array; + } + + /// + /// Reads the specified number of bits from the array. + /// + /// The number of bits to read. + /// The value read from the array. + public int ReadBits(uint bits) + { + int value = 0; + + for (uint i = 0; i < bits; i++) + { + int bit = (this.array[this.offset] >> (7 - this.bitOffset)) & 0x01; + value = (value << 1) | bit; + + this.bitOffset++; + + if (this.bitOffset == 8) + { + this.bitOffset = 0; + this.offset++; + } + } + + return value; + } + + /// + /// Moves the reader to the next row of byte-aligned data. + /// + public void NextRow() + { + if (this.bitOffset > 0) + { + this.bitOffset = 0; + this.offset++; + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs index ce04e0225..e9d9556bd 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs @@ -119,6 +119,18 @@ namespace ImageSharp.Tests } } + [Theory] + [MemberData(nameof(Bilevel_Data))] + [MemberData(nameof(Grayscale4_Data))] + [MemberData(nameof(Grayscale8_Data))] + public void Decode_WritesPixelData(byte[] inputData, int bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult) + { + AssertDecode(expectedResult, pixels => + { + WhiteIsZeroTiffColor.Decode(inputData, new[] { (uint)bitsPerSample }, pixels, left, top, width, height); + }); + } + [Theory] [MemberData(nameof(Bilevel_Data))] public void Decode_WritesPixelData_Bilevel(byte[] inputData, int bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult) diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs index 2cf638459..168ecf0bc 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs @@ -176,6 +176,8 @@ namespace ImageSharp.Tests } [Theory] + [InlineData(false, TiffPhotometricInterpretation.WhiteIsZero, new[] { 3 }, TiffColorType.WhiteIsZero)] + [InlineData(true, TiffPhotometricInterpretation.WhiteIsZero, new[] { 3 }, TiffColorType.WhiteIsZero)] [InlineData(false, TiffPhotometricInterpretation.WhiteIsZero, new[] { 8 }, TiffColorType.WhiteIsZero8)] [InlineData(true, TiffPhotometricInterpretation.WhiteIsZero, new[] { 8 }, TiffColorType.WhiteIsZero8)] [InlineData(false, TiffPhotometricInterpretation.WhiteIsZero, new[] { 4 }, TiffColorType.WhiteIsZero4)] @@ -287,33 +289,57 @@ namespace ImageSharp.Tests } [Theory] - [InlineData(false, TiffPhotometricInterpretation.WhiteIsZero, new[] { 3 })] - [InlineData(true, TiffPhotometricInterpretation.WhiteIsZero, new[] { 3 })] - public void ReadImageFormat_ThrowsExceptionForUnsupportedBitDepth(bool isLittleEndian, ushort photometricInterpretation, int[] bitsPerSample) + [InlineData(false, new[] { 8u })] + [InlineData(true, new[] { 8u })] + [InlineData(false, new[] { 4u })] + [InlineData(true, new[] { 4u })] + [InlineData(false, new[] { 1u })] + [InlineData(true, new[] { 1u })] + [InlineData(false, new[] { 1u, 2u, 3u })] + [InlineData(true, new[] { 1u, 2u, 3u })] + [InlineData(false, new[] { 8u, 8u, 8u })] + [InlineData(true, new[] { 8u, 8u, 8u })] + public void ReadImageFormat_ReadsBitsPerSample(bool isLittleEndian, uint[] bitsPerSample) { Stream stream = CreateTiffGenIfd() - .WithEntry(TiffGenEntry.Integer(TiffTags.PhotometricInterpretation, TiffType.Short, photometricInterpretation)) .WithEntry(TiffGenEntry.Integer(TiffTags.BitsPerSample, TiffType.Short, bitsPerSample)) .ToStream(isLittleEndian); TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null, null); TiffIfd ifd = decoder.ReadIfd(0); + decoder.ReadImageFormat(ifd); - var e = Assert.Throws(() => decoder.ReadImageFormat(ifd)); + Assert.Equal(bitsPerSample, decoder.BitsPerSample); + } + + [Theory] + [InlineData(false, TiffPhotometricInterpretation.WhiteIsZero)] + [InlineData(true, TiffPhotometricInterpretation.WhiteIsZero)] + public void ReadImageFormat_ReadsBitsPerSample_DefaultsToBilevel(bool isLittleEndian, ushort photometricInterpretation) + { + Stream stream = CreateTiffGenIfd() + .WithEntry(TiffGenEntry.Integer(TiffTags.PhotometricInterpretation, TiffType.Short, photometricInterpretation)) + .WithoutEntry(TiffTags.BitsPerSample) + .ToStream(isLittleEndian); + + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null, null); + TiffIfd ifd = decoder.ReadIfd(0); + decoder.ReadImageFormat(ifd); - Assert.Equal("The specified TIFF bit-depth is not supported.", e.Message); + Assert.Equal(new[] { 1u }, decoder.BitsPerSample); } [Theory] - [InlineData(TiffColorType.WhiteIsZero8, 100, 80, 100 * 80)] - [InlineData(TiffColorType.WhiteIsZero4, 100, 80, 50 * 80)] - [InlineData(TiffColorType.WhiteIsZero4, 99, 80, 50 * 80)] - [InlineData(TiffColorType.WhiteIsZero1, 160, 80, 20 * 80)] - [InlineData(TiffColorType.WhiteIsZero1, 153, 80, 20 * 80)] - public void CalculateImageBufferSize_ReturnsCorrectSize(ushort colorType, int width, int height, int expectedResult) + [InlineData(new uint[] { 1 }, 160, 80, 20 * 80)] + [InlineData(new uint[] { 1 }, 153, 80, 20 * 80)] + [InlineData(new uint[] { 3 }, 100, 80, 38 * 80)] + [InlineData(new uint[] { 4 }, 100, 80, 50 * 80)] + [InlineData(new uint[] { 4 }, 99, 80, 50 * 80)] + [InlineData(new uint[] { 8 }, 100, 80, 100 * 80)] + public void CalculateImageBufferSize_ReturnsCorrectSize(uint[] bitsPerSample, int width, int height, int expectedResult) { TiffDecoderCore decoder = new TiffDecoderCore(null, null); - decoder.ColorType = (TiffColorType)colorType; + decoder.BitsPerSample = bitsPerSample; int bufferSize = decoder.CalculateImageBufferSize(width, height); From 4fd46dbe286efa77062ddb28717d1859aceeb57b Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Mon, 15 May 2017 19:59:55 +0100 Subject: [PATCH 035/275] Add support for BlackIsZero --- .../BlackIsZero1TiffColor.cs | 54 ++++++ .../BlackIsZero4TiffColor.cs | 62 +++++++ .../BlackIsZero8TiffColor.cs | 46 +++++ .../BlackIsZeroTiffColor.cs | 53 ++++++ .../TiffColorType.cs | 20 +++ .../Formats/Tiff/TiffDecoderCore.cs | 57 ++++++ .../BlackIsZeroTiffColorTests.cs | 164 ++++++++++++++++++ .../Formats/Tiff/TiffDecoderImageTests.cs | 14 +- 8 files changed, 468 insertions(+), 2 deletions(-) create mode 100644 src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs create mode 100644 src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs create mode 100644 src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs create mode 100644 src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs new file mode 100644 index 000000000..1b9e194e1 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs @@ -0,0 +1,54 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats.Tiff +{ + using System; + using System.Runtime.CompilerServices; + using ImageSharp; + using ImageSharp.PixelFormats; + + /// + /// Implements the 'BlackIsZero' photometric interpretation (optimised for bilevel images). + /// + internal static class BlackIsZero1TiffColor + { + /// + /// Decodes pixel data using the current photometric interpretation. + /// + /// The pixel format. + /// The buffer to read image data from. + /// The image buffer to write pixels to. + /// The x-coordinate of the left-hand side of the image block. + /// The y-coordinate of the top of the image block. + /// The width of the image block. + /// The height of the image block. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Decode(byte[] data, PixelAccessor pixels, int left, int top, int width, int height) + where TPixel : struct, IPixel + { + TPixel color = default(TPixel); + + uint offset = 0; + + for (int y = top; y < top + height; y++) + { + for (int x = left; x < left + width; x += 8) + { + byte b = data[offset++]; + int maxShift = Math.Min(left + width - x, 8); + + for (int shift = 0; shift < maxShift; shift++) + { + int bit = (b >> (7 - shift)) & 1; + byte intensity = (bit == 1) ? (byte)255 : (byte)0; + color.PackFromBytes(intensity, intensity, intensity, 255); + pixels[x + shift, y] = color; + } + } + } + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs new file mode 100644 index 000000000..b52e5e045 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs @@ -0,0 +1,62 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats.Tiff +{ + using System.Runtime.CompilerServices; + using ImageSharp; + using ImageSharp.PixelFormats; + + /// + /// Implements the 'BlackIsZero' photometric interpretation (optimised for 4-bit grayscale images). + /// + internal static class BlackIsZero4TiffColor + { + /// + /// Decodes pixel data using the current photometric interpretation. + /// + /// The pixel format. + /// The buffer to read image data from. + /// The image buffer to write pixels to. + /// The x-coordinate of the left-hand side of the image block. + /// The y-coordinate of the top of the image block. + /// The width of the image block. + /// The height of the image block. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Decode(byte[] data, PixelAccessor pixels, int left, int top, int width, int height) + where TPixel : struct, IPixel + { + TPixel color = default(TPixel); + + uint offset = 0; + bool isOddWidth = (width & 1) == 1; + + for (int y = top; y < top + height; y++) + { + for (int x = left; x < left + width - 1; x += 2) + { + byte byteData = data[offset++]; + + byte intensity1 = (byte)(((byteData & 0xF0) >> 4) * 17); + color.PackFromBytes(intensity1, intensity1, intensity1, 255); + pixels[x, y] = color; + + byte intensity2 = (byte)((byteData & 0x0F) * 17); + color.PackFromBytes(intensity2, intensity2, intensity2, 255); + pixels[x + 1, y] = color; + } + + if (isOddWidth) + { + byte byteData = data[offset++]; + + byte intensity1 = (byte)(((byteData & 0xF0) >> 4) * 17); + color.PackFromBytes(intensity1, intensity1, intensity1, 255); + pixels[left + width - 1, y] = color; + } + } + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs new file mode 100644 index 000000000..ae9cf4615 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs @@ -0,0 +1,46 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats.Tiff +{ + using System.Runtime.CompilerServices; + using ImageSharp; + using ImageSharp.PixelFormats; + + /// + /// Implements the 'BlackIsZero' photometric interpretation (optimised for 8-bit grayscale images). + /// + internal static class BlackIsZero8TiffColor + { + /// + /// Decodes pixel data using the current photometric interpretation. + /// + /// The pixel format. + /// The buffer to read image data from. + /// The image buffer to write pixels to. + /// The x-coordinate of the left-hand side of the image block. + /// The y-coordinate of the top of the image block. + /// The width of the image block. + /// The height of the image block. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Decode(byte[] data, PixelAccessor pixels, int left, int top, int width, int height) + where TPixel : struct, IPixel + { + TPixel color = default(TPixel); + + uint offset = 0; + + for (int y = top; y < top + height; y++) + { + for (int x = left; x < left + width; x++) + { + byte intensity = data[offset++]; + color.PackFromBytes(intensity, intensity, intensity, 255); + pixels[x, y] = color; + } + } + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs new file mode 100644 index 000000000..18654f271 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs @@ -0,0 +1,53 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats.Tiff +{ + using System; + using System.Numerics; + using System.Runtime.CompilerServices; + using ImageSharp; + using ImageSharp.PixelFormats; + + /// + /// Implements the 'BlackIsZero' photometric interpretation (for all bit depths). + /// + internal static class BlackIsZeroTiffColor + { + /// + /// Decodes pixel data using the current photometric interpretation. + /// + /// The pixel format. + /// The buffer to read image data from. + /// The number of bits per sample for each pixel. + /// The image buffer to write pixels to. + /// The x-coordinate of the left-hand side of the image block. + /// The y-coordinate of the top of the image block. + /// The width of the image block. + /// The height of the image block. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Decode(byte[] data, uint[] bitsPerSample, PixelAccessor pixels, int left, int top, int width, int height) + where TPixel : struct, IPixel + { + TPixel color = default(TPixel); + + BitReader bitReader = new BitReader(data); + float factor = (float)Math.Pow(2, bitsPerSample[0]) - 1.0f; + + for (int y = top; y < top + height; y++) + { + for (int x = left; x < left + width; x++) + { + int value = bitReader.ReadBits(bitsPerSample[0]); + float intensity = ((float)value) / factor; + color.PackFromVector4(new Vector4(intensity, intensity, intensity, 1.0f)); + pixels[x, y] = color; + } + + bitReader.NextRow(); + } + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs index b86e17959..f4a15aec2 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs @@ -10,6 +10,26 @@ namespace ImageSharp.Formats.Tiff /// internal enum TiffColorType { + /// + /// Grayscale: 0 is imaged as black. The maximum value is imaged as white. + /// + BlackIsZero, + + /// + /// Grayscale: 0 is imaged as black. The maximum value is imaged as white. Optimised implementation for bilevel images. + /// + BlackIsZero1, + + /// + /// Grayscale: 0 is imaged as black. The maximum value is imaged as white. Optimised implementation for 4-bit images. + /// + BlackIsZero4, + + /// + /// Grayscale: 0 is imaged as black. The maximum value is imaged as white. Optimised implementation for 8-bit images. + /// + BlackIsZero8, + /// /// Grayscale: 0 is imaged as white. The maximum value is imaged as black. /// diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 98635eca7..9a25fa9b9 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -334,6 +334,51 @@ namespace ImageSharp.Formats break; } + case TiffPhotometricInterpretation.BlackIsZero: + { + if (ifd.TryGetIfdEntry(TiffTags.BitsPerSample, out TiffIfdEntry bitsPerSampleEntry)) + { + this.BitsPerSample = this.ReadUnsignedIntegerArray(ref bitsPerSampleEntry); + + if (this.BitsPerSample.Length == 1) + { + switch (this.BitsPerSample[0]) + { + case 8: + { + this.ColorType = TiffColorType.BlackIsZero8; + break; + } + + case 4: + { + this.ColorType = TiffColorType.BlackIsZero4; + break; + } + + case 1: + { + this.ColorType = TiffColorType.BlackIsZero1; + break; + } + + default: + { + this.ColorType = TiffColorType.BlackIsZero; + break; + } + } + } + } + else + { + this.ColorType = TiffColorType.BlackIsZero1; + this.BitsPerSample = new[] { 1u }; + } + + break; + } + default: throw new NotSupportedException("The specified TIFF photometric interpretation is not supported."); } @@ -407,6 +452,18 @@ namespace ImageSharp.Formats case TiffColorType.WhiteIsZero8: WhiteIsZero8TiffColor.Decode(data, pixels, left, top, width, height); break; + case TiffColorType.BlackIsZero: + BlackIsZeroTiffColor.Decode(data, this.BitsPerSample, pixels, left, top, width, height); + break; + case TiffColorType.BlackIsZero1: + BlackIsZero1TiffColor.Decode(data, pixels, left, top, width, height); + break; + case TiffColorType.BlackIsZero4: + BlackIsZero4TiffColor.Decode(data, pixels, left, top, width, height); + break; + case TiffColorType.BlackIsZero8: + BlackIsZero8TiffColor.Decode(data, pixels, left, top, width, height); + break; default: throw new InvalidOperationException(); } diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs new file mode 100644 index 000000000..8c4f78846 --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs @@ -0,0 +1,164 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System.Collections.Generic; + using Xunit; + + using ImageSharp.Formats.Tiff; + + public class BlackIsZeroTiffColorTests : PhotometricInterpretationTestBase + { + private static Rgba32 Gray000 = new Rgba32(0, 0, 0, 255); + private static Rgba32 Gray128 = new Rgba32(128, 128, 128, 255); + private static Rgba32 Gray255 = new Rgba32(255, 255, 255, 255); + private static Rgba32 Gray0 = new Rgba32(0, 0, 0, 255); + private static Rgba32 Gray8 = new Rgba32(136, 136, 136, 255); + private static Rgba32 GrayF = new Rgba32(255, 255, 255, 255); + private static Rgba32 Bit0 = new Rgba32(0, 0, 0, 255); + private static Rgba32 Bit1 = new Rgba32(255, 255, 255, 255); + + private static byte[] Bilevel_Bytes4x4 = new byte[] { 0b01010000, + 0b11110000, + 0b01110000, + 0b10010000 }; + + private static Rgba32[][] Bilevel_Result4x4 = new[] { new[] { Bit0, Bit1, Bit0, Bit1 }, + new[] { Bit1, Bit1, Bit1, Bit1 }, + new[] { Bit0, Bit1, Bit1, Bit1 }, + new[] { Bit1, Bit0, Bit0, Bit1 }}; + + private static byte[] Bilevel_Bytes12x4 = new byte[] { 0b01010101, 0b01010000, + 0b11111111, 0b11111111, + 0b01101001, 0b10100000, + 0b10010000, 0b01100000}; + + private static Rgba32[][] Bilevel_Result12x4 = new[] { new[] { Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1 }, + new[] { Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1 }, + new[] { Bit0, Bit1, Bit1, Bit0, Bit1, Bit0, Bit0, Bit1, Bit1, Bit0, Bit1, Bit0 }, + new[] { Bit1, Bit0, Bit0, Bit1, Bit0, Bit0, Bit0, Bit0, Bit0, Bit1, Bit1, Bit0 }}; + + private static byte[] Grayscale4_Bytes4x4 = new byte[] { 0x8F, 0x0F, + 0xFF, 0xFF, + 0x08, 0x8F, + 0xF0, 0xF8 }; + + private static Rgba32[][] Grayscale4_Result4x4 = new[] { new[] { Gray8, GrayF, Gray0, GrayF }, + new[] { GrayF, GrayF, GrayF, GrayF }, + new[] { Gray0, Gray8, Gray8, GrayF }, + new[] { GrayF, Gray0, GrayF, Gray8 }}; + + private static byte[] Grayscale4_Bytes3x4 = new byte[] { 0x8F, 0x00, + 0xFF, 0xF0, + 0x08, 0x80, + 0xF0, 0xF0 }; + + private static Rgba32[][] Grayscale4_Result3x4 = new[] { new[] { Gray8, GrayF, Gray0 }, + new[] { GrayF, GrayF, GrayF }, + new[] { Gray0, Gray8, Gray8 }, + new[] { GrayF, Gray0, GrayF }}; + + private static byte[] Grayscale8_Bytes4x4 = new byte[] { 128, 255, 000, 255, + 255, 255, 255, 255, + 000, 128, 128, 255, + 255, 000, 255, 128 }; + + private static Rgba32[][] Grayscale8_Result4x4 = new[] { new[] { Gray128, Gray255, Gray000, Gray255 }, + new[] { Gray255, Gray255, Gray255, Gray255 }, + new[] { Gray000, Gray128, Gray128, Gray255 }, + new[] { Gray255, Gray000, Gray255, Gray128 }}; + + public static IEnumerable Bilevel_Data + { + get + { + yield return new object[] { Bilevel_Bytes4x4, 1, 0, 0, 4, 4, Bilevel_Result4x4 }; + yield return new object[] { Bilevel_Bytes4x4, 1, 0, 0, 4, 4, Offset(Bilevel_Result4x4, 0, 0, 6, 6) }; + yield return new object[] { Bilevel_Bytes4x4, 1, 1, 0, 4, 4, Offset(Bilevel_Result4x4, 1, 0, 6, 6) }; + yield return new object[] { Bilevel_Bytes4x4, 1, 0, 1, 4, 4, Offset(Bilevel_Result4x4, 0, 1, 6, 6) }; + yield return new object[] { Bilevel_Bytes4x4, 1, 1, 1, 4, 4, Offset(Bilevel_Result4x4, 1, 1, 6, 6) }; + + yield return new object[] { Bilevel_Bytes12x4, 1, 0, 0, 12, 4, Bilevel_Result12x4 }; + yield return new object[] { Bilevel_Bytes12x4, 1, 0, 0, 12, 4, Offset(Bilevel_Result12x4, 0, 0, 18, 6) }; + yield return new object[] { Bilevel_Bytes12x4, 1, 1, 0, 12, 4, Offset(Bilevel_Result12x4, 1, 0, 18, 6) }; + yield return new object[] { Bilevel_Bytes12x4, 1, 0, 1, 12, 4, Offset(Bilevel_Result12x4, 0, 1, 18, 6) }; + yield return new object[] { Bilevel_Bytes12x4, 1, 1, 1, 12, 4, Offset(Bilevel_Result12x4, 1, 1, 18, 6) }; + } + } + + public static IEnumerable Grayscale4_Data + { + get + { + yield return new object[] { Grayscale4_Bytes4x4, 4, 0, 0, 4, 4, Grayscale4_Result4x4 }; + yield return new object[] { Grayscale4_Bytes4x4, 4, 0, 0, 4, 4, Offset(Grayscale4_Result4x4, 0, 0, 6, 6) }; + yield return new object[] { Grayscale4_Bytes4x4, 4, 1, 0, 4, 4, Offset(Grayscale4_Result4x4, 1, 0, 6, 6) }; + yield return new object[] { Grayscale4_Bytes4x4, 4, 0, 1, 4, 4, Offset(Grayscale4_Result4x4, 0, 1, 6, 6) }; + yield return new object[] { Grayscale4_Bytes4x4, 4, 1, 1, 4, 4, Offset(Grayscale4_Result4x4, 1, 1, 6, 6) }; + + yield return new object[] { Grayscale4_Bytes3x4, 4, 0, 0, 3, 4, Grayscale4_Result3x4 }; + yield return new object[] { Grayscale4_Bytes3x4, 4, 0, 0, 3, 4, Offset(Grayscale4_Result3x4, 0, 0, 6, 6) }; + yield return new object[] { Grayscale4_Bytes3x4, 4, 1, 0, 3, 4, Offset(Grayscale4_Result3x4, 1, 0, 6, 6) }; + yield return new object[] { Grayscale4_Bytes3x4, 4, 0, 1, 3, 4, Offset(Grayscale4_Result3x4, 0, 1, 6, 6) }; + yield return new object[] { Grayscale4_Bytes3x4, 4, 1, 1, 3, 4, Offset(Grayscale4_Result3x4, 1, 1, 6, 6) }; + } + } + + public static IEnumerable Grayscale8_Data + { + get + { + yield return new object[] { Grayscale8_Bytes4x4, 8, 0, 0, 4, 4, Grayscale8_Result4x4 }; + yield return new object[] { Grayscale8_Bytes4x4, 8, 0, 0, 4, 4, Offset(Grayscale8_Result4x4, 0, 0, 6, 6) }; + yield return new object[] { Grayscale8_Bytes4x4, 8, 1, 0, 4, 4, Offset(Grayscale8_Result4x4, 1, 0, 6, 6) }; + yield return new object[] { Grayscale8_Bytes4x4, 8, 0, 1, 4, 4, Offset(Grayscale8_Result4x4, 0, 1, 6, 6) }; + yield return new object[] { Grayscale8_Bytes4x4, 8, 1, 1, 4, 4, Offset(Grayscale8_Result4x4, 1, 1, 6, 6) }; + } + } + + [Theory] + [MemberData(nameof(Bilevel_Data))] + [MemberData(nameof(Grayscale4_Data))] + [MemberData(nameof(Grayscale8_Data))] + public void Decode_WritesPixelData(byte[] inputData, int bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult) + { + AssertDecode(expectedResult, pixels => + { + BlackIsZeroTiffColor.Decode(inputData, new[] { (uint)bitsPerSample }, pixels, left, top, width, height); + }); + } + + [Theory] + [MemberData(nameof(Bilevel_Data))] + public void Decode_WritesPixelData_Bilevel(byte[] inputData, int bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult) + { + AssertDecode(expectedResult, pixels => + { + BlackIsZero1TiffColor.Decode(inputData, pixels, left, top, width, height); + }); + } + + [Theory] + [MemberData(nameof(Grayscale4_Data))] + public void Decode_WritesPixelData_4Bit(byte[] inputData, int bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult) + { + AssertDecode(expectedResult, pixels => + { + BlackIsZero4TiffColor.Decode(inputData, pixels, left, top, width, height); + }); + } + + [Theory] + [MemberData(nameof(Grayscale8_Data))] + public void Decode_WritesPixelData_8Bit(byte[] inputData, int bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult) + { + AssertDecode(expectedResult, pixels => + { + BlackIsZero8TiffColor.Decode(inputData, pixels, left, top, width, height); + }); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs index 168ecf0bc..b1760f083 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs @@ -184,6 +184,14 @@ namespace ImageSharp.Tests [InlineData(true, TiffPhotometricInterpretation.WhiteIsZero, new[] { 4 }, TiffColorType.WhiteIsZero4)] [InlineData(false, TiffPhotometricInterpretation.WhiteIsZero, new[] { 1 }, TiffColorType.WhiteIsZero1)] [InlineData(true, TiffPhotometricInterpretation.WhiteIsZero, new[] { 1 }, TiffColorType.WhiteIsZero1)] + [InlineData(false, TiffPhotometricInterpretation.BlackIsZero, new[] { 3 }, TiffColorType.BlackIsZero)] + [InlineData(true, TiffPhotometricInterpretation.BlackIsZero, new[] { 3 }, TiffColorType.BlackIsZero)] + [InlineData(false, TiffPhotometricInterpretation.BlackIsZero, new[] { 8 }, TiffColorType.BlackIsZero8)] + [InlineData(true, TiffPhotometricInterpretation.BlackIsZero, new[] { 8 }, TiffColorType.BlackIsZero8)] + [InlineData(false, TiffPhotometricInterpretation.BlackIsZero, new[] { 4 }, TiffColorType.BlackIsZero4)] + [InlineData(true, TiffPhotometricInterpretation.BlackIsZero, new[] { 4 }, TiffColorType.BlackIsZero4)] + [InlineData(false, TiffPhotometricInterpretation.BlackIsZero, new[] { 1 }, TiffColorType.BlackIsZero1)] + [InlineData(true, TiffPhotometricInterpretation.BlackIsZero, new[] { 1 }, TiffColorType.BlackIsZero1)] public void ReadImageFormat_DeterminesCorrectColorImplementation(bool isLittleEndian, ushort photometricInterpretation, int[] bitsPerSample, int colorType) { Stream stream = CreateTiffGenIfd() @@ -201,6 +209,8 @@ namespace ImageSharp.Tests [Theory] [InlineData(false, TiffPhotometricInterpretation.WhiteIsZero, TiffColorType.WhiteIsZero1)] [InlineData(true, TiffPhotometricInterpretation.WhiteIsZero, TiffColorType.WhiteIsZero1)] + [InlineData(false, TiffPhotometricInterpretation.BlackIsZero, TiffColorType.BlackIsZero1)] + [InlineData(true, TiffPhotometricInterpretation.BlackIsZero, TiffColorType.BlackIsZero1)] public void ReadImageFormat_DeterminesCorrectColorImplementation_DefaultsToBilevel(bool isLittleEndian, ushort photometricInterpretation, int colorType) { Stream stream = CreateTiffGenIfd() @@ -250,7 +260,6 @@ namespace ImageSharp.Tests } [Theory] - [InlineData(false, TiffPhotometricInterpretation.BlackIsZero)] [InlineData(false, TiffPhotometricInterpretation.CieLab)] [InlineData(false, TiffPhotometricInterpretation.ColorFilterArray)] [InlineData(false, TiffPhotometricInterpretation.IccLab)] @@ -262,7 +271,6 @@ namespace ImageSharp.Tests [InlineData(false, TiffPhotometricInterpretation.TransparencyMask)] [InlineData(false, TiffPhotometricInterpretation.YCbCr)] [InlineData(false, 999)] - [InlineData(true, TiffPhotometricInterpretation.BlackIsZero)] [InlineData(true, TiffPhotometricInterpretation.CieLab)] [InlineData(true, TiffPhotometricInterpretation.ColorFilterArray)] [InlineData(true, TiffPhotometricInterpretation.IccLab)] @@ -315,6 +323,8 @@ namespace ImageSharp.Tests [Theory] [InlineData(false, TiffPhotometricInterpretation.WhiteIsZero)] [InlineData(true, TiffPhotometricInterpretation.WhiteIsZero)] + [InlineData(false, TiffPhotometricInterpretation.BlackIsZero)] + [InlineData(true, TiffPhotometricInterpretation.BlackIsZero)] public void ReadImageFormat_ReadsBitsPerSample_DefaultsToBilevel(bool isLittleEndian, ushort photometricInterpretation) { Stream stream = CreateTiffGenIfd() From 80fb83d76060d3c926248408bfc6e3c02245faa7 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Tue, 16 May 2017 11:24:28 +0100 Subject: [PATCH 036/275] Add support for pallete color images --- .../PaletteTiffColor.cs | 73 +++++++++ .../TiffColorType.cs | 7 +- .../Formats/Tiff/TiffDecoderCore.cs | 154 +++++++++++------- .../PaletteTiffColorTests.cs | 144 ++++++++++++++++ .../Formats/Tiff/TiffDecoderImageTests.cs | 118 +++++++++++++- 5 files changed, 433 insertions(+), 63 deletions(-) create mode 100644 src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs new file mode 100644 index 000000000..4f4536331 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs @@ -0,0 +1,73 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats.Tiff +{ + using System; + using System.Numerics; + using System.Runtime.CompilerServices; + using ImageSharp; + using ImageSharp.PixelFormats; + + /// + /// Implements the 'PaletteTiffColor' photometric interpretation (for all bit depths). + /// + internal static class PaletteTiffColor + { + /// + /// Decodes pixel data using the current photometric interpretation. + /// + /// The pixel format. + /// The buffer to read image data from. + /// The number of bits per sample for each pixel. + /// The RGB color lookup table to use for decoding the image. + /// The image buffer to write pixels to. + /// The x-coordinate of the left-hand side of the image block. + /// The y-coordinate of the top of the image block. + /// The width of the image block. + /// The height of the image block. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Decode(byte[] data, uint[] bitsPerSample, uint[] colorMap, PixelAccessor pixels, int left, int top, int width, int height) + where TPixel : struct, IPixel + { + int colorCount = (int)Math.Pow(2, bitsPerSample[0]); + TPixel[] palette = GeneratePalette(colorMap, colorCount); + + BitReader bitReader = new BitReader(data); + + for (int y = top; y < top + height; y++) + { + for (int x = left; x < left + width; x++) + { + int index = bitReader.ReadBits(bitsPerSample[0]); + pixels[x, y] = palette[index]; + } + + bitReader.NextRow(); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static TPixel[] GeneratePalette(uint[] colorMap, int colorCount) + where TPixel : struct, IPixel + { + TPixel[] palette = new TPixel[colorCount]; + + int rOffset = 0; + int gOffset = colorCount; + int bOffset = colorCount * 2; + + for (int i = 0; i < palette.Length; i++) + { + float r = colorMap[rOffset + i] / 65535F; + float g = colorMap[gOffset + i] / 65535F; + float b = colorMap[bOffset + i] / 65535F; + palette[i].PackFromVector4(new Vector4(r, g, b, 1.0f)); + } + + return palette; + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs index f4a15aec2..c63d6febd 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs @@ -48,6 +48,11 @@ namespace ImageSharp.Formats.Tiff /// /// Grayscale: 0 is imaged as white. The maximum value is imaged as black. Optimised implementation for 8-bit images. /// - WhiteIsZero8 + WhiteIsZero8, + + /// + /// Palette-color. + /// + PaletteColor } } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 9a25fa9b9..5ebce1f04 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -57,6 +57,11 @@ namespace ImageSharp.Formats /// public uint[] BitsPerSample { get; set; } + /// + /// Gets or sets the lookup table for RGB palette colored images. + /// + public uint[] ColorMap { get; set; } + /// /// Gets or sets the photometric interpretation implementation to use when decoding the image. /// @@ -287,93 +292,128 @@ namespace ImageSharp.Formats } } + if (ifd.TryGetIfdEntry(TiffTags.BitsPerSample, out TiffIfdEntry bitsPerSampleEntry)) + { + this.BitsPerSample = this.ReadUnsignedIntegerArray(ref bitsPerSampleEntry); + } + else + { + if (photometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero || + photometricInterpretation == TiffPhotometricInterpretation.BlackIsZero) + { + this.BitsPerSample = new[] { 1u }; + } + else + { + throw new ImageFormatException("The TIFF BitsPerSample entry is missing."); + } + } + switch (photometricInterpretation) { case TiffPhotometricInterpretation.WhiteIsZero: { - if (ifd.TryGetIfdEntry(TiffTags.BitsPerSample, out TiffIfdEntry bitsPerSampleEntry)) + if (this.BitsPerSample.Length == 1) { - this.BitsPerSample = this.ReadUnsignedIntegerArray(ref bitsPerSampleEntry); - - if (this.BitsPerSample.Length == 1) + switch (this.BitsPerSample[0]) { - switch (this.BitsPerSample[0]) - { - case 8: - { - this.ColorType = TiffColorType.WhiteIsZero8; - break; - } - - case 4: - { - this.ColorType = TiffColorType.WhiteIsZero4; - break; - } + case 8: + { + this.ColorType = TiffColorType.WhiteIsZero8; + break; + } + + case 4: + { + this.ColorType = TiffColorType.WhiteIsZero4; + break; + } + + case 1: + { + this.ColorType = TiffColorType.WhiteIsZero1; + break; + } + + default: + { + this.ColorType = TiffColorType.WhiteIsZero; + break; + } + } + } + else + { + throw new NotSupportedException("The number of samples in the TIFF BitsPerSample entry is not supported."); + } - case 1: - { - this.ColorType = TiffColorType.WhiteIsZero1; - break; - } + break; + } - default: - { - this.ColorType = TiffColorType.WhiteIsZero; - break; - } - } + case TiffPhotometricInterpretation.BlackIsZero: + { + if (this.BitsPerSample.Length == 1) + { + switch (this.BitsPerSample[0]) + { + case 8: + { + this.ColorType = TiffColorType.BlackIsZero8; + break; + } + + case 4: + { + this.ColorType = TiffColorType.BlackIsZero4; + break; + } + + case 1: + { + this.ColorType = TiffColorType.BlackIsZero1; + break; + } + + default: + { + this.ColorType = TiffColorType.BlackIsZero; + break; + } } } else { - this.ColorType = TiffColorType.WhiteIsZero1; - this.BitsPerSample = new[] { 1u }; + throw new NotSupportedException("The number of samples in the TIFF BitsPerSample entry is not supported."); } break; } - case TiffPhotometricInterpretation.BlackIsZero: + case TiffPhotometricInterpretation.PaletteColor: { - if (ifd.TryGetIfdEntry(TiffTags.BitsPerSample, out TiffIfdEntry bitsPerSampleEntry)) + if (ifd.TryGetIfdEntry(TiffTags.ColorMap, out TiffIfdEntry colorMapEntry)) { - this.BitsPerSample = this.ReadUnsignedIntegerArray(ref bitsPerSampleEntry); + this.ColorMap = this.ReadUnsignedIntegerArray(ref colorMapEntry); if (this.BitsPerSample.Length == 1) { switch (this.BitsPerSample[0]) { - case 8: - { - this.ColorType = TiffColorType.BlackIsZero8; - break; - } - - case 4: - { - this.ColorType = TiffColorType.BlackIsZero4; - break; - } - - case 1: - { - this.ColorType = TiffColorType.BlackIsZero1; - break; - } - default: { - this.ColorType = TiffColorType.BlackIsZero; + this.ColorType = TiffColorType.PaletteColor; break; } } } + else + { + throw new NotSupportedException("The number of samples in the TIFF BitsPerSample entry is not supported."); + } } else { - this.ColorType = TiffColorType.BlackIsZero1; - this.BitsPerSample = new[] { 1u }; + throw new ImageFormatException("The TIFF ColorMap entry is missing for a pallete color image."); } break; @@ -398,7 +438,8 @@ namespace ImageSharp.Formats bitsPerPixel += this.BitsPerSample[i]; } - int bytesPerRow = ((width * (int)bitsPerPixel) + 7) / 8; + int sampleMultiplier = this.ColorType == TiffColorType.PaletteColor ? 3 : 1; + int bytesPerRow = ((width * (int)bitsPerPixel * sampleMultiplier) + 7) / 8; return bytesPerRow * height; } @@ -464,6 +505,9 @@ namespace ImageSharp.Formats case TiffColorType.BlackIsZero8: BlackIsZero8TiffColor.Decode(data, pixels, left, top, width, height); break; + case TiffColorType.PaletteColor: + PaletteTiffColor.Decode(data, this.BitsPerSample, this.ColorMap, pixels, left, top, width, height); + break; default: throw new InvalidOperationException(); } diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs new file mode 100644 index 000000000..8a77c67f0 --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs @@ -0,0 +1,144 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System.Collections.Generic; + using Xunit; + + using ImageSharp.Formats.Tiff; + using ImageSharp.PixelFormats; + + public class PaletteTiffColorTests : PhotometricInterpretationTestBase + { + public static uint[][] Palette4_ColorPalette { get => GeneratePalette(16); } + + public static uint[] Palette4_ColorMap { get => GenerateColorMap(Palette4_ColorPalette); } + + private static byte[] Palette4_Bytes4x4 = new byte[] { 0x01, 0x23, + 0x4A, 0xD2, + 0x12, 0x34, + 0xAB, 0xEF }; + + private static Rgba32[][] Palette4_Result4x4 = GenerateResult(Palette4_ColorPalette, + new[] { new[] { 0x00, 0x01, 0x02, 0x03 }, + new[] { 0x04, 0x0A, 0x0D, 0x02 }, + new[] { 0x01, 0x02, 0x03, 0x04 }, + new[] { 0x0A, 0x0B, 0x0E, 0x0F }}); + + private static byte[] Palette4_Bytes3x4 = new byte[] { 0x01, 0x20, + 0x4A, 0xD0, + 0x12, 0x30, + 0xAB, 0xE0 }; + + private static Rgba32[][] Palette4_Result3x4 = GenerateResult(Palette4_ColorPalette, + new[] { new[] { 0x00, 0x01, 0x02 }, + new[] { 0x04, 0x0A, 0x0D }, + new[] { 0x01, 0x02, 0x03 }, + new[] { 0x0A, 0x0B, 0x0E }}); + + public static IEnumerable Palette4_Data + { + get + { + yield return new object[] { Palette4_Bytes4x4, 4, Palette4_ColorMap, 0, 0, 4, 4, Palette4_Result4x4 }; + yield return new object[] { Palette4_Bytes4x4, 4, Palette4_ColorMap, 0, 0, 4, 4, Offset(Palette4_Result4x4, 0, 0, 6, 6) }; + yield return new object[] { Palette4_Bytes4x4, 4, Palette4_ColorMap, 1, 0, 4, 4, Offset(Palette4_Result4x4, 1, 0, 6, 6) }; + yield return new object[] { Palette4_Bytes4x4, 4, Palette4_ColorMap, 0, 1, 4, 4, Offset(Palette4_Result4x4, 0, 1, 6, 6) }; + yield return new object[] { Palette4_Bytes4x4, 4, Palette4_ColorMap, 1, 1, 4, 4, Offset(Palette4_Result4x4, 1, 1, 6, 6) }; + + yield return new object[] { Palette4_Bytes3x4, 4, Palette4_ColorMap, 0, 0, 3, 4, Palette4_Result3x4 }; + yield return new object[] { Palette4_Bytes3x4, 4, Palette4_ColorMap, 0, 0, 3, 4, Offset(Palette4_Result3x4, 0, 0, 6, 6) }; + yield return new object[] { Palette4_Bytes3x4, 4, Palette4_ColorMap, 1, 0, 3, 4, Offset(Palette4_Result3x4, 1, 0, 6, 6) }; + yield return new object[] { Palette4_Bytes3x4, 4, Palette4_ColorMap, 0, 1, 3, 4, Offset(Palette4_Result3x4, 0, 1, 6, 6) }; + yield return new object[] { Palette4_Bytes3x4, 4, Palette4_ColorMap, 1, 1, 3, 4, Offset(Palette4_Result3x4, 1, 1, 6, 6) }; + + } + } + + public static uint[][] Palette8_ColorPalette { get => GeneratePalette(256); } + + public static uint[] Palette8_ColorMap { get => GenerateColorMap(Palette8_ColorPalette); } + + private static byte[] Palette8_Bytes4x4 = new byte[] { 000, 001, 002, 003, + 100, 110, 120, 130, + 000, 255, 128, 255, + 050, 100, 150, 200 }; + + private static Rgba32[][] Palette8_Result4x4 = GenerateResult(Palette8_ColorPalette, + new[] { new[] { 000, 001, 002, 003 }, + new[] { 100, 110, 120, 130 }, + new[] { 000, 255, 128, 255 }, + new[] { 050, 100, 150, 200 }}); + + public static IEnumerable Palette8_Data + { + get + { + yield return new object[] { Palette8_Bytes4x4, 8, Palette8_ColorMap, 0, 0, 4, 4, Palette8_Result4x4 }; + yield return new object[] { Palette8_Bytes4x4, 8, Palette8_ColorMap, 0, 0, 4, 4, Offset(Palette8_Result4x4, 0, 0, 6, 6) }; + yield return new object[] { Palette8_Bytes4x4, 8, Palette8_ColorMap, 1, 0, 4, 4, Offset(Palette8_Result4x4, 1, 0, 6, 6) }; + yield return new object[] { Palette8_Bytes4x4, 8, Palette8_ColorMap, 0, 1, 4, 4, Offset(Palette8_Result4x4, 0, 1, 6, 6) }; + yield return new object[] { Palette8_Bytes4x4, 8, Palette8_ColorMap, 1, 1, 4, 4, Offset(Palette8_Result4x4, 1, 1, 6, 6) }; + } + } + + [Theory] + [MemberData(nameof(Palette4_Data))] + [MemberData(nameof(Palette8_Data))] + public void Decode_WritesPixelData(byte[] inputData, int bitsPerSample, uint[] colorMap, int left, int top, int width, int height, Rgba32[][] expectedResult) + { + AssertDecode(expectedResult, pixels => + { + PaletteTiffColor.Decode(inputData, new[] { (uint)bitsPerSample }, colorMap, pixels, left, top, width, height); + }); + } + + private static uint[][] GeneratePalette(int count) + { + uint[][] palette = new uint[count][]; + + for (uint i = 0; i < count; i++) + { + palette[i] = new uint[] { (i * 2u) % 65536u, (i * 2625u) % 65536u, (i * 29401u) % 65536u }; + } + + return palette; + } + + private static uint[] GenerateColorMap(uint[][] colorPalette) + { + int colorCount = colorPalette.Length; + uint[] colorMap = new uint[colorCount * 3]; + + for (int i = 0; i < colorCount; i++) + { + colorMap[colorCount * 0 + i] = colorPalette[i][0]; + colorMap[colorCount * 1 + i] = colorPalette[i][1]; + colorMap[colorCount * 2 + i] = colorPalette[i][2]; + } + + return colorMap; + } + + private static Rgba32[][] GenerateResult(uint[][] colorPalette, int[][] pixelLookup) + { + Rgba32[][] result = new Rgba32[pixelLookup.Length][]; + + for (int y = 0; y < pixelLookup.Length; y++) + { + result[y] = new Rgba32[pixelLookup[y].Length]; + + for (int x = 0; x < pixelLookup[y].Length; x++) + { + uint[] sourceColor = colorPalette[pixelLookup[y][x]]; + result[y][x] = new Rgba32(sourceColor[0] / 65535F, sourceColor[1] / 65535F, sourceColor[2] / 65535F); + } + } + + return result; + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs index b1760f083..39c5dc8c4 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs @@ -192,6 +192,14 @@ namespace ImageSharp.Tests [InlineData(true, TiffPhotometricInterpretation.BlackIsZero, new[] { 4 }, TiffColorType.BlackIsZero4)] [InlineData(false, TiffPhotometricInterpretation.BlackIsZero, new[] { 1 }, TiffColorType.BlackIsZero1)] [InlineData(true, TiffPhotometricInterpretation.BlackIsZero, new[] { 1 }, TiffColorType.BlackIsZero1)] + [InlineData(false, TiffPhotometricInterpretation.PaletteColor, new[] { 3 }, TiffColorType.PaletteColor)] + [InlineData(true, TiffPhotometricInterpretation.PaletteColor, new[] { 3 }, TiffColorType.PaletteColor)] + [InlineData(false, TiffPhotometricInterpretation.PaletteColor, new[] { 8 }, TiffColorType.PaletteColor)] + [InlineData(true, TiffPhotometricInterpretation.PaletteColor, new[] { 8 }, TiffColorType.PaletteColor)] + [InlineData(false, TiffPhotometricInterpretation.PaletteColor, new[] { 4 }, TiffColorType.PaletteColor)] + [InlineData(true, TiffPhotometricInterpretation.PaletteColor, new[] { 4 }, TiffColorType.PaletteColor)] + [InlineData(false, TiffPhotometricInterpretation.PaletteColor, new[] { 1 }, TiffColorType.PaletteColor)] + [InlineData(true, TiffPhotometricInterpretation.PaletteColor, new[] { 1 }, TiffColorType.PaletteColor)] public void ReadImageFormat_DeterminesCorrectColorImplementation(bool isLittleEndian, ushort photometricInterpretation, int[] bitsPerSample, int colorType) { Stream stream = CreateTiffGenIfd() @@ -265,7 +273,6 @@ namespace ImageSharp.Tests [InlineData(false, TiffPhotometricInterpretation.IccLab)] [InlineData(false, TiffPhotometricInterpretation.ItuLab)] [InlineData(false, TiffPhotometricInterpretation.LinearRaw)] - [InlineData(false, TiffPhotometricInterpretation.PaletteColor)] [InlineData(false, TiffPhotometricInterpretation.Rgb)] [InlineData(false, TiffPhotometricInterpretation.Separated)] [InlineData(false, TiffPhotometricInterpretation.TransparencyMask)] @@ -276,7 +283,6 @@ namespace ImageSharp.Tests [InlineData(true, TiffPhotometricInterpretation.IccLab)] [InlineData(true, TiffPhotometricInterpretation.ItuLab)] [InlineData(true, TiffPhotometricInterpretation.LinearRaw)] - [InlineData(true, TiffPhotometricInterpretation.PaletteColor)] [InlineData(true, TiffPhotometricInterpretation.Rgb)] [InlineData(true, TiffPhotometricInterpretation.Separated)] [InlineData(true, TiffPhotometricInterpretation.TransparencyMask)] @@ -303,10 +309,10 @@ namespace ImageSharp.Tests [InlineData(true, new[] { 4u })] [InlineData(false, new[] { 1u })] [InlineData(true, new[] { 1u })] - [InlineData(false, new[] { 1u, 2u, 3u })] - [InlineData(true, new[] { 1u, 2u, 3u })] - [InlineData(false, new[] { 8u, 8u, 8u })] - [InlineData(true, new[] { 8u, 8u, 8u })] + // [InlineData(false, new[] { 1u, 2u, 3u })] + // [InlineData(true, new[] { 1u, 2u, 3u })] + // [InlineData(false, new[] { 8u, 8u, 8u })] + // [InlineData(true, new[] { 8u, 8u, 8u })] public void ReadImageFormat_ReadsBitsPerSample(bool isLittleEndian, uint[] bitsPerSample) { Stream stream = CreateTiffGenIfd() @@ -339,6 +345,84 @@ namespace ImageSharp.Tests Assert.Equal(new[] { 1u }, decoder.BitsPerSample); } + [Theory] + [MemberData(nameof(IsLittleEndianValues))] + public void ReadImageFormat_ThrowsExceptionForMissingBitsPerSample(bool isLittleEndian) + { + Stream stream = CreateTiffGenIfd() + .WithEntry(TiffGenEntry.Integer(TiffTags.PhotometricInterpretation, TiffType.Short, (int)TiffPhotometricInterpretation.PaletteColor)) + .WithoutEntry(TiffTags.BitsPerSample) + .ToStream(isLittleEndian); + + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null, null); + TiffIfd ifd = decoder.ReadIfd(0); + + var e = Assert.Throws(() => decoder.ReadImageFormat(ifd)); + + Assert.Equal("The TIFF BitsPerSample entry is missing.", e.Message); + } + + [Theory] + [InlineData(false, TiffPhotometricInterpretation.WhiteIsZero, new int[] { })] + [InlineData(true, TiffPhotometricInterpretation.WhiteIsZero, new int[] { })] + [InlineData(false, TiffPhotometricInterpretation.BlackIsZero, new int[] { })] + [InlineData(true, TiffPhotometricInterpretation.BlackIsZero, new int[] { })] + [InlineData(false, TiffPhotometricInterpretation.PaletteColor, new int[] { })] + [InlineData(true, TiffPhotometricInterpretation.PaletteColor, new int[] { })] + [InlineData(false, TiffPhotometricInterpretation.WhiteIsZero, new[] { 8, 8 })] + [InlineData(true, TiffPhotometricInterpretation.WhiteIsZero, new[] { 8, 8 })] + [InlineData(false, TiffPhotometricInterpretation.BlackIsZero, new[] { 8, 8 })] + [InlineData(true, TiffPhotometricInterpretation.BlackIsZero, new[] { 8, 8 })] + [InlineData(false, TiffPhotometricInterpretation.PaletteColor, new[] { 8, 8 })] + [InlineData(true, TiffPhotometricInterpretation.PaletteColor, new[] { 8, 8 })] + public void ReadImageFormat_ThrowsExceptionForUnsupportedNumberOfSamples(bool isLittleEndian, ushort photometricInterpretation, int[] bitsPerSample) + { + Stream stream = CreateTiffGenIfd() + .WithEntry(TiffGenEntry.Integer(TiffTags.PhotometricInterpretation, TiffType.Short, photometricInterpretation)) + .WithEntry(TiffGenEntry.Integer(TiffTags.BitsPerSample, TiffType.Short, bitsPerSample)) + .ToStream(isLittleEndian); + + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null, null); + TiffIfd ifd = decoder.ReadIfd(0); + + var e = Assert.Throws(() => decoder.ReadImageFormat(ifd)); + + Assert.Equal("The number of samples in the TIFF BitsPerSample entry is not supported.", e.Message); + } + + [Theory] + [MemberData(nameof(IsLittleEndianValues))] + public void ReadImageFormat_ReadsColorMap(bool isLittleEndian) + { + Stream stream = CreateTiffGenIfd() + .WithEntry(TiffGenEntry.Integer(TiffTags.PhotometricInterpretation, TiffType.Short, (int)TiffPhotometricInterpretation.PaletteColor)) + .WithEntry(TiffGenEntry.Integer(TiffTags.ColorMap, TiffType.Short, new int[] { 10, 20, 30, 40, 50, 60 })) + .ToStream(isLittleEndian); + + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null, null); + TiffIfd ifd = decoder.ReadIfd(0); + decoder.ReadImageFormat(ifd); + + Assert.Equal(new uint[] { 10, 20, 30, 40, 50, 60 }, decoder.ColorMap); + } + + [Theory] + [MemberData(nameof(IsLittleEndianValues))] + public void ReadImageFormat_ThrowsExceptionForMissingColorMap(bool isLittleEndian) + { + Stream stream = CreateTiffGenIfd() + .WithEntry(TiffGenEntry.Integer(TiffTags.PhotometricInterpretation, TiffType.Short, (int)TiffPhotometricInterpretation.PaletteColor)) + .WithoutEntry(TiffTags.ColorMap) + .ToStream(isLittleEndian); + + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null, null); + TiffIfd ifd = decoder.ReadIfd(0); + + var e = Assert.Throws(() => decoder.ReadImageFormat(ifd)); + + Assert.Equal("The TIFF ColorMap entry is missing for a pallete color image.", e.Message); + } + [Theory] [InlineData(new uint[] { 1 }, 160, 80, 20 * 80)] [InlineData(new uint[] { 1 }, 153, 80, 20 * 80)] @@ -349,6 +433,25 @@ namespace ImageSharp.Tests public void CalculateImageBufferSize_ReturnsCorrectSize(uint[] bitsPerSample, int width, int height, int expectedResult) { TiffDecoderCore decoder = new TiffDecoderCore(null, null); + decoder.ColorType = TiffColorType.WhiteIsZero; + decoder.BitsPerSample = bitsPerSample; + + int bufferSize = decoder.CalculateImageBufferSize(width, height); + + Assert.Equal(expectedResult, bufferSize); + } + + [Theory] + [InlineData(new uint[] { 1 }, 160, 80, 60 * 80)] + [InlineData(new uint[] { 1 }, 153, 80, 58 * 80)] + [InlineData(new uint[] { 3 }, 100, 80, 113 * 80)] + [InlineData(new uint[] { 4 }, 100, 80, 150 * 80)] + [InlineData(new uint[] { 4 }, 99, 80, 149 * 80)] + [InlineData(new uint[] { 8 }, 100, 80, 300 * 80)] + public void CalculateImageBufferSize_ReturnsCorrectSize_ForPaletteColor(uint[] bitsPerSample, int width, int height, int expectedResult) + { + TiffDecoderCore decoder = new TiffDecoderCore(null, null); + decoder.ColorType = TiffColorType.PaletteColor; decoder.BitsPerSample = bitsPerSample; int bufferSize = decoder.CalculateImageBufferSize(width, height); @@ -369,7 +472,8 @@ namespace ImageSharp.Tests TiffGenEntry.Integer(TiffTags.ResolutionUnit, TiffType.Short, 2), TiffGenEntry.Integer(TiffTags.PhotometricInterpretation, TiffType.Short, (int)TiffPhotometricInterpretation.WhiteIsZero), TiffGenEntry.Integer(TiffTags.BitsPerSample, TiffType.Short, new int[] { 8 }), - TiffGenEntry.Integer(TiffTags.Compression, TiffType.Short, (int)TiffCompression.None) + TiffGenEntry.Integer(TiffTags.Compression, TiffType.Short, (int)TiffCompression.None), + TiffGenEntry.Integer(TiffTags.ColorMap, TiffType.Short, new int[256]) } }; } From 3f4041b5909bb9a960fcd32a9b750669a5a503de Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Tue, 16 May 2017 20:04:06 +0100 Subject: [PATCH 037/275] Add support for RGB full color images --- .../PhotometricInterpretation/RgbTiffColor.cs | 56 +++++++ .../TiffColorType.cs | 7 +- .../Formats/Tiff/TiffDecoderCore.cs | 24 +++ .../RgbTiffColorTests.cs | 151 ++++++++++++++++++ .../Formats/Tiff/TiffDecoderImageTests.cs | 55 ++++--- 5 files changed, 264 insertions(+), 29 deletions(-) create mode 100644 src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs new file mode 100644 index 000000000..e62ee7dc9 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs @@ -0,0 +1,56 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats.Tiff +{ + using System; + using System.Numerics; + using System.Runtime.CompilerServices; + using ImageSharp; + using ImageSharp.PixelFormats; + + /// + /// Implements the 'RGB' photometric interpretation (for all bit depths). + /// + internal static class RgbTiffColor + { + /// + /// Decodes pixel data using the current photometric interpretation. + /// + /// The pixel format. + /// The buffer to read image data from. + /// The number of bits per sample for each pixel. + /// The image buffer to write pixels to. + /// The x-coordinate of the left-hand side of the image block. + /// The y-coordinate of the top of the image block. + /// The width of the image block. + /// The height of the image block. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Decode(byte[] data, uint[] bitsPerSample, PixelAccessor pixels, int left, int top, int width, int height) + where TPixel : struct, IPixel + { + TPixel color = default(TPixel); + + BitReader bitReader = new BitReader(data); + float rFactor = (float)Math.Pow(2, bitsPerSample[0]) - 1.0f; + float gFactor = (float)Math.Pow(2, bitsPerSample[1]) - 1.0f; + float bFactor = (float)Math.Pow(2, bitsPerSample[2]) - 1.0f; + + for (int y = top; y < top + height; y++) + { + for (int x = left; x < left + width; x++) + { + float r = ((float)bitReader.ReadBits(bitsPerSample[0])) / rFactor; + float g = ((float)bitReader.ReadBits(bitsPerSample[1])) / gFactor; + float b = ((float)bitReader.ReadBits(bitsPerSample[2])) / bFactor; + color.PackFromVector4(new Vector4(r, g, b, 1.0f)); + pixels[x, y] = color; + } + + bitReader.NextRow(); + } + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs index c63d6febd..5d85f6553 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs @@ -53,6 +53,11 @@ namespace ImageSharp.Formats.Tiff /// /// Palette-color. /// - PaletteColor + PaletteColor, + + /// + /// RGB Full Color. + /// + Rgb, } } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 5ebce1f04..e31706674 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -389,6 +389,27 @@ namespace ImageSharp.Formats break; } + case TiffPhotometricInterpretation.Rgb: + { + if (this.BitsPerSample.Length == 3) + { + if (this.BitsPerSample[0] == 8 && this.BitsPerSample[1] == 8 && this.BitsPerSample[2] == 8) + { + this.ColorType = TiffColorType.Rgb; + } + else + { + this.ColorType = TiffColorType.Rgb; + } + } + else + { + throw new NotSupportedException("The number of samples in the TIFF BitsPerSample entry is not supported."); + } + + break; + } + case TiffPhotometricInterpretation.PaletteColor: { if (ifd.TryGetIfdEntry(TiffTags.ColorMap, out TiffIfdEntry colorMapEntry)) @@ -505,6 +526,9 @@ namespace ImageSharp.Formats case TiffColorType.BlackIsZero8: BlackIsZero8TiffColor.Decode(data, pixels, left, top, width, height); break; + case TiffColorType.Rgb: + RgbTiffColor.Decode(data, this.BitsPerSample, pixels, left, top, width, height); + break; case TiffColorType.PaletteColor: PaletteTiffColor.Decode(data, this.BitsPerSample, this.ColorMap, pixels, left, top, width, height); break; diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs new file mode 100644 index 000000000..c67913d65 --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs @@ -0,0 +1,151 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System.Collections.Generic; + using Xunit; + + using ImageSharp.Formats.Tiff; + + public class RgbTiffColorTests : PhotometricInterpretationTestBase + { + private static Rgba32 Rgb4_000 = new Rgba32(0, 0, 0, 255); + private static Rgba32 Rgb4_444 = new Rgba32(68, 68, 68, 255); + private static Rgba32 Rgb4_888 = new Rgba32(136, 136, 136, 255); + private static Rgba32 Rgb4_CCC = new Rgba32(204, 204, 204, 255); + private static Rgba32 Rgb4_FFF = new Rgba32(255, 255, 255, 255); + private static Rgba32 Rgb4_F00 = new Rgba32(255, 0, 0, 255); + private static Rgba32 Rgb4_0F0 = new Rgba32(0, 255, 0, 255); + private static Rgba32 Rgb4_00F = new Rgba32(0, 0, 255, 255); + private static Rgba32 Rgb4_F0F = new Rgba32(255, 0, 255, 255); + private static Rgba32 Rgb4_400 = new Rgba32(68, 0, 0, 255); + private static Rgba32 Rgb4_800 = new Rgba32(136, 0, 0, 255); + private static Rgba32 Rgb4_C00 = new Rgba32(204, 0, 0, 255); + private static Rgba32 Rgb4_48C = new Rgba32(68, 136, 204, 255); + + private static byte[] Rgb4_Bytes4x4 = new byte[] { 0x00, 0x0F, 0xFF, 0x00, 0x0F, 0xFF, + 0xF0, 0x00, 0xF0, 0x00, 0xFF, 0x0F, + 0x40, 0x08, 0x00, 0xC0, 0x04, 0x8C, + 0x00, 0x04, 0x44, 0x88, 0x8C, 0xCC }; + + private static Rgba32[][] Rgb4_Result4x4 = new[] { new[] { Rgb4_000, Rgb4_FFF, Rgb4_000, Rgb4_FFF }, + new[] { Rgb4_F00, Rgb4_0F0, Rgb4_00F, Rgb4_F0F }, + new[] { Rgb4_400, Rgb4_800, Rgb4_C00, Rgb4_48C }, + new[] { Rgb4_000, Rgb4_444, Rgb4_888, Rgb4_CCC }}; + + private static byte[] Rgb4_Bytes3x4 = new byte[] { 0x00, 0x0F, 0xFF, 0x00, 0x00, + 0xF0, 0x00, 0xF0, 0x00, 0xF0, + 0x40, 0x08, 0x00, 0xC0, 0x00, + 0x00, 0x04, 0x44, 0x88, 0x80 }; + + private static Rgba32[][] Rgb4_Result3x4 = new[] { new[] { Rgb4_000, Rgb4_FFF, Rgb4_000 }, + new[] { Rgb4_F00, Rgb4_0F0, Rgb4_00F }, + new[] { Rgb4_400, Rgb4_800, Rgb4_C00 }, + new[] { Rgb4_000, Rgb4_444, Rgb4_888 }}; + + public static IEnumerable Rgb4_Data + { + get + { + yield return new object[] { Rgb4_Bytes4x4, new[] { 4u, 4u, 4u }, 0, 0, 4, 4, Rgb4_Result4x4 }; + yield return new object[] { Rgb4_Bytes4x4, new[] { 4u, 4u, 4u }, 0, 0, 4, 4, Offset(Rgb4_Result4x4, 0, 0, 6, 6) }; + yield return new object[] { Rgb4_Bytes4x4, new[] { 4u, 4u, 4u }, 1, 0, 4, 4, Offset(Rgb4_Result4x4, 1, 0, 6, 6) }; + yield return new object[] { Rgb4_Bytes4x4, new[] { 4u, 4u, 4u }, 0, 1, 4, 4, Offset(Rgb4_Result4x4, 0, 1, 6, 6) }; + yield return new object[] { Rgb4_Bytes4x4, new[] { 4u, 4u, 4u }, 1, 1, 4, 4, Offset(Rgb4_Result4x4, 1, 1, 6, 6) }; + + yield return new object[] { Rgb4_Bytes3x4, new[] { 4u, 4u, 4u }, 0, 0, 3, 4, Rgb4_Result3x4 }; + yield return new object[] { Rgb4_Bytes3x4, new[] { 4u, 4u, 4u }, 0, 0, 3, 4, Offset(Rgb4_Result3x4, 0, 0, 6, 6) }; + yield return new object[] { Rgb4_Bytes3x4, new[] { 4u, 4u, 4u }, 1, 0, 3, 4, Offset(Rgb4_Result3x4, 1, 0, 6, 6) }; + yield return new object[] { Rgb4_Bytes3x4, new[] { 4u, 4u, 4u }, 0, 1, 3, 4, Offset(Rgb4_Result3x4, 0, 1, 6, 6) }; + yield return new object[] { Rgb4_Bytes3x4, new[] { 4u, 4u, 4u }, 1, 1, 3, 4, Offset(Rgb4_Result3x4, 1, 1, 6, 6) }; + } + } + + private static Rgba32 Rgb8_000 = new Rgba32(0, 0, 0, 255); + private static Rgba32 Rgb8_444 = new Rgba32(64, 64, 64, 255); + private static Rgba32 Rgb8_888 = new Rgba32(128, 128, 128, 255); + private static Rgba32 Rgb8_CCC = new Rgba32(192, 192, 192, 255); + private static Rgba32 Rgb8_FFF = new Rgba32(255, 255, 255, 255); + private static Rgba32 Rgb8_F00 = new Rgba32(255, 0, 0, 255); + private static Rgba32 Rgb8_0F0 = new Rgba32(0, 255, 0, 255); + private static Rgba32 Rgb8_00F = new Rgba32(0, 0, 255, 255); + private static Rgba32 Rgb8_F0F = new Rgba32(255, 0, 255, 255); + private static Rgba32 Rgb8_400 = new Rgba32(64, 0, 0, 255); + private static Rgba32 Rgb8_800 = new Rgba32(128, 0, 0, 255); + private static Rgba32 Rgb8_C00 = new Rgba32(192, 0, 0, 255); + private static Rgba32 Rgb8_48C = new Rgba32(64, 128, 192, 255); + + private static byte[] Rgb8_Bytes4x4 = new byte[] { 000, 000, 000, 255, 255, 255, 000, 000, 000, 255, 255, 255, + 255, 000, 000, 000, 255, 000, 000, 000, 255, 255, 000, 255, + 064, 000, 000, 128, 000, 000, 192, 000, 000, 064, 128, 192, + 000, 000, 000, 064, 064, 064, 128, 128, 128, 192, 192, 192 }; + + private static Rgba32[][] Rgb8_Result4x4 = new[] { new[] { Rgb8_000, Rgb8_FFF, Rgb8_000, Rgb8_FFF }, + new[] { Rgb8_F00, Rgb8_0F0, Rgb8_00F, Rgb8_F0F }, + new[] { Rgb8_400, Rgb8_800, Rgb8_C00, Rgb8_48C }, + new[] { Rgb8_000, Rgb8_444, Rgb8_888, Rgb8_CCC }}; + + public static IEnumerable Rgb8_Data + { + get + { + yield return new object[] { Rgb8_Bytes4x4, new[] { 8u, 8u, 8u }, 0, 0, 4, 4, Rgb8_Result4x4 }; + yield return new object[] { Rgb8_Bytes4x4, new[] { 8u, 8u, 8u }, 0, 0, 4, 4, Offset(Rgb8_Result4x4, 0, 0, 6, 6) }; + yield return new object[] { Rgb8_Bytes4x4, new[] { 8u, 8u, 8u }, 1, 0, 4, 4, Offset(Rgb8_Result4x4, 1, 0, 6, 6) }; + yield return new object[] { Rgb8_Bytes4x4, new[] { 8u, 8u, 8u }, 0, 1, 4, 4, Offset(Rgb8_Result4x4, 0, 1, 6, 6) }; + yield return new object[] { Rgb8_Bytes4x4, new[] { 8u, 8u, 8u }, 1, 1, 4, 4, Offset(Rgb8_Result4x4, 1, 1, 6, 6) }; + } + } + + private static Rgba32 Rgb484_000 = new Rgba32(0, 0, 0, 255); + private static Rgba32 Rgb484_444 = new Rgba32(68, 64, 68, 255); + private static Rgba32 Rgb484_888 = new Rgba32(136, 128, 136, 255); + private static Rgba32 Rgb484_CCC = new Rgba32(204, 192, 204, 255); + private static Rgba32 Rgb484_FFF = new Rgba32(255, 255, 255, 255); + private static Rgba32 Rgb484_F00 = new Rgba32(255, 0, 0, 255); + private static Rgba32 Rgb484_0F0 = new Rgba32(0, 255, 0, 255); + private static Rgba32 Rgb484_00F = new Rgba32(0, 0, 255, 255); + private static Rgba32 Rgb484_F0F = new Rgba32(255, 0, 255, 255); + private static Rgba32 Rgb484_400 = new Rgba32(68, 0, 0, 255); + private static Rgba32 Rgb484_800 = new Rgba32(136, 0, 0, 255); + private static Rgba32 Rgb484_C00 = new Rgba32(204, 0, 0, 255); + private static Rgba32 Rgb484_48C = new Rgba32(68, 128, 204, 255); + + private static byte[] Rgb484_Bytes4x4 = new byte[] { 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, + 0xF0, 0x00, 0x0F, 0xF0, 0x00, 0x0F, 0xF0, 0x0F, + 0x40, 0x00, 0x80, 0x00, 0xC0, 0x00, 0x48, 0x0C, + 0x00, 0x00, 0x44, 0x04, 0x88, 0x08, 0xCC, 0x0C }; + + private static Rgba32[][] Rgb484_Result4x4 = new[] { new[] { Rgb484_000, Rgb484_FFF, Rgb484_000, Rgb484_FFF }, + new[] { Rgb484_F00, Rgb484_0F0, Rgb484_00F, Rgb484_F0F }, + new[] { Rgb484_400, Rgb484_800, Rgb484_C00, Rgb484_48C }, + new[] { Rgb484_000, Rgb484_444, Rgb484_888, Rgb484_CCC }}; + + public static IEnumerable Rgb484_Data + { + get + { + yield return new object[] { Rgb484_Bytes4x4, new[] { 4u, 8u, 4u }, 0, 0, 4, 4, Rgb484_Result4x4 }; + yield return new object[] { Rgb484_Bytes4x4, new[] { 4u, 8u, 4u }, 0, 0, 4, 4, Offset(Rgb484_Result4x4, 0, 0, 6, 6) }; + yield return new object[] { Rgb484_Bytes4x4, new[] { 4u, 8u, 4u }, 1, 0, 4, 4, Offset(Rgb484_Result4x4, 1, 0, 6, 6) }; + yield return new object[] { Rgb484_Bytes4x4, new[] { 4u, 8u, 4u }, 0, 1, 4, 4, Offset(Rgb484_Result4x4, 0, 1, 6, 6) }; + yield return new object[] { Rgb484_Bytes4x4, new[] { 4u, 8u, 4u }, 1, 1, 4, 4, Offset(Rgb484_Result4x4, 1, 1, 6, 6) }; + } + } + + [Theory] + [MemberData(nameof(Rgb4_Data))] + [MemberData(nameof(Rgb8_Data))] + [MemberData(nameof(Rgb484_Data))] + public void Decode_WritesPixelData(byte[] inputData, uint[] bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult) + { + AssertDecode(expectedResult, pixels => + { + RgbTiffColor.Decode(inputData, bitsPerSample, pixels, left, top, width, height); + }); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs index 39c5dc8c4..2ba37ac49 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs @@ -200,6 +200,10 @@ namespace ImageSharp.Tests [InlineData(true, TiffPhotometricInterpretation.PaletteColor, new[] { 4 }, TiffColorType.PaletteColor)] [InlineData(false, TiffPhotometricInterpretation.PaletteColor, new[] { 1 }, TiffColorType.PaletteColor)] [InlineData(true, TiffPhotometricInterpretation.PaletteColor, new[] { 1 }, TiffColorType.PaletteColor)] + [InlineData(false, TiffPhotometricInterpretation.Rgb, new[] { 4, 4, 4 }, TiffColorType.Rgb)] + [InlineData(true, TiffPhotometricInterpretation.Rgb, new[] { 4, 4, 4 }, TiffColorType.Rgb)] + [InlineData(false, TiffPhotometricInterpretation.Rgb, new[] { 8, 8, 8 }, TiffColorType.Rgb)] + [InlineData(true, TiffPhotometricInterpretation.Rgb, new[] { 8, 8, 8 }, TiffColorType.Rgb)] public void ReadImageFormat_DeterminesCorrectColorImplementation(bool isLittleEndian, ushort photometricInterpretation, int[] bitsPerSample, int colorType) { Stream stream = CreateTiffGenIfd() @@ -273,7 +277,6 @@ namespace ImageSharp.Tests [InlineData(false, TiffPhotometricInterpretation.IccLab)] [InlineData(false, TiffPhotometricInterpretation.ItuLab)] [InlineData(false, TiffPhotometricInterpretation.LinearRaw)] - [InlineData(false, TiffPhotometricInterpretation.Rgb)] [InlineData(false, TiffPhotometricInterpretation.Separated)] [InlineData(false, TiffPhotometricInterpretation.TransparencyMask)] [InlineData(false, TiffPhotometricInterpretation.YCbCr)] @@ -283,7 +286,6 @@ namespace ImageSharp.Tests [InlineData(true, TiffPhotometricInterpretation.IccLab)] [InlineData(true, TiffPhotometricInterpretation.ItuLab)] [InlineData(true, TiffPhotometricInterpretation.LinearRaw)] - [InlineData(true, TiffPhotometricInterpretation.Rgb)] [InlineData(true, TiffPhotometricInterpretation.Separated)] [InlineData(true, TiffPhotometricInterpretation.TransparencyMask)] [InlineData(true, TiffPhotometricInterpretation.YCbCr)] @@ -369,12 +371,18 @@ namespace ImageSharp.Tests [InlineData(true, TiffPhotometricInterpretation.BlackIsZero, new int[] { })] [InlineData(false, TiffPhotometricInterpretation.PaletteColor, new int[] { })] [InlineData(true, TiffPhotometricInterpretation.PaletteColor, new int[] { })] + [InlineData(false, TiffPhotometricInterpretation.Rgb, new int[] { })] + [InlineData(true, TiffPhotometricInterpretation.Rgb, new int[] { })] [InlineData(false, TiffPhotometricInterpretation.WhiteIsZero, new[] { 8, 8 })] [InlineData(true, TiffPhotometricInterpretation.WhiteIsZero, new[] { 8, 8 })] [InlineData(false, TiffPhotometricInterpretation.BlackIsZero, new[] { 8, 8 })] [InlineData(true, TiffPhotometricInterpretation.BlackIsZero, new[] { 8, 8 })] [InlineData(false, TiffPhotometricInterpretation.PaletteColor, new[] { 8, 8 })] [InlineData(true, TiffPhotometricInterpretation.PaletteColor, new[] { 8, 8 })] + [InlineData(false, TiffPhotometricInterpretation.Rgb, new[] { 8 })] + [InlineData(true, TiffPhotometricInterpretation.Rgb, new[] { 8 })] + [InlineData(false, TiffPhotometricInterpretation.Rgb, new[] { 8, 8 })] + [InlineData(true, TiffPhotometricInterpretation.Rgb, new[] { 8, 8 })] public void ReadImageFormat_ThrowsExceptionForUnsupportedNumberOfSamples(bool isLittleEndian, ushort photometricInterpretation, int[] bitsPerSample) { Stream stream = CreateTiffGenIfd() @@ -424,34 +432,25 @@ namespace ImageSharp.Tests } [Theory] - [InlineData(new uint[] { 1 }, 160, 80, 20 * 80)] - [InlineData(new uint[] { 1 }, 153, 80, 20 * 80)] - [InlineData(new uint[] { 3 }, 100, 80, 38 * 80)] - [InlineData(new uint[] { 4 }, 100, 80, 50 * 80)] - [InlineData(new uint[] { 4 }, 99, 80, 50 * 80)] - [InlineData(new uint[] { 8 }, 100, 80, 100 * 80)] - public void CalculateImageBufferSize_ReturnsCorrectSize(uint[] bitsPerSample, int width, int height, int expectedResult) + [InlineData(TiffColorType.WhiteIsZero, new uint[] { 1 }, 160, 80, 20 * 80)] + [InlineData(TiffColorType.WhiteIsZero, new uint[] { 1 }, 153, 80, 20 * 80)] + [InlineData(TiffColorType.WhiteIsZero, new uint[] { 3 }, 100, 80, 38 * 80)] + [InlineData(TiffColorType.WhiteIsZero, new uint[] { 4 }, 100, 80, 50 * 80)] + [InlineData(TiffColorType.WhiteIsZero, new uint[] { 4 }, 99, 80, 50 * 80)] + [InlineData(TiffColorType.WhiteIsZero, new uint[] { 8 }, 100, 80, 100 * 80)] + [InlineData(TiffColorType.PaletteColor, new uint[] { 1 }, 160, 80, 60 * 80)] + [InlineData(TiffColorType.PaletteColor, new uint[] { 1 }, 153, 80, 58 * 80)] + [InlineData(TiffColorType.PaletteColor, new uint[] { 3 }, 100, 80, 113 * 80)] + [InlineData(TiffColorType.PaletteColor, new uint[] { 4 }, 100, 80, 150 * 80)] + [InlineData(TiffColorType.PaletteColor, new uint[] { 4 }, 99, 80, 149 * 80)] + [InlineData(TiffColorType.PaletteColor, new uint[] { 8 }, 100, 80, 300 * 80)] + [InlineData(TiffColorType.Rgb, new uint[] { 8, 8, 8 }, 100, 80, 300 * 80)] + [InlineData(TiffColorType.Rgb, new uint[] { 4, 4, 4 }, 100, 80, 150 * 80)] + [InlineData(TiffColorType.Rgb, new uint[] { 4, 8, 4 }, 100, 80, 200 * 80)] + public void CalculateImageBufferSize_ReturnsCorrectSize(ushort colorType, uint[] bitsPerSample, int width, int height, int expectedResult) { TiffDecoderCore decoder = new TiffDecoderCore(null, null); - decoder.ColorType = TiffColorType.WhiteIsZero; - decoder.BitsPerSample = bitsPerSample; - - int bufferSize = decoder.CalculateImageBufferSize(width, height); - - Assert.Equal(expectedResult, bufferSize); - } - - [Theory] - [InlineData(new uint[] { 1 }, 160, 80, 60 * 80)] - [InlineData(new uint[] { 1 }, 153, 80, 58 * 80)] - [InlineData(new uint[] { 3 }, 100, 80, 113 * 80)] - [InlineData(new uint[] { 4 }, 100, 80, 150 * 80)] - [InlineData(new uint[] { 4 }, 99, 80, 149 * 80)] - [InlineData(new uint[] { 8 }, 100, 80, 300 * 80)] - public void CalculateImageBufferSize_ReturnsCorrectSize_ForPaletteColor(uint[] bitsPerSample, int width, int height, int expectedResult) - { - TiffDecoderCore decoder = new TiffDecoderCore(null, null); - decoder.ColorType = TiffColorType.PaletteColor; + decoder.ColorType = (TiffColorType)colorType; decoder.BitsPerSample = bitsPerSample; int bufferSize = decoder.CalculateImageBufferSize(width, height); From 0b67805d557060da3de8cd70cdd38667935902d7 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Tue, 16 May 2017 20:11:57 +0100 Subject: [PATCH 038/275] Add optimised implementation for 8-bit RGB images --- .../Rgb888TiffColor.cs | 50 +++++++++++++++++++ .../TiffColorType.cs | 5 ++ .../Formats/Tiff/TiffDecoderCore.cs | 5 +- .../RgbTiffColorTests.cs | 10 ++++ .../Formats/Tiff/TiffDecoderImageTests.cs | 4 +- 5 files changed, 71 insertions(+), 3 deletions(-) create mode 100644 src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs new file mode 100644 index 000000000..afe88510e --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs @@ -0,0 +1,50 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats.Tiff +{ + using System; + using System.Numerics; + using System.Runtime.CompilerServices; + using ImageSharp; + using ImageSharp.PixelFormats; + + /// + /// Implements the 'RGB' photometric interpretation (optimised for 8-bit full color images). + /// + internal static class Rgb888TiffColor + { + /// + /// Decodes pixel data using the current photometric interpretation. + /// + /// The pixel format. + /// The buffer to read image data from. + /// The image buffer to write pixels to. + /// The x-coordinate of the left-hand side of the image block. + /// The y-coordinate of the top of the image block. + /// The width of the image block. + /// The height of the image block. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Decode(byte[] data, PixelAccessor pixels, int left, int top, int width, int height) + where TPixel : struct, IPixel + { + TPixel color = default(TPixel); + + uint offset = 0; + + for (int y = top; y < top + height; y++) + { + for (int x = left; x < left + width; x++) + { + byte r = data[offset++]; + byte g = data[offset++]; + byte b = data[offset++]; + color.PackFromBytes(r, g, b, 255); + pixels[x, y] = color; + } + } + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs index 5d85f6553..630696b77 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs @@ -59,5 +59,10 @@ namespace ImageSharp.Formats.Tiff /// RGB Full Color. /// Rgb, + + /// + /// RGB Full Color. Optimised implementation for 8-bit images. + /// + Rgb888 } } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index e31706674..7419f1759 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -395,7 +395,7 @@ namespace ImageSharp.Formats { if (this.BitsPerSample[0] == 8 && this.BitsPerSample[1] == 8 && this.BitsPerSample[2] == 8) { - this.ColorType = TiffColorType.Rgb; + this.ColorType = TiffColorType.Rgb888; } else { @@ -529,6 +529,9 @@ namespace ImageSharp.Formats case TiffColorType.Rgb: RgbTiffColor.Decode(data, this.BitsPerSample, pixels, left, top, width, height); break; + case TiffColorType.Rgb888: + Rgb888TiffColor.Decode(data, pixels, left, top, width, height); + break; case TiffColorType.PaletteColor: PaletteTiffColor.Decode(data, this.BitsPerSample, this.ColorMap, pixels, left, top, width, height); break; diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs index c67913d65..06122484b 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs @@ -147,5 +147,15 @@ namespace ImageSharp.Tests RgbTiffColor.Decode(inputData, bitsPerSample, pixels, left, top, width, height); }); } + + [Theory] + [MemberData(nameof(Rgb8_Data))] + public void Decode_WritesPixelData_8Bit(byte[] inputData, uint[] bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult) + { + AssertDecode(expectedResult, pixels => + { + Rgb888TiffColor.Decode(inputData, pixels, left, top, width, height); + }); + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs index 2ba37ac49..693ddfea1 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs @@ -202,8 +202,8 @@ namespace ImageSharp.Tests [InlineData(true, TiffPhotometricInterpretation.PaletteColor, new[] { 1 }, TiffColorType.PaletteColor)] [InlineData(false, TiffPhotometricInterpretation.Rgb, new[] { 4, 4, 4 }, TiffColorType.Rgb)] [InlineData(true, TiffPhotometricInterpretation.Rgb, new[] { 4, 4, 4 }, TiffColorType.Rgb)] - [InlineData(false, TiffPhotometricInterpretation.Rgb, new[] { 8, 8, 8 }, TiffColorType.Rgb)] - [InlineData(true, TiffPhotometricInterpretation.Rgb, new[] { 8, 8, 8 }, TiffColorType.Rgb)] + [InlineData(false, TiffPhotometricInterpretation.Rgb, new[] { 8, 8, 8 }, TiffColorType.Rgb888)] + [InlineData(true, TiffPhotometricInterpretation.Rgb, new[] { 8, 8, 8 }, TiffColorType.Rgb888)] public void ReadImageFormat_DeterminesCorrectColorImplementation(bool isLittleEndian, ushort photometricInterpretation, int[] bitsPerSample, int colorType) { Stream stream = CreateTiffGenIfd() From d773963d7408f3c234743ee6387e27a0c7dc5849 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Sun, 21 May 2017 21:54:28 +0100 Subject: [PATCH 039/275] Add support for Deflate (and OldDeflate) compression --- .../Compression/DeflateTiffCompression.cs | 60 ++++ .../Tiff/Compression/TiffCompressionType.cs | 5 + .../Formats/Tiff/TiffDecoderCore.cs | 10 + .../Formats/Tiff/Utils/SubStream.cs | 177 ++++++++++ .../Formats/Tiff/Utils/TiffUtils.cs | 10 + .../DeflateTiffCompressionTests.cs | 48 +++ .../Formats/Tiff/TiffDecoderImageTests.cs | 8 +- .../Formats/Tiff/Utils/SubStreamTests.cs | 327 ++++++++++++++++++ 8 files changed, 641 insertions(+), 4 deletions(-) create mode 100644 src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs create mode 100644 src/ImageSharp/Formats/Tiff/Utils/SubStream.cs create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Utils/SubStreamTests.cs diff --git a/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs new file mode 100644 index 000000000..1af8362a0 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs @@ -0,0 +1,60 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats.Tiff +{ + using System; + using System.Buffers; + using System.IO; + using System.IO.Compression; + using System.Runtime.CompilerServices; + + /// + /// Class to handle cases where TIFF image data is compressed using Deflate compression. + /// + /// + /// Note that the 'OldDeflate' compression type is identical to the 'Deflate' compression type. + /// + internal static class DeflateTiffCompression + { + /// + /// Decompresses image data into the supplied buffer. + /// + /// The to read image data from. + /// The number of bytes to read from the input stream. + /// The output buffer for uncompressed data. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Decompress(Stream stream, int byteCount, byte[] buffer) + { + // Read the 'zlib' header information + int cmf = stream.ReadByte(); + int flag = stream.ReadByte(); + + if ((cmf & 0x0f) != 8) + { + throw new Exception($"Bad compression method for ZLIB header: cmf={cmf}"); + } + + // If the 'fdict' flag is set then we should skip the next four bytes + bool fdict = (flag & 32) != 0; + + if (fdict) + { + stream.ReadByte(); + stream.ReadByte(); + stream.ReadByte(); + stream.ReadByte(); + } + + // The subsequent data is the Deflate compressed data (except for the last four bytes of checksum) + int headerLength = fdict ? 10 : 6; + SubStream subStream = new SubStream(stream, byteCount - headerLength); + using (DeflateStream deflateStream = new DeflateStream(stream, CompressionMode.Decompress, true)) + { + deflateStream.ReadFull(buffer); + } + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs index 6f9ce8f87..6108194c4 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs @@ -19,5 +19,10 @@ namespace ImageSharp.Formats.Tiff /// Image data is compressed using PackBits compression. /// PackBits = 1, + + /// + /// Image data is compressed using Deflate compression. + /// + Deflate = 2, } } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 7419f1759..e7c98cad7 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -268,6 +268,13 @@ namespace ImageSharp.Formats break; } + case TiffCompression.Deflate: + case TiffCompression.OldDeflate: + { + this.CompressionType = TiffCompressionType.Deflate; + break; + } + default: { throw new NotSupportedException("The specified TIFF compression format is not supported."); @@ -482,6 +489,9 @@ namespace ImageSharp.Formats case TiffCompressionType.PackBits: PackBitsTiffCompression.Decompress(this.InputStream, (int)byteCount, buffer); break; + case TiffCompressionType.Deflate: + DeflateTiffCompression.Decompress(this.InputStream, (int)byteCount, buffer); + break; default: throw new InvalidOperationException(); } diff --git a/src/ImageSharp/Formats/Tiff/Utils/SubStream.cs b/src/ImageSharp/Formats/Tiff/Utils/SubStream.cs new file mode 100644 index 000000000..3bab41edb --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Utils/SubStream.cs @@ -0,0 +1,177 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// +namespace ImageSharp.Formats.Tiff +{ + using System; + using System.IO; + + /// + /// Utility class to encapsulate a sub-portion of another . + /// + /// + /// Note that disposing of the does not dispose the underlying + /// . + /// + internal class SubStream : Stream + { + private Stream innerStream; + private long offset; + private long endOffset; + private long length; + + /// + /// Initializes a new instance of the class. + /// + /// The underlying to wrap. + /// The length of the sub-stream. + /// + /// Note that calling the sub-stream with start from the current offset of the + /// underlying + /// + public SubStream(Stream innerStream, long length) + { + this.innerStream = innerStream; + this.offset = this.innerStream.Position; + this.endOffset = this.offset + length; + this.length = length; + } + + /// + /// Initializes a new instance of the class. + /// + /// The underlying to wrap. + /// The offset of the sub-stream within the underlying . + /// The length of the sub-stream. + /// + /// Note that calling the constructor will immediately move the underlying + /// to the specified offset. + /// + public SubStream(Stream innerStream, long offset, long length) + { + this.innerStream = innerStream; + this.offset = offset; + this.endOffset = offset + length; + this.length = length; + + innerStream.Seek(offset, SeekOrigin.Begin); + } + + /// + public override bool CanRead + { + get + { + return true; + } + } + + /// + public override bool CanWrite + { + get + { + return false; + } + } + + /// + public override bool CanSeek + { + get + { + return this.innerStream.CanSeek; + } + } + + /// + public override long Length + { + get + { + return this.length; + } + } + + /// + public override long Position + { + get + { + return this.innerStream.Position - this.offset; + } + + set + { + this.Seek(value, SeekOrigin.Begin); + } + } + + /// + public override void Flush() + { + throw new NotSupportedException(); + } + + /// + public override int Read(byte[] buffer, int offset, int count) + { + long bytesRemaining = this.endOffset - this.innerStream.Position; + + if (bytesRemaining < count) + { + count = (int)bytesRemaining; + } + + return this.innerStream.Read(buffer, offset, count); + } + + /// + public override int ReadByte() + { + if (this.innerStream.Position < this.endOffset) + { + return this.innerStream.ReadByte(); + } + else + { + return -1; + } + } + + /// + public override void Write(byte[] array, int offset, int count) + { + throw new NotSupportedException(); + } + + /// + public override void WriteByte(byte value) + { + throw new NotSupportedException(); + } + + /// + public override long Seek(long offset, SeekOrigin origin) + { + switch (origin) + { + case SeekOrigin.Current: + return this.innerStream.Seek(offset, SeekOrigin.Current) - this.offset; + case SeekOrigin.Begin: + return this.innerStream.Seek(this.offset + offset, SeekOrigin.Begin) - this.offset; + case SeekOrigin.End: + return this.innerStream.Seek(this.endOffset - offset, SeekOrigin.Begin) - this.offset; + default: + throw new ArgumentException("Invalid seek origin."); + } + } + + /// + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs index 64e352745..59b249105 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs @@ -34,5 +34,15 @@ namespace ImageSharp.Formats.Tiff count -= bytesRead; } } + + /// + /// Reads all bytes from the input stream into a buffer until the end of stream or the buffer is full. + /// + /// The stream to read from. + /// A buffer to store the retrieved data. + public static void ReadFull(this Stream stream, byte[] buffer) + { + ReadFull(stream, buffer, buffer.Length); + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs new file mode 100644 index 000000000..e63700880 --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs @@ -0,0 +1,48 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System.IO; + using Xunit; + + using ImageSharp.Formats; + using ImageSharp.Formats.Tiff; + + public class DeflateTiffCompressionTests + { + [Theory] + [InlineData(new byte[] { })] + [InlineData(new byte[] { 42 })] // One byte + [InlineData(new byte[] { 42, 16, 128, 53, 96, 218, 7, 64, 3, 4, 97 })] // Random bytes + [InlineData(new byte[] { 1, 2, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 3, 4 })] // Repeated bytes + [InlineData(new byte[] { 1, 2, 42, 53, 42, 53, 42, 53, 42, 53, 42, 53, 3, 4 })] // Repeated sequence + public void Decompress_ReadsData(byte[] data) + { + using (Stream stream = CreateCompressedStream(data)) + { + byte[] buffer = new byte[data.Length]; + + DeflateTiffCompression.Decompress(stream, data.Length, buffer); + + Assert.Equal(data, buffer); + } + } + + private static Stream CreateCompressedStream(byte[] data) + { + Stream compressedStream = new MemoryStream(); + + using (Stream uncompressedStream = new MemoryStream(data), + deflateStream = new ZlibDeflateStream(compressedStream, 6)) + { + uncompressedStream.CopyTo(deflateStream); + } + + compressedStream.Seek(0, SeekOrigin.Begin); + return compressedStream; + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs index 693ddfea1..6eef305ff 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs @@ -125,6 +125,10 @@ namespace ImageSharp.Tests [InlineData(true, TiffCompression.None, TiffCompressionType.None)] [InlineData(false, TiffCompression.PackBits, TiffCompressionType.PackBits)] [InlineData(true, TiffCompression.PackBits, TiffCompressionType.PackBits)] + [InlineData(false, TiffCompression.Deflate, TiffCompressionType.Deflate)] + [InlineData(true, TiffCompression.Deflate, TiffCompressionType.Deflate)] + [InlineData(false, TiffCompression.OldDeflate, TiffCompressionType.Deflate)] + [InlineData(true, TiffCompression.OldDeflate, TiffCompressionType.Deflate)] public void ReadImageFormat_DeterminesCorrectCompressionImplementation(bool isLittleEndian, ushort compression, int compressionType) { Stream stream = CreateTiffGenIfd() @@ -142,23 +146,19 @@ namespace ImageSharp.Tests [InlineData(false, TiffCompression.Ccitt1D)] [InlineData(false, TiffCompression.CcittGroup3Fax)] [InlineData(false, TiffCompression.CcittGroup4Fax)] - [InlineData(false, TiffCompression.Deflate)] [InlineData(false, TiffCompression.ItuTRecT43)] [InlineData(false, TiffCompression.ItuTRecT82)] [InlineData(false, TiffCompression.Jpeg)] [InlineData(false, TiffCompression.Lzw)] - [InlineData(false, TiffCompression.OldDeflate)] [InlineData(false, TiffCompression.OldJpeg)] [InlineData(false, 999)] [InlineData(true, TiffCompression.Ccitt1D)] [InlineData(true, TiffCompression.CcittGroup3Fax)] [InlineData(true, TiffCompression.CcittGroup4Fax)] - [InlineData(true, TiffCompression.Deflate)] [InlineData(true, TiffCompression.ItuTRecT43)] [InlineData(true, TiffCompression.ItuTRecT82)] [InlineData(true, TiffCompression.Jpeg)] [InlineData(true, TiffCompression.Lzw)] - [InlineData(true, TiffCompression.OldDeflate)] [InlineData(true, TiffCompression.OldJpeg)] [InlineData(true, 999)] public void ReadImageFormat_ThrowsExceptionForUnsupportedCompression(bool isLittleEndian, ushort compression) diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Utils/SubStreamTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Utils/SubStreamTests.cs new file mode 100644 index 000000000..b57a77c74 --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Utils/SubStreamTests.cs @@ -0,0 +1,327 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System; + using System.IO; + using Xunit; + + using ImageSharp.Formats.Tiff; + + public class SubStreamTests + { + [Fact] + public void Constructor_PositionsStreamCorrectly_WithSpecifiedOffset() + { + Stream innerStream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + innerStream.Position = 2; + + SubStream stream = new SubStream(innerStream, 4, 6); + + Assert.Equal(0, stream.Position); + Assert.Equal(6, stream.Length); + Assert.Equal(4, innerStream.Position); + } + + [Fact] + public void Constructor_PositionsStreamCorrectly_WithCurrentOffset() + { + Stream innerStream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + innerStream.Position = 2; + + SubStream stream = new SubStream(innerStream, 6); + + Assert.Equal(0, stream.Position); + Assert.Equal(6, stream.Length); + Assert.Equal(2, innerStream.Position); + } + + [Fact] + public void CanRead_ReturnsTrue() + { + Stream innerStream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + SubStream stream = new SubStream(innerStream, 2, 6); + + Assert.True(stream.CanRead); + } + + [Fact] + public void CanWrite_ReturnsFalse() + { + Stream innerStream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + SubStream stream = new SubStream(innerStream, 2, 6); + + Assert.False(stream.CanWrite); + } + + [Fact] + public void CanSeek_ReturnsTrue() + { + Stream innerStream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + SubStream stream = new SubStream(innerStream, 2, 6); + + Assert.True(stream.CanSeek); + } + + [Fact] + public void Length_ReturnsTheConstrainedLength() + { + Stream innerStream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + SubStream stream = new SubStream(innerStream, 2, 6); + + Assert.Equal(6, stream.Length); + } + + [Fact] + public void Position_ReturnsZeroBeforeReading() + { + Stream innerStream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + SubStream stream = new SubStream(innerStream, 2, 6); + + Assert.Equal(0, stream.Position); + Assert.Equal(2, innerStream.Position); + } + + [Fact] + public void Position_ReturnsPositionAfterReading() + { + Stream innerStream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + SubStream stream = new SubStream(innerStream, 2, 6); + + stream.Read(new byte[2], 0, 2); + + Assert.Equal(2, stream.Position); + Assert.Equal(4, innerStream.Position); + } + + [Fact] + public void Position_ReturnsPositionAfterReadingTwice() + { + Stream innerStream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + SubStream stream = new SubStream(innerStream, 2, 6); + + stream.Read(new byte[2], 0, 2); + stream.Read(new byte[2], 0, 2); + + Assert.Equal(4, stream.Position); + Assert.Equal(6, innerStream.Position); + } + + [Fact] + public void Position_SettingPropertySeeksToNewPosition() + { + Stream innerStream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + SubStream stream = new SubStream(innerStream, 2, 6); + + stream.Position = 3; + + Assert.Equal(3, stream.Position); + Assert.Equal(5, innerStream.Position); + } + + [Fact] + public void Flush_ThrowsNotSupportedException() + { + Stream innerStream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + SubStream stream = new SubStream(innerStream, 2, 6); + + Assert.Throws(() => stream.Flush()); + } + + [Fact] + public void Read_Reads_FromStartOfSubStream() + { + Stream innerStream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + SubStream stream = new SubStream(innerStream, 2, 6); + + byte[] buffer = new byte[3]; + var result = stream.Read(buffer, 0, 3); + + Assert.Equal(new byte[] { 3, 4, 5 }, buffer); + Assert.Equal(3, result); + } + + [Theory] + [InlineData(2, SeekOrigin.Begin)] + [InlineData(1, SeekOrigin.Current)] + [InlineData(4, SeekOrigin.End)] + public void Read_Reads_FromMiddleOfSubStream(long offset, SeekOrigin origin) + { + Stream innerStream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + SubStream stream = new SubStream(innerStream, 2, 6); + + stream.Position = 1; + stream.Seek(offset, origin); + byte[] buffer = new byte[3]; + var result = stream.Read(buffer, 0, 3); + + Assert.Equal(new byte[] { 5, 6, 7 }, buffer); + Assert.Equal(3, result); + } + + [Theory] + [InlineData(3, SeekOrigin.Begin)] + [InlineData(2, SeekOrigin.Current)] + [InlineData(3, SeekOrigin.End)] + public void Read_Reads_FromEndOfSubStream(long offset, SeekOrigin origin) + { + Stream innerStream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + SubStream stream = new SubStream(innerStream, 2, 6); + + stream.Position = 1; + stream.Seek(offset, origin); + byte[] buffer = new byte[3]; + var result = stream.Read(buffer, 0, 3); + + Assert.Equal(new byte[] { 6, 7, 8 }, buffer); + Assert.Equal(3, result); + } + + [Theory] + [InlineData(4, SeekOrigin.Begin)] + [InlineData(3, SeekOrigin.Current)] + [InlineData(2, SeekOrigin.End)] + public void Read_Reads_FromBeyondEndOfSubStream(long offset, SeekOrigin origin) + { + Stream innerStream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + SubStream stream = new SubStream(innerStream, 2, 6); + + stream.Position = 1; + stream.Seek(offset, origin); + byte[] buffer = new byte[3]; + var result = stream.Read(buffer, 0, 3); + + Assert.Equal(new byte[] { 7, 8, 0 }, buffer); + Assert.Equal(2, result); + } + + [Fact] + public void ReadByte_Reads_FromStartOfSubStream() + { + Stream innerStream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + SubStream stream = new SubStream(innerStream, 2, 6); + + var result = stream.ReadByte(); + + Assert.Equal(3, result); + } + + [Fact] + public void ReadByte_Reads_FromMiddleOfSubStream() + { + Stream innerStream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + SubStream stream = new SubStream(innerStream, 2, 6); + + stream.Position = 3; + var result = stream.ReadByte(); + + Assert.Equal(6, result); + } + + [Fact] + public void ReadByte_Reads_FromEndOfSubStream() + { + Stream innerStream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + SubStream stream = new SubStream(innerStream, 2, 6); + + stream.Position = 5; + var result = stream.ReadByte(); + + Assert.Equal(8, result); + } + + [Fact] + public void ReadByte_Reads_FromBeyondEndOfSubStream() + { + Stream innerStream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + SubStream stream = new SubStream(innerStream, 2, 6); + + stream.Position = 5; + stream.ReadByte(); + var result = stream.ReadByte(); + + Assert.Equal(-1, result); + } + + [Fact] + public void Write_ThrowsNotSupportedException() + { + Stream innerStream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + SubStream stream = new SubStream(innerStream, 2, 6); + + Assert.Throws(() => stream.Write(new byte[] { 1, 2 }, 0, 2)); + } + + [Fact] + public void WriteByte_ThrowsNotSupportedException() + { + Stream innerStream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + SubStream stream = new SubStream(innerStream, 2, 6); + + Assert.Throws(() => stream.WriteByte(42)); + } + + [Fact] + public void Seek_MovesToNewPosition_FromBegin() + { + Stream innerStream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + SubStream stream = new SubStream(innerStream, 2, 6); + + stream.Position = 1; + long result = stream.Seek(2, SeekOrigin.Begin); + + Assert.Equal(2, result); + Assert.Equal(2, stream.Position); + Assert.Equal(4, innerStream.Position); + } + + [Fact] + public void Seek_MovesToNewPosition_FromCurrent() + { + Stream innerStream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + SubStream stream = new SubStream(innerStream, 2, 6); + + stream.Position = 1; + long result = stream.Seek(2, SeekOrigin.Current); + + Assert.Equal(3, result); + Assert.Equal(3, stream.Position); + Assert.Equal(5, innerStream.Position); + } + + [Fact] + public void Seek_MovesToNewPosition_FromEnd() + { + Stream innerStream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + SubStream stream = new SubStream(innerStream, 2, 6); + + stream.Position = 1; + long result = stream.Seek(2, SeekOrigin.End); + + Assert.Equal(4, result); + Assert.Equal(4, stream.Position); + Assert.Equal(6, innerStream.Position); + } + + [Fact] + public void Seek_ThrowsException_WithInvalidOrigin() + { + Stream innerStream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + SubStream stream = new SubStream(innerStream, 2, 6); + + var e = Assert.Throws(() => stream.Seek(2, (SeekOrigin)99)); + Assert.Equal("Invalid seek origin.", e.Message); + } + + public void SetLength_ThrowsNotSupportedException() + { + Stream innerStream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + SubStream stream = new SubStream(innerStream, 2, 6); + + Assert.Throws(() => stream.SetLength(5)); + } + } +} \ No newline at end of file From 240d86b070413a2e1f7600625bf280fa2d19e45d Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Thu, 25 May 2017 10:48:06 +0100 Subject: [PATCH 040/275] Add support for RGB (planar) pixel formats --- .../RgbPlanarTiffColor.cs | 60 ++++++ .../TiffColorType.cs | 7 +- .../Formats/Tiff/TiffDecoderCore.cs | 108 ++++++++-- .../PhotometricInterpretationTestBase.cs | 11 +- .../RgbPlanarTiffColorTests.cs | 199 ++++++++++++++++++ .../Formats/Tiff/TiffDecoderImageTests.cs | 103 ++++++++- .../ImageSharp.Formats.Tiff.Tests.csproj | 1 + 7 files changed, 461 insertions(+), 28 deletions(-) create mode 100644 src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs new file mode 100644 index 000000000..bcd8e171b --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs @@ -0,0 +1,60 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats.Tiff +{ + using System; + using System.Numerics; + using System.Runtime.CompilerServices; + using ImageSharp; + using ImageSharp.PixelFormats; + + /// + /// Implements the 'RGB' photometric interpretation with 'Planar' layout (for all bit depths). + /// + internal static class RgbPlanarTiffColor + { + /// + /// Decodes pixel data using the current photometric interpretation. + /// + /// The pixel format. + /// The buffers to read image data from. + /// The number of bits per sample for each pixel. + /// The image buffer to write pixels to. + /// The x-coordinate of the left-hand side of the image block. + /// The y-coordinate of the top of the image block. + /// The width of the image block. + /// The height of the image block. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Decode(byte[][] data, uint[] bitsPerSample, PixelAccessor pixels, int left, int top, int width, int height) + where TPixel : struct, IPixel + { + TPixel color = default(TPixel); + + BitReader rBitReader = new BitReader(data[0]); + BitReader gBitReader = new BitReader(data[1]); + BitReader bBitReader = new BitReader(data[2]); + float rFactor = (float)Math.Pow(2, bitsPerSample[0]) - 1.0f; + float gFactor = (float)Math.Pow(2, bitsPerSample[1]) - 1.0f; + float bFactor = (float)Math.Pow(2, bitsPerSample[2]) - 1.0f; + + for (int y = top; y < top + height; y++) + { + for (int x = left; x < left + width; x++) + { + float r = ((float)rBitReader.ReadBits(bitsPerSample[0])) / rFactor; + float g = ((float)gBitReader.ReadBits(bitsPerSample[1])) / gFactor; + float b = ((float)bBitReader.ReadBits(bitsPerSample[2])) / bFactor; + color.PackFromVector4(new Vector4(r, g, b, 1.0f)); + pixels[x, y] = color; + } + + rBitReader.NextRow(); + gBitReader.NextRow(); + bBitReader.NextRow(); + } + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs index 630696b77..36e00edf4 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs @@ -63,6 +63,11 @@ namespace ImageSharp.Formats.Tiff /// /// RGB Full Color. Optimised implementation for 8-bit images. /// - Rgb888 + Rgb888, + + /// + /// RGB Full Color. Planar configuration of data. + /// + RgbPlanar, } } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index e7c98cad7..a2d1f37c8 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -82,6 +82,11 @@ namespace ImageSharp.Formats /// public bool IsLittleEndian { get; private set; } + /// + /// Gets or sets the planar configuration type to use when decoding the image. + /// + public TiffPlanarConfiguration PlanarConfiguration { get; set; } + /// /// Calculates the size (in bytes) of the data contained within an IFD entry. /// @@ -281,6 +286,15 @@ namespace ImageSharp.Formats } } + if (ifd.TryGetIfdEntry(TiffTags.PlanarConfiguration, out TiffIfdEntry planarConfigurationEntry)) + { + this.PlanarConfiguration = (TiffPlanarConfiguration)this.ReadUnsignedInteger(ref planarConfigurationEntry); + } + else + { + this.PlanarConfiguration = TiffPlanarConfiguration.Chunky; + } + TiffPhotometricInterpretation photometricInterpretation; if (ifd.TryGetIfdEntry(TiffTags.PhotometricInterpretation, out TiffIfdEntry photometricInterpretationEntry)) @@ -400,13 +414,20 @@ namespace ImageSharp.Formats { if (this.BitsPerSample.Length == 3) { - if (this.BitsPerSample[0] == 8 && this.BitsPerSample[1] == 8 && this.BitsPerSample[2] == 8) + if (this.PlanarConfiguration == TiffPlanarConfiguration.Chunky) { - this.ColorType = TiffColorType.Rgb888; + if (this.BitsPerSample[0] == 8 && this.BitsPerSample[1] == 8 && this.BitsPerSample[2] == 8) + { + this.ColorType = TiffColorType.Rgb888; + } + else + { + this.ColorType = TiffColorType.Rgb; + } } else { - this.ColorType = TiffColorType.Rgb; + this.ColorType = TiffColorType.RgbPlanar; } } else @@ -457,17 +478,25 @@ namespace ImageSharp.Formats /// /// The width for the desired pixel buffer. /// The height for the desired pixel buffer. + /// The index of the plane for planar image configuration (or zero for chunky). /// The size (in bytes) of the required pixel buffer. - public int CalculateImageBufferSize(int width, int height) + public int CalculateImageBufferSize(int width, int height, int plane) { uint bitsPerPixel = 0; - for (int i = 0; i < this.BitsPerSample.Length; i++) + + if (this.PlanarConfiguration == TiffPlanarConfiguration.Chunky) { - bitsPerPixel += this.BitsPerSample[i]; + for (int i = 0; i < this.BitsPerSample.Length; i++) + { + bitsPerPixel += this.BitsPerSample[i]; + } + } + else + { + bitsPerPixel = this.BitsPerSample[plane]; } - int sampleMultiplier = this.ColorType == TiffColorType.PaletteColor ? 3 : 1; - int bytesPerRow = ((width * (int)bitsPerPixel * sampleMultiplier) + 7) / 8; + int bytesPerRow = ((width * (int)bitsPerPixel) + 7) / 8; return bytesPerRow * height; } @@ -498,7 +527,7 @@ namespace ImageSharp.Formats } /// - /// Decodes pixel data using the current photometric interpretation. + /// Decodes pixel data using the current photometric interpretation (chunky configuration). /// /// The pixel format. /// The buffer to read image data from. @@ -507,7 +536,7 @@ namespace ImageSharp.Formats /// The y-coordinate of the top of the image block. /// The width of the image block. /// The height of the image block. - public void ProcessImageBlock(byte[] data, PixelAccessor pixels, int left, int top, int width, int height) + public void ProcessImageBlockChunky(byte[] data, PixelAccessor pixels, int left, int top, int width, int height) where TPixel : struct, IPixel { switch (this.ColorType) @@ -550,6 +579,29 @@ namespace ImageSharp.Formats } } + /// + /// Decodes pixel data using the current photometric interpretation (planar configuration). + /// + /// The pixel format. + /// The buffer to read image data from. + /// The image buffer to write pixels to. + /// The x-coordinate of the left-hand side of the image block. + /// The y-coordinate of the top of the image block. + /// The width of the image block. + /// The height of the image block. + public void ProcessImageBlockPlanar(byte[][] data, PixelAccessor pixels, int left, int top, int width, int height) + where TPixel : struct, IPixel + { + switch (this.ColorType) + { + case TiffColorType.RgbPlanar: + RgbPlanarTiffColor.Decode(data, this.BitsPerSample, pixels, left, top, width, height); + break; + default: + throw new InvalidOperationException(); + } + } + /// /// Reads the data from a as an array of bytes. /// @@ -1108,25 +1160,47 @@ namespace ImageSharp.Formats private void DecodeImageStrips(Image image, int rowsPerStrip, uint[] stripOffsets, uint[] stripByteCounts) where TPixel : struct, IPixel { - int uncompressedStripSize = this.CalculateImageBufferSize(image.Width, rowsPerStrip); + int stripsPerPixel = this.PlanarConfiguration == TiffPlanarConfiguration.Chunky ? 1 : this.BitsPerSample.Length; + int stripsPerPlane = stripOffsets.Length / stripsPerPixel; using (PixelAccessor pixels = image.Lock()) { - byte[] stripBytes = ArrayPool.Shared.Rent(uncompressedStripSize); + byte[][] stripBytes = new byte[stripsPerPixel][]; + + for (int stripIndex = 0; stripIndex < stripBytes.Length; stripIndex++) + { + int uncompressedStripSize = this.CalculateImageBufferSize(image.Width, rowsPerStrip, stripIndex); + stripBytes[stripIndex] = ArrayPool.Shared.Rent(uncompressedStripSize); + } try { - for (int i = 0; i < stripOffsets.Length; i++) + for (int i = 0; i < stripsPerPlane; i++) { - int stripHeight = i < stripOffsets.Length - 1 || image.Height % rowsPerStrip == 0 ? rowsPerStrip : image.Height % rowsPerStrip; + int stripHeight = i < stripsPerPlane - 1 || image.Height % rowsPerStrip == 0 ? rowsPerStrip : image.Height % rowsPerStrip; + + for (int planeIndex = 0; planeIndex < stripsPerPixel; planeIndex++) + { + int stripIndex = i * stripsPerPixel + planeIndex; + this.DecompressImageBlock(stripOffsets[stripIndex], stripByteCounts[stripIndex], stripBytes[planeIndex]); + } - this.DecompressImageBlock(stripOffsets[i], stripByteCounts[i], stripBytes); - this.ProcessImageBlock(stripBytes, pixels, 0, rowsPerStrip * i, image.Width, stripHeight); + if (this.PlanarConfiguration == TiffPlanarConfiguration.Chunky) + { + this.ProcessImageBlockChunky(stripBytes[0], pixels, 0, rowsPerStrip * i, image.Width, stripHeight); + } + else + { + this.ProcessImageBlockPlanar(stripBytes, pixels, 0, rowsPerStrip * i, image.Width, stripHeight); + } } } finally { - ArrayPool.Shared.Return(stripBytes); + for (int stripIndex = 0; stripIndex < stripBytes.Length; stripIndex++) + { + ArrayPool.Shared.Return(stripBytes[stripIndex]); + } } } } diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs index ab9a89116..c07c37832 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs @@ -7,9 +7,12 @@ namespace ImageSharp.Tests { using System; using Xunit; + using ImageSharp; public abstract class PhotometricInterpretationTestBase { + public static Rgba32 DefaultColor = new Rgba32(42, 96, 18, 128); + public static Rgba32[][] Offset(Rgba32[][] input, int xOffset, int yOffset, int width, int height) { int inputHeight = input.Length; @@ -20,6 +23,11 @@ namespace ImageSharp.Tests for (int y = 0; y < output.Length; y++) { output[y] = new Rgba32[width]; + + for (int x = 0; x < width; x++) + { + output[y][x] = DefaultColor; + } } for (int y = 0; y < inputHeight; y++) @@ -38,6 +46,7 @@ namespace ImageSharp.Tests int resultWidth = expectedResult[0].Length; int resultHeight = expectedResult.Length; Image image = new Image(resultWidth, resultHeight); + image.Fill(DefaultColor); using (PixelAccessor pixels = image.Lock()) { @@ -51,7 +60,7 @@ namespace ImageSharp.Tests for (int x = 0; x < resultWidth; x++) { Assert.True(expectedResult[y][x] == pixels[x, y], - $"Pixel ({x}, {y}) should be {expectedResult[y][x]} but was {pixels[x,y]}"); + $"Pixel ({x}, {y}) should be {expectedResult[y][x]} but was {pixels[x, y]}"); } } } diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs new file mode 100644 index 000000000..2b06a8af5 --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs @@ -0,0 +1,199 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System.Collections.Generic; + using Xunit; + + using ImageSharp.Formats.Tiff; + + public class RgbPlanarTiffColorTests : PhotometricInterpretationTestBase + { + private static Rgba32 Rgb4_000 = new Rgba32(0, 0, 0, 255); + private static Rgba32 Rgb4_444 = new Rgba32(68, 68, 68, 255); + private static Rgba32 Rgb4_888 = new Rgba32(136, 136, 136, 255); + private static Rgba32 Rgb4_CCC = new Rgba32(204, 204, 204, 255); + private static Rgba32 Rgb4_FFF = new Rgba32(255, 255, 255, 255); + private static Rgba32 Rgb4_F00 = new Rgba32(255, 0, 0, 255); + private static Rgba32 Rgb4_0F0 = new Rgba32(0, 255, 0, 255); + private static Rgba32 Rgb4_00F = new Rgba32(0, 0, 255, 255); + private static Rgba32 Rgb4_F0F = new Rgba32(255, 0, 255, 255); + private static Rgba32 Rgb4_400 = new Rgba32(68, 0, 0, 255); + private static Rgba32 Rgb4_800 = new Rgba32(136, 0, 0, 255); + private static Rgba32 Rgb4_C00 = new Rgba32(204, 0, 0, 255); + private static Rgba32 Rgb4_48C = new Rgba32(68, 136, 204, 255); + + private static byte[] Rgb4_Bytes4x4_R = new byte[] { 0x0F, 0x0F, + 0xF0, 0x0F, + 0x48, 0xC4, + 0x04, 0x8C }; + + private static byte[] Rgb4_Bytes4x4_G = new byte[] { 0x0F, 0x0F, + 0x0F, 0x00, + 0x00, 0x08, + 0x04, 0x8C }; + + private static byte[] Rgb4_Bytes4x4_B = new byte[] { 0x0F, 0x0F, + 0x00, 0xFF, + 0x00, 0x0C, + 0x04, 0x8C }; + + private static byte[][] Rgb4_Bytes4x4 = new[] { Rgb4_Bytes4x4_R, Rgb4_Bytes4x4_G, Rgb4_Bytes4x4_B }; + + private static Rgba32[][] Rgb4_Result4x4 = new[] { new[] { Rgb4_000, Rgb4_FFF, Rgb4_000, Rgb4_FFF }, + new[] { Rgb4_F00, Rgb4_0F0, Rgb4_00F, Rgb4_F0F }, + new[] { Rgb4_400, Rgb4_800, Rgb4_C00, Rgb4_48C }, + new[] { Rgb4_000, Rgb4_444, Rgb4_888, Rgb4_CCC }}; + + private static byte[] Rgb4_Bytes3x4_R = new byte[] { 0x0F, 0x00, + 0xF0, 0x00, + 0x48, 0xC0, + 0x04, 0x80 }; + + private static byte[] Rgb4_Bytes3x4_G = new byte[] { 0x0F, 0x00, + 0x0F, 0x00, + 0x00, 0x00, + 0x04, 0x80 }; + + private static byte[] Rgb4_Bytes3x4_B = new byte[] { 0x0F, 0x00, + 0x00, 0xF0, + 0x00, 0x00, + 0x04, 0x80 }; + + private static byte[][] Rgb4_Bytes3x4 = new[] { Rgb4_Bytes3x4_R, Rgb4_Bytes3x4_G, Rgb4_Bytes3x4_B }; + + private static Rgba32[][] Rgb4_Result3x4 = new[] { new[] { Rgb4_000, Rgb4_FFF, Rgb4_000 }, + new[] { Rgb4_F00, Rgb4_0F0, Rgb4_00F }, + new[] { Rgb4_400, Rgb4_800, Rgb4_C00 }, + new[] { Rgb4_000, Rgb4_444, Rgb4_888 }}; + + public static IEnumerable Rgb4_Data + { + get + { + yield return new object[] { Rgb4_Bytes4x4, new[] { 4u, 4u, 4u }, 0, 0, 4, 4, Rgb4_Result4x4 }; + yield return new object[] { Rgb4_Bytes4x4, new[] { 4u, 4u, 4u }, 0, 0, 4, 4, Offset(Rgb4_Result4x4, 0, 0, 6, 6) }; + yield return new object[] { Rgb4_Bytes4x4, new[] { 4u, 4u, 4u }, 1, 0, 4, 4, Offset(Rgb4_Result4x4, 1, 0, 6, 6) }; + yield return new object[] { Rgb4_Bytes4x4, new[] { 4u, 4u, 4u }, 0, 1, 4, 4, Offset(Rgb4_Result4x4, 0, 1, 6, 6) }; + yield return new object[] { Rgb4_Bytes4x4, new[] { 4u, 4u, 4u }, 1, 1, 4, 4, Offset(Rgb4_Result4x4, 1, 1, 6, 6) }; + + yield return new object[] { Rgb4_Bytes3x4, new[] { 4u, 4u, 4u }, 0, 0, 3, 4, Rgb4_Result3x4 }; + yield return new object[] { Rgb4_Bytes3x4, new[] { 4u, 4u, 4u }, 0, 0, 3, 4, Offset(Rgb4_Result3x4, 0, 0, 6, 6) }; + yield return new object[] { Rgb4_Bytes3x4, new[] { 4u, 4u, 4u }, 1, 0, 3, 4, Offset(Rgb4_Result3x4, 1, 0, 6, 6) }; + yield return new object[] { Rgb4_Bytes3x4, new[] { 4u, 4u, 4u }, 0, 1, 3, 4, Offset(Rgb4_Result3x4, 0, 1, 6, 6) }; + yield return new object[] { Rgb4_Bytes3x4, new[] { 4u, 4u, 4u }, 1, 1, 3, 4, Offset(Rgb4_Result3x4, 1, 1, 6, 6) }; + } + } + + private static Rgba32 Rgb8_000 = new Rgba32(0, 0, 0, 255); + private static Rgba32 Rgb8_444 = new Rgba32(64, 64, 64, 255); + private static Rgba32 Rgb8_888 = new Rgba32(128, 128, 128, 255); + private static Rgba32 Rgb8_CCC = new Rgba32(192, 192, 192, 255); + private static Rgba32 Rgb8_FFF = new Rgba32(255, 255, 255, 255); + private static Rgba32 Rgb8_F00 = new Rgba32(255, 0, 0, 255); + private static Rgba32 Rgb8_0F0 = new Rgba32(0, 255, 0, 255); + private static Rgba32 Rgb8_00F = new Rgba32(0, 0, 255, 255); + private static Rgba32 Rgb8_F0F = new Rgba32(255, 0, 255, 255); + private static Rgba32 Rgb8_400 = new Rgba32(64, 0, 0, 255); + private static Rgba32 Rgb8_800 = new Rgba32(128, 0, 0, 255); + private static Rgba32 Rgb8_C00 = new Rgba32(192, 0, 0, 255); + private static Rgba32 Rgb8_48C = new Rgba32(64, 128, 192, 255); + + private static byte[] Rgb8_Bytes4x4_R = new byte[] { 000, 255, 000, 255, + 255, 000, 000, 255, + 064, 128, 192, 064, + 000, 064, 128, 192 }; + + private static byte[] Rgb8_Bytes4x4_G = new byte[] { 000, 255, 000, 255, + 000, 255, 000, 000, + 000, 000, 000, 128, + 000, 064, 128, 192 }; + + private static byte[] Rgb8_Bytes4x4_B = new byte[] { 000, 255, 000, 255, + 000, 000, 255, 255, + 000, 000, 000, 192, + 000, 064, 128, 192 }; + + private static byte[][] Rgb8_Bytes4x4 = new[] { Rgb8_Bytes4x4_R, Rgb8_Bytes4x4_G, Rgb8_Bytes4x4_B }; + + private static Rgba32[][] Rgb8_Result4x4 = new[] { new[] { Rgb8_000, Rgb8_FFF, Rgb8_000, Rgb8_FFF }, + new[] { Rgb8_F00, Rgb8_0F0, Rgb8_00F, Rgb8_F0F }, + new[] { Rgb8_400, Rgb8_800, Rgb8_C00, Rgb8_48C }, + new[] { Rgb8_000, Rgb8_444, Rgb8_888, Rgb8_CCC }}; + + public static IEnumerable Rgb8_Data + { + get + { + yield return new object[] { Rgb8_Bytes4x4, new[] { 8u, 8u, 8u }, 0, 0, 4, 4, Rgb8_Result4x4 }; + yield return new object[] { Rgb8_Bytes4x4, new[] { 8u, 8u, 8u }, 0, 0, 4, 4, Offset(Rgb8_Result4x4, 0, 0, 6, 6) }; + yield return new object[] { Rgb8_Bytes4x4, new[] { 8u, 8u, 8u }, 1, 0, 4, 4, Offset(Rgb8_Result4x4, 1, 0, 6, 6) }; + yield return new object[] { Rgb8_Bytes4x4, new[] { 8u, 8u, 8u }, 0, 1, 4, 4, Offset(Rgb8_Result4x4, 0, 1, 6, 6) }; + yield return new object[] { Rgb8_Bytes4x4, new[] { 8u, 8u, 8u }, 1, 1, 4, 4, Offset(Rgb8_Result4x4, 1, 1, 6, 6) }; + } + } + + private static Rgba32 Rgb484_000 = new Rgba32(0, 0, 0, 255); + private static Rgba32 Rgb484_444 = new Rgba32(68, 64, 68, 255); + private static Rgba32 Rgb484_888 = new Rgba32(136, 128, 136, 255); + private static Rgba32 Rgb484_CCC = new Rgba32(204, 192, 204, 255); + private static Rgba32 Rgb484_FFF = new Rgba32(255, 255, 255, 255); + private static Rgba32 Rgb484_F00 = new Rgba32(255, 0, 0, 255); + private static Rgba32 Rgb484_0F0 = new Rgba32(0, 255, 0, 255); + private static Rgba32 Rgb484_00F = new Rgba32(0, 0, 255, 255); + private static Rgba32 Rgb484_F0F = new Rgba32(255, 0, 255, 255); + private static Rgba32 Rgb484_400 = new Rgba32(68, 0, 0, 255); + private static Rgba32 Rgb484_800 = new Rgba32(136, 0, 0, 255); + private static Rgba32 Rgb484_C00 = new Rgba32(204, 0, 0, 255); + private static Rgba32 Rgb484_48C = new Rgba32(68, 128, 204, 255); + + private static byte[] Rgb484_Bytes4x4_R = new byte[] { 0x0F, 0x0F, + 0xF0, 0x0F, + 0x48, 0xC4, + 0x04, 0x8C }; + + private static byte[] Rgb484_Bytes4x4_G = new byte[] { 0x00, 0xFF, 0x00, 0xFF, + 0x00, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, + 0x00, 0x40, 0x80, 0xC0 }; + + private static byte[] Rgb484_Bytes4x4_B = new byte[] { 0x0F, 0x0F, + 0x00, 0xFF, + 0x00, 0x0C, + 0x04, 0x8C }; + + private static Rgba32[][] Rgb484_Result4x4 = new[] { new[] { Rgb484_000, Rgb484_FFF, Rgb484_000, Rgb484_FFF }, + new[] { Rgb484_F00, Rgb484_0F0, Rgb484_00F, Rgb484_F0F }, + new[] { Rgb484_400, Rgb484_800, Rgb484_C00, Rgb484_48C }, + new[] { Rgb484_000, Rgb484_444, Rgb484_888, Rgb484_CCC }}; + + private static byte[][] Rgb484_Bytes4x4 = new[] { Rgb484_Bytes4x4_R, Rgb484_Bytes4x4_G, Rgb484_Bytes4x4_B }; + + public static IEnumerable Rgb484_Data + { + get + { + yield return new object[] { Rgb484_Bytes4x4, new[] { 4u, 8u, 4u }, 0, 0, 4, 4, Rgb484_Result4x4 }; + yield return new object[] { Rgb484_Bytes4x4, new[] { 4u, 8u, 4u }, 0, 0, 4, 4, Offset(Rgb484_Result4x4, 0, 0, 6, 6) }; + yield return new object[] { Rgb484_Bytes4x4, new[] { 4u, 8u, 4u }, 1, 0, 4, 4, Offset(Rgb484_Result4x4, 1, 0, 6, 6) }; + yield return new object[] { Rgb484_Bytes4x4, new[] { 4u, 8u, 4u }, 0, 1, 4, 4, Offset(Rgb484_Result4x4, 0, 1, 6, 6) }; + yield return new object[] { Rgb484_Bytes4x4, new[] { 4u, 8u, 4u }, 1, 1, 4, 4, Offset(Rgb484_Result4x4, 1, 1, 6, 6) }; + } + } + + [Theory] + [MemberData(nameof(Rgb4_Data))] + [MemberData(nameof(Rgb8_Data))] + [MemberData(nameof(Rgb484_Data))] + public void Decode_WritesPixelData(byte[][] inputData, uint[] bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult) + { + AssertDecode(expectedResult, pixels => + { + RgbPlanarTiffColor.Decode(inputData, bitsPerSample, pixels, left, top, width, height); + }); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs index 6eef305ff..c779a631e 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs @@ -204,10 +204,31 @@ namespace ImageSharp.Tests [InlineData(true, TiffPhotometricInterpretation.Rgb, new[] { 4, 4, 4 }, TiffColorType.Rgb)] [InlineData(false, TiffPhotometricInterpretation.Rgb, new[] { 8, 8, 8 }, TiffColorType.Rgb888)] [InlineData(true, TiffPhotometricInterpretation.Rgb, new[] { 8, 8, 8 }, TiffColorType.Rgb888)] - public void ReadImageFormat_DeterminesCorrectColorImplementation(bool isLittleEndian, ushort photometricInterpretation, int[] bitsPerSample, int colorType) + public void ReadImageFormat_DeterminesCorrectColorImplementation_Chunky(bool isLittleEndian, ushort photometricInterpretation, int[] bitsPerSample, int colorType) { Stream stream = CreateTiffGenIfd() .WithEntry(TiffGenEntry.Integer(TiffTags.PhotometricInterpretation, TiffType.Short, photometricInterpretation)) + .WithEntry(TiffGenEntry.Integer(TiffTags.PlanarConfiguration, TiffType.Short, (int)TiffPlanarConfiguration.Chunky)) + .WithEntry(TiffGenEntry.Integer(TiffTags.BitsPerSample, TiffType.Short, bitsPerSample)) + .ToStream(isLittleEndian); + + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null, null); + TiffIfd ifd = decoder.ReadIfd(0); + decoder.ReadImageFormat(ifd); + + Assert.Equal((TiffColorType)colorType, decoder.ColorType); + } + + [Theory] + [InlineData(false, TiffPhotometricInterpretation.Rgb, new[] { 4, 4, 4 }, TiffColorType.RgbPlanar)] + [InlineData(true, TiffPhotometricInterpretation.Rgb, new[] { 4, 4, 4 }, TiffColorType.RgbPlanar)] + [InlineData(false, TiffPhotometricInterpretation.Rgb, new[] { 8, 8, 8 }, TiffColorType.RgbPlanar)] + [InlineData(true, TiffPhotometricInterpretation.Rgb, new[] { 8, 8, 8 }, TiffColorType.RgbPlanar)] + public void ReadImageFormat_DeterminesCorrectColorImplementation_Planar(bool isLittleEndian, ushort photometricInterpretation, int[] bitsPerSample, int colorType) + { + Stream stream = CreateTiffGenIfd() + .WithEntry(TiffGenEntry.Integer(TiffTags.PhotometricInterpretation, TiffType.Short, photometricInterpretation)) + .WithEntry(TiffGenEntry.Integer(TiffTags.PlanarConfiguration, TiffType.Short, (int)TiffPlanarConfiguration.Planar)) .WithEntry(TiffGenEntry.Integer(TiffTags.BitsPerSample, TiffType.Short, bitsPerSample)) .ToStream(isLittleEndian); @@ -431,6 +452,43 @@ namespace ImageSharp.Tests Assert.Equal("The TIFF ColorMap entry is missing for a pallete color image.", e.Message); } + [Theory] + [InlineData(false, TiffPlanarConfiguration.Chunky)] + [InlineData(true, TiffPlanarConfiguration.Chunky)] + [InlineData(false, TiffPlanarConfiguration.Planar)] + [InlineData(true, TiffPlanarConfiguration.Planar)] + public void ReadImageFormat_ReadsPlanarConfiguration(bool isLittleEndian, int planarConfiguration) + { + Stream stream = CreateTiffGenIfd() + .WithEntry(TiffGenEntry.Integer(TiffTags.PhotometricInterpretation, TiffType.Short, (int)TiffPhotometricInterpretation.Rgb)) + .WithEntry(TiffGenEntry.Integer(TiffTags.BitsPerSample, TiffType.Short, new int[] { 8, 8, 8 })) + .WithEntry(TiffGenEntry.Integer(TiffTags.PlanarConfiguration, TiffType.Short, (int)planarConfiguration)) + .ToStream(isLittleEndian); + + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null, null); + TiffIfd ifd = decoder.ReadIfd(0); + decoder.ReadImageFormat(ifd); + + Assert.Equal((TiffPlanarConfiguration)planarConfiguration, decoder.PlanarConfiguration); + } + + [Theory] + [MemberData(nameof(IsLittleEndianValues))] + public void ReadImageFormat_DefaultsPlanarConfigurationToChunky(bool isLittleEndian) + { + Stream stream = CreateTiffGenIfd() + .WithEntry(TiffGenEntry.Integer(TiffTags.PhotometricInterpretation, TiffType.Short, (int)TiffPhotometricInterpretation.Rgb)) + .WithEntry(TiffGenEntry.Integer(TiffTags.BitsPerSample, TiffType.Short, new int[] { 8, 8, 8 })) + .WithoutEntry(TiffTags.PlanarConfiguration) + .ToStream(isLittleEndian); + + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null, null); + TiffIfd ifd = decoder.ReadIfd(0); + decoder.ReadImageFormat(ifd); + + Assert.Equal(TiffPlanarConfiguration.Chunky, decoder.PlanarConfiguration); + } + [Theory] [InlineData(TiffColorType.WhiteIsZero, new uint[] { 1 }, 160, 80, 20 * 80)] [InlineData(TiffColorType.WhiteIsZero, new uint[] { 1 }, 153, 80, 20 * 80)] @@ -438,22 +496,49 @@ namespace ImageSharp.Tests [InlineData(TiffColorType.WhiteIsZero, new uint[] { 4 }, 100, 80, 50 * 80)] [InlineData(TiffColorType.WhiteIsZero, new uint[] { 4 }, 99, 80, 50 * 80)] [InlineData(TiffColorType.WhiteIsZero, new uint[] { 8 }, 100, 80, 100 * 80)] - [InlineData(TiffColorType.PaletteColor, new uint[] { 1 }, 160, 80, 60 * 80)] - [InlineData(TiffColorType.PaletteColor, new uint[] { 1 }, 153, 80, 58 * 80)] - [InlineData(TiffColorType.PaletteColor, new uint[] { 3 }, 100, 80, 113 * 80)] - [InlineData(TiffColorType.PaletteColor, new uint[] { 4 }, 100, 80, 150 * 80)] - [InlineData(TiffColorType.PaletteColor, new uint[] { 4 }, 99, 80, 149 * 80)] - [InlineData(TiffColorType.PaletteColor, new uint[] { 8 }, 100, 80, 300 * 80)] + [InlineData(TiffColorType.PaletteColor, new uint[] { 1 }, 160, 80, 20 * 80)] + [InlineData(TiffColorType.PaletteColor, new uint[] { 1 }, 153, 80, 20 * 80)] + [InlineData(TiffColorType.PaletteColor, new uint[] { 3 }, 100, 80, 38 * 80)] + [InlineData(TiffColorType.PaletteColor, new uint[] { 4 }, 100, 80, 50 * 80)] + [InlineData(TiffColorType.PaletteColor, new uint[] { 4 }, 99, 80, 50 * 80)] + [InlineData(TiffColorType.PaletteColor, new uint[] { 8 }, 100, 80, 100 * 80)] [InlineData(TiffColorType.Rgb, new uint[] { 8, 8, 8 }, 100, 80, 300 * 80)] [InlineData(TiffColorType.Rgb, new uint[] { 4, 4, 4 }, 100, 80, 150 * 80)] [InlineData(TiffColorType.Rgb, new uint[] { 4, 8, 4 }, 100, 80, 200 * 80)] - public void CalculateImageBufferSize_ReturnsCorrectSize(ushort colorType, uint[] bitsPerSample, int width, int height, int expectedResult) + public void CalculateImageBufferSize_ReturnsCorrectSize_Chunky(ushort colorType, uint[] bitsPerSample, int width, int height, int expectedResult) + { + TiffDecoderCore decoder = new TiffDecoderCore(null, null); + decoder.ColorType = (TiffColorType)colorType; + decoder.PlanarConfiguration = TiffPlanarConfiguration.Chunky; + decoder.BitsPerSample = bitsPerSample; + + int bufferSize = decoder.CalculateImageBufferSize(width, height, 0); + + Assert.Equal(expectedResult, bufferSize); + } + + [Theory] + [InlineData(TiffColorType.Rgb, new uint[] { 8, 8, 8 }, 100, 80, 0, 100 * 80)] + [InlineData(TiffColorType.Rgb, new uint[] { 8, 8, 8 }, 100, 80, 1, 100 * 80)] + [InlineData(TiffColorType.Rgb, new uint[] { 8, 8, 8 }, 100, 80, 2, 100 * 80)] + [InlineData(TiffColorType.Rgb, new uint[] { 4, 4, 4 }, 100, 80, 0, 50 * 80)] + [InlineData(TiffColorType.Rgb, new uint[] { 4, 4, 4 }, 100, 80, 1, 50 * 80)] + [InlineData(TiffColorType.Rgb, new uint[] { 4, 4, 4 }, 100, 80, 2, 50 * 80)] + [InlineData(TiffColorType.Rgb, new uint[] { 4, 8, 4 }, 100, 80, 0, 50 * 80)] + [InlineData(TiffColorType.Rgb, new uint[] { 4, 8, 4 }, 100, 80, 1, 100 * 80)] + [InlineData(TiffColorType.Rgb, new uint[] { 4, 8, 4 }, 100, 80, 2, 50 * 80)] + [InlineData(TiffColorType.Rgb, new uint[] { 4, 8, 4 }, 99, 80, 0, 50 * 80)] + [InlineData(TiffColorType.Rgb, new uint[] { 4, 8, 4 }, 99, 80, 1, 99 * 80)] + [InlineData(TiffColorType.Rgb, new uint[] { 4, 8, 4 }, 99, 80, 2, 50 * 80)] + + public void CalculateImageBufferSize_ReturnsCorrectSize_Planar(ushort colorType, uint[] bitsPerSample, int width, int height, int plane, int expectedResult) { TiffDecoderCore decoder = new TiffDecoderCore(null, null); decoder.ColorType = (TiffColorType)colorType; + decoder.PlanarConfiguration = TiffPlanarConfiguration.Planar; decoder.BitsPerSample = bitsPerSample; - int bufferSize = decoder.CalculateImageBufferSize(width, height); + int bufferSize = decoder.CalculateImageBufferSize(width, height, plane); Assert.Equal(expectedResult, bufferSize); } diff --git a/tests/ImageSharp.Formats.Tiff.Tests/ImageSharp.Formats.Tiff.Tests.csproj b/tests/ImageSharp.Formats.Tiff.Tests/ImageSharp.Formats.Tiff.Tests.csproj index dae9c9db3..3e861f778 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/ImageSharp.Formats.Tiff.Tests.csproj +++ b/tests/ImageSharp.Formats.Tiff.Tests/ImageSharp.Formats.Tiff.Tests.csproj @@ -14,6 +14,7 @@ + From b03165cd2c588a2d636fd77ebacfe12a0fb7ab4f Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Thu, 25 May 2017 11:09:17 +0100 Subject: [PATCH 041/275] Add helper function when reading entries with defaults --- .../Formats/Tiff/TiffDecoderCore.cs | 47 +++++++++++-------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index a2d1f37c8..f3a55412b 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -208,11 +208,7 @@ namespace ImageSharp.Formats Image image = new Image(this.configuration, width, height); - TiffResolutionUnit resolutionUnit = TiffResolutionUnit.Inch; - if (ifd.TryGetIfdEntry(TiffTags.ResolutionUnit, out TiffIfdEntry resolutionUnitEntry)) - { - resolutionUnit = (TiffResolutionUnit)this.ReadUnsignedInteger(ref resolutionUnitEntry); - } + TiffResolutionUnit resolutionUnit = (TiffResolutionUnit)this.ReadUnsignedInteger(ifd, TiffTags.ResolutionUnit, (uint)TiffResolutionUnit.Inch); if (resolutionUnit != TiffResolutionUnit.None) { @@ -252,12 +248,7 @@ namespace ImageSharp.Formats /// The IFD to read the image format information for. public void ReadImageFormat(TiffIfd ifd) { - TiffCompression compression = TiffCompression.None; - - if (ifd.TryGetIfdEntry(TiffTags.Compression, out TiffIfdEntry compressionEntry)) - { - compression = (TiffCompression)this.ReadUnsignedInteger(ref compressionEntry); - } + TiffCompression compression = (TiffCompression)this.ReadUnsignedInteger(ifd, TiffTags.Compression, (uint)TiffCompression.None); switch (compression) { @@ -286,14 +277,7 @@ namespace ImageSharp.Formats } } - if (ifd.TryGetIfdEntry(TiffTags.PlanarConfiguration, out TiffIfdEntry planarConfigurationEntry)) - { - this.PlanarConfiguration = (TiffPlanarConfiguration)this.ReadUnsignedInteger(ref planarConfigurationEntry); - } - else - { - this.PlanarConfiguration = TiffPlanarConfiguration.Chunky; - } + this.PlanarConfiguration = (TiffPlanarConfiguration)this.ReadUnsignedInteger(ifd, TiffTags.PlanarConfiguration, (uint)TiffPlanarConfiguration.Chunky); TiffPhotometricInterpretation photometricInterpretation; @@ -653,6 +637,29 @@ namespace ImageSharp.Formats } } + /// + /// Reads the data for a specified tag of a as an unsigned integer value. + /// + /// The to read from. + /// The tag ID to search for. + /// The default value if the entry is missing + /// The data. + /// + /// Thrown if the data-type specified by the file cannot be converted to a , or if + /// there is an array of items. + /// + public uint ReadUnsignedInteger(TiffIfd ifd, ushort tag, uint defaultValue) + { + if (ifd.TryGetIfdEntry(tag, out TiffIfdEntry entry)) + { + return this.ReadUnsignedInteger(ref entry); + } + else + { + return defaultValue; + } + } + /// /// Reads the data from a as a signed integer value. /// @@ -1181,7 +1188,7 @@ namespace ImageSharp.Formats for (int planeIndex = 0; planeIndex < stripsPerPixel; planeIndex++) { - int stripIndex = i * stripsPerPixel + planeIndex; + int stripIndex = (i * stripsPerPixel) + planeIndex; this.DecompressImageBlock(stripOffsets[stripIndex], stripByteCounts[stripIndex], stripBytes[planeIndex]); } From 49df888a50af4a3a049df4bbad8121ba5bfff8c4 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Thu, 25 May 2017 11:42:42 +0100 Subject: [PATCH 042/275] PixelAccessor needs to be internal in unit tests --- .../PhotometricInterpretationTestBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs index c07c37832..7b3663573 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs @@ -41,7 +41,7 @@ namespace ImageSharp.Tests return output; } - public static void AssertDecode(Rgba32[][] expectedResult, Action> decodeAction) + internal static void AssertDecode(Rgba32[][] expectedResult, Action> decodeAction) { int resultWidth = expectedResult[0].Length; int resultHeight = expectedResult.Length; From 63218369f6689f6e741be6a34e6a6a92da625cea Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Tue, 30 May 2017 22:15:47 +0100 Subject: [PATCH 043/275] Add support for LZW compression --- .../Tiff/Compression/LzwTiffCompression.cs | 35 ++ .../Tiff/Compression/TiffCompressionType.cs | 5 + .../Formats/Tiff/TiffDecoderCore.cs | 9 + .../Formats/Tiff/Utils/TiffLzwDecoder.cs | 272 ++++++++++ .../Formats/Tiff/Utils/TiffLzwEncoder.cs | 496 ++++++++++++++++++ .../DeflateTiffCompressionTests.cs | 2 +- .../Compression/LzwTiffCompressionTests.cs | 47 ++ .../Formats/Tiff/TiffDecoderImageTests.cs | 4 +- 8 files changed, 867 insertions(+), 3 deletions(-) create mode 100644 src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs create mode 100644 src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs create mode 100644 src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs diff --git a/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs new file mode 100644 index 000000000..ae4d22a71 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs @@ -0,0 +1,35 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats.Tiff +{ + using System; + using System.Buffers; + using System.IO; + using System.IO.Compression; + using System.Runtime.CompilerServices; + + /// + /// Class to handle cases where TIFF image data is compressed using LZW compression. + /// + internal static class LzwTiffCompression + { + /// + /// Decompresses image data into the supplied buffer. + /// + /// The to read image data from. + /// The number of bytes to read from the input stream. + /// The output buffer for uncompressed data. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Decompress(Stream stream, int byteCount, byte[] buffer) + { + SubStream subStream = new SubStream(stream, byteCount); + using (var decoder = new TiffLzwDecoder(subStream)) + { + decoder.DecodePixels(buffer.Length, 8, buffer); + } + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs index 6108194c4..3ea9270c8 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs @@ -24,5 +24,10 @@ namespace ImageSharp.Formats.Tiff /// Image data is compressed using Deflate compression. /// Deflate = 2, + + /// + /// Image data is compressed using LZW compression. + /// + Lzw = 3, } } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index f3a55412b..942e510d3 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -271,6 +271,12 @@ namespace ImageSharp.Formats break; } + case TiffCompression.Lzw: + { + this.CompressionType = TiffCompressionType.Lzw; + break; + } + default: { throw new NotSupportedException("The specified TIFF compression format is not supported."); @@ -505,6 +511,9 @@ namespace ImageSharp.Formats case TiffCompressionType.Deflate: DeflateTiffCompression.Decompress(this.InputStream, (int)byteCount, buffer); break; + case TiffCompressionType.Lzw: + LzwTiffCompression.Decompress(this.InputStream, (int)byteCount, buffer); + break; default: throw new InvalidOperationException(); } diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs new file mode 100644 index 000000000..f6ad7b3a4 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs @@ -0,0 +1,272 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats.Tiff +{ + using System; + using System.Buffers; + using System.IO; + + /// + /// Decompresses and decodes data using the dynamic LZW algorithms. + /// + /// + /// This code is based on the used for GIF decoding. There is potential + /// for a shared implementation. Differences between the GIF and TIFF implementations of the LZW + /// encoding are: (i) The GIF implementation includes an initial 'data size' byte, whilst this is + /// always 8 for TIFF. (ii) The GIF implementation writes a number of sub-blocks with an initial + /// byte indicating the length of the sub-block. In TIFF the data is written as a single block + /// with no length indicator (this can be determined from the 'StripByteCounts' entry). + /// + internal sealed class TiffLzwDecoder : IDisposable + { + /// + /// The max decoder pixel stack size. + /// + private const int MaxStackSize = 4096; + + /// + /// The null code. + /// + private const int NullCode = -1; + + /// + /// The stream to decode. + /// + private readonly Stream stream; + + /// + /// The prefix buffer. + /// + private readonly int[] prefix; + + /// + /// The suffix buffer. + /// + private readonly int[] suffix; + + /// + /// The pixel stack buffer. + /// + private readonly int[] pixelStack; + + /// + /// A value indicating whether this instance of the given entity has been disposed. + /// + /// if this instance has been disposed; otherwise, . + /// + /// If the entity is disposed, it must not be disposed a second + /// time. The isDisposed field is set the first time the entity + /// is disposed. If the isDisposed field is true, then the Dispose() + /// method will not dispose again. This help not to prolong the entity's + /// life in the Garbage Collector. + /// + private bool isDisposed; + + /// + /// Initializes a new instance of the class + /// and sets the stream, where the compressed data should be read from. + /// + /// The stream to read from. + /// is null. + public TiffLzwDecoder(Stream stream) + { + Guard.NotNull(stream, nameof(stream)); + + this.stream = stream; + + this.prefix = ArrayPool.Shared.Rent(MaxStackSize); + this.suffix = ArrayPool.Shared.Rent(MaxStackSize); + this.pixelStack = ArrayPool.Shared.Rent(MaxStackSize + 1); + + Array.Clear(this.prefix, 0, MaxStackSize); + Array.Clear(this.suffix, 0, MaxStackSize); + Array.Clear(this.pixelStack, 0, MaxStackSize + 1); + } + + /// + /// Decodes and decompresses all pixel indices from the stream. + /// + /// The length of the compressed data. + /// Size of the data. + /// The pixel array to decode to. + public void DecodePixels(int length, int dataSize, byte[] pixels) + { + Guard.MustBeLessThan(dataSize, int.MaxValue, nameof(dataSize)); + + // Calculate the clear code. The value of the clear code is 2 ^ dataSize + int clearCode = 1 << dataSize; + + int codeSize = dataSize + 1; + + // Calculate the end code + int endCode = clearCode + 1; + + // Calculate the available code. + int availableCode = clearCode + 2; + + // Jillzhangs Code see: http://giflib.codeplex.com/ + // Adapted from John Cristy's ImageMagick. + int code; + int oldCode = NullCode; + int codeMask = (1 << codeSize) - 1; + int bits = 0; + + int top = 0; + int count = 0; + int bi = 0; + int xyz = 0; + + int data = 0; + int first = 0; + + for (code = 0; code < clearCode; code++) + { + this.prefix[code] = 0; + this.suffix[code] = (byte)code; + } + + byte[] buffer = new byte[255]; + while (xyz < length) + { + if (top == 0) + { + if (bits < codeSize) + { + // Load bytes until there are enough bits for a code. + if (count == 0) + { + // Read a new data block. + count = this.ReadBlock(buffer); + if (count == 0) + { + break; + } + + bi = 0; + } + + data += buffer[bi] << bits; + + bits += 8; + bi++; + count--; + continue; + } + + // Get the next code + code = data & codeMask; + data >>= codeSize; + bits -= codeSize; + + // Interpret the code + if (code > availableCode || code == endCode) + { + break; + } + + if (code == clearCode) + { + // Reset the decoder + codeSize = dataSize + 1; + codeMask = (1 << codeSize) - 1; + availableCode = clearCode + 2; + oldCode = NullCode; + continue; + } + + if (oldCode == NullCode) + { + this.pixelStack[top++] = this.suffix[code]; + oldCode = code; + first = code; + continue; + } + + int inCode = code; + if (code == availableCode) + { + this.pixelStack[top++] = (byte)first; + + code = oldCode; + } + + while (code > clearCode) + { + this.pixelStack[top++] = this.suffix[code]; + code = this.prefix[code]; + } + + first = this.suffix[code]; + + this.pixelStack[top++] = this.suffix[code]; + + // Fix for Gifs that have "deferred clear code" as per here : + // https://bugzilla.mozilla.org/show_bug.cgi?id=55918 + if (availableCode < MaxStackSize) + { + this.prefix[availableCode] = oldCode; + this.suffix[availableCode] = first; + availableCode++; + if (availableCode == codeMask + 1 && availableCode < MaxStackSize) + { + codeSize++; + codeMask = (1 << codeSize) - 1; + } + } + + oldCode = inCode; + } + + // Pop a pixel off the pixel stack. + top--; + + // Clear missing pixels + pixels[xyz++] = (byte)this.pixelStack[top]; + } + } + + /// + public void Dispose() + { + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + this.Dispose(true); + } + + /// + /// Reads the next data block from the stream. For consistency with the GIF decoder, + /// the image is read in blocks - For TIFF this is always a maximum of 255 + /// + /// The buffer to store the block in. + /// + /// The . + /// + private int ReadBlock(byte[] buffer) + { + return this.stream.Read(buffer, 0, 255); + } + + /// + /// Disposes the object and frees resources for the Garbage Collector. + /// + /// If true, the object gets disposed. + private void Dispose(bool disposing) + { + if (this.isDisposed) + { + return; + } + + if (disposing) + { + ArrayPool.Shared.Return(this.prefix); + ArrayPool.Shared.Return(this.suffix); + ArrayPool.Shared.Return(this.pixelStack); + } + + this.isDisposed = true; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs new file mode 100644 index 000000000..1ad7c3128 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs @@ -0,0 +1,496 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats.Tiff +{ + using System; + using System.Buffers; + using System.IO; + + /// + /// Encodes and compresses the image data using dynamic Lempel-Ziv compression. + /// + /// + /// Adapted from Jef Poskanzer's Java port by way of J. M. G. Elliott. K Weiner 12/00 + /// + /// GIFCOMPR.C - GIF Image compression routines + /// + /// + /// Lempel-Ziv compression based on 'compress'. GIF modifications by + /// David Rowley (mgardi@watdcsu.waterloo.edu) + /// + /// GIF Image compression - modified 'compress' + /// + /// Based on: compress.c - File compression ala IEEE Computer, June 1984. + /// By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas) + /// Jim McKie (decvax!mcvax!jim) + /// Steve Davies (decvax!vax135!petsd!peora!srd) + /// Ken Turkowski (decvax!decwrl!turtlevax!ken) + /// James A. Woods (decvax!ihnp4!ames!jaw) + /// Joe Orost (decvax!vax135!petsd!joe) + /// + /// + /// This code is based on the used for GIF encoding. There is potential + /// for a shared implementation. Differences between the GIF and TIFF implementations of the LZW + /// encoding are: (i) The GIF implementation includes an initial 'data size' byte, whilst this is + /// always 8 for TIFF. (ii) The GIF implementation writes a number of sub-blocks with an initial + /// byte indicating the length of the sub-block. In TIFF the data is written as a single block + /// with no length indicator (this can be determined from the 'StripByteCounts' entry). + /// + /// + internal sealed class TiffLzwEncoder : IDisposable + { + /// + /// The end-of-file marker + /// + private const int Eof = -1; + + /// + /// The maximum number of bits. + /// + private const int Bits = 12; + + /// + /// 80% occupancy + /// + private const int HashSize = 5003; + + /// + /// Mask used when shifting pixel values + /// + private static readonly int[] Masks = + { + 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, + 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF + }; + + /// + /// The working pixel array + /// + private readonly byte[] pixelArray; + + /// + /// The initial code size. + /// + private readonly int initialCodeSize; + + /// + /// The hash table. + /// + private readonly int[] hashTable; + + /// + /// The code table. + /// + private readonly int[] codeTable; + + /// + /// Define the storage for the packet accumulator. + /// + private readonly byte[] accumulators = new byte[256]; + + /// + /// A value indicating whether this instance of the given entity has been disposed. + /// + /// if this instance has been disposed; otherwise, . + /// + /// If the entity is disposed, it must not be disposed a second + /// time. The isDisposed field is set the first time the entity + /// is disposed. If the isDisposed field is true, then the Dispose() + /// method will not dispose again. This help not to prolong the entity's + /// life in the Garbage Collector. + /// + private bool isDisposed; + + /// + /// The current pixel + /// + private int currentPixel; + + /// + /// Number of bits/code + /// + private int bitCount; + + /// + /// User settable max # bits/code + /// + private int maxbits = Bits; + + /// + /// maximum code, given bitCount + /// + private int maxcode; + + /// + /// should NEVER generate this code + /// + private int maxmaxcode = 1 << Bits; + + /// + /// For dynamic table sizing + /// + private int hsize = HashSize; + + /// + /// First unused entry + /// + private int freeEntry; + + /// + /// Block compression parameters -- after all codes are used up, + /// and compression rate changes, start over. + /// + private bool clearFlag; + + /// + /// Algorithm: use open addressing double hashing (no chaining) on the + /// prefix code / next character combination. We do a variant of Knuth's + /// algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime + /// secondary probe. Here, the modular division first probe is gives way + /// to a faster exclusive-or manipulation. Also do block compression with + /// an adaptive reset, whereby the code table is cleared when the compression + /// ratio decreases, but after the table fills. The variable-length output + /// codes are re-sized at this point, and a special CLEAR code is generated + /// for the decompressor. Late addition: construct the table according to + /// file size for noticeable speed improvement on small files. Please direct + /// questions about this implementation to ames!jaw. + /// + private int globalInitialBits; + + /// + /// The clear code. + /// + private int clearCode; + + /// + /// The end-of-file code. + /// + private int eofCode; + + /// + /// Output the given code. + /// Inputs: + /// code: A bitCount-bit integer. If == -1, then EOF. This assumes + /// that bitCount =< wordsize - 1. + /// Outputs: + /// Outputs code to the file. + /// Assumptions: + /// Chars are 8 bits long. + /// Algorithm: + /// Maintain a BITS character long buffer (so that 8 codes will + /// fit in it exactly). Use the VAX insv instruction to insert each + /// code in turn. When the buffer fills up empty it and start over. + /// + private int currentAccumulator; + + /// + /// The current bits. + /// + private int currentBits; + + /// + /// Number of characters so far in this 'packet' + /// + private int accumulatorCount; + + /// + /// Initializes a new instance of the class. + /// + /// The array of indexed pixels. + /// The color depth in bits. + public TiffLzwEncoder(byte[] indexedPixels, int colorDepth) + { + this.pixelArray = indexedPixels; + this.initialCodeSize = Math.Max(2, colorDepth); + + this.hashTable = ArrayPool.Shared.Rent(HashSize); + this.codeTable = ArrayPool.Shared.Rent(HashSize); + Array.Clear(this.hashTable, 0, HashSize); + Array.Clear(this.codeTable, 0, HashSize); + } + + /// + /// Encodes and compresses the indexed pixels to the stream. + /// + /// The stream to write to. + public void Encode(Stream stream) + { + this.currentPixel = 0; + + // Compress and write the pixel data + this.Compress(this.initialCodeSize + 1, stream); + } + + /// + public void Dispose() + { + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + this.Dispose(true); + } + + /// + /// Gets the maximum code value + /// + /// The number of bits + /// See + private static int GetMaxcode(int bitCount) + { + return (1 << bitCount) - 1; + } + + /// + /// Add a character to the end of the current packet, and if it is 254 characters, + /// flush the packet to disk. + /// + /// The character to add. + /// The stream to write to. + private void AddCharacter(byte c, Stream stream) + { + this.accumulators[this.accumulatorCount++] = c; + if (this.accumulatorCount >= 254) + { + this.FlushPacket(stream); + } + } + + /// + /// Table clear for block compress + /// + /// The output stream. + private void ClearBlock(Stream stream) + { + this.ResetCodeTable(this.hsize); + this.freeEntry = this.clearCode + 2; + this.clearFlag = true; + + this.Output(this.clearCode, stream); + } + + /// + /// Reset the code table. + /// + /// The hash size. + private void ResetCodeTable(int size) + { + for (int i = 0; i < size; ++i) + { + this.hashTable[i] = -1; + } + } + + /// + /// Compress the packets to the stream. + /// + /// The initial bits. + /// The stream to write to. + private void Compress(int intialBits, Stream stream) + { + int fcode; + int c; + int ent; + int hsizeReg; + int hshift; + + // Set up the globals: globalInitialBits - initial number of bits + this.globalInitialBits = intialBits; + + // Set up the necessary values + this.clearFlag = false; + this.bitCount = this.globalInitialBits; + this.maxcode = GetMaxcode(this.bitCount); + + this.clearCode = 1 << (intialBits - 1); + this.eofCode = this.clearCode + 1; + this.freeEntry = this.clearCode + 2; + + this.accumulatorCount = 0; // clear packet + + ent = this.NextPixel(); + + hshift = 0; + for (fcode = this.hsize; fcode < 65536; fcode *= 2) + { + ++hshift; + } + + hshift = 8 - hshift; // set hash code range bound + + hsizeReg = this.hsize; + + this.ResetCodeTable(hsizeReg); // clear hash table + + this.Output(this.clearCode, stream); + + while ((c = this.NextPixel()) != Eof) + { + fcode = (c << this.maxbits) + ent; + int i = (c << hshift) ^ ent /* = 0 */; + + if (this.hashTable[i] == fcode) + { + ent = this.codeTable[i]; + continue; + } + + // Non-empty slot + if (this.hashTable[i] >= 0) + { + int disp = hsizeReg - i; + if (i == 0) + { + disp = 1; + } + + do + { + if ((i -= disp) < 0) + { + i += hsizeReg; + } + + if (this.hashTable[i] == fcode) + { + ent = this.codeTable[i]; + break; + } + } + while (this.hashTable[i] >= 0); + + if (this.hashTable[i] == fcode) + { + continue; + } + } + + this.Output(ent, stream); + ent = c; + if (this.freeEntry < this.maxmaxcode) + { + this.codeTable[i] = this.freeEntry++; // code -> hashtable + this.hashTable[i] = fcode; + } + else + { + this.ClearBlock(stream); + } + } + + // Put out the final code. + this.Output(ent, stream); + + this.Output(this.eofCode, stream); + } + + /// + /// Flush the packet to disk, and reset the accumulator. + /// + /// The output stream. + private void FlushPacket(Stream outStream) + { + if (this.accumulatorCount > 0) + { + outStream.Write(this.accumulators, 0, this.accumulatorCount); + this.accumulatorCount = 0; + } + } + + /// + /// Return the next pixel from the image + /// + /// + /// The + /// + private int NextPixel() + { + if (this.currentPixel == this.pixelArray.Length) + { + return Eof; + } + + this.currentPixel++; + return this.pixelArray[this.currentPixel - 1] & 0xff; + } + + /// + /// Output the current code to the stream. + /// + /// The code. + /// The stream to write to. + private void Output(int code, Stream outs) + { + this.currentAccumulator &= Masks[this.currentBits]; + + if (this.currentBits > 0) + { + this.currentAccumulator |= code << this.currentBits; + } + else + { + this.currentAccumulator = code; + } + + this.currentBits += this.bitCount; + + while (this.currentBits >= 8) + { + this.AddCharacter((byte)(this.currentAccumulator & 0xff), outs); + this.currentAccumulator >>= 8; + this.currentBits -= 8; + } + + // If the next entry is going to be too big for the code size, + // then increase it, if possible. + if (this.freeEntry > this.maxcode || this.clearFlag) + { + if (this.clearFlag) + { + this.maxcode = GetMaxcode(this.bitCount = this.globalInitialBits); + this.clearFlag = false; + } + else + { + ++this.bitCount; + this.maxcode = this.bitCount == this.maxbits + ? this.maxmaxcode + : GetMaxcode(this.bitCount); + } + } + + if (code == this.eofCode) + { + // At EOF, write the rest of the buffer. + while (this.currentBits > 0) + { + this.AddCharacter((byte)(this.currentAccumulator & 0xff), outs); + this.currentAccumulator >>= 8; + this.currentBits -= 8; + } + + this.FlushPacket(outs); + } + } + + /// + /// Disposes the object and frees resources for the Garbage Collector. + /// + /// If true, the object gets disposed. + private void Dispose(bool disposing) + { + if (this.isDisposed) + { + return; + } + + if (disposing) + { + ArrayPool.Shared.Return(this.hashTable); + ArrayPool.Shared.Return(this.codeTable); + } + + this.isDisposed = true; + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs index e63700880..7021684d5 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs @@ -25,7 +25,7 @@ namespace ImageSharp.Tests { byte[] buffer = new byte[data.Length]; - DeflateTiffCompression.Decompress(stream, data.Length, buffer); + DeflateTiffCompression.Decompress(stream, (int)stream.Length, buffer); Assert.Equal(data, buffer); } diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs new file mode 100644 index 000000000..e54d0dd5d --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs @@ -0,0 +1,47 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System.IO; + using Xunit; + + using ImageSharp.Formats; + using ImageSharp.Formats.Tiff; + + public class LzwTiffCompressionTests + { + [Theory] + [InlineData(new byte[] { })] + [InlineData(new byte[] { 42 })] // One byte + [InlineData(new byte[] { 42, 16, 128, 53, 96, 218, 7, 64, 3, 4, 97 })] // Random bytes + [InlineData(new byte[] { 1, 2, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 3, 4 })] // Repeated bytes + [InlineData(new byte[] { 1, 2, 42, 53, 42, 53, 42, 53, 42, 53, 42, 53, 3, 4 })] // Repeated sequence + public void Decompress_ReadsData(byte[] data) + { + using (Stream stream = CreateCompressedStream(data)) + { + byte[] buffer = new byte[data.Length]; + + LzwTiffCompression.Decompress(stream, (int)stream.Length, buffer); + + Assert.Equal(data, buffer); + } + } + + private static Stream CreateCompressedStream(byte[] data) + { + Stream compressedStream = new MemoryStream(); + + using (var encoder = new TiffLzwEncoder(data, 8)) + { + encoder.Encode(compressedStream); + } + + compressedStream.Seek(0, SeekOrigin.Begin); + return compressedStream; + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs index c779a631e..9f90e691d 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs @@ -129,6 +129,8 @@ namespace ImageSharp.Tests [InlineData(true, TiffCompression.Deflate, TiffCompressionType.Deflate)] [InlineData(false, TiffCompression.OldDeflate, TiffCompressionType.Deflate)] [InlineData(true, TiffCompression.OldDeflate, TiffCompressionType.Deflate)] + [InlineData(false, TiffCompression.Lzw, TiffCompressionType.Lzw)] + [InlineData(true, TiffCompression.Lzw, TiffCompressionType.Lzw)] public void ReadImageFormat_DeterminesCorrectCompressionImplementation(bool isLittleEndian, ushort compression, int compressionType) { Stream stream = CreateTiffGenIfd() @@ -149,7 +151,6 @@ namespace ImageSharp.Tests [InlineData(false, TiffCompression.ItuTRecT43)] [InlineData(false, TiffCompression.ItuTRecT82)] [InlineData(false, TiffCompression.Jpeg)] - [InlineData(false, TiffCompression.Lzw)] [InlineData(false, TiffCompression.OldJpeg)] [InlineData(false, 999)] [InlineData(true, TiffCompression.Ccitt1D)] @@ -158,7 +159,6 @@ namespace ImageSharp.Tests [InlineData(true, TiffCompression.ItuTRecT43)] [InlineData(true, TiffCompression.ItuTRecT82)] [InlineData(true, TiffCompression.Jpeg)] - [InlineData(true, TiffCompression.Lzw)] [InlineData(true, TiffCompression.OldJpeg)] [InlineData(true, 999)] public void ReadImageFormat_ThrowsExceptionForUnsupportedCompression(bool isLittleEndian, ushort compression) From e4c2e08451faeca6f4b598ff6044bba33b8f5f26 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Wed, 31 May 2017 11:18:07 +0100 Subject: [PATCH 044/275] Add TIFF README.md file --- src/ImageSharp/Formats/Tiff/README.md | 209 ++++++++++++++++++++++++++ 1 file changed, 209 insertions(+) create mode 100644 src/ImageSharp/Formats/Tiff/README.md diff --git a/src/ImageSharp/Formats/Tiff/README.md b/src/ImageSharp/Formats/Tiff/README.md new file mode 100644 index 000000000..3ffa6bd0b --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/README.md @@ -0,0 +1,209 @@ +# ImageSharp TIFF codec + +## References +- TIFF + - [TIFF 6.0 Specification](http://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf),(http://www.npes.org/pdf/TIFF-v6.pdf) + - [TIFF Supplement 1](http://partners.adobe.com/public/developer/en/tiff/TIFFPM6.pdf) + - [TIFF Supplement 2](http://partners.adobe.com/public/developer/en/tiff/TIFFphotoshop.pdf) + - [TIFF Supplement 3](http://chriscox.org/TIFFTN3d1.pdf) + - [TIFF-F/FX Extension (RFC2301)](http://www.ietf.org/rfc/rfc2301.txt) + - [TIFF/EP Extension (Wikipedia)](https://en.wikipedia.org/wiki/TIFF/EP) + - [Adobe TIFF Pages](http://partners.adobe.com/public/developer/tiff/index.html) + - [Unofficial TIFF FAQ](http://www.awaresystems.be/imaging/tiff/faq.html) + +- DNG + - [Adobe DNG Pages](https://helpx.adobe.com/photoshop/digital-negative.html) + +- Metadata (EXIF) + - [EXIF 2.3 Specification](http://www.cipa.jp/std/documents/e/DC-008-2012_E.pdf) + +- Metadata (XMP) + - [Adobe XMP Pages](http://www.adobe.com/products/xmp.html) + - [Adobe XMP Developer Centre](http://www.adobe.com/devnet/xmp.html) + +## Implementation Status + +### Baseline TIFF Tags + +| |Encoder|Decoder|Comments | +|---------------------------|:-----:|:-----:|--------------------------| +|NewSubfileType | [ ] | [ ] | | +|SubfileType | [ ] | [ ] | | +|ImageWidth | [ ] | [ ] | | +|ImageLength | [ ] | [ ] | | +|BitsPerSample | [ ] | [ ] | | +|Compression | [ ] | [ ] | | +|PhotometricInterpretation | [ ] | [ ] | | +|Threshholding | [ ] | [ ] | | +|CellWidth | [ ] | [ ] | | +|CellLength | [ ] | [ ] | | +|FillOrder | [ ] | [ ] | | +|ImageDescription | [ ] | [ ] | | +|Make | [ ] | [ ] | | +|Model | [ ] | [ ] | | +|StripOffsets | [ ] | [ ] | | +|Orientation | [ ] | [ ] | | +|SamplesPerPixel | [ ] | [ ] | | +|RowsPerStrip | [ ] | [ ] | | +|StripByteCounts | [ ] | [ ] | | +|MinSampleValue | [ ] | [ ] | | +|MaxSampleValue | [ ] | [ ] | | +|XResolution | [ ] | [ ] | | +|YResolution | [ ] | [ ] | | +|PlanarConfiguration | [ ] | [ ] | | +|FreeOffsets | [ ] | [ ] | | +|FreeByteCounts | [ ] | [ ] | | +|GrayResponseUnit | [ ] | [ ] | | +|GrayResponseCurve | [ ] | [ ] | | +|ResolutionUnit | [ ] | [ ] | | +|Software | [ ] | [ ] | | +|DateTime | [ ] | [ ] | | +|Artist | [ ] | [ ] | | +|HostComputer | [ ] | [ ] | | +|ColorMap | [ ] | [ ] | | +|ExtraSamples | [ ] | [ ] | | +|Copyright | [ ] | [ ] | | + +### Extension TIFF Tags + +| |Encoder|Decoder|Comments | +|---------------------------|:-----:|:-----:|--------------------------| +|NewSubfileType | [ ] | [ ] | | +|DocumentName | [ ] | [ ] | | +|PageName | [ ] | [ ] | | +|XPosition | [ ] | [ ] | | +|YPosition | [ ] | [ ] | | +|T4Options | [ ] | [ ] | | +|T6Options | [ ] | [ ] | | +|PageNumber | [ ] | [ ] | | +|TransferFunction | [ ] | [ ] | | +|Predictor | [ ] | [ ] | | +|WhitePoint | [ ] | [ ] | | +|PrimaryChromaticities | [ ] | [ ] | | +|HalftoneHints | [ ] | [ ] | | +|TileWidth | [ ] | [ ] | | +|TileLength | [ ] | [ ] | | +|TileOffsets | [ ] | [ ] | | +|TileByteCounts | [ ] | [ ] | | +|BadFaxLines | [ ] | [ ] | | +|CleanFaxData | [ ] | [ ] | | +|ConsecutiveBadFaxLines | [ ] | [ ] | | +|SubIFDs | [ ] | [ ] | | +|InkSet | [ ] | [ ] | | +|InkNames | [ ] | [ ] | | +|NumberOfInks | [ ] | [ ] | | +|DotRange | [ ] | [ ] | | +|TargetPrinter | [ ] | [ ] | | +|SampleFormat | [ ] | [ ] | | +|SMinSampleValue | [ ] | [ ] | | +|SMaxSampleValue | [ ] | [ ] | | +|TransferRange | [ ] | [ ] | | +|ClipPath | [ ] | [ ] | | +|XClipPathUnits | [ ] | [ ] | | +|YClipPathUnits | [ ] | [ ] | | +|Indexed | [ ] | [ ] | | +|JPEGTables | [ ] | [ ] | | +|OPIProxy | [ ] | [ ] | | +|GlobalParametersIFD | [ ] | [ ] | | +|ProfileType | [ ] | [ ] | | +|FaxProfile | [ ] | [ ] | | +|CodingMethods | [ ] | [ ] | | +|VersionYear | [ ] | [ ] | | +|ModeNumber | [ ] | [ ] | | +|Decode | [ ] | [ ] | | +|DefaultImageColor | [ ] | [ ] | | +|JPEGProc | [ ] | [ ] | | +|JPEGInterchangeFormat | [ ] | [ ] | | +|JPEGInterchangeFormatLength| [ ] | [ ] | | +|JPEGRestartInterval | [ ] | [ ] | | +|JPEGLosslessPredictors | [ ] | [ ] | | +|JPEGPointTransforms | [ ] | [ ] | | +|JPEGQTables | [ ] | [ ] | | +|JPEGDCTables | [ ] | [ ] | | +|JPEGACTables | [ ] | [ ] | | +|YCbCrCoefficients | [ ] | [ ] | | +|YCbCrSubSampling | [ ] | [ ] | | +|YCbCrPositioning | [ ] | [ ] | | +|ReferenceBlackWhite | [ ] | [ ] | | +|StripRowCounts | [ ] | [ ] | | +|XMP | [ ] | [ ] | | +|ImageID | [ ] | [ ] | | +|ImageLayer | [ ] | [ ] | | + +### Private TIFF Tags + +| |Encoder|Decoder|Comments | +|---------------------------|:-----:|:-----:|--------------------------| +|Wang Annotation | [ ] | [ ] | | +|MD FileTag | [ ] | [ ] | | +|MD ScalePixel | [ ] | [ ] | | +|MD ColorTable | [ ] | [ ] | | +|MD LabName | [ ] | [ ] | | +|MD SampleInfo | [ ] | [ ] | | +|MD PrepDate | [ ] | [ ] | | +|MD PrepTime | [ ] | [ ] | | +|MD FileUnits | [ ] | [ ] | | +|ModelPixelScaleTag | [ ] | [ ] | | +|IPTC | [ ] | [ ] | | +|INGR Packet Data Tag | [ ] | [ ] | | +|INGR Flag Registers | [ ] | [ ] | | +|IrasB Transformation Matrix| [ ] | [ ] | | +|ModelTiepointTag | [ ] | [ ] | | +|ModelTransformationTag | [ ] | [ ] | | +|Photoshop | [ ] | [ ] | | +|Exif IFD | [ ] | [ ] | | +|ICC Profile | [ ] | [ ] | | +|GeoKeyDirectoryTag | [ ] | [ ] | | +|GeoDoubleParamsTag | [ ] | [ ] | | +|GeoAsciiParamsTag | [ ] | [ ] | | +|GPS IFD | [ ] | [ ] | | +|HylaFAX FaxRecvParams | [ ] | [ ] | | +|HylaFAX FaxSubAddress | [ ] | [ ] | | +|HylaFAX FaxRecvTime | [ ] | [ ] | | +|ImageSourceData | [ ] | [ ] | | +|Interoperability IFD | [ ] | [ ] | | +|GDAL_METADATA | [ ] | [ ] | | +|GDAL_NODATA | [ ] | [ ] | | +|Oce Scanjob Description | [ ] | [ ] | | +|Oce Application Selector | [ ] | [ ] | | +|Oce Identification Number | [ ] | [ ] | | +|Oce ImageLogic Characteristics| [ ] | [ ] | | +|DNGVersion | [ ] | [ ] | | +|DNGBackwardVersion | [ ] | [ ] | | +|UniqueCameraModel | [ ] | [ ] | | +|LocalizedCameraModel | [ ] | [ ] | | +|CFAPlaneColor | [ ] | [ ] | | +|CFALayout | [ ] | [ ] | | +|LinearizationTable | [ ] | [ ] | | +|BlackLevelRepeatDim | [ ] | [ ] | | +|BlackLevel | [ ] | [ ] | | +|BlackLevelDeltaH | [ ] | [ ] | | +|BlackLevelDeltaV | [ ] | [ ] | | +|WhiteLevel | [ ] | [ ] | | +|DefaultScale | [ ] | [ ] | | +|DefaultCropOrigin | [ ] | [ ] | | +|DefaultCropSize | [ ] | [ ] | | +|ColorMatrix1 | [ ] | [ ] | | +|ColorMatrix2 | [ ] | [ ] | | +|CameraCalibration1 | [ ] | [ ] | | +|CameraCalibration2 | [ ] | [ ] | | +|ReductionMatrix1 | [ ] | [ ] | | +|ReductionMatrix2 | [ ] | [ ] | | +|AnalogBalance | [ ] | [ ] | | +|AsShotNeutral | [ ] | [ ] | | +|AsShotWhiteXY | [ ] | [ ] | | +|BaselineExposure | [ ] | [ ] | | +|BaselineNoise | [ ] | [ ] | | +|BaselineSharpness | [ ] | [ ] | | +|BayerGreenSplit | [ ] | [ ] | | +|LinearResponseLimit | [ ] | [ ] | | +|CameraSerialNumber | [ ] | [ ] | | +|LensInfo | [ ] | [ ] | | +|ChromaBlurRadius | [ ] | [ ] | | +|AntiAliasStrength | [ ] | [ ] | | +|DNGPrivateData | [ ] | [ ] | | +|MakerNoteSafety | [ ] | [ ] | | +|CalibrationIlluminant1 | [ ] | [ ] | | +|CalibrationIlluminant2 | [ ] | [ ] | | +|BestQualityScale | [ ] | [ ] | | +|Alias Layer Metadata | [ ] | [ ] | | From 03b16bc8e96166c0fd97dcefe711191b2e497509 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Wed, 31 May 2017 11:28:59 +0100 Subject: [PATCH 045/275] Add more items to TIFF readme --- src/ImageSharp/Formats/Tiff/README.md | 370 ++++++++++++++------------ 1 file changed, 200 insertions(+), 170 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/README.md b/src/ImageSharp/Formats/Tiff/README.md index 3ffa6bd0b..9bc825f5b 100644 --- a/src/ImageSharp/Formats/Tiff/README.md +++ b/src/ImageSharp/Formats/Tiff/README.md @@ -23,187 +23,217 @@ ## Implementation Status +### Compression Formats + +| |Encoder|Decoder|Comments | +|---------------------------|:-----:|:-----:|--------------------------| +|None | | | | +|Ccitt1D | | | | +|PackBits | | | | +|CcittGroup3Fax | | | | +|CcittGroup4Fax | | | | +|Lzw | | | | +|Old Jpeg | | | | +|Jpeg (Technote 2) | | | | +|Deflate (Technote 2) | | | | +|Old Deflate (Technote 2) | | | | + +### Photometric Interpretation Formats + +| |Encoder|Decoder|Comments | +|---------------------------|:-----:|:-----:|--------------------------| +|WhiteIsZero | | | | +|BlackIsZero | | | | +|Rgb (Chunky) | | | | +|Rgb (Planar) | | | | +|PaletteColor | | | | +|TransparencyMask | | | | +|Separated (TIFF Extension) | | | | +|YCbCr (TIFF Extension) | | | | +|CieLab (TIFF Extension) | | | | +|IccLab (TechNote 1) | | | | + ### Baseline TIFF Tags | |Encoder|Decoder|Comments | |---------------------------|:-----:|:-----:|--------------------------| -|NewSubfileType | [ ] | [ ] | | -|SubfileType | [ ] | [ ] | | -|ImageWidth | [ ] | [ ] | | -|ImageLength | [ ] | [ ] | | -|BitsPerSample | [ ] | [ ] | | -|Compression | [ ] | [ ] | | -|PhotometricInterpretation | [ ] | [ ] | | -|Threshholding | [ ] | [ ] | | -|CellWidth | [ ] | [ ] | | -|CellLength | [ ] | [ ] | | -|FillOrder | [ ] | [ ] | | -|ImageDescription | [ ] | [ ] | | -|Make | [ ] | [ ] | | -|Model | [ ] | [ ] | | -|StripOffsets | [ ] | [ ] | | -|Orientation | [ ] | [ ] | | -|SamplesPerPixel | [ ] | [ ] | | -|RowsPerStrip | [ ] | [ ] | | -|StripByteCounts | [ ] | [ ] | | -|MinSampleValue | [ ] | [ ] | | -|MaxSampleValue | [ ] | [ ] | | -|XResolution | [ ] | [ ] | | -|YResolution | [ ] | [ ] | | -|PlanarConfiguration | [ ] | [ ] | | -|FreeOffsets | [ ] | [ ] | | -|FreeByteCounts | [ ] | [ ] | | -|GrayResponseUnit | [ ] | [ ] | | -|GrayResponseCurve | [ ] | [ ] | | -|ResolutionUnit | [ ] | [ ] | | -|Software | [ ] | [ ] | | -|DateTime | [ ] | [ ] | | -|Artist | [ ] | [ ] | | -|HostComputer | [ ] | [ ] | | -|ColorMap | [ ] | [ ] | | -|ExtraSamples | [ ] | [ ] | | -|Copyright | [ ] | [ ] | | +|NewSubfileType | | | | +|SubfileType | | | | +|ImageWidth | | | | +|ImageLength | | | | +|BitsPerSample | | | | +|Compression | | | | +|PhotometricInterpretation | | | | +|Threshholding | | | | +|CellWidth | | | | +|CellLength | | | | +|FillOrder | | | | +|ImageDescription | | | | +|Make | | | | +|Model | | | | +|StripOffsets | | | | +|Orientation | | | | +|SamplesPerPixel | | | | +|RowsPerStrip | | | | +|StripByteCounts | | | | +|MinSampleValue | | | | +|MaxSampleValue | | | | +|XResolution | | | | +|YResolution | | | | +|PlanarConfiguration | | | | +|FreeOffsets | | | | +|FreeByteCounts | | | | +|GrayResponseUnit | | | | +|GrayResponseCurve | | | | +|ResolutionUnit | | | | +|Software | | | | +|DateTime | | | | +|Artist | | | | +|HostComputer | | | | +|ColorMap | | | | +|ExtraSamples | | | | +|Copyright | | | | ### Extension TIFF Tags | |Encoder|Decoder|Comments | |---------------------------|:-----:|:-----:|--------------------------| -|NewSubfileType | [ ] | [ ] | | -|DocumentName | [ ] | [ ] | | -|PageName | [ ] | [ ] | | -|XPosition | [ ] | [ ] | | -|YPosition | [ ] | [ ] | | -|T4Options | [ ] | [ ] | | -|T6Options | [ ] | [ ] | | -|PageNumber | [ ] | [ ] | | -|TransferFunction | [ ] | [ ] | | -|Predictor | [ ] | [ ] | | -|WhitePoint | [ ] | [ ] | | -|PrimaryChromaticities | [ ] | [ ] | | -|HalftoneHints | [ ] | [ ] | | -|TileWidth | [ ] | [ ] | | -|TileLength | [ ] | [ ] | | -|TileOffsets | [ ] | [ ] | | -|TileByteCounts | [ ] | [ ] | | -|BadFaxLines | [ ] | [ ] | | -|CleanFaxData | [ ] | [ ] | | -|ConsecutiveBadFaxLines | [ ] | [ ] | | -|SubIFDs | [ ] | [ ] | | -|InkSet | [ ] | [ ] | | -|InkNames | [ ] | [ ] | | -|NumberOfInks | [ ] | [ ] | | -|DotRange | [ ] | [ ] | | -|TargetPrinter | [ ] | [ ] | | -|SampleFormat | [ ] | [ ] | | -|SMinSampleValue | [ ] | [ ] | | -|SMaxSampleValue | [ ] | [ ] | | -|TransferRange | [ ] | [ ] | | -|ClipPath | [ ] | [ ] | | -|XClipPathUnits | [ ] | [ ] | | -|YClipPathUnits | [ ] | [ ] | | -|Indexed | [ ] | [ ] | | -|JPEGTables | [ ] | [ ] | | -|OPIProxy | [ ] | [ ] | | -|GlobalParametersIFD | [ ] | [ ] | | -|ProfileType | [ ] | [ ] | | -|FaxProfile | [ ] | [ ] | | -|CodingMethods | [ ] | [ ] | | -|VersionYear | [ ] | [ ] | | -|ModeNumber | [ ] | [ ] | | -|Decode | [ ] | [ ] | | -|DefaultImageColor | [ ] | [ ] | | -|JPEGProc | [ ] | [ ] | | -|JPEGInterchangeFormat | [ ] | [ ] | | -|JPEGInterchangeFormatLength| [ ] | [ ] | | -|JPEGRestartInterval | [ ] | [ ] | | -|JPEGLosslessPredictors | [ ] | [ ] | | -|JPEGPointTransforms | [ ] | [ ] | | -|JPEGQTables | [ ] | [ ] | | -|JPEGDCTables | [ ] | [ ] | | -|JPEGACTables | [ ] | [ ] | | -|YCbCrCoefficients | [ ] | [ ] | | -|YCbCrSubSampling | [ ] | [ ] | | -|YCbCrPositioning | [ ] | [ ] | | -|ReferenceBlackWhite | [ ] | [ ] | | -|StripRowCounts | [ ] | [ ] | | -|XMP | [ ] | [ ] | | -|ImageID | [ ] | [ ] | | -|ImageLayer | [ ] | [ ] | | +|NewSubfileType | | | | +|DocumentName | | | | +|PageName | | | | +|XPosition | | | | +|YPosition | | | | +|T4Options | | | | +|T6Options | | | | +|PageNumber | | | | +|TransferFunction | | | | +|Predictor | | | | +|WhitePoint | | | | +|PrimaryChromaticities | | | | +|HalftoneHints | | | | +|TileWidth | | | | +|TileLength | | | | +|TileOffsets | | | | +|TileByteCounts | | | | +|BadFaxLines | | | | +|CleanFaxData | | | | +|ConsecutiveBadFaxLines | | | | +|SubIFDs | | | | +|InkSet | | | | +|InkNames | | | | +|NumberOfInks | | | | +|DotRange | | | | +|TargetPrinter | | | | +|SampleFormat | | | | +|SMinSampleValue | | | | +|SMaxSampleValue | | | | +|TransferRange | | | | +|ClipPath | | | | +|XClipPathUnits | | | | +|YClipPathUnits | | | | +|Indexed | | | | +|JPEGTables | | | | +|OPIProxy | | | | +|GlobalParametersIFD | | | | +|ProfileType | | | | +|FaxProfile | | | | +|CodingMethods | | | | +|VersionYear | | | | +|ModeNumber | | | | +|Decode | | | | +|DefaultImageColor | | | | +|JPEGProc | | | | +|JPEGInterchangeFormat | | | | +|JPEGInterchangeFormatLength| | | | +|JPEGRestartInterval | | | | +|JPEGLosslessPredictors | | | | +|JPEGPointTransforms | | | | +|JPEGQTables | | | | +|JPEGDCTables | | | | +|JPEGACTables | | | | +|YCbCrCoefficients | | | | +|YCbCrSubSampling | | | | +|YCbCrPositioning | | | | +|ReferenceBlackWhite | | | | +|StripRowCounts | | | | +|XMP | | | | +|ImageID | | | | +|ImageLayer | | | | ### Private TIFF Tags | |Encoder|Decoder|Comments | |---------------------------|:-----:|:-----:|--------------------------| -|Wang Annotation | [ ] | [ ] | | -|MD FileTag | [ ] | [ ] | | -|MD ScalePixel | [ ] | [ ] | | -|MD ColorTable | [ ] | [ ] | | -|MD LabName | [ ] | [ ] | | -|MD SampleInfo | [ ] | [ ] | | -|MD PrepDate | [ ] | [ ] | | -|MD PrepTime | [ ] | [ ] | | -|MD FileUnits | [ ] | [ ] | | -|ModelPixelScaleTag | [ ] | [ ] | | -|IPTC | [ ] | [ ] | | -|INGR Packet Data Tag | [ ] | [ ] | | -|INGR Flag Registers | [ ] | [ ] | | -|IrasB Transformation Matrix| [ ] | [ ] | | -|ModelTiepointTag | [ ] | [ ] | | -|ModelTransformationTag | [ ] | [ ] | | -|Photoshop | [ ] | [ ] | | -|Exif IFD | [ ] | [ ] | | -|ICC Profile | [ ] | [ ] | | -|GeoKeyDirectoryTag | [ ] | [ ] | | -|GeoDoubleParamsTag | [ ] | [ ] | | -|GeoAsciiParamsTag | [ ] | [ ] | | -|GPS IFD | [ ] | [ ] | | -|HylaFAX FaxRecvParams | [ ] | [ ] | | -|HylaFAX FaxSubAddress | [ ] | [ ] | | -|HylaFAX FaxRecvTime | [ ] | [ ] | | -|ImageSourceData | [ ] | [ ] | | -|Interoperability IFD | [ ] | [ ] | | -|GDAL_METADATA | [ ] | [ ] | | -|GDAL_NODATA | [ ] | [ ] | | -|Oce Scanjob Description | [ ] | [ ] | | -|Oce Application Selector | [ ] | [ ] | | -|Oce Identification Number | [ ] | [ ] | | -|Oce ImageLogic Characteristics| [ ] | [ ] | | -|DNGVersion | [ ] | [ ] | | -|DNGBackwardVersion | [ ] | [ ] | | -|UniqueCameraModel | [ ] | [ ] | | -|LocalizedCameraModel | [ ] | [ ] | | -|CFAPlaneColor | [ ] | [ ] | | -|CFALayout | [ ] | [ ] | | -|LinearizationTable | [ ] | [ ] | | -|BlackLevelRepeatDim | [ ] | [ ] | | -|BlackLevel | [ ] | [ ] | | -|BlackLevelDeltaH | [ ] | [ ] | | -|BlackLevelDeltaV | [ ] | [ ] | | -|WhiteLevel | [ ] | [ ] | | -|DefaultScale | [ ] | [ ] | | -|DefaultCropOrigin | [ ] | [ ] | | -|DefaultCropSize | [ ] | [ ] | | -|ColorMatrix1 | [ ] | [ ] | | -|ColorMatrix2 | [ ] | [ ] | | -|CameraCalibration1 | [ ] | [ ] | | -|CameraCalibration2 | [ ] | [ ] | | -|ReductionMatrix1 | [ ] | [ ] | | -|ReductionMatrix2 | [ ] | [ ] | | -|AnalogBalance | [ ] | [ ] | | -|AsShotNeutral | [ ] | [ ] | | -|AsShotWhiteXY | [ ] | [ ] | | -|BaselineExposure | [ ] | [ ] | | -|BaselineNoise | [ ] | [ ] | | -|BaselineSharpness | [ ] | [ ] | | -|BayerGreenSplit | [ ] | [ ] | | -|LinearResponseLimit | [ ] | [ ] | | -|CameraSerialNumber | [ ] | [ ] | | -|LensInfo | [ ] | [ ] | | -|ChromaBlurRadius | [ ] | [ ] | | -|AntiAliasStrength | [ ] | [ ] | | -|DNGPrivateData | [ ] | [ ] | | -|MakerNoteSafety | [ ] | [ ] | | -|CalibrationIlluminant1 | [ ] | [ ] | | -|CalibrationIlluminant2 | [ ] | [ ] | | -|BestQualityScale | [ ] | [ ] | | -|Alias Layer Metadata | [ ] | [ ] | | +|Wang Annotation | | | | +|MD FileTag | | | | +|MD ScalePixel | | | | +|MD ColorTable | | | | +|MD LabName | | | | +|MD SampleInfo | | | | +|MD PrepDate | | | | +|MD PrepTime | | | | +|MD FileUnits | | | | +|ModelPixelScaleTag | | | | +|IPTC | | | | +|INGR Packet Data Tag | | | | +|INGR Flag Registers | | | | +|IrasB Transformation Matrix| | | | +|ModelTiepointTag | | | | +|ModelTransformationTag | | | | +|Photoshop | | | | +|Exif IFD | | | | +|ICC Profile | | | | +|GeoKeyDirectoryTag | | | | +|GeoDoubleParamsTag | | | | +|GeoAsciiParamsTag | | | | +|GPS IFD | | | | +|HylaFAX FaxRecvParams | | | | +|HylaFAX FaxSubAddress | | | | +|HylaFAX FaxRecvTime | | | | +|ImageSourceData | | | | +|Interoperability IFD | | | | +|GDAL_METADATA | | | | +|GDAL_NODATA | | | | +|Oce Scanjob Description | | | | +|Oce Application Selector | | | | +|Oce Identification Number | | | | +|Oce ImageLogic Characteristics| | | | +|DNGVersion | | | | +|DNGBackwardVersion | | | | +|UniqueCameraModel | | | | +|LocalizedCameraModel | | | | +|CFAPlaneColor | | | | +|CFALayout | | | | +|LinearizationTable | | | | +|BlackLevelRepeatDim | | | | +|BlackLevel | | | | +|BlackLevelDeltaH | | | | +|BlackLevelDeltaV | | | | +|WhiteLevel | | | | +|DefaultScale | | | | +|DefaultCropOrigin | | | | +|DefaultCropSize | | | | +|ColorMatrix1 | | | | +|ColorMatrix2 | | | | +|CameraCalibration1 | | | | +|CameraCalibration2 | | | | +|ReductionMatrix1 | | | | +|ReductionMatrix2 | | | | +|AnalogBalance | | | | +|AsShotNeutral | | | | +|AsShotWhiteXY | | | | +|BaselineExposure | | | | +|BaselineNoise | | | | +|BaselineSharpness | | | | +|BayerGreenSplit | | | | +|LinearResponseLimit | | | | +|CameraSerialNumber | | | | +|LensInfo | | | | +|ChromaBlurRadius | | | | +|AntiAliasStrength | | | | +|DNGPrivateData | | | | +|MakerNoteSafety | | | | +|CalibrationIlluminant1 | | | | +|CalibrationIlluminant2 | | | | +|BestQualityScale | | | | +|Alias Layer Metadata | | | | From 59b5df5dd97ffd553e1c6df9cdfb656fc0c1437d Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Mon, 5 Jun 2017 10:49:23 +0100 Subject: [PATCH 046/275] Fill TIFF Readme tables with current status. --- src/ImageSharp/Formats/Tiff/README.md | 59 ++++++++++++++++----------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/README.md b/src/ImageSharp/Formats/Tiff/README.md index 9bc825f5b..d668ed449 100644 --- a/src/ImageSharp/Formats/Tiff/README.md +++ b/src/ImageSharp/Formats/Tiff/README.md @@ -23,30 +23,41 @@ ## Implementation Status +### Deviations from the TIFF spec (to be fixed) + +- Decoder + - A Baseline TIFF reader must skip over extra components (e.g. RGB with 4 samples per pixels) + - NB: Need to handle this for both planar and chunky data + - If the SampleFormat field is present and not 1 - fail gracefully if you cannot handle this + - Compression=None should treat 16/32-BitsPerSample for all samples as SHORT/LONG (for byte order and padding rows) + - RowsPerStrip should default to 2^32-1 (effectively infinity) to store the image as a single strip + - Check Planar format data - is this encoded as strips in order RGBRGBRGB or RRRGGGBBB? + - Make sure we ignore any strips that are not needed for the image (if too many are present) + ### Compression Formats | |Encoder|Decoder|Comments | |---------------------------|:-----:|:-----:|--------------------------| -|None | | | | +|None | | Y | | |Ccitt1D | | | | -|PackBits | | | | +|PackBits | | Y | | |CcittGroup3Fax | | | | |CcittGroup4Fax | | | | -|Lzw | | | | +|Lzw | | Y | Based on ImageSharp GIF LZW implementation - this code could be modified to be (i) shared, or (ii) optimised for each case | |Old Jpeg | | | | |Jpeg (Technote 2) | | | | -|Deflate (Technote 2) | | | | -|Old Deflate (Technote 2) | | | | +|Deflate (Technote 2) | | Y | | +|Old Deflate (Technote 2) | | Y | | ### Photometric Interpretation Formats | |Encoder|Decoder|Comments | |---------------------------|:-----:|:-----:|--------------------------| -|WhiteIsZero | | | | -|BlackIsZero | | | | -|Rgb (Chunky) | | | | -|Rgb (Planar) | | | | -|PaletteColor | | | | +|WhiteIsZero | | Y | General + 1/4/8-bit optimised implementations | +|BlackIsZero | | Y | General + 1/4/8-bit optimised implementations | +|Rgb (Chunky) | | Y | General + Rgb888 optimised implementation | +|Rgb (Planar) | | Y | General implementation only | +|PaletteColor | | Y | General implementation only | |TransparencyMask | | | | |Separated (TIFF Extension) | | | | |YCbCr (TIFF Extension) | | | | @@ -59,11 +70,11 @@ |---------------------------|:-----:|:-----:|--------------------------| |NewSubfileType | | | | |SubfileType | | | | -|ImageWidth | | | | -|ImageLength | | | | -|BitsPerSample | | | | -|Compression | | | | -|PhotometricInterpretation | | | | +|ImageWidth | | Y | | +|ImageLength | | Y | | +|BitsPerSample | | Y | | +|Compression | | Y | | +|PhotometricInterpretation | | Y | | |Threshholding | | | | |CellWidth | | | | |CellLength | | | | @@ -71,26 +82,26 @@ |ImageDescription | | | | |Make | | | | |Model | | | | -|StripOffsets | | | | +|StripOffsets | | Y | | |Orientation | | | | -|SamplesPerPixel | | | | -|RowsPerStrip | | | | -|StripByteCounts | | | | +|SamplesPerPixel | | | Currently ignored, as can be inferred from count of BitsPerSample | +|RowsPerStrip | | Y | | +|StripByteCounts | | Y | | |MinSampleValue | | | | |MaxSampleValue | | | | -|XResolution | | | | -|YResolution | | | | -|PlanarConfiguration | | | | +|XResolution | | Y | | +|YResolution | | Y | | +|PlanarConfiguration | | Y | | |FreeOffsets | | | | |FreeByteCounts | | | | |GrayResponseUnit | | | | |GrayResponseCurve | | | | -|ResolutionUnit | | | | +|ResolutionUnit | | Y | | |Software | | | | |DateTime | | | | |Artist | | | | |HostComputer | | | | -|ColorMap | | | | +|ColorMap | | Y | | |ExtraSamples | | | | |Copyright | | | | From 7af04b1415cf781dc3b1b014f6d55865bf320b22 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Mon, 5 Jun 2017 11:07:21 +0100 Subject: [PATCH 047/275] Refactor TIFF metadata reading into separate method --- .../Formats/Tiff/TiffDecoderCore.cs | 33 +++++---- .../Formats/Tiff/TiffDecoderImageTests.cs | 57 +-------------- .../Formats/Tiff/TiffDecoderMetadataTests.cs | 70 +++++++++++++++++++ 3 files changed, 91 insertions(+), 69 deletions(-) create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 942e510d3..806d56334 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -208,6 +208,25 @@ namespace ImageSharp.Formats Image image = new Image(this.configuration, width, height); + this.ReadMetadata(ifd, image); + this.ReadImageFormat(ifd); + + if (ifd.TryGetIfdEntry(TiffTags.RowsPerStrip, out TiffIfdEntry rowsPerStripEntry) + && ifd.TryGetIfdEntry(TiffTags.StripOffsets, out TiffIfdEntry stripOffsetsEntry) + && ifd.TryGetIfdEntry(TiffTags.StripByteCounts, out TiffIfdEntry stripByteCountsEntry)) + { + int rowsPerStrip = (int)this.ReadUnsignedInteger(ref rowsPerStripEntry); + uint[] stripOffsets = this.ReadUnsignedIntegerArray(ref stripOffsetsEntry); + uint[] stripByteCounts = this.ReadUnsignedIntegerArray(ref stripByteCountsEntry); + this.DecodeImageStrips(image, rowsPerStrip, stripOffsets, stripByteCounts); + } + + return image; + } + + public void ReadMetadata(TiffIfd ifd, Image image) + where TPixel : struct, IPixel + { TiffResolutionUnit resolutionUnit = (TiffResolutionUnit)this.ReadUnsignedInteger(ifd, TiffTags.ResolutionUnit, (uint)TiffResolutionUnit.Inch); if (resolutionUnit != TiffResolutionUnit.None) @@ -226,20 +245,6 @@ namespace ImageSharp.Formats image.MetaData.VerticalResolution = yResolution.ToDouble() * resolutionUnitFactor; } } - - this.ReadImageFormat(ifd); - - if (ifd.TryGetIfdEntry(TiffTags.RowsPerStrip, out TiffIfdEntry rowsPerStripEntry) - && ifd.TryGetIfdEntry(TiffTags.StripOffsets, out TiffIfdEntry stripOffsetsEntry) - && ifd.TryGetIfdEntry(TiffTags.StripByteCounts, out TiffIfdEntry stripByteCountsEntry)) - { - int rowsPerStrip = (int)this.ReadUnsignedInteger(ref rowsPerStripEntry); - uint[] stripOffsets = this.ReadUnsignedIntegerArray(ref stripOffsetsEntry); - uint[] stripByteCounts = this.ReadUnsignedIntegerArray(ref stripByteCountsEntry); - this.DecodeImageStrips(image, rowsPerStrip, stripOffsets, stripByteCounts); - } - - return image; } /// diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs index 9f90e691d..3d6cf355a 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs @@ -16,8 +16,6 @@ namespace ImageSharp.Tests { public const int ImageWidth = 200; public const int ImageHeight = 150; - public const int XResolution = 100; - public const int YResolution = 200; public static object[][] IsLittleEndianValues = new[] { new object[] { false }, new object[] { true } }; @@ -37,57 +35,6 @@ namespace ImageSharp.Tests Assert.Equal(ImageHeight, image.Height); } - [Theory] - [InlineData(false, 150u, 1u, 200u, 1u, 2u /* Inch */, 150.0, 200.0)] - [InlineData(false, 150u, 1u, 200u, 1u, 3u /* Cm */, 150.0 * 2.54, 200.0 * 2.54)] - [InlineData(false, 150u, 1u, 200u, 1u, 1u /* None */, 96.0, 96.0)] - [InlineData(false, 150u, 1u, 200u, 1u, null /* Inch */, 150.0, 200.0)] - [InlineData(false, 5u, 2u, 9u, 4u, 2u /* Inch */, 2.5, 2.25)] - [InlineData(false, null, null, null, null, null /* Inch */, 96.0, 96.0)] - [InlineData(false, 150u, 1u, null, null, 2u /* Inch */, 150.0, 96.0)] - [InlineData(false, null, null, 200u, 1u, 2u /* Inch */, 96.0, 200.0)] - [InlineData(true, 150u, 1u, 200u, 1u, 2u /* Inch */, 150.0, 200.0)] - [InlineData(true, 150u, 1u, 200u, 1u, 3u /* Cm */, 150.0 * 2.54, 200.0 * 2.54)] - [InlineData(true, 150u, 1u, 200u, 1u, 1u /* None */, 96.0, 96.0)] - [InlineData(true, 5u, 2u, 9u, 4u, 2u /* Inch */, 2.5, 2.25)] - [InlineData(true, 150u, 1u, 200u, 1u, null /* Inch */, 150.0, 200.0)] - [InlineData(true, null, null, null, null, null /* Inch */, 96.0, 96.0)] - [InlineData(true, 150u, 1u, null, null, 2u /* Inch */, 150.0, 96.0)] - [InlineData(true, null, null, 200u, 1u, 2u /* Inch */, 96.0, 200.0)] - public void DecodeImage_SetsImageResolution(bool isLittleEndian, uint? xResolutionNumerator, uint? xResolutionDenominator, - uint? yResolutionNumerator, uint? yResolutionDenominator, uint? resolutionUnit, - double expectedHorizonalResolution, double expectedVerticalResolution) - { - TiffGenIfd ifdGen = CreateTiffGenIfd() - .WithoutEntry(TiffTags.XResolution) - .WithoutEntry(TiffTags.YResolution) - .WithoutEntry(TiffTags.ResolutionUnit); - - if (xResolutionNumerator != null) - { - ifdGen.WithEntry(TiffGenEntry.Rational(TiffTags.XResolution, xResolutionNumerator.Value, xResolutionDenominator.Value)); - } - - if (yResolutionNumerator != null) - { - ifdGen.WithEntry(TiffGenEntry.Rational(TiffTags.YResolution, yResolutionNumerator.Value, yResolutionDenominator.Value)); - } - - if (resolutionUnit != null) - { - ifdGen.WithEntry(TiffGenEntry.Integer(TiffTags.ResolutionUnit, TiffType.Short, resolutionUnit.Value)); - } - - Stream stream = ifdGen.ToStream(isLittleEndian); - - TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null, null); - TiffIfd ifd = decoder.ReadIfd(0); - Image image = decoder.DecodeImage(ifd); - - Assert.Equal(expectedHorizonalResolution, image.MetaData.HorizontalResolution, 10); - Assert.Equal(expectedVerticalResolution, image.MetaData.VerticalResolution, 10); - } - [Theory] [MemberData(nameof(IsLittleEndianValues))] public void DecodeImage_ThrowsException_WithMissingImageWidth(bool isLittleEndian) @@ -551,8 +498,8 @@ namespace ImageSharp.Tests { TiffGenEntry.Integer(TiffTags.ImageWidth, TiffType.Long, ImageWidth), TiffGenEntry.Integer(TiffTags.ImageLength, TiffType.Long, ImageHeight), - TiffGenEntry.Rational(TiffTags.XResolution, XResolution, 1), - TiffGenEntry.Rational(TiffTags.YResolution, YResolution, 1), + TiffGenEntry.Rational(TiffTags.XResolution, 100, 1), + TiffGenEntry.Rational(TiffTags.YResolution, 200, 1), TiffGenEntry.Integer(TiffTags.ResolutionUnit, TiffType.Short, 2), TiffGenEntry.Integer(TiffTags.PhotometricInterpretation, TiffType.Short, (int)TiffPhotometricInterpretation.WhiteIsZero), TiffGenEntry.Integer(TiffTags.BitsPerSample, TiffType.Short, new int[] { 8 }), diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs new file mode 100644 index 000000000..b3dd30f5e --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs @@ -0,0 +1,70 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System; + using System.IO; + using Xunit; + + using ImageSharp.Formats; + using ImageSharp.Formats.Tiff; + + public class TiffDecoderMetadataTests + { + public static object[][] IsLittleEndianValues = new[] { new object[] { false }, + new object[] { true } }; + + [Theory] + [InlineData(false, 150u, 1u, 200u, 1u, 2u /* Inch */, 150.0, 200.0)] + [InlineData(false, 150u, 1u, 200u, 1u, 3u /* Cm */, 150.0 * 2.54, 200.0 * 2.54)] + [InlineData(false, 150u, 1u, 200u, 1u, 1u /* None */, 96.0, 96.0)] + [InlineData(false, 150u, 1u, 200u, 1u, null /* Inch */, 150.0, 200.0)] + [InlineData(false, 5u, 2u, 9u, 4u, 2u /* Inch */, 2.5, 2.25)] + [InlineData(false, null, null, null, null, null /* Inch */, 96.0, 96.0)] + [InlineData(false, 150u, 1u, null, null, 2u /* Inch */, 150.0, 96.0)] + [InlineData(false, null, null, 200u, 1u, 2u /* Inch */, 96.0, 200.0)] + [InlineData(true, 150u, 1u, 200u, 1u, 2u /* Inch */, 150.0, 200.0)] + [InlineData(true, 150u, 1u, 200u, 1u, 3u /* Cm */, 150.0 * 2.54, 200.0 * 2.54)] + [InlineData(true, 150u, 1u, 200u, 1u, 1u /* None */, 96.0, 96.0)] + [InlineData(true, 5u, 2u, 9u, 4u, 2u /* Inch */, 2.5, 2.25)] + [InlineData(true, 150u, 1u, 200u, 1u, null /* Inch */, 150.0, 200.0)] + [InlineData(true, null, null, null, null, null /* Inch */, 96.0, 96.0)] + [InlineData(true, 150u, 1u, null, null, 2u /* Inch */, 150.0, 96.0)] + [InlineData(true, null, null, 200u, 1u, 2u /* Inch */, 96.0, 200.0)] + public void DecodeImage_SetsImageResolution(bool isLittleEndian, uint? xResolutionNumerator, uint? xResolutionDenominator, + uint? yResolutionNumerator, uint? yResolutionDenominator, uint? resolutionUnit, + double expectedHorizonalResolution, double expectedVerticalResolution) + { + TiffGenIfd ifdGen = new TiffGenIfd(); + + if (xResolutionNumerator != null) + { + ifdGen.WithEntry(TiffGenEntry.Rational(TiffTags.XResolution, xResolutionNumerator.Value, xResolutionDenominator.Value)); + } + + if (yResolutionNumerator != null) + { + ifdGen.WithEntry(TiffGenEntry.Rational(TiffTags.YResolution, yResolutionNumerator.Value, yResolutionDenominator.Value)); + } + + if (resolutionUnit != null) + { + ifdGen.WithEntry(TiffGenEntry.Integer(TiffTags.ResolutionUnit, TiffType.Short, resolutionUnit.Value)); + } + + Stream stream = ifdGen.ToStream(isLittleEndian); + + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null, null); + TiffIfd ifd = decoder.ReadIfd(0); + Image image = new Image(null, 20, 20); + + decoder.ReadMetadata(ifd, image); + + Assert.Equal(expectedHorizonalResolution, image.MetaData.HorizontalResolution, 10); + Assert.Equal(expectedVerticalResolution, image.MetaData.VerticalResolution, 10); + } + } +} \ No newline at end of file From 53d17fc40e9bd5aab1c052fb3d8a1631b3f9af97 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Tue, 6 Jun 2017 16:21:14 +0100 Subject: [PATCH 048/275] Read baseline TIFF metadata --- src/ImageSharp/Formats/Tiff/README.md | 16 ++-- .../Formats/Tiff/TiffDecoderCore.cs | 49 ++++++++++++ .../Formats/Tiff/TiffMetadataNames.cs | 53 +++++++++++++ .../Formats/Tiff/TiffDecoderMetadataTests.cs | 74 ++++++++++++++++++- 4 files changed, 181 insertions(+), 11 deletions(-) create mode 100644 src/ImageSharp/Formats/Tiff/TiffMetadataNames.cs diff --git a/src/ImageSharp/Formats/Tiff/README.md b/src/ImageSharp/Formats/Tiff/README.md index d668ed449..c2527b008 100644 --- a/src/ImageSharp/Formats/Tiff/README.md +++ b/src/ImageSharp/Formats/Tiff/README.md @@ -79,9 +79,9 @@ |CellWidth | | | | |CellLength | | | | |FillOrder | | | | -|ImageDescription | | | | -|Make | | | | -|Model | | | | +|ImageDescription | | Y | | +|Make | | Y | | +|Model | | Y | | |StripOffsets | | Y | | |Orientation | | | | |SamplesPerPixel | | | Currently ignored, as can be inferred from count of BitsPerSample | @@ -97,13 +97,13 @@ |GrayResponseUnit | | | | |GrayResponseCurve | | | | |ResolutionUnit | | Y | | -|Software | | | | -|DateTime | | | | -|Artist | | | | -|HostComputer | | | | +|Software | | Y | | +|DateTime | | Y | | +|Artist | | Y | | +|HostComputer | | Y | | |ColorMap | | Y | | |ExtraSamples | | | | -|Copyright | | | | +|Copyright | | Y | | ### Extension TIFF Tags diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 806d56334..d2446bb76 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -224,6 +224,12 @@ namespace ImageSharp.Formats return image; } + /// + /// Reads the image metadata from a specified IFD. + /// + /// The pixel format. + /// The IFD to read the image from. + /// The image to write the metadata to. public void ReadMetadata(TiffIfd ifd, Image image) where TPixel : struct, IPixel { @@ -245,6 +251,49 @@ namespace ImageSharp.Formats image.MetaData.VerticalResolution = yResolution.ToDouble() * resolutionUnitFactor; } } + + if (!this.options.IgnoreMetadata) + { + if (ifd.TryGetIfdEntry(TiffTags.Artist, out TiffIfdEntry artistEntry)) + { + image.MetaData.Properties.Add(new ImageProperty(TiffMetadataNames.Artist, this.ReadString(ref artistEntry))); + } + + if (ifd.TryGetIfdEntry(TiffTags.Copyright, out TiffIfdEntry copyrightEntry)) + { + image.MetaData.Properties.Add(new ImageProperty(TiffMetadataNames.Copyright, this.ReadString(ref copyrightEntry))); + } + + if (ifd.TryGetIfdEntry(TiffTags.DateTime, out TiffIfdEntry dateTimeEntry)) + { + image.MetaData.Properties.Add(new ImageProperty(TiffMetadataNames.DateTime, this.ReadString(ref dateTimeEntry))); + } + + if (ifd.TryGetIfdEntry(TiffTags.HostComputer, out TiffIfdEntry hostComputerEntry)) + { + image.MetaData.Properties.Add(new ImageProperty(TiffMetadataNames.HostComputer, this.ReadString(ref hostComputerEntry))); + } + + if (ifd.TryGetIfdEntry(TiffTags.ImageDescription, out TiffIfdEntry imageDescriptionEntry)) + { + image.MetaData.Properties.Add(new ImageProperty(TiffMetadataNames.ImageDescription, this.ReadString(ref imageDescriptionEntry))); + } + + if (ifd.TryGetIfdEntry(TiffTags.Make, out TiffIfdEntry makeEntry)) + { + image.MetaData.Properties.Add(new ImageProperty(TiffMetadataNames.Make, this.ReadString(ref makeEntry))); + } + + if (ifd.TryGetIfdEntry(TiffTags.Model, out TiffIfdEntry modelEntry)) + { + image.MetaData.Properties.Add(new ImageProperty(TiffMetadataNames.Model, this.ReadString(ref modelEntry))); + } + + if (ifd.TryGetIfdEntry(TiffTags.Software, out TiffIfdEntry softwareEntry)) + { + image.MetaData.Properties.Add(new ImageProperty(TiffMetadataNames.Software, this.ReadString(ref softwareEntry))); + } + } } /// diff --git a/src/ImageSharp/Formats/Tiff/TiffMetadataNames.cs b/src/ImageSharp/Formats/Tiff/TiffMetadataNames.cs new file mode 100644 index 000000000..4591986b0 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/TiffMetadataNames.cs @@ -0,0 +1,53 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + /// + /// Defines constants for each of the supported TIFF metadata types. + /// + public static class TiffMetadataNames + { + /// + /// Person who created the image. + /// + public const string Artist = "Artist"; + + /// + /// Copyright notice. + /// + public const string Copyright = "Copyright"; + + /// + /// Date and time of image creation. + /// + public const string DateTime = "DateTime"; + + /// + /// The computer and/or operating system in use at the time of image creation. + /// + public const string HostComputer = "HostComputer"; + + /// + /// A string that describes the subject of the image. + /// + public const string ImageDescription = "ImageDescription"; + + /// + /// The scanner/camera manufacturer. + /// + public const string Make = "Make"; + + /// + /// The scanner/camera model name or number. + /// + public const string Model = "Model"; + + /// + /// Name and version number of the software package(s) used to create the image. + /// + public const string Software = "Software"; + } +} diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs index b3dd30f5e..e418d0d67 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs @@ -7,6 +7,7 @@ namespace ImageSharp.Tests { using System; using System.IO; + using System.Linq; using Xunit; using ImageSharp.Formats; @@ -14,8 +15,22 @@ namespace ImageSharp.Tests public class TiffDecoderMetadataTests { - public static object[][] IsLittleEndianValues = new[] { new object[] { false }, - new object[] { true } }; + public static object[][] BaselineMetadataValues = new[] { new object[] { false, TiffTags.Artist, TiffMetadataNames.Artist, "My Artist Name" }, + new object[] { false, TiffTags.Copyright, TiffMetadataNames.Copyright, "My Copyright Statement" }, + new object[] { false, TiffTags.DateTime, TiffMetadataNames.DateTime, "My DateTime Value" }, + new object[] { false, TiffTags.HostComputer, TiffMetadataNames.HostComputer, "My Host Computer Name" }, + new object[] { false, TiffTags.ImageDescription, TiffMetadataNames.ImageDescription, "My Image Description" }, + new object[] { false, TiffTags.Make, TiffMetadataNames.Make, "My Camera Make" }, + new object[] { false, TiffTags.Model, TiffMetadataNames.Model, "My Camera Model" }, + new object[] { false, TiffTags.Software, TiffMetadataNames.Software, "My Imaging Software" }, + new object[] { true, TiffTags.Artist, TiffMetadataNames.Artist, "My Artist Name" }, + new object[] { true, TiffTags.Copyright, TiffMetadataNames.Copyright, "My Copyright Statement" }, + new object[] { true, TiffTags.DateTime, TiffMetadataNames.DateTime, "My DateTime Value" }, + new object[] { true, TiffTags.HostComputer, TiffMetadataNames.HostComputer, "My Host Computer Name" }, + new object[] { true, TiffTags.ImageDescription, TiffMetadataNames.ImageDescription, "My Image Description" }, + new object[] { true, TiffTags.Make, TiffMetadataNames.Make, "My Camera Make" }, + new object[] { true, TiffTags.Model, TiffMetadataNames.Model, "My Camera Model" }, + new object[] { true, TiffTags.Software, TiffMetadataNames.Software, "My Imaging Software" }}; [Theory] [InlineData(false, 150u, 1u, 200u, 1u, 2u /* Inch */, 150.0, 200.0)] @@ -34,7 +49,7 @@ namespace ImageSharp.Tests [InlineData(true, null, null, null, null, null /* Inch */, 96.0, 96.0)] [InlineData(true, 150u, 1u, null, null, 2u /* Inch */, 150.0, 96.0)] [InlineData(true, null, null, 200u, 1u, 2u /* Inch */, 96.0, 200.0)] - public void DecodeImage_SetsImageResolution(bool isLittleEndian, uint? xResolutionNumerator, uint? xResolutionDenominator, + public void ReadMetadata_SetsImageResolution(bool isLittleEndian, uint? xResolutionNumerator, uint? xResolutionDenominator, uint? yResolutionNumerator, uint? yResolutionDenominator, uint? resolutionUnit, double expectedHorizonalResolution, double expectedVerticalResolution) { @@ -66,5 +81,58 @@ namespace ImageSharp.Tests Assert.Equal(expectedHorizonalResolution, image.MetaData.HorizontalResolution, 10); Assert.Equal(expectedVerticalResolution, image.MetaData.VerticalResolution, 10); } + + [Theory] + [MemberData(nameof(BaselineMetadataValues))] + public void ReadMetadata_SetsAsciiMetadata(bool isLittleEndian, ushort tag, string metadataName, string metadataValue) + { + Stream stream = new TiffGenIfd() + { + Entries = + { + TiffGenEntry.Integer(TiffTags.ImageWidth, TiffType.Long, 150), + TiffGenEntry.Integer(TiffTags.ImageLength, TiffType.Long, 210), + TiffGenEntry.Ascii(tag, metadataValue), + TiffGenEntry.Integer(TiffTags.Orientation, TiffType.Short, 1) + } + } + .ToStream(isLittleEndian); + + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null, null); + TiffIfd ifd = decoder.ReadIfd(0); + Image image = new Image(null, 20, 20); + + decoder.ReadMetadata(ifd, image); + var metadata = image.MetaData.Properties.FirstOrDefault(m => m.Name == metadataName)?.Value; + + Assert.Equal(metadataValue, metadata); + } + + [Theory] + [MemberData(nameof(BaselineMetadataValues))] + public void ReadMetadata_DoesntSetMetadataIfIgnoring(bool isLittleEndian, ushort tag, string metadataName, string metadataValue) + { + Stream stream = new TiffGenIfd() + { + Entries = + { + TiffGenEntry.Integer(TiffTags.ImageWidth, TiffType.Long, 150), + TiffGenEntry.Integer(TiffTags.ImageLength, TiffType.Long, 210), + TiffGenEntry.Ascii(tag, metadataValue), + TiffGenEntry.Integer(TiffTags.Orientation, TiffType.Short, 1) + } + } + .ToStream(isLittleEndian); + + DecoderOptions options = new DecoderOptions() { IgnoreMetadata = true }; + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, options, null); + TiffIfd ifd = decoder.ReadIfd(0); + Image image = new Image(null, 20, 20); + + decoder.ReadMetadata(ifd, image); + var metadata = image.MetaData.Properties.FirstOrDefault(m => m.Name == metadataName)?.Value; + + Assert.Equal(null, metadata); + } } } \ No newline at end of file From f41eb1101cc0b4103ee5879dd8a60d39ff160ce4 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Tue, 6 Jun 2017 18:55:34 +0100 Subject: [PATCH 049/275] Use new pixel packing methods --- .../Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs | 2 +- .../Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs | 6 +++--- .../Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs | 2 +- .../Tiff/PhotometricInterpretation/Rgb888TiffColor.cs | 2 +- .../Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs | 2 +- .../Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs | 6 +++--- .../Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs index 1b9e194e1..a4de21874 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs @@ -44,7 +44,7 @@ namespace ImageSharp.Formats.Tiff { int bit = (b >> (7 - shift)) & 1; byte intensity = (bit == 1) ? (byte)255 : (byte)0; - color.PackFromBytes(intensity, intensity, intensity, 255); + color.PackFromRgba32(new Rgba32(intensity, intensity, intensity, 255)); pixels[x + shift, y] = color; } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs index b52e5e045..42d829ef8 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs @@ -40,11 +40,11 @@ namespace ImageSharp.Formats.Tiff byte byteData = data[offset++]; byte intensity1 = (byte)(((byteData & 0xF0) >> 4) * 17); - color.PackFromBytes(intensity1, intensity1, intensity1, 255); + color.PackFromRgba32(new Rgba32(intensity1, intensity1, intensity1, 255)); pixels[x, y] = color; byte intensity2 = (byte)((byteData & 0x0F) * 17); - color.PackFromBytes(intensity2, intensity2, intensity2, 255); + color.PackFromRgba32(new Rgba32(intensity2, intensity2, intensity2, 255)); pixels[x + 1, y] = color; } @@ -53,7 +53,7 @@ namespace ImageSharp.Formats.Tiff byte byteData = data[offset++]; byte intensity1 = (byte)(((byteData & 0xF0) >> 4) * 17); - color.PackFromBytes(intensity1, intensity1, intensity1, 255); + color.PackFromRgba32(new Rgba32(intensity1, intensity1, intensity1, 255)); pixels[left + width - 1, y] = color; } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs index ae9cf4615..b30cbe264 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs @@ -37,7 +37,7 @@ namespace ImageSharp.Formats.Tiff for (int x = left; x < left + width; x++) { byte intensity = data[offset++]; - color.PackFromBytes(intensity, intensity, intensity, 255); + color.PackFromRgba32(new Rgba32(intensity, intensity, intensity, 255)); pixels[x, y] = color; } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs index afe88510e..a4c1c8ad5 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs @@ -41,7 +41,7 @@ namespace ImageSharp.Formats.Tiff byte r = data[offset++]; byte g = data[offset++]; byte b = data[offset++]; - color.PackFromBytes(r, g, b, 255); + color.PackFromRgba32(new Rgba32(r, g, b, 255)); pixels[x, y] = color; } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs index 34bc5e731..25d01a2fb 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs @@ -44,7 +44,7 @@ namespace ImageSharp.Formats.Tiff { int bit = (b >> (7 - shift)) & 1; byte intensity = (bit == 1) ? (byte)0 : (byte)255; - color.PackFromBytes(intensity, intensity, intensity, 255); + color.PackFromRgba32(new Rgba32(intensity, intensity, intensity, 255)); pixels[x + shift, y] = color; } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs index 00653feb4..8aef89dc5 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs @@ -40,11 +40,11 @@ namespace ImageSharp.Formats.Tiff byte byteData = data[offset++]; byte intensity1 = (byte)((15 - ((byteData & 0xF0) >> 4)) * 17); - color.PackFromBytes(intensity1, intensity1, intensity1, 255); + color.PackFromRgba32(new Rgba32(intensity1, intensity1, intensity1, 255)); pixels[x, y] = color; byte intensity2 = (byte)((15 - (byteData & 0x0F)) * 17); - color.PackFromBytes(intensity2, intensity2, intensity2, 255); + color.PackFromRgba32(new Rgba32(intensity2, intensity2, intensity2, 255)); pixels[x + 1, y] = color; } @@ -53,7 +53,7 @@ namespace ImageSharp.Formats.Tiff byte byteData = data[offset++]; byte intensity1 = (byte)((15 - ((byteData & 0xF0) >> 4)) * 17); - color.PackFromBytes(intensity1, intensity1, intensity1, 255); + color.PackFromRgba32(new Rgba32(intensity1, intensity1, intensity1, 255)); pixels[left + width - 1, y] = color; } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs index 8168839ad..469767510 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs @@ -37,7 +37,7 @@ namespace ImageSharp.Formats.Tiff for (int x = left; x < left + width; x++) { byte intensity = (byte)(255 - data[offset++]); - color.PackFromBytes(intensity, intensity, intensity, 255); + color.PackFromRgba32(new Rgba32(intensity, intensity, intensity, 255)); pixels[x, y] = color; } } From 22f0a1d617c68c214862d23d036ea2ee44ebd173 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Tue, 6 Jun 2017 20:24:25 +0100 Subject: [PATCH 050/275] Add some TIFF sample images (autogenerated) --- .../Tiff/Calliphora_grayscale_uncompressed.tiff | 3 +++ .../Tiff/Calliphora_palette_uncompressed.tiff | 3 +++ .../Formats/Tiff/Calliphora_rgb_deflate.tiff | 3 +++ .../TestImages/Formats/Tiff/Calliphora_rgb_jpeg.tiff | 3 +++ .../TestImages/Formats/Tiff/Calliphora_rgb_lzw.tiff | 3 +++ .../Formats/Tiff/Calliphora_rgb_packbits.tiff | 3 +++ .../Formats/Tiff/Calliphora_rgb_uncompressed.tiff | 3 +++ .../TestImages/Formats/Tiff/genimages.ps1 | 12 ++++++++++++ 8 files changed, 33 insertions(+) create mode 100644 tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_grayscale_uncompressed.tiff create mode 100644 tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_palette_uncompressed.tiff create mode 100644 tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_rgb_deflate.tiff create mode 100644 tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_rgb_jpeg.tiff create mode 100644 tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_rgb_lzw.tiff create mode 100644 tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_rgb_packbits.tiff create mode 100644 tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_rgb_uncompressed.tiff create mode 100644 tests/ImageSharp.Tests/TestImages/Formats/Tiff/genimages.ps1 diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_grayscale_uncompressed.tiff b/tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_grayscale_uncompressed.tiff new file mode 100644 index 000000000..5db7ef564 --- /dev/null +++ b/tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_grayscale_uncompressed.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0283f2be39a151ca3ed19be97ebe4a6b17978ed251dd4d0d568895865fec24c7 +size 964588 diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_palette_uncompressed.tiff b/tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_palette_uncompressed.tiff new file mode 100644 index 000000000..1592645c8 --- /dev/null +++ b/tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_palette_uncompressed.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b70500348b1af7828c15e7782eaca105ff749136d7c45eb4cab8c5cd5269c3f6 +size 966134 diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_rgb_deflate.tiff b/tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_rgb_deflate.tiff new file mode 100644 index 000000000..c2ebed364 --- /dev/null +++ b/tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_rgb_deflate.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:da6e6a35a0bb0f5f2d49e3c5f0eb2deb7118718dd08844f66a6cb72f48b5c489 +size 1476294 diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_rgb_jpeg.tiff b/tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_rgb_jpeg.tiff new file mode 100644 index 000000000..c9f5fadee --- /dev/null +++ b/tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_rgb_jpeg.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ba79ffac35e16208406e073492d770d3a77e51a47112aa02ab8fd98b5a8487b2 +size 198564 diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_rgb_lzw.tiff b/tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_rgb_lzw.tiff new file mode 100644 index 000000000..3a37054cc --- /dev/null +++ b/tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_rgb_lzw.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:36b828df14ffda9b64f8eed99714e7af9d6324efe2349a972003af7166fc4629 +size 1792988 diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_rgb_packbits.tiff b/tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_rgb_packbits.tiff new file mode 100644 index 000000000..862db0b39 --- /dev/null +++ b/tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_rgb_packbits.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:59dbb48f10c40cbbd4f5617a9f57536790ce0b9a4cc241dc8d6257095598cb76 +size 2891292 diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_rgb_uncompressed.tiff b/tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_rgb_uncompressed.tiff new file mode 100644 index 000000000..7ebd74d9d --- /dev/null +++ b/tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_rgb_uncompressed.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:acb46c990af78fcb0e63849f0f26acffe26833d5cfd2fc77a728baf92166eea3 +size 2893218 diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Tiff/genimages.ps1 b/tests/ImageSharp.Tests/TestImages/Formats/Tiff/genimages.ps1 new file mode 100644 index 000000000..6ed0c080c --- /dev/null +++ b/tests/ImageSharp.Tests/TestImages/Formats/Tiff/genimages.ps1 @@ -0,0 +1,12 @@ +$Gm_Exe = "C:\Program Files\GraphicsMagick-1.3.25-Q16\gm.exe" +$Source_Image = "..\Jpg\baseline\Calliphora.jpg" +$Output_Prefix = ".\Calliphora" + +& $Gm_Exe convert $Source_Image -compress None -type TrueColor $Output_Prefix"_rgb_uncompressed.tiff" +& $Gm_Exe convert $Source_Image -compress LZW -type TrueColor $Output_Prefix"_rgb_lzw.tiff" +& $Gm_Exe convert $Source_Image -compress RLE -type TrueColor $Output_Prefix"_rgb_packbits.tiff" +& $Gm_Exe convert $Source_Image -compress JPEG -type TrueColor $Output_Prefix"_rgb_jpeg.tiff" +& $Gm_Exe convert $Source_Image -compress Zip -type TrueColor $Output_Prefix"_rgb_deflate.tiff" + +& $Gm_Exe convert $Source_Image -compress None -type Grayscale $Output_Prefix"_grayscale_uncompressed.tiff" +& $Gm_Exe convert $Source_Image -compress None -colors 256 $Output_Prefix"_palette_uncompressed.tiff" \ No newline at end of file From 06ed5221c44ef3c085de212ebad6f14ab16e7a11 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Tue, 6 Jun 2017 21:11:01 +0100 Subject: [PATCH 051/275] Add TIFF as a default image format --- src/ImageSharp/Configuration.cs | 1 + .../Formats/Tiff/TiffDecoderHeaderTests.cs | 12 +++--------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index fa983d355..57cd66eb4 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -109,6 +109,7 @@ namespace ImageSharp config.AddImageFormat(new Formats.JpegFormat()); config.AddImageFormat(new Formats.GifFormat()); config.AddImageFormat(new Formats.BmpFormat()); + config.AddImageFormat(new Formats.TiffFormat()); return config; } diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs index ae581d293..0f03c3207 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs @@ -68,7 +68,7 @@ namespace ImageSharp.Tests TiffDecoder decoder = new TiffDecoder(); - ImageFormatException e = Assert.Throws(() => { TestDecode(decoder, stream); }); + ImageFormatException e = Assert.Throws(() => { decoder.Decode(Configuration.Default, stream, null); }); Assert.Equal("Invalid TIFF file header.", e.Message); } @@ -86,7 +86,7 @@ namespace ImageSharp.Tests TiffDecoder decoder = new TiffDecoder(); - ImageFormatException e = Assert.Throws(() => { TestDecode(decoder, stream); }); + ImageFormatException e = Assert.Throws(() => { decoder.Decode(Configuration.Default, stream, null); }); Assert.Equal("Invalid TIFF file header.", e.Message); } @@ -103,15 +103,9 @@ namespace ImageSharp.Tests TiffDecoder decoder = new TiffDecoder(); - ImageFormatException e = Assert.Throws(() => { TestDecode(decoder, stream); }); + ImageFormatException e = Assert.Throws(() => { decoder.Decode(Configuration.Default, stream, null); }); Assert.Equal("Invalid TIFF file header.", e.Message); } - - private void TestDecode(TiffDecoder decoder, Stream stream) - { - Configuration.Default.AddImageFormat(new TiffFormat()); - Image image = decoder.Decode(Configuration.Default, stream, null); - } } } \ No newline at end of file From b2979f9b96388d7adca46dfa77a9cc7a56f0fc98 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Wed, 7 Jun 2017 10:44:16 +0100 Subject: [PATCH 052/275] Add 'DecodeTiff' benchmark --- tests/ImageSharp.Benchmarks/BenchmarkBase.cs | 1 + .../ImageSharp.Benchmarks/Image/DecodeTiff.cs | 54 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 tests/ImageSharp.Benchmarks/Image/DecodeTiff.cs diff --git a/tests/ImageSharp.Benchmarks/BenchmarkBase.cs b/tests/ImageSharp.Benchmarks/BenchmarkBase.cs index d6e8ac692..b1aadac0a 100644 --- a/tests/ImageSharp.Benchmarks/BenchmarkBase.cs +++ b/tests/ImageSharp.Benchmarks/BenchmarkBase.cs @@ -17,6 +17,7 @@ Configuration.Default.AddImageFormat(new PngFormat()); Configuration.Default.AddImageFormat(new BmpFormat()); Configuration.Default.AddImageFormat(new GifFormat()); + Configuration.Default.AddImageFormat(new TiffFormat()); } } } diff --git a/tests/ImageSharp.Benchmarks/Image/DecodeTiff.cs b/tests/ImageSharp.Benchmarks/Image/DecodeTiff.cs new file mode 100644 index 000000000..3c57e5fd2 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Image/DecodeTiff.cs @@ -0,0 +1,54 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Benchmarks.Image +{ + using System.Drawing; + using System.IO; + + using BenchmarkDotNet.Attributes; + + using CoreImage = ImageSharp.Image; + + using CoreSize = ImageSharp.Size; + + public class DecodeTiff : BenchmarkBase + { + private byte[] tiffBytes; + + [GlobalSetup] + public void ReadImages() + { + if (this.tiffBytes == null) + { + this.tiffBytes = File.ReadAllBytes("../ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_rgb_uncompressed.tiff"); + } + } + + [Benchmark(Baseline = true, Description = "System.Drawing Tiff")] + public Size TiffSystemDrawing() + { + using (MemoryStream memoryStream = new MemoryStream(this.tiffBytes)) + { + using (Image image = Image.FromStream(memoryStream)) + { + return image.Size; + } + } + } + + [Benchmark(Description = "ImageSharp Tiff")] + public CoreSize TiffCore() + { + using (MemoryStream memoryStream = new MemoryStream(this.tiffBytes)) + { + using (Image image = CoreImage.Load(memoryStream)) + { + return new CoreSize(image.Width, image.Height); + } + } + } + } +} From fd0f49f050bec0c9fe151c752628f1f3f237c558 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Wed, 7 Jun 2017 11:45:00 +0100 Subject: [PATCH 053/275] Add TiffEncoder and write TIFF header --- .../Formats/Tiff/Constants/TiffConstants.cs | 15 ++++ src/ImageSharp/Formats/Tiff/TiffEncoder.cs | 3 +- .../Formats/Tiff/TiffEncoderCore.cs | 79 +++++++++++++++++++ .../Formats/Tiff/TiffEncoderHeaderTests.cs | 61 ++++++++++++++ 4 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs index 1858d49b8..10a3478c0 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs @@ -20,6 +20,16 @@ namespace ImageSharp.Formats.Tiff /// public const byte ByteOrderBigEndian = 0x4D; + /// + /// Byte order markers for indicating little endian encoding. + /// + public const ushort ByteOrderLittleEndianShort = 0x4949; + + /// + /// Byte order markers for indicating big endian encoding. + /// + public const ushort ByteOrderBigEndianShort = 0x4D4D; + /// /// Magic number used within the image file header to identify a TIFF format file. /// @@ -59,5 +69,10 @@ namespace ImageSharp.Formats.Tiff /// Size (in bytes) of the Double data type /// public const int SizeOfDouble = 8; + + /// + /// Size (in bytes) of the word boundary to allign data to when required + /// + public const int SizeOfWordBoundary = 4; } } diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs index 7bcb575db..75ff7dcd4 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs @@ -33,7 +33,8 @@ namespace ImageSharp.Formats public void Encode(Image image, Stream stream, ITiffEncoderOptions options) where TPixel : struct, IPixel { - throw new NotImplementedException(); + var encode = new TiffEncoderCore(options); + encode.Encode(image, stream); } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs new file mode 100644 index 000000000..74e8338c2 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -0,0 +1,79 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + using System; + using System.Buffers; + using System.IO; + using System.Linq; + using System.Runtime.CompilerServices; + using System.Text; + using ImageSharp.Formats.Tiff; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + + using Quantizers; + + using static ComparableExtensions; + + /// + /// Performs the TIFF encoding operation. + /// + internal sealed class TiffEncoderCore + { + /// + /// The options for the encoder. + /// + private readonly ITiffEncoderOptions options; + + /// + /// Initializes a new instance of the class. + /// + /// The options for the encoder. + public TiffEncoderCore(ITiffEncoderOptions options) + { + this.options = options ?? new TiffEncoderOptions(); + } + + /// + /// Encodes the image to the specified stream from the . + /// + /// The pixel format. + /// The to encode from. + /// The to encode the image data to. + public void Encode(Image image, Stream stream) + where TPixel : struct, IPixel + { + Guard.NotNull(image, nameof(image)); + Guard.NotNull(stream, nameof(stream)); + + using (BinaryWriter writer = new BinaryWriter(stream, Encoding.UTF8, true)) + { + this.WriteHeader(writer, 0); + } + } + + /// + /// Writes the TIFF file header. + /// + /// The to write data to. + /// The byte offset to the first IFD in the file. + public void WriteHeader(BinaryWriter writer, uint firstIfdOffset) + { + if (firstIfdOffset == 0 || firstIfdOffset % TiffConstants.SizeOfWordBoundary != 0) + { + throw new ArgumentException("IFD offsets must be non-zero and on a word boundary.", nameof(firstIfdOffset)); + } + + ushort byteOrderMarker = BitConverter.IsLittleEndian ? TiffConstants.ByteOrderLittleEndianShort + : TiffConstants.ByteOrderBigEndianShort; + + writer.Write(byteOrderMarker); + writer.Write((ushort)42); + writer.Write(firstIfdOffset); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs new file mode 100644 index 000000000..d5c21f594 --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs @@ -0,0 +1,61 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System; + using System.IO; + using Xunit; + + using ImageSharp.Formats; + using System.Text; + + public class TiffEncoderHeaderTests + { + [Fact] + public void WriteHeader_WritesValidHeader() + { + MemoryStream stream = new MemoryStream(); + TiffEncoderCore encoder = new TiffEncoderCore(null); + + using (BinaryWriter writer = new BinaryWriter(stream, Encoding.UTF8, true)) + { + encoder.WriteHeader(writer, 1232); + } + + stream.Position = 0; + Assert.Equal(8, stream.Length); + Assert.Equal(new byte[] { 0x49, 0x49, 42, 0, 0xD0, 0x04, 0x00, 0x00 }, stream.ToArray()); + } + + [Fact] + public void WriteHeader_ThrowsExceptionIfFirstIfdOffsetIsZero() + { + MemoryStream stream = new MemoryStream(); + TiffEncoderCore encoder = new TiffEncoderCore(null); + + using (BinaryWriter writer = new BinaryWriter(stream, Encoding.UTF8, true)) + { + ArgumentException e = Assert.Throws(() => { encoder.WriteHeader(writer, 0); }); + Assert.Equal("IFD offsets must be non-zero and on a word boundary.\r\nParameter name: firstIfdOffset", e.Message); + Assert.Equal("firstIfdOffset", e.ParamName); + } + } + + [Fact] + public void WriteHeader_ThrowsExceptionIfIfdOffsetIsNotOnAWordBoundary() + { + MemoryStream stream = new MemoryStream(); + TiffEncoderCore encoder = new TiffEncoderCore(null); + + using (BinaryWriter writer = new BinaryWriter(stream, Encoding.UTF8, true)) + { + ArgumentException e = Assert.Throws(() => { encoder.WriteHeader(writer, 1234); }); + Assert.Equal("IFD offsets must be non-zero and on a word boundary.\r\nParameter name: firstIfdOffset", e.Message); + Assert.Equal("firstIfdOffset", e.ParamName); + } + } + } +} \ No newline at end of file From 51dc7ded67351568c53840624e20627161a068c1 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Mon, 19 Jun 2017 21:46:02 +0100 Subject: [PATCH 054/275] Add TIFF IFD encoding --- .../Formats/Tiff/Constants/TiffConstants.cs | 5 - .../Formats/Tiff/TiffEncoderCore.cs | 88 +++++- .../Formats/Tiff/Utils/TiffWriter.cs | 109 +++++++ .../Formats/Tiff/TiffEncoderHeaderTests.cs | 33 +- .../Formats/Tiff/TiffEncoderIfdTests.cs | 299 ++++++++++++++++++ .../Formats/Tiff/Utils/TiffWriterTests.cs | 135 ++++++++ 6 files changed, 629 insertions(+), 40 deletions(-) create mode 100644 src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderIfdTests.cs create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Utils/TiffWriterTests.cs diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs index 10a3478c0..5c03d33b0 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs @@ -69,10 +69,5 @@ namespace ImageSharp.Formats.Tiff /// Size (in bytes) of the Double data type /// public const int SizeOfDouble = 8; - - /// - /// Size (in bytes) of the word boundary to allign data to when required - /// - public const int SizeOfWordBoundary = 4; } } diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 74e8338c2..d32e34c43 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -7,6 +7,7 @@ namespace ImageSharp.Formats { using System; using System.Buffers; + using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.CompilerServices; @@ -50,30 +51,95 @@ namespace ImageSharp.Formats Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); - using (BinaryWriter writer = new BinaryWriter(stream, Encoding.UTF8, true)) + using (TiffWriter writer = new TiffWriter(stream)) { - this.WriteHeader(writer, 0); + long firstIfdMarker = this.WriteHeader(writer); + long nextIfdMarker = this.WriteImage(writer, image, firstIfdMarker); } } /// /// Writes the TIFF file header. /// - /// The to write data to. - /// The byte offset to the first IFD in the file. - public void WriteHeader(BinaryWriter writer, uint firstIfdOffset) + /// The to write data to. + /// The marker to write the first IFD offset. + public long WriteHeader(TiffWriter writer) { - if (firstIfdOffset == 0 || firstIfdOffset % TiffConstants.SizeOfWordBoundary != 0) - { - throw new ArgumentException("IFD offsets must be non-zero and on a word boundary.", nameof(firstIfdOffset)); - } - ushort byteOrderMarker = BitConverter.IsLittleEndian ? TiffConstants.ByteOrderLittleEndianShort : TiffConstants.ByteOrderBigEndianShort; writer.Write(byteOrderMarker); writer.Write((ushort)42); - writer.Write(firstIfdOffset); + long firstIfdMarker = writer.PlaceMarker(); + + return firstIfdMarker; + } + + /// + /// Writes a TIFF IFD block. + /// + /// The to write data to. + /// The IFD entries to write to the file. + /// The marker to write the next IFD offset (if present). + public long WriteIfd(TiffWriter writer, List entries) + { + if (entries.Count == 0) + { + throw new ArgumentException("There must be at least one entry per IFD.", nameof(entries)); + } + + uint dataOffset = (uint)writer.Position + (uint)(6 + (entries.Count * 12)); + List largeDataBlocks = new List(); + + entries.Sort((a, b) => a.Tag - b.Tag); + + writer.Write((ushort)entries.Count); + + foreach (TiffIfdEntry entry in entries) + { + writer.Write(entry.Tag); + writer.Write((ushort)entry.Type); + writer.Write(entry.Count); + + if (entry.Value.Length <= 4) + { + writer.WritePadded(entry.Value); + } + else + { + largeDataBlocks.Add(entry.Value); + writer.Write(dataOffset); + dataOffset += (uint)(entry.Value.Length + (entry.Value.Length % 2)); + } + } + + long nextIfdMarker = writer.PlaceMarker(); + + foreach (byte[] dataBlock in largeDataBlocks) + { + writer.Write(dataBlock); + + if (dataBlock.Length % 2 == 1) + { + writer.Write((byte)0); + } + } + + return nextIfdMarker; + } + + /// + /// Writes all data required to define an image + /// + /// The pixel format. + /// The to write data to. + /// The to encode from. + /// The marker to write this IFD offset. + /// The marker to write the next IFD offset (if present). + public long WriteImage(TiffWriter writer, Image image, long ifdOffset) + where TPixel : struct, IPixel + { + throw new NotImplementedException(); } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs new file mode 100644 index 000000000..201e7b4da --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -0,0 +1,109 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// +namespace ImageSharp.Formats.Tiff +{ + using System; + using System.Collections.Generic; + using System.IO; + + /// + /// Utility class for writing TIFF data to a . + /// + internal class TiffWriter : IDisposable + { + private readonly Stream output; + + private readonly byte[] paddingBytes = new byte[4]; + + private readonly List references = new List(); + + /// Initializes a new instance of the class. + /// The output stream. + public TiffWriter(Stream output) + { + this.output = output; + } + + /// + /// Gets a flag indicating whether the architecture is little-endian. + /// + public bool IsLittleEndian => BitConverter.IsLittleEndian; + + /// + /// Returns the current position within the stream. + /// + public long Position => this.output.Position; + + /// Writes an empty four bytes to the stream, returning the offset to be written later. + /// The offset to be written later + public long PlaceMarker() + { + long offset = this.output.Position; + this.Write(0u); + return offset; + } + + /// Writes an array of bytes to the current stream. + /// The bytes to write. + public void Write(byte[] value) + { + this.output.Write(value, 0, value.Length); + } + + /// Writes a byte to the current stream. + /// The byte to write. + public void Write(byte value) + { + this.output.Write(new byte[] { value }, 0, 1); + } + + /// Writes a two-byte unsigned integer to the current stream. + /// The two-byte unsigned integer to write. + public void Write(ushort value) + { + byte[] bytes = BitConverter.GetBytes(value); + this.output.Write(bytes, 0, 2); + } + + /// Writes a four-byte unsigned integer to the current stream. + /// The four-byte unsigned integer to write. + public void Write(uint value) + { + byte[] bytes = BitConverter.GetBytes(value); + this.output.Write(bytes, 0, 4); + } + + /// Writes an array of bytes to the current stream, padded to four-bytes. + /// The bytes to write. + public void WritePadded(byte[] value) + { + this.output.Write(value, 0, value.Length); + + if (value.Length < 4) + { + this.output.Write(this.paddingBytes, 0, 4 - value.Length); + } + } + + /// Writes a four-byte unsigned integer to the specified marker in the stream. + /// The offset returned when placing the marker + /// The four-byte unsigned integer to write. + public void WriteMarker(long offset, uint value) + { + long currentOffset = this.output.Position; + this.output.Seek(offset, SeekOrigin.Begin); + this.Write(value); + this.output.Seek(currentOffset, SeekOrigin.Begin); + } + + /// + /// Disposes instance, ensuring any unwritten data is flushed. + /// + public void Dispose() + { + this.output.Flush(); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs index d5c21f594..76d15f6a1 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs @@ -7,9 +7,11 @@ namespace ImageSharp.Tests { using System; using System.IO; + using System.Linq; using Xunit; using ImageSharp.Formats; + using ImageSharp.Formats.Tiff; using System.Text; public class TiffEncoderHeaderTests @@ -20,41 +22,24 @@ namespace ImageSharp.Tests MemoryStream stream = new MemoryStream(); TiffEncoderCore encoder = new TiffEncoderCore(null); - using (BinaryWriter writer = new BinaryWriter(stream, Encoding.UTF8, true)) + using (TiffWriter writer = new TiffWriter(stream)) { - encoder.WriteHeader(writer, 1232); + long firstIfdMarker = encoder.WriteHeader(writer); } - stream.Position = 0; - Assert.Equal(8, stream.Length); - Assert.Equal(new byte[] { 0x49, 0x49, 42, 0, 0xD0, 0x04, 0x00, 0x00 }, stream.ToArray()); + Assert.Equal(new byte[] { 0x49, 0x49, 42, 0, 0x00, 0x00, 0x00, 0x00 }, stream.ToArray()); } [Fact] - public void WriteHeader_ThrowsExceptionIfFirstIfdOffsetIsZero() + public void WriteHeader_ReturnsFirstIfdMarker() { MemoryStream stream = new MemoryStream(); TiffEncoderCore encoder = new TiffEncoderCore(null); - using (BinaryWriter writer = new BinaryWriter(stream, Encoding.UTF8, true)) + using (TiffWriter writer = new TiffWriter(stream)) { - ArgumentException e = Assert.Throws(() => { encoder.WriteHeader(writer, 0); }); - Assert.Equal("IFD offsets must be non-zero and on a word boundary.\r\nParameter name: firstIfdOffset", e.Message); - Assert.Equal("firstIfdOffset", e.ParamName); - } - } - - [Fact] - public void WriteHeader_ThrowsExceptionIfIfdOffsetIsNotOnAWordBoundary() - { - MemoryStream stream = new MemoryStream(); - TiffEncoderCore encoder = new TiffEncoderCore(null); - - using (BinaryWriter writer = new BinaryWriter(stream, Encoding.UTF8, true)) - { - ArgumentException e = Assert.Throws(() => { encoder.WriteHeader(writer, 1234); }); - Assert.Equal("IFD offsets must be non-zero and on a word boundary.\r\nParameter name: firstIfdOffset", e.Message); - Assert.Equal("firstIfdOffset", e.ParamName); + long firstIfdMarker = encoder.WriteHeader(writer); + Assert.Equal(4, firstIfdMarker); } } } diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderIfdTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderIfdTests.cs new file mode 100644 index 000000000..c4c4fb84b --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderIfdTests.cs @@ -0,0 +1,299 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System; + using System.IO; + using System.Linq; + using Xunit; + + using ImageSharp.Formats; + using ImageSharp.Formats.Tiff; + using System.Text; + using System.Collections.Generic; + + public class TiffEncoderIfdTests + { + [Fact] + public void WriteIfd_DataIsCorrectLength() + { + MemoryStream stream = new MemoryStream(); + TiffEncoderCore encoder = new TiffEncoderCore(null); + + List entries = new List() + { + new TiffIfdEntry(TiffTags.ImageWidth, TiffType.Long, 1, new byte[] { 1, 2, 3, 4 }), + new TiffIfdEntry(TiffTags.ImageLength, TiffType.Long, 1, new byte[] { 5, 6, 7, 8 }), + new TiffIfdEntry(TiffTags.Compression, TiffType.Long, 1, new byte[] { 9, 10, 11, 12 }) + }; + + using (TiffWriter writer = new TiffWriter(stream)) + { + long nextIfdMarker = encoder.WriteIfd(writer, entries); + } + + Assert.Equal(2 + 12 * 3 + 4, stream.Length); + } + + [Fact] + public void WriteIfd_WritesNumberOfEntries() + { + MemoryStream stream = new MemoryStream(); + TiffEncoderCore encoder = new TiffEncoderCore(null); + + List entries = new List() + { + new TiffIfdEntry(TiffTags.ImageWidth, TiffType.Long, 1, new byte[] { 1, 2, 3, 4 }), + new TiffIfdEntry(TiffTags.ImageLength, TiffType.Long, 1, new byte[] { 5, 6, 7, 8 }), + new TiffIfdEntry(TiffTags.Compression, TiffType.Long, 1, new byte[] { 9, 10, 11, 12 }) + }; + + using (TiffWriter writer = new TiffWriter(stream)) + { + long nextIfdMarker = encoder.WriteIfd(writer, entries); + } + + var ifdEntryBytes = stream.ToArray().Take(2).ToArray(); + Assert.Equal(new byte[] { 3, 0 }, ifdEntryBytes); + } + + [Fact] + public void WriteIfd_ReturnsNextIfdMarker() + { + MemoryStream stream = new MemoryStream(); + TiffEncoderCore encoder = new TiffEncoderCore(null); + + List entries = new List() + { + new TiffIfdEntry(TiffTags.ImageWidth, TiffType.Long, 1, new byte[] { 1, 2, 3, 4 }), + new TiffIfdEntry(TiffTags.ImageLength, TiffType.Long, 1, new byte[] { 5, 6, 7, 8 }), + new TiffIfdEntry(TiffTags.Compression, TiffType.Long, 1, new byte[] { 9, 10, 11, 12 }) + }; + + using (TiffWriter writer = new TiffWriter(stream)) + { + long nextIfdMarker = encoder.WriteIfd(writer, entries); + Assert.Equal(2 + 12 * 3, nextIfdMarker); + } + } + + [Fact] + public void WriteIfd_WritesTagIdForEachEntry() + { + MemoryStream stream = new MemoryStream(); + TiffEncoderCore encoder = new TiffEncoderCore(null); + + List entries = new List() + { + new TiffIfdEntry(10, TiffType.Long, 1, new byte[] { 1, 2, 3, 4 }), + new TiffIfdEntry(20, TiffType.Long, 1, new byte[] { 5, 6, 7, 8 }), + new TiffIfdEntry(30, TiffType.Long, 1, new byte[] { 9, 10, 11, 12 }) + }; + + using (TiffWriter writer = new TiffWriter(stream)) + { + long nextIfdMarker = encoder.WriteIfd(writer, entries); + } + + var ifdEntry1Bytes = stream.ToArray().Skip(2 + 12 * 0).Take(2).ToArray(); + var ifdEntry2Bytes = stream.ToArray().Skip(2 + 12 * 1).Take(2).ToArray(); + var ifdEntry3Bytes = stream.ToArray().Skip(2 + 12 * 2).Take(2).ToArray(); + + Assert.Equal(new byte[] { 10, 0 }, ifdEntry1Bytes); + Assert.Equal(new byte[] { 20, 0 }, ifdEntry2Bytes); + Assert.Equal(new byte[] { 30, 0 }, ifdEntry3Bytes); + } + + [Fact] + public void WriteIfd_WritesTypeForEachEntry() + { + MemoryStream stream = new MemoryStream(); + TiffEncoderCore encoder = new TiffEncoderCore(null); + + List entries = new List() + { + new TiffIfdEntry(TiffTags.ImageWidth, TiffType.Long, 1, new byte[] { 1, 2, 3, 4 }), + new TiffIfdEntry(TiffTags.ImageLength, TiffType.Short, 2, new byte[] { 5, 6, 7, 8 }), + new TiffIfdEntry(TiffTags.Compression, TiffType.Ascii, 4, new byte[] { (byte)'A', (byte)'B', (byte)'C', 0 }) + }; + + using (TiffWriter writer = new TiffWriter(stream)) + { + long nextIfdMarker = encoder.WriteIfd(writer, entries); + } + + var ifdEntry1Bytes = stream.ToArray().Skip(4 + 12 * 0).Take(2).ToArray(); + var ifdEntry2Bytes = stream.ToArray().Skip(4 + 12 * 1).Take(2).ToArray(); + var ifdEntry3Bytes = stream.ToArray().Skip(4 + 12 * 2).Take(2).ToArray(); + + Assert.Equal(new byte[] { 4, 0 }, ifdEntry1Bytes); + Assert.Equal(new byte[] { 3, 0 }, ifdEntry2Bytes); + Assert.Equal(new byte[] { 2, 0 }, ifdEntry3Bytes); + } + + [Fact] + public void WriteIfd_WritesCountForEachEntry() + { + MemoryStream stream = new MemoryStream(); + TiffEncoderCore encoder = new TiffEncoderCore(null); + + List entries = new List() + { + new TiffIfdEntry(TiffTags.ImageWidth, TiffType.Long, 1, new byte[] { 1, 2, 3, 4 }), + new TiffIfdEntry(TiffTags.ImageLength, TiffType.Short, 2, new byte[] { 5, 6, 7, 8 }), + new TiffIfdEntry(TiffTags.Compression, TiffType.Ascii, 4, new byte[] { (byte)'A', (byte)'B', (byte)'C', 0 }) + }; + + using (TiffWriter writer = new TiffWriter(stream)) + { + long nextIfdMarker = encoder.WriteIfd(writer, entries); + } + + var ifdEntry1Bytes = stream.ToArray().Skip(6 + 12 * 0).Take(4).ToArray(); + var ifdEntry2Bytes = stream.ToArray().Skip(6 + 12 * 1).Take(4).ToArray(); + var ifdEntry3Bytes = stream.ToArray().Skip(6 + 12 * 2).Take(4).ToArray(); + + Assert.Equal(new byte[] { 1, 0, 0, 0 }, ifdEntry1Bytes); + Assert.Equal(new byte[] { 2, 0, 0, 0 }, ifdEntry2Bytes); + Assert.Equal(new byte[] { 4, 0, 0, 0 }, ifdEntry3Bytes); + } + + [Fact] + public void WriteIfd_WritesDataInline() + { + MemoryStream stream = new MemoryStream(); + TiffEncoderCore encoder = new TiffEncoderCore(null); + + List entries = new List() + { + new TiffIfdEntry(TiffTags.ImageWidth, TiffType.Long, 1, new byte[] { 1, 2, 3, 4 }), + new TiffIfdEntry(TiffTags.ImageLength, TiffType.Short, 2, new byte[] { 5, 6, 7, 8 }), + new TiffIfdEntry(TiffTags.Compression, TiffType.Ascii, 3, new byte[] { (byte)'A', (byte)'B', 0 }) + }; + + using (TiffWriter writer = new TiffWriter(stream)) + { + long nextIfdMarker = encoder.WriteIfd(writer, entries); + } + + var ifdEntry1Bytes = stream.ToArray().Skip(10 + 12 * 0).Take(4).ToArray(); + var ifdEntry2Bytes = stream.ToArray().Skip(10 + 12 * 1).Take(4).ToArray(); + var ifdEntry3Bytes = stream.ToArray().Skip(10 + 12 * 2).Take(4).ToArray(); + + Assert.Equal(new byte[] { 1, 2, 3, 4 }, ifdEntry1Bytes); + Assert.Equal(new byte[] { 5, 6, 7, 8 }, ifdEntry2Bytes); + Assert.Equal(new byte[] { (byte)'A', (byte)'B', 0, 0 }, ifdEntry3Bytes); + } + + [Fact] + public void WriteIfd_WritesDataByReference() + { + MemoryStream stream = new MemoryStream(); + TiffEncoderCore encoder = new TiffEncoderCore(null); + + List entries = new List() + { + new TiffIfdEntry(TiffTags.ImageWidth, TiffType.Byte, 8, new byte[] { 1, 2, 3, 4, 4, 3, 2, 1 }), + new TiffIfdEntry(TiffTags.ImageLength, TiffType.Short, 4, new byte[] { 5, 6, 7, 8, 9, 10, 11, 12 }), + new TiffIfdEntry(TiffTags.Compression, TiffType.Ascii, 3, new byte[] { (byte)'A', (byte)'B', 0 }) + }; + + using (TiffWriter writer = new TiffWriter(stream)) + { + writer.Write(new byte[] { 1, 2, 3, 4 }); + long nextIfdMarker = encoder.WriteIfd(writer, entries); + } + + var ifdEntry1Bytes = stream.ToArray().Skip(14 + 12 * 0).Take(4).ToArray(); + var ifdEntry1Data = stream.ToArray().Skip(46).Take(8).ToArray(); + var ifdEntry2Bytes = stream.ToArray().Skip(14 + 12 * 1).Take(4).ToArray(); + var ifdEntry2Data = stream.ToArray().Skip(54).Take(8).ToArray(); + var ifdEntry3Bytes = stream.ToArray().Skip(14 + 12 * 2).Take(4).ToArray(); + + Assert.Equal(new byte[] { 46, 0, 0, 0 }, ifdEntry1Bytes); + Assert.Equal(new byte[] { 1, 2, 3, 4, 4, 3, 2, 1 }, ifdEntry1Data); + Assert.Equal(new byte[] { 54, 0, 0, 0 }, ifdEntry2Bytes); + Assert.Equal(new byte[] { 5, 6, 7, 8, 9, 10, 11, 12 }, ifdEntry2Data); + Assert.Equal(new byte[] { (byte)'A', (byte)'B', 0, 0 }, ifdEntry3Bytes); + } + + [Fact] + public void WriteIfd_WritesDataByReferenceOnWordBoundary() + { + MemoryStream stream = new MemoryStream(); + TiffEncoderCore encoder = new TiffEncoderCore(null); + + List entries = new List() + { + new TiffIfdEntry(TiffTags.ImageWidth, TiffType.Byte, 8, new byte[] { 1, 2, 3, 4, 5 }), + new TiffIfdEntry(TiffTags.ImageLength, TiffType.Short, 4, new byte[] { 5, 6, 7, 8, 9, 10, 11, 12 }), + new TiffIfdEntry(TiffTags.Compression, TiffType.Ascii, 3, new byte[] { (byte)'A', (byte)'B', 0 }) + }; + + using (TiffWriter writer = new TiffWriter(stream)) + { + writer.Write(new byte[] { 1, 2, 3, 4 }); + long nextIfdMarker = encoder.WriteIfd(writer, entries); + } + + var ifdEntry1Bytes = stream.ToArray().Skip(14 + 12 * 0).Take(4).ToArray(); + var ifdEntry1Data = stream.ToArray().Skip(46).Take(5).ToArray(); + var ifdEntry2Bytes = stream.ToArray().Skip(14 + 12 * 1).Take(4).ToArray(); + var ifdEntry2Data = stream.ToArray().Skip(52).Take(8).ToArray(); + var ifdEntry3Bytes = stream.ToArray().Skip(14 + 12 * 2).Take(4).ToArray(); + + Assert.Equal(new byte[] { 46, 0, 0, 0 }, ifdEntry1Bytes); + Assert.Equal(new byte[] { 1, 2, 3, 4, 5 }, ifdEntry1Data); + Assert.Equal(new byte[] { 52, 0, 0, 0 }, ifdEntry2Bytes); + Assert.Equal(new byte[] { 5, 6, 7, 8, 9, 10, 11, 12 }, ifdEntry2Data); + Assert.Equal(new byte[] { (byte)'A', (byte)'B', 0, 0 }, ifdEntry3Bytes); + } + + [Fact] + public void WriteIfd_WritesEntriesInCorrectOrder() + { + MemoryStream stream = new MemoryStream(); + TiffEncoderCore encoder = new TiffEncoderCore(null); + + List entries = new List() + { + new TiffIfdEntry(10, TiffType.Long, 1, new byte[] { 1, 2, 3, 4 }), + new TiffIfdEntry(30, TiffType.Long, 1, new byte[] { 5, 6, 7, 8 }), + new TiffIfdEntry(20, TiffType.Long, 1, new byte[] { 9, 10, 11, 12 }) + }; + + using (TiffWriter writer = new TiffWriter(stream)) + { + long nextIfdMarker = encoder.WriteIfd(writer, entries); + } + + var ifdEntry1Bytes = stream.ToArray().Skip(2 + 12 * 0).Take(2).ToArray(); + var ifdEntry2Bytes = stream.ToArray().Skip(2 + 12 * 1).Take(2).ToArray(); + var ifdEntry3Bytes = stream.ToArray().Skip(2 + 12 * 2).Take(2).ToArray(); + + Assert.Equal(new byte[] { 10, 0 }, ifdEntry1Bytes); + Assert.Equal(new byte[] { 20, 0 }, ifdEntry2Bytes); + Assert.Equal(new byte[] { 30, 0 }, ifdEntry3Bytes); + } + + [Fact] + public void WriteIfd_ThrowsException_IfNoEntriesArePresent() + { + MemoryStream stream = new MemoryStream(); + TiffEncoderCore encoder = new TiffEncoderCore(null); + + List entries = new List(); + + using (TiffWriter writer = new TiffWriter(stream)) + { + ArgumentException e = Assert.Throws(() => { encoder.WriteIfd(writer, entries); }); + + Assert.Equal("There must be at least one entry per IFD.\r\nParameter name: entries", e.Message); + Assert.Equal("entries", e.ParamName); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Utils/TiffWriterTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Utils/TiffWriterTests.cs new file mode 100644 index 000000000..31582fb6d --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Utils/TiffWriterTests.cs @@ -0,0 +1,135 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System; + using System.IO; + using Xunit; + + using ImageSharp.Formats.Tiff; + + public class TiffWriterTests + { + [Fact] + public void IsLittleEndian_IsTrueOnWindows() + { + MemoryStream stream = new MemoryStream(); + + using (TiffWriter writer = new TiffWriter(stream)) + { + Assert.True(writer.IsLittleEndian); + } + } + + [Theory] + [InlineData(new byte[] {}, 0)] + [InlineData(new byte[] { 42 }, 1)] + [InlineData(new byte[] { 1, 2, 3, 4, 5 }, 5)] + public void Position_EqualsTheStreamPosition(byte[] data, long expectedResult) + { + MemoryStream stream = new MemoryStream(); + + using (TiffWriter writer = new TiffWriter(stream)) + { + writer.Write(data); + Assert.Equal(writer.Position, expectedResult); + } + } + + [Fact] + public void Write_WritesByte() + { + MemoryStream stream = new MemoryStream(); + + using (TiffWriter writer = new TiffWriter(stream)) + { + writer.Write((byte)42); + } + + Assert.Equal(new byte[] { 42 }, stream.ToArray()); + } + + [Fact] + public void Write_WritesByteArray() + { + MemoryStream stream = new MemoryStream(); + + using (TiffWriter writer = new TiffWriter(stream)) + { + writer.Write(new byte[] { 2, 4, 6, 8 }); + } + + Assert.Equal(new byte[] { 2, 4, 6, 8 }, stream.ToArray()); + } + + [Fact] + public void Write_WritesUInt16() + { + MemoryStream stream = new MemoryStream(); + + using (TiffWriter writer = new TiffWriter(stream)) + { + writer.Write((ushort)1234); + } + + Assert.Equal(new byte[] { 0xD2, 0x04 }, stream.ToArray()); + } + + [Fact] + public void Write_WritesUInt32() + { + MemoryStream stream = new MemoryStream(); + + using (TiffWriter writer = new TiffWriter(stream)) + { + writer.Write((uint)12345678); + } + + Assert.Equal(new byte[] { 0x4E, 0x61, 0xBC, 0x00 }, stream.ToArray()); + } + + [Theory] + [InlineData(new byte[] { }, new byte[] { 0, 0, 0, 0 })] + [InlineData(new byte[] { 2 }, new byte[] { 2, 0, 0, 0 })] + [InlineData(new byte[] { 2, 4 }, new byte[] { 2, 4, 0, 0 })] + [InlineData(new byte[] { 2, 4, 6 }, new byte[] { 2, 4, 6, 0 })] + [InlineData(new byte[] { 2, 4, 6, 8 }, new byte[] { 2, 4, 6, 8 })] + [InlineData(new byte[] { 2, 4, 6, 8, 10, 12 }, new byte[] { 2, 4, 6, 8, 10, 12 })] + public void WritePadded_WritesByteArray(byte[] bytes, byte[] expectedResult) + { + MemoryStream stream = new MemoryStream(); + + using (TiffWriter writer = new TiffWriter(stream)) + { + writer.WritePadded(bytes); + } + + Assert.Equal(expectedResult, stream.ToArray()); + } + + [Fact] + public void WriteMarker_WritesToPlacedPosition() + { + MemoryStream stream = new MemoryStream(); + + using (TiffWriter writer = new TiffWriter(stream)) + { + writer.Write((uint)0x11111111); + long marker = writer.PlaceMarker(); + writer.Write((uint)0x33333333); + + writer.WriteMarker(marker, 0x12345678); + + writer.Write((uint)0x44444444); + } + + Assert.Equal(new byte[] { 0x11, 0x11, 0x11, 0x11, + 0x78, 0x56, 0x34, 0x12, + 0x33, 0x33, 0x33, 0x33, + 0x44, 0x44, 0x44, 0x44 }, stream.ToArray()); + } + } +} \ No newline at end of file From 596a738daaa31eca5aeffa373a31b127740f6a25 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Thu, 22 Jun 2017 12:53:36 +0100 Subject: [PATCH 055/275] Write metadata to a TIFF file --- .../Formats/Tiff/TiffEncoderCore.cs | 98 +++++ .../Tiff/TiffIfd/TiffIfdEntryCreator.cs | 354 +++++++++++++++ .../Formats/Tiff/TiffEncoderMetadataTests.cs | 58 +++ .../Tiff/TiffIfd/TiffIfdEntryCreatorTests.cs | 410 ++++++++++++++++++ .../TestUtilities/Tiff/TiffIfdParser.cs | 77 ++++ 5 files changed, 997 insertions(+) create mode 100644 src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntryCreator.cs create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderMetadataTests.cs create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryCreatorTests.cs create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffIfdParser.cs diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index d32e34c43..5f1148ade 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -39,6 +39,16 @@ namespace ImageSharp.Formats this.options = options ?? new TiffEncoderOptions(); } + /// + /// Gets or sets the photometric interpretation implementation to use when encoding the image. + /// + public TiffColorType ColorType { get; set; } + + /// + /// Gets or sets the compression implementation to use when encoding the image. + /// + public TiffCompressionType CompressionType { get; set; } + /// /// Encodes the image to the specified stream from the . /// @@ -138,6 +148,94 @@ namespace ImageSharp.Formats /// The marker to write the next IFD offset (if present). public long WriteImage(TiffWriter writer, Image image, long ifdOffset) where TPixel : struct, IPixel + { + List ifdEntries = new List(); + + this.AddImageFormat(image, ifdEntries); + this.AddMetadata(image, ifdEntries); + + writer.WriteMarker(ifdOffset, (uint)writer.Position); + long nextIfdMarker = this.WriteIfd(writer, ifdEntries); + + return nextIfdMarker; + } + + /// + /// Adds image metadata to the specified IFD. + /// + /// The pixel format. + /// The to encode from. + /// The metadata entries to add to the IFD. + public void AddMetadata(Image image, List ifdEntries) + where TPixel : struct, IPixel + { + ifdEntries.AddUnsignedRational(TiffTags.XResolution, new Rational(image.MetaData.HorizontalResolution)); + ifdEntries.AddUnsignedRational(TiffTags.YResolution, new Rational(image.MetaData.VerticalResolution)); + ifdEntries.AddUnsignedShort(TiffTags.ResolutionUnit, (uint)TiffResolutionUnit.Inch); + + foreach (ImageProperty metadata in image.MetaData.Properties) + { + switch (metadata.Name) + { + case TiffMetadataNames.Artist: + { + ifdEntries.AddAscii(TiffTags.Artist, metadata.Value); + break; + } + + case TiffMetadataNames.Copyright: + { + ifdEntries.AddAscii(TiffTags.Copyright, metadata.Value); + break; + } + + case TiffMetadataNames.DateTime: + { + ifdEntries.AddAscii(TiffTags.DateTime, metadata.Value); + break; + } + + case TiffMetadataNames.HostComputer: + { + ifdEntries.AddAscii(TiffTags.HostComputer, metadata.Value); + break; + } + + case TiffMetadataNames.ImageDescription: + { + ifdEntries.AddAscii(TiffTags.ImageDescription, metadata.Value); + break; + } + + case TiffMetadataNames.Make: + { + ifdEntries.AddAscii(TiffTags.Make, metadata.Value); + break; + } + + case TiffMetadataNames.Model: + { + ifdEntries.AddAscii(TiffTags.Model, metadata.Value); + break; + } + + case TiffMetadataNames.Software: + { + ifdEntries.AddAscii(TiffTags.Software, metadata.Value); + break; + } + } + } + } + + /// + /// Adds image format information to the specified IFD. + /// + /// The pixel format. + /// The to encode from. + /// The image format entries to add to the IFD. + public void AddImageFormat(Image image, List ifdEntries) + where TPixel : struct, IPixel { throw new NotImplementedException(); } diff --git a/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntryCreator.cs b/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntryCreator.cs new file mode 100644 index 000000000..c30ce5c8a --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntryCreator.cs @@ -0,0 +1,354 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats.Tiff +{ + using System; + using System.Collections.Generic; + using System.Text; + + /// + /// Utility class for generating TIFF IFD entries. + /// + internal static class TiffIfdEntryCreator + { + /// + /// Adds a new of type 'Byte' from a unsigned integer. + /// + /// The list of to add the new entry to. + /// The tag for the resulting entry. + /// The value for the resulting entry. + public static void AddUnsignedByte(this List entries, ushort tag, uint value) + { + TiffIfdEntryCreator.AddUnsignedByte(entries, tag, new[] { value }); + } + + /// + /// Adds a new of type 'Byte' from an array of unsigned integers. + /// + /// The list of to add the new entry to. + /// The tag for the resulting entry. + /// The value for the resulting entry. + public static void AddUnsignedByte(this List entries, ushort tag, uint[] value) + { + byte[] bytes = new byte[value.Length]; + + for (int i = 0; i < value.Length; i++) + { + bytes[i] = (byte)value[i]; + } + + entries.Add(new TiffIfdEntry(tag, TiffType.Byte, (uint)value.Length, bytes)); + } + + /// + /// Adds a new of type 'Short' from a unsigned integer. + /// + /// The list of to add the new entry to. + /// The tag for the resulting entry. + /// The value for the resulting entry. + public static void AddUnsignedShort(this List entries, ushort tag, uint value) + { + TiffIfdEntryCreator.AddUnsignedShort(entries, tag, new[] { value }); + } + + /// + /// Adds a new of type 'Short' from an array of unsigned integers. + /// + /// The list of to add the new entry to. + /// The tag for the resulting entry. + /// The value for the resulting entry. + public static void AddUnsignedShort(this List entries, ushort tag, uint[] value) + { + byte[] bytes = new byte[value.Length * TiffConstants.SizeOfShort]; + + for (int i = 0; i < value.Length; i++) + { + ToBytes((ushort)value[i], bytes, i * TiffConstants.SizeOfShort); + } + + entries.Add(new TiffIfdEntry(tag, TiffType.Short, (uint)value.Length, bytes)); + } + + /// + /// Adds a new of type 'Long' from a unsigned integer. + /// + /// The list of to add the new entry to. + /// The tag for the resulting entry. + /// The value for the resulting entry. + public static void AddUnsignedLong(this List entries, ushort tag, uint value) + { + TiffIfdEntryCreator.AddUnsignedLong(entries, tag, new[] { value }); + } + + /// + /// Adds a new of type 'Long' from an array of unsigned integers. + /// + /// The list of to add the new entry to. + /// The tag for the resulting entry. + /// The value for the resulting entry. + public static void AddUnsignedLong(this List entries, ushort tag, uint[] value) + { + byte[] bytes = new byte[value.Length * TiffConstants.SizeOfLong]; + + for (int i = 0; i < value.Length; i++) + { + ToBytes(value[i], bytes, i * TiffConstants.SizeOfLong); + } + + entries.Add(new TiffIfdEntry(tag, TiffType.Long, (uint)value.Length, bytes)); + } + + /// + /// Adds a new of type 'SByte' from a signed integer. + /// + /// The list of to add the new entry to. + /// The tag for the resulting entry. + /// The value for the resulting entry. + public static void AddSignedByte(this List entries, ushort tag, int value) + { + TiffIfdEntryCreator.AddSignedByte(entries, tag, new[] { value }); + } + + /// + /// Adds a new of type 'SByte' from an array of signed integers. + /// + /// The list of to add the new entry to. + /// The tag for the resulting entry. + /// The value for the resulting entry. + public static void AddSignedByte(this List entries, ushort tag, int[] value) + { + byte[] bytes = new byte[value.Length]; + + for (int i = 0; i < value.Length; i++) + { + bytes[i] = (byte)((sbyte)value[i]); + } + + entries.Add(new TiffIfdEntry(tag, TiffType.SByte, (uint)value.Length, bytes)); + } + + /// + /// Adds a new of type 'SShort' from a signed integer. + /// + /// The list of to add the new entry to. + /// The tag for the resulting entry. + /// The value for the resulting entry. + public static void AddSignedShort(this List entries, ushort tag, int value) + { + TiffIfdEntryCreator.AddSignedShort(entries, tag, new[] { value }); + } + + /// + /// Adds a new of type 'SShort' from an array of signed integers. + /// + /// The list of to add the new entry to. + /// The tag for the resulting entry. + /// The value for the resulting entry. + public static void AddSignedShort(this List entries, ushort tag, int[] value) + { + byte[] bytes = new byte[value.Length * TiffConstants.SizeOfShort]; + + for (int i = 0; i < value.Length; i++) + { + ToBytes((short)value[i], bytes, i * TiffConstants.SizeOfShort); + } + + entries.Add(new TiffIfdEntry(tag, TiffType.SShort, (uint)value.Length, bytes)); + } + + /// + /// Adds a new of type 'SLong' from a signed integer. + /// + /// The list of to add the new entry to. + /// The tag for the resulting entry. + /// The value for the resulting entry. + public static void AddSignedLong(this List entries, ushort tag, int value) + { + TiffIfdEntryCreator.AddSignedLong(entries, tag, new[] { value }); + } + + /// + /// Adds a new of type 'SLong' from an array of signed integers. + /// + /// The list of to add the new entry to. + /// The tag for the resulting entry. + /// The value for the resulting entry. + public static void AddSignedLong(this List entries, ushort tag, int[] value) + { + byte[] bytes = new byte[value.Length * TiffConstants.SizeOfLong]; + + for (int i = 0; i < value.Length; i++) + { + ToBytes(value[i], bytes, i * TiffConstants.SizeOfLong); + } + + entries.Add(new TiffIfdEntry(tag, TiffType.SLong, (uint)value.Length, bytes)); + } + + /// + /// Adds a new of type 'Ascii' from a string. + /// + /// The list of to add the new entry to. + /// The tag for the resulting entry. + /// The value for the resulting entry. + public static void AddAscii(this List entries, ushort tag, string value) + { + byte[] bytes = Encoding.UTF8.GetBytes(value + "\0"); + + entries.Add(new TiffIfdEntry(tag, TiffType.Ascii, (uint)bytes.Length, bytes)); + } + + /// + /// Adds a new of type 'Rational' from a . + /// + /// The list of to add the new entry to. + /// The tag for the resulting entry. + /// The value for the resulting entry. + public static void AddUnsignedRational(this List entries, ushort tag, Rational value) + { + TiffIfdEntryCreator.AddUnsignedRational(entries, tag, new[] { value }); + } + + /// + /// Adds a new of type 'Rational' from an array of values. + /// + /// The list of to add the new entry to. + /// The tag for the resulting entry. + /// The value for the resulting entry. + public static void AddUnsignedRational(this List entries, ushort tag, Rational[] value) + { + byte[] bytes = new byte[value.Length * TiffConstants.SizeOfRational]; + + for (int i = 0; i < value.Length; i++) + { + int offset = i * TiffConstants.SizeOfRational; + ToBytes(value[i].Numerator, bytes, offset); + ToBytes(value[i].Denominator, bytes, offset + TiffConstants.SizeOfLong); + } + + entries.Add(new TiffIfdEntry(tag, TiffType.Rational, (uint)value.Length, bytes)); + } + + /// + /// Adds a new of type 'SRational' from a . + /// + /// The list of to add the new entry to. + /// The tag for the resulting entry. + /// The value for the resulting entry. + public static void AddSignedRational(this List entries, ushort tag, SignedRational value) + { + TiffIfdEntryCreator.AddSignedRational(entries, tag, new[] { value }); + } + + /// + /// Adds a new of type 'SRational' from an array of values. + /// + /// The list of to add the new entry to. + /// The tag for the resulting entry. + /// The value for the resulting entry. + public static void AddSignedRational(this List entries, ushort tag, SignedRational[] value) + { + byte[] bytes = new byte[value.Length * TiffConstants.SizeOfRational]; + + for (int i = 0; i < value.Length; i++) + { + int offset = i * TiffConstants.SizeOfRational; + ToBytes(value[i].Numerator, bytes, offset); + ToBytes(value[i].Denominator, bytes, offset + TiffConstants.SizeOfLong); + } + + entries.Add(new TiffIfdEntry(tag, TiffType.SRational, (uint)value.Length, bytes)); + } + + /// + /// Adds a new of type 'Float' from a floating-point value. + /// + /// The list of to add the new entry to. + /// The tag for the resulting entry. + /// The value for the resulting entry. + public static void AddFloat(this List entries, ushort tag, float value) + { + TiffIfdEntryCreator.AddFloat(entries, tag, new[] { value }); + } + + /// + /// Adds a new of type 'Float' from an array of floating-point values. + /// + /// The list of to add the new entry to. + /// The tag for the resulting entry. + /// The value for the resulting entry. + public static void AddFloat(this List entries, ushort tag, float[] value) + { + byte[] bytes = new byte[value.Length * TiffConstants.SizeOfFloat]; + + for (int i = 0; i < value.Length; i++) + { + byte[] itemBytes = BitConverter.GetBytes(value[i]); + Array.Copy(itemBytes, 0, bytes, i * TiffConstants.SizeOfFloat, TiffConstants.SizeOfFloat); + } + + entries.Add(new TiffIfdEntry(tag, TiffType.Float, (uint)value.Length, bytes)); + } + + /// + /// Adds a new of type 'Double' from a floating-point value. + /// + /// The list of to add the new entry to. + /// The tag for the resulting entry. + /// The value for the resulting entry. + public static void AddDouble(this List entries, ushort tag, double value) + { + TiffIfdEntryCreator.AddDouble(entries, tag, new[] { value }); + } + + /// + /// Adds a new of type 'Double' from an array of floating-point values. + /// + /// The list of to add the new entry to. + /// The tag for the resulting entry. + /// The value for the resulting entry. + public static void AddDouble(this List entries, ushort tag, double[] value) + { + byte[] bytes = new byte[value.Length * TiffConstants.SizeOfDouble]; + + for (int i = 0; i < value.Length; i++) + { + byte[] itemBytes = BitConverter.GetBytes(value[i]); + Array.Copy(itemBytes, 0, bytes, i * TiffConstants.SizeOfDouble, TiffConstants.SizeOfDouble); + } + + entries.Add(new TiffIfdEntry(tag, TiffType.Double, (uint)value.Length, bytes)); + } + + private static void ToBytes(ushort value, byte[] bytes, int offset) + { + bytes[offset + 0] = (byte)value; + bytes[offset + 1] = (byte)(value >> 8); + } + + private static void ToBytes(uint value, byte[] bytes, int offset) + { + bytes[offset + 0] = (byte)value; + bytes[offset + 1] = (byte)(value >> 8); + bytes[offset + 2] = (byte)(value >> 16); + bytes[offset + 3] = (byte)(value >> 24); + } + + private static void ToBytes(short value, byte[] bytes, int offset) + { + bytes[offset + 0] = (byte)value; + bytes[offset + 1] = (byte)(value >> 8); + } + + private static void ToBytes(int value, byte[] bytes, int offset) + { + bytes[offset + 0] = (byte)value; + bytes[offset + 1] = (byte)(value >> 8); + bytes[offset + 2] = (byte)(value >> 16); + bytes[offset + 3] = (byte)(value >> 24); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderMetadataTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderMetadataTests.cs new file mode 100644 index 000000000..4dec7630c --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderMetadataTests.cs @@ -0,0 +1,58 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System; + using System.IO; + using System.Linq; + using Xunit; + + using ImageSharp.Formats; + using ImageSharp.Formats.Tiff; + using System.Collections.Generic; + + public class TiffEncoderMetadataTests + { + public static object[][] BaselineMetadataValues = new[] { new object[] { TiffTags.Artist, TiffMetadataNames.Artist, "My Artist Name" }, + new object[] { TiffTags.Copyright, TiffMetadataNames.Copyright, "My Copyright Statement" }, + new object[] { TiffTags.DateTime, TiffMetadataNames.DateTime, "My DateTime Value" }, + new object[] { TiffTags.HostComputer, TiffMetadataNames.HostComputer, "My Host Computer Name" }, + new object[] { TiffTags.ImageDescription, TiffMetadataNames.ImageDescription, "My Image Description" }, + new object[] { TiffTags.Make, TiffMetadataNames.Make, "My Camera Make" }, + new object[] { TiffTags.Model, TiffMetadataNames.Model, "My Camera Model" }, + new object[] { TiffTags.Software, TiffMetadataNames.Software, "My Imaging Software" }}; + + [Fact] + public void AddMetadata_SetsImageResolution() + { + Image image = new Image(100, 100); + image.MetaData.HorizontalResolution = 40.0; + image.MetaData.VerticalResolution = 50.5; + TiffEncoderCore encoder = new TiffEncoderCore(null); + + List ifdEntries = new List(); + encoder.AddMetadata(image, ifdEntries); + + Assert.Equal(new Rational(40, 1), ifdEntries.GetUnsignedRational(TiffTags.XResolution)); + Assert.Equal(new Rational(101, 2), ifdEntries.GetUnsignedRational(TiffTags.YResolution)); + Assert.Equal(TiffResolutionUnit.Inch, (TiffResolutionUnit?)ifdEntries.GetInteger(TiffTags.ResolutionUnit)); + } + + [Theory] + [MemberData(nameof(BaselineMetadataValues))] + public void AddMetadata_SetsAsciiMetadata(ushort tag, string metadataName, string metadataValue) + { + Image image = new Image(100, 100); + image.MetaData.Properties.Add(new ImageProperty(metadataName, metadataValue)); + TiffEncoderCore encoder = new TiffEncoderCore(null); + + List ifdEntries = new List(); + encoder.AddMetadata(image, ifdEntries); + + Assert.Equal(metadataValue + "\0", ifdEntries.GetAscii(tag)); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryCreatorTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryCreatorTests.cs new file mode 100644 index 000000000..036ab4621 --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryCreatorTests.cs @@ -0,0 +1,410 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using Xunit; + + using ImageSharp.Formats; + using ImageSharp.Formats.Tiff; + + public class TiffIfdEntryCreatorTests + { + [Theory] + [InlineDataAttribute(new byte[] { 0 }, 0)] + [InlineDataAttribute(new byte[] { 1 }, 1)] + [InlineDataAttribute(new byte[] { 255 }, 255)] + public void AddUnsignedByte_AddsSingleValue(byte[] bytes, uint value) + { + var entries = new List(); + + entries.AddUnsignedByte(TiffTags.ImageWidth, value); + + var entry = entries[0]; + Assert.Equal(TiffTags.ImageWidth, entry.Tag); + Assert.Equal(TiffType.Byte, entry.Type); + Assert.Equal(1u, entry.Count); + Assert.Equal(bytes, entry.Value); + } + + [Theory] + [InlineDataAttribute(new byte[] { 0 }, new uint[] { 0 })] + [InlineDataAttribute(new byte[] { 0, 1, 2 }, new uint[] { 0, 1, 2 })] + [InlineDataAttribute(new byte[] { 0, 1, 2, 3, 4, 5, 6 }, new uint[] { 0, 1, 2, 3, 4, 5, 6 })] + public void AddUnsignedByte_AddsArray(byte[] bytes, uint[] value) + { + var entries = new List(); + + entries.AddUnsignedByte(TiffTags.ImageWidth, value); + + var entry = entries[0]; + Assert.Equal(TiffTags.ImageWidth, entry.Tag); + Assert.Equal(TiffType.Byte, entry.Type); + Assert.Equal((uint)value.Length, entry.Count); + Assert.Equal(bytes, entry.Value); + } + + [Theory] + [InlineDataAttribute(new byte[] { 0, 0 }, 0)] + [InlineDataAttribute(new byte[] { 1, 0 }, 1)] + [InlineDataAttribute(new byte[] { 0, 1 }, 256)] + [InlineDataAttribute(new byte[] { 2, 1 }, 258)] + [InlineDataAttribute(new byte[] { 255, 255 }, UInt16.MaxValue)] + public void AddUnsignedShort_AddsSingleValue(byte[] bytes, uint value) + { + var entries = new List(); + + entries.AddUnsignedShort(TiffTags.ImageWidth, value); + + var entry = entries[0]; + Assert.Equal(TiffTags.ImageWidth, entry.Tag); + Assert.Equal(TiffType.Short, entry.Type); + Assert.Equal(1u, entry.Count); + Assert.Equal(bytes, entry.Value); + } + + [Theory] + [InlineDataAttribute(new byte[] { 1, 0 }, new uint[] { 1 })] + [InlineDataAttribute(new byte[] { 1, 0, 3, 2 }, new uint[] { 1, 515 })] + [InlineDataAttribute(new byte[] { 1, 0, 3, 2, 5, 4 }, new uint[] { 1, 515, 1029 })] + public void AddUnsignedShort_AddsArray(byte[] bytes, uint[] value) + { + var entries = new List(); + + entries.AddUnsignedShort(TiffTags.ImageWidth, value); + + var entry = entries[0]; + Assert.Equal(TiffTags.ImageWidth, entry.Tag); + Assert.Equal(TiffType.Short, entry.Type); + Assert.Equal((uint)value.Length, entry.Count); + Assert.Equal(bytes, entry.Value); + } + + [Theory] + [InlineDataAttribute(new byte[] { 0, 0, 0, 0 }, 0)] + [InlineDataAttribute(new byte[] { 1, 0, 0, 0 }, 1)] + [InlineDataAttribute(new byte[] { 0, 1, 0, 0 }, 256)] + [InlineDataAttribute(new byte[] { 0, 0, 1, 0 }, 256 * 256)] + [InlineDataAttribute(new byte[] { 0, 0, 0, 1 }, 256 * 256 * 256)] + [InlineDataAttribute(new byte[] { 1, 2, 3, 4 }, 67305985)] + [InlineDataAttribute(new byte[] { 255, 255, 255, 255 }, UInt32.MaxValue)] + public void AddUnsignedLong_AddsSingleValue(byte[] bytes, uint value) + { + var entries = new List(); + + entries.AddUnsignedLong(TiffTags.ImageWidth, value); + + var entry = entries[0]; + Assert.Equal(TiffTags.ImageWidth, entry.Tag); + Assert.Equal(TiffType.Long, entry.Type); + Assert.Equal(1u, entry.Count); + Assert.Equal(bytes, entry.Value); + } + + [Theory] + [InlineDataAttribute(new byte[] { 4, 3, 2, 1 }, new uint[] { 0x01020304 })] + [InlineDataAttribute(new byte[] { 4, 3, 2, 1, 6, 5, 4, 3 }, new uint[] { 0x01020304, 0x03040506 })] + public void AddUnsignedLong_AddsArray(byte[] bytes, uint[] value) + { + var entries = new List(); + + entries.AddUnsignedLong(TiffTags.ImageWidth, value); + + var entry = entries[0]; + Assert.Equal(TiffTags.ImageWidth, entry.Tag); + Assert.Equal(TiffType.Long, entry.Type); + Assert.Equal((uint)value.Length, entry.Count); + Assert.Equal(bytes, entry.Value); + } + + [Theory] + [InlineDataAttribute(new byte[] { 0 }, 0)] + [InlineDataAttribute(new byte[] { 1 }, 1)] + [InlineDataAttribute(new byte[] { 255 }, -1)] + public void AddSignedByte_AddsSingleValue(byte[] bytes, int value) + { + var entries = new List(); + + entries.AddSignedByte(TiffTags.ImageWidth, value); + + var entry = entries[0]; + Assert.Equal(TiffTags.ImageWidth, entry.Tag); + Assert.Equal(TiffType.SByte, entry.Type); + Assert.Equal(1u, entry.Count); + Assert.Equal(bytes, entry.Value); + } + + [Theory] + [InlineDataAttribute(new byte[] { 0 }, new int[] { 0 })] + [InlineDataAttribute(new byte[] { 0, 255, 2 }, new int[] { 0, -1, 2 })] + [InlineDataAttribute(new byte[] { 0, 255, 2, 3, 4, 5, 6 }, new int[] { 0, -1, 2, 3, 4, 5, 6 })] + public void AddSignedByte_AddsArray(byte[] bytes, int[] value) + { + var entries = new List(); + + entries.AddSignedByte(TiffTags.ImageWidth, value); + + var entry = entries[0]; + Assert.Equal(TiffTags.ImageWidth, entry.Tag); + Assert.Equal(TiffType.SByte, entry.Type); + Assert.Equal((uint)value.Length, entry.Count); + Assert.Equal(bytes, entry.Value); + } + + [Theory] + [InlineDataAttribute(new byte[] { 0, 0 }, 0)] + [InlineDataAttribute(new byte[] { 1, 0 }, 1)] + [InlineDataAttribute(new byte[] { 0, 1 }, 256)] + [InlineDataAttribute(new byte[] { 2, 1 }, 258)] + [InlineDataAttribute(new byte[] { 255, 255 }, -1)] + public void AddSignedShort_AddsSingleValue(byte[] bytes, int value) + { + var entries = new List(); + + entries.AddSignedShort(TiffTags.ImageWidth, value); + + var entry = entries[0]; + Assert.Equal(TiffTags.ImageWidth, entry.Tag); + Assert.Equal(TiffType.SShort, entry.Type); + Assert.Equal(1u, entry.Count); + Assert.Equal(bytes, entry.Value); + } + + [Theory] + [InlineDataAttribute(new byte[] { 1, 0 }, new int[] { 1 })] + [InlineDataAttribute(new byte[] { 1, 0, 255, 255 }, new int[] { 1, -1 })] + [InlineDataAttribute(new byte[] { 1, 0, 255, 255, 5, 4 }, new int[] { 1, -1, 1029 })] + public void AddSignedShort_AddsArray(byte[] bytes, int[] value) + { + var entries = new List(); + + entries.AddSignedShort(TiffTags.ImageWidth, value); + + var entry = entries[0]; + Assert.Equal(TiffTags.ImageWidth, entry.Tag); + Assert.Equal(TiffType.SShort, entry.Type); + Assert.Equal((uint)value.Length, entry.Count); + Assert.Equal(bytes, entry.Value); + } + + [Theory] + [InlineDataAttribute(new byte[] { 0, 0, 0, 0 }, 0)] + [InlineDataAttribute(new byte[] { 1, 0, 0, 0 }, 1)] + [InlineDataAttribute(new byte[] { 0, 1, 0, 0 }, 256)] + [InlineDataAttribute(new byte[] { 0, 0, 1, 0 }, 256 * 256)] + [InlineDataAttribute(new byte[] { 0, 0, 0, 1 }, 256 * 256 * 256)] + [InlineDataAttribute(new byte[] { 1, 2, 3, 4 }, 67305985)] + [InlineDataAttribute(new byte[] { 255, 255, 255, 255 }, -1)] + public void AddSignedLong_AddsSingleValue(byte[] bytes, int value) + { + var entries = new List(); + + entries.AddSignedLong(TiffTags.ImageWidth, value); + + var entry = entries[0]; + Assert.Equal(TiffTags.ImageWidth, entry.Tag); + Assert.Equal(TiffType.SLong, entry.Type); + Assert.Equal(1u, entry.Count); + Assert.Equal(bytes, entry.Value); + } + + [Theory] + [InlineDataAttribute(new byte[] { 4, 3, 2, 1 }, new int[] { 0x01020304 })] + [InlineDataAttribute(new byte[] { 4, 3, 2, 1, 255, 255, 255, 255 }, new int[] { 0x01020304, -1 })] + public void AddSignedLong_AddsArray(byte[] bytes, int[] value) + { + var entries = new List(); + + entries.AddSignedLong(TiffTags.ImageWidth, value); + + var entry = entries[0]; + Assert.Equal(TiffTags.ImageWidth, entry.Tag); + Assert.Equal(TiffType.SLong, entry.Type); + Assert.Equal((uint)value.Length, entry.Count); + Assert.Equal(bytes, entry.Value); + } + + [Theory] + [InlineDataAttribute(new byte[] { 0 }, "")] + [InlineDataAttribute(new byte[] { (byte)'A', (byte)'B', (byte)'C', 0 }, "ABC")] + [InlineDataAttribute(new byte[] { (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', 0 }, "ABCDEF")] + [InlineDataAttribute(new byte[] { (byte)'A', (byte)'B', (byte)'C', (byte)'D', 0, (byte)'E', (byte)'F', (byte)'G', (byte)'H', 0 }, "ABCD\0EFGH")] + public void AddAscii_AddsEntry(byte[] bytes, string value) + { + var entries = new List(); + + entries.AddAscii(TiffTags.ImageWidth, value); + + var entry = entries[0]; + Assert.Equal(TiffTags.ImageWidth, entry.Tag); + Assert.Equal(TiffType.Ascii, entry.Type); + Assert.Equal((uint)bytes.Length, entry.Count); + Assert.Equal(bytes, entry.Value); + } + + [Theory] + [InlineDataAttribute(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, 0)] + [InlineDataAttribute(new byte[] { 2, 0, 0, 0, 1, 0, 0, 0 }, 2, 1)] + [InlineDataAttribute(new byte[] { 1, 0, 0, 0, 2, 0, 0, 0 }, 1, 2)] + public void AddUnsignedRational_AddsSingleValue(byte[] bytes, uint numerator, uint denominator) + { + var entries = new List(); + + entries.AddUnsignedRational(TiffTags.ImageWidth, new Rational(numerator, denominator)); + + var entry = entries[0]; + Assert.Equal(TiffTags.ImageWidth, entry.Tag); + Assert.Equal(TiffType.Rational, entry.Type); + Assert.Equal(1u, entry.Count); + Assert.Equal(bytes, entry.Value); + } + + [Theory] + [InlineDataAttribute(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, new uint[] { 0 }, new uint[] { 0 })] + [InlineDataAttribute(new byte[] { 1, 0, 0, 0, 2, 0, 0, 0 }, new uint[] { 1 }, new uint[] { 2 })] + [InlineDataAttribute(new byte[] { 1, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0 }, new uint[] { 1, 2 }, new uint[] { 2, 3 })] + public void AddUnsignedRational_AddsArray(byte[] bytes, uint[] numerators, uint[] denominators) + { + var entries = new List(); + Rational[] value = Enumerable.Range(0, numerators.Length).Select(i => new Rational(numerators[i], denominators[i])).ToArray(); + + entries.AddUnsignedRational(TiffTags.ImageWidth, value); + + var entry = entries[0]; + Assert.Equal(TiffTags.ImageWidth, entry.Tag); + Assert.Equal(TiffType.Rational, entry.Type); + Assert.Equal((uint)numerators.Length, entry.Count); + Assert.Equal(bytes, entry.Value); + } + + [Theory] + [InlineDataAttribute(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, 0)] + [InlineDataAttribute(new byte[] { 2, 0, 0, 0, 1, 0, 0, 0 }, 2, 1)] + [InlineDataAttribute(new byte[] { 1, 0, 0, 0, 2, 0, 0, 0 }, 1, 2)] + [InlineDataAttribute(new byte[] { 255, 255, 255, 255, 2, 0, 0, 0 }, -1, 2)] + public void AddSignedRational_AddsSingleValue(byte[] bytes, int numerator, int denominator) + { + var entries = new List(); + + entries.AddSignedRational(TiffTags.ImageWidth, new SignedRational(numerator, denominator)); + + var entry = entries[0]; + Assert.Equal(TiffTags.ImageWidth, entry.Tag); + Assert.Equal(TiffType.SRational, entry.Type); + Assert.Equal(1u, entry.Count); + Assert.Equal(bytes, entry.Value); + } + + [Theory] + [InlineDataAttribute(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, new int[] { 0 }, new int[] { 0 })] + [InlineDataAttribute(new byte[] { 2, 0, 0, 0, 1, 0, 0, 0 }, new int[] { 2 }, new int[] { 1 })] + [InlineDataAttribute(new byte[] { 1, 0, 0, 0, 2, 0, 0, 0 }, new int[] { 1 }, new int[] { 2 })] + [InlineDataAttribute(new byte[] { 255, 255, 255, 255, 2, 0, 0, 0 }, new int[] { -1 }, new int[] { 2 })] + [InlineDataAttribute(new byte[] { 255, 255, 255, 255, 2, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0 }, new int[] { -1, 2 }, new int[] { 2, 3 })] + public void AddSignedRational_AddsArray(byte[] bytes, int[] numerators, int[] denominators) + { + var entries = new List(); + SignedRational[] value = Enumerable.Range(0, numerators.Length).Select(i => new SignedRational(numerators[i], denominators[i])).ToArray(); + + entries.AddSignedRational(TiffTags.ImageWidth, value); + + var entry = entries[0]; + Assert.Equal(TiffTags.ImageWidth, entry.Tag); + Assert.Equal(TiffType.SRational, entry.Type); + Assert.Equal((uint)numerators.Length, entry.Count); + Assert.Equal(bytes, entry.Value); + } + + [Theory] + [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0x00 }, 0.0F)] + [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x80, 0x3F }, 1.0F)] + [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0xC0 }, -2.0F)] + [InlineDataAttribute(new byte[] { 0xFF, 0xFF, 0x7F, 0x7F }, float.MaxValue)] + [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x80, 0x7F }, float.PositiveInfinity)] + [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x80, 0xFF }, float.NegativeInfinity)] + public void AddFloat_AddsSingleValue(byte[] bytes, float value) + { + var entries = new List(); + + entries.AddFloat(TiffTags.ImageWidth, value); + + var entry = entries[0]; + Assert.Equal(TiffTags.ImageWidth, entry.Tag); + Assert.Equal(TiffType.Float, entry.Type); + Assert.Equal(1u, entry.Count); + Assert.Equal(bytes, entry.Value); + } + + [Theory] + [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0x00 }, new float[] { 0.0F })] + [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x80, 0x3F }, new float[] { 1.0F })] + [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0xC0 }, new float[] { -2.0F })] + [InlineDataAttribute(new byte[] { 0xFF, 0xFF, 0x7F, 0x7F }, new float[] { float.MaxValue })] + [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x80, 0x7F }, new float[] { float.PositiveInfinity })] + [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x80, 0xFF }, new float[] { float.NegativeInfinity })] + [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0xC0 }, new float[] { 0.0F, 1.0F, -2.0F })] + public void AddFloat_AddsArray(byte[] bytes, float[] value) + { + var entries = new List(); + + entries.AddFloat(TiffTags.ImageWidth, value); + + var entry = entries[0]; + Assert.Equal(TiffTags.ImageWidth, entry.Tag); + Assert.Equal(TiffType.Float, entry.Type); + Assert.Equal((uint)value.Length, entry.Count); + Assert.Equal(bytes, entry.Value); + } + + [Theory] + [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0.0)] + [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F }, 1.0)] + [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40 }, 2.0)] + [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0 }, -2.0)] + [InlineDataAttribute(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0x7F }, double.MaxValue)] + [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x7F }, double.PositiveInfinity)] + [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF }, double.NegativeInfinity)] + [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF }, double.NaN)] + public void AddDouble_AddsSingleValue(byte[] bytes, double value) + { + var entries = new List(); + + entries.AddDouble(TiffTags.ImageWidth, value); + + var entry = entries[0]; + Assert.Equal(TiffTags.ImageWidth, entry.Tag); + Assert.Equal(TiffType.Double, entry.Type); + Assert.Equal(1u, entry.Count); + Assert.Equal(bytes, entry.Value); + } + + [Theory] + [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, new double[] { 0.0 })] + [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F }, new double[] { 1.0 })] + [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40 }, new double[] { 2.0 })] + [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0 }, new double[] { -2.0 })] + [InlineDataAttribute(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0x7F }, new double[] { double.MaxValue })] + [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x7F }, new double[] { double.PositiveInfinity })] + [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF }, new double[] { double.NegativeInfinity })] + [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF }, new double[] { double.NaN })] + [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0 }, new double[] { 0.0, 1.0, -2.0 })] + public void AddDouble_AddsArray(byte[] bytes, double[] value) + { + var entries = new List(); + + entries.AddDouble(TiffTags.ImageWidth, value); + + var entry = entries[0]; + Assert.Equal(TiffTags.ImageWidth, entry.Tag); + Assert.Equal(TiffType.Double, entry.Type); + Assert.Equal((uint)value.Length, entry.Count); + Assert.Equal(bytes, entry.Value); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffIfdParser.cs b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffIfdParser.cs new file mode 100644 index 000000000..f8d744037 --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffIfdParser.cs @@ -0,0 +1,77 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using ImageSharp.Formats.Tiff; + using Xunit; + + /// + /// A utility data structure to decode Tiff IFD entries in unit tests. + /// + internal static class TiffIfdParser + { + public static int? GetInteger(this List entries, ushort tag) + { + TiffIfdEntry entry = entries.FirstOrDefault(e => e.Tag == tag); + + if (entry.Tag == 0) + return null; + + Assert.Equal(1u, entry.Count); + + switch (entry.Type) + { + case TiffType.Byte: + return entry.Value[0]; + case TiffType.SByte: + return (sbyte)entry.Value[0]; + case TiffType.Short: + return BitConverter.ToUInt16(entry.Value, 0); + case TiffType.SShort: + return BitConverter.ToInt16(entry.Value, 0); + case TiffType.Long: + return (int)BitConverter.ToUInt32(entry.Value, 0); + case TiffType.SLong: + return BitConverter.ToInt32(entry.Value, 0); + default: + Assert.True(1 == 1, "TIFF IFD entry is not convertable to an integer."); + return null; + } + } + + public static Rational? GetUnsignedRational(this List entries, ushort tag) + { + TiffIfdEntry entry = entries.FirstOrDefault(e => e.Tag == tag); + + if (entry.Tag == 0) + return null; + + Assert.Equal(TiffType.Rational, entry.Type); + Assert.Equal(1u, entry.Count); + + uint numerator = BitConverter.ToUInt32(entry.Value, 0); + uint denominator = BitConverter.ToUInt32(entry.Value, 4); + + return new Rational(numerator, denominator); + } + + public static string GetAscii(this List entries, ushort tag) + { + TiffIfdEntry entry = entries.FirstOrDefault(e => e.Tag == tag); + + if (entry.Tag == 0) + return null; + + Assert.Equal(TiffType.Ascii, entry.Type); + + return Encoding.UTF8.GetString(entry.Value, 0, (int)entry.Count); + } + } +} \ No newline at end of file From 6603badd2252575f7324828893b6474d4a3ca87e Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Tue, 4 Jul 2017 22:46:53 +0100 Subject: [PATCH 056/275] Update xUnit version (and fix test warnings) --- .../Formats/Tiff/TiffDecoderIfdEntryTests.cs | 650 +++++++++--------- .../Formats/Tiff/TiffDecoderIfdTests.cs | 2 +- .../Formats/Tiff/TiffDecoderImageTests.cs | 262 +++---- .../Formats/Tiff/TiffDecoderMetadataTests.cs | 2 +- .../Formats/Tiff/TiffIfd/TiffIfdTests.cs | 8 +- .../Formats/Tiff/Utils/SubStreamTests.cs | 1 + .../ImageSharp.Formats.Tiff.Tests.csproj | 4 +- 7 files changed, 465 insertions(+), 464 deletions(-) diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs index 846495f67..8cd7e4e9e 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs @@ -16,20 +16,20 @@ namespace ImageSharp.Tests public class TiffDecoderIfdEntryTests { [Theory] - [InlineDataAttribute(TiffType.Byte, 1u, 1u)] - [InlineDataAttribute(TiffType.Ascii, 1u, 1u)] - [InlineDataAttribute(TiffType.Short, 1u, 2u)] - [InlineDataAttribute(TiffType.Long, 1u, 4u)] - [InlineDataAttribute(TiffType.Rational, 1u, 8u)] - [InlineDataAttribute(TiffType.SByte, 1u, 1u)] - [InlineDataAttribute(TiffType.Undefined, 1u, 1u)] - [InlineDataAttribute(TiffType.SShort, 1u, 2u)] - [InlineDataAttribute(TiffType.SLong, 1u, 4u)] - [InlineDataAttribute(TiffType.SRational, 1u, 8u)] - [InlineDataAttribute(TiffType.Float, 1u, 4u)] - [InlineDataAttribute(TiffType.Double, 1u, 8u)] - [InlineDataAttribute(TiffType.Ifd, 1u, 4u)] - [InlineDataAttribute((TiffType)999, 1u, 0u)] + [InlineDataAttribute((ushort)TiffType.Byte, 1u, 1u)] + [InlineDataAttribute((ushort)TiffType.Ascii, 1u, 1u)] + [InlineDataAttribute((ushort)TiffType.Short, 1u, 2u)] + [InlineDataAttribute((ushort)TiffType.Long, 1u, 4u)] + [InlineDataAttribute((ushort)TiffType.Rational, 1u, 8u)] + [InlineDataAttribute((ushort)TiffType.SByte, 1u, 1u)] + [InlineDataAttribute((ushort)TiffType.Undefined, 1u, 1u)] + [InlineDataAttribute((ushort)TiffType.SShort, 1u, 2u)] + [InlineDataAttribute((ushort)TiffType.SLong, 1u, 4u)] + [InlineDataAttribute((ushort)TiffType.SRational, 1u, 8u)] + [InlineDataAttribute((ushort)TiffType.Float, 1u, 4u)] + [InlineDataAttribute((ushort)TiffType.Double, 1u, 8u)] + [InlineDataAttribute((ushort)TiffType.Ifd, 1u, 4u)] + [InlineDataAttribute((ushort)999, 1u, 0u)] public void GetSizeOfData_SingleItem_ReturnsCorrectSize(ushort type, uint count, uint expectedSize) { TiffIfdEntry entry = new TiffIfdEntry(TiffTags.ImageWidth, (TiffType)type, count, new byte[4]); @@ -38,20 +38,20 @@ namespace ImageSharp.Tests } [Theory] - [InlineDataAttribute(TiffType.Byte, 15u, 15u)] - [InlineDataAttribute(TiffType.Ascii, 20u, 20u)] - [InlineDataAttribute(TiffType.Short, 18u, 36u)] - [InlineDataAttribute(TiffType.Long, 4u, 16u)] - [InlineDataAttribute(TiffType.Rational, 9u, 72u)] - [InlineDataAttribute(TiffType.SByte, 5u, 5u)] - [InlineDataAttribute(TiffType.Undefined, 136u, 136u)] - [InlineDataAttribute(TiffType.SShort, 12u, 24u)] - [InlineDataAttribute(TiffType.SLong, 15u, 60u)] - [InlineDataAttribute(TiffType.SRational, 10u, 80u)] - [InlineDataAttribute(TiffType.Float, 2u, 8u)] - [InlineDataAttribute(TiffType.Double, 2u, 16u)] - [InlineDataAttribute(TiffType.Ifd, 10u, 40u)] - [InlineDataAttribute((TiffType)999, 1050u, 0u)] + [InlineDataAttribute((ushort)TiffType.Byte, 15u, 15u)] + [InlineDataAttribute((ushort)TiffType.Ascii, 20u, 20u)] + [InlineDataAttribute((ushort)TiffType.Short, 18u, 36u)] + [InlineDataAttribute((ushort)TiffType.Long, 4u, 16u)] + [InlineDataAttribute((ushort)TiffType.Rational, 9u, 72u)] + [InlineDataAttribute((ushort)TiffType.SByte, 5u, 5u)] + [InlineDataAttribute((ushort)TiffType.Undefined, 136u, 136u)] + [InlineDataAttribute((ushort)TiffType.SShort, 12u, 24u)] + [InlineDataAttribute((ushort)TiffType.SLong, 15u, 60u)] + [InlineDataAttribute((ushort)TiffType.SRational, 10u, 80u)] + [InlineDataAttribute((ushort)TiffType.Float, 2u, 8u)] + [InlineDataAttribute((ushort)TiffType.Double, 2u, 16u)] + [InlineDataAttribute((ushort)TiffType.Ifd, 10u, 40u)] + [InlineDataAttribute((ushort)999, 1050u, 0u)] public void GetSizeOfData_Array_ReturnsCorrectSize(ushort type, uint count, uint expectedSize) { TiffIfdEntry entry = new TiffIfdEntry(TiffTags.ImageWidth, (TiffType)type, count, new byte[4]); @@ -60,28 +60,28 @@ namespace ImageSharp.Tests } [Theory] - [InlineDataAttribute(TiffType.Byte, 1u, new byte[] { 17 }, false)] - [InlineDataAttribute(TiffType.Byte, 1u, new byte[] { 17 }, true)] - [InlineDataAttribute(TiffType.Byte, 2u, new byte[] { 17, 28 }, false)] - [InlineDataAttribute(TiffType.Byte, 2u, new byte[] { 17, 28 }, true)] - [InlineDataAttribute(TiffType.Byte, 4u, new byte[] { 17, 28, 2, 9 }, false)] - [InlineDataAttribute(TiffType.Byte, 4u, new byte[] { 17, 28, 2, 9 }, true)] - [InlineDataAttribute(TiffType.Byte, 5u, new byte[] { 17, 28, 2, 9, 13 }, false)] - [InlineDataAttribute(TiffType.Byte, 5u, new byte[] { 17, 28, 2, 9, 13 }, true)] - [InlineDataAttribute(TiffType.Byte, 10u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2, 127, 86 }, false)] - [InlineDataAttribute(TiffType.Byte, 10u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2, 127, 86 }, true)] - [InlineDataAttribute(TiffType.Short, 1u, new byte[] { 17, 28 }, false)] - [InlineDataAttribute(TiffType.Short, 1u, new byte[] { 17, 28 }, true)] - [InlineDataAttribute(TiffType.Short, 2u, new byte[] { 17, 28, 2, 9 }, false)] - [InlineDataAttribute(TiffType.Short, 2u, new byte[] { 17, 28, 2, 9 }, true)] - [InlineDataAttribute(TiffType.Short, 3u, new byte[] { 17, 28, 2, 9, 13, 37 }, false)] - [InlineDataAttribute(TiffType.Short, 3u, new byte[] { 17, 28, 2, 9, 13, 37 }, true)] - [InlineDataAttribute(TiffType.Long, 1u, new byte[] { 17, 28, 2, 9 }, false)] - [InlineDataAttribute(TiffType.Long, 1u, new byte[] { 17, 28, 2, 9 }, true)] - [InlineDataAttribute(TiffType.Long, 2u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2 }, false)] - [InlineDataAttribute(TiffType.Long, 2u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2 }, true)] - [InlineDataAttribute(TiffType.Rational, 1u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2 }, false)] - [InlineDataAttribute(TiffType.Rational, 1u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2 }, true)] + [InlineDataAttribute((ushort)TiffType.Byte, 1u, new byte[] { 17 }, false)] + [InlineDataAttribute((ushort)TiffType.Byte, 1u, new byte[] { 17 }, true)] + [InlineDataAttribute((ushort)TiffType.Byte, 2u, new byte[] { 17, 28 }, false)] + [InlineDataAttribute((ushort)TiffType.Byte, 2u, new byte[] { 17, 28 }, true)] + [InlineDataAttribute((ushort)TiffType.Byte, 4u, new byte[] { 17, 28, 2, 9 }, false)] + [InlineDataAttribute((ushort)TiffType.Byte, 4u, new byte[] { 17, 28, 2, 9 }, true)] + [InlineDataAttribute((ushort)TiffType.Byte, 5u, new byte[] { 17, 28, 2, 9, 13 }, false)] + [InlineDataAttribute((ushort)TiffType.Byte, 5u, new byte[] { 17, 28, 2, 9, 13 }, true)] + [InlineDataAttribute((ushort)TiffType.Byte, 10u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2, 127, 86 }, false)] + [InlineDataAttribute((ushort)TiffType.Byte, 10u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2, 127, 86 }, true)] + [InlineDataAttribute((ushort)TiffType.Short, 1u, new byte[] { 17, 28 }, false)] + [InlineDataAttribute((ushort)TiffType.Short, 1u, new byte[] { 17, 28 }, true)] + [InlineDataAttribute((ushort)TiffType.Short, 2u, new byte[] { 17, 28, 2, 9 }, false)] + [InlineDataAttribute((ushort)TiffType.Short, 2u, new byte[] { 17, 28, 2, 9 }, true)] + [InlineDataAttribute((ushort)TiffType.Short, 3u, new byte[] { 17, 28, 2, 9, 13, 37 }, false)] + [InlineDataAttribute((ushort)TiffType.Short, 3u, new byte[] { 17, 28, 2, 9, 13, 37 }, true)] + [InlineDataAttribute((ushort)TiffType.Long, 1u, new byte[] { 17, 28, 2, 9 }, false)] + [InlineDataAttribute((ushort)TiffType.Long, 1u, new byte[] { 17, 28, 2, 9 }, true)] + [InlineDataAttribute((ushort)TiffType.Long, 2u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2 }, false)] + [InlineDataAttribute((ushort)TiffType.Long, 2u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2 }, true)] + [InlineDataAttribute((ushort)TiffType.Rational, 1u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2 }, false)] + [InlineDataAttribute((ushort)TiffType.Rational, 1u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2 }, true)] public void ReadBytes_ReturnsExpectedData(ushort type, uint count, byte[] bytes, bool isLittleEndian) { (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, count, bytes), isLittleEndian); @@ -95,16 +95,16 @@ namespace ImageSharp.Tests } [Theory] - [InlineDataAttribute(TiffType.Byte, 5u, new byte[] { 17, 28, 2, 9, 13 }, false)] - [InlineDataAttribute(TiffType.Byte, 5u, new byte[] { 17, 28, 2, 9, 13 }, true)] - [InlineDataAttribute(TiffType.Byte, 10u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2, 127, 86 }, false)] - [InlineDataAttribute(TiffType.Byte, 10u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2, 127, 86 }, true)] - [InlineDataAttribute(TiffType.Short, 3u, new byte[] { 17, 28, 2, 9, 13, 37 }, false)] - [InlineDataAttribute(TiffType.Short, 3u, new byte[] { 17, 28, 2, 9, 13, 37 }, true)] - [InlineDataAttribute(TiffType.Long, 2u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2 }, false)] - [InlineDataAttribute(TiffType.Long, 2u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2 }, true)] - [InlineDataAttribute(TiffType.Rational, 1u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2 }, false)] - [InlineDataAttribute(TiffType.Rational, 1u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2 }, true)] + [InlineDataAttribute((ushort)TiffType.Byte, 5u, new byte[] { 17, 28, 2, 9, 13 }, false)] + [InlineDataAttribute((ushort)TiffType.Byte, 5u, new byte[] { 17, 28, 2, 9, 13 }, true)] + [InlineDataAttribute((ushort)TiffType.Byte, 10u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2, 127, 86 }, false)] + [InlineDataAttribute((ushort)TiffType.Byte, 10u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2, 127, 86 }, true)] + [InlineDataAttribute((ushort)TiffType.Short, 3u, new byte[] { 17, 28, 2, 9, 13, 37 }, false)] + [InlineDataAttribute((ushort)TiffType.Short, 3u, new byte[] { 17, 28, 2, 9, 13, 37 }, true)] + [InlineDataAttribute((ushort)TiffType.Long, 2u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2 }, false)] + [InlineDataAttribute((ushort)TiffType.Long, 2u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2 }, true)] + [InlineDataAttribute((ushort)TiffType.Rational, 1u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2 }, false)] + [InlineDataAttribute((ushort)TiffType.Rational, 1u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2 }, true)] public void ReadBytes_CachesDataLongerThanFourBytes(ushort type, uint count, byte[] bytes, bool isLittleEndian) { (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, count, bytes), isLittleEndian); @@ -118,36 +118,36 @@ namespace ImageSharp.Tests } [Theory] - [InlineDataAttribute(TiffType.Byte, true, new byte[] { 0, 1, 2, 3 }, 0)] - [InlineDataAttribute(TiffType.Byte, true, new byte[] { 1, 2, 3, 4 }, 1)] - [InlineDataAttribute(TiffType.Byte, true, new byte[] { 255, 2, 3, 4 }, 255)] - [InlineDataAttribute(TiffType.Byte, false, new byte[] { 0, 1, 2, 3 }, 0)] - [InlineDataAttribute(TiffType.Byte, false, new byte[] { 1, 2, 3, 4 }, 1)] - [InlineDataAttribute(TiffType.Byte, false, new byte[] { 255, 2, 3, 4 }, 255)] - [InlineDataAttribute(TiffType.Short, true, new byte[] { 0, 0, 2, 3 }, 0)] - [InlineDataAttribute(TiffType.Short, true, new byte[] { 1, 0, 2, 3 }, 1)] - [InlineDataAttribute(TiffType.Short, true, new byte[] { 0, 1, 2, 3 }, 256)] - [InlineDataAttribute(TiffType.Short, true, new byte[] { 2, 1, 2, 3 }, 258)] - [InlineDataAttribute(TiffType.Short, true, new byte[] { 255, 255, 2, 3 }, UInt16.MaxValue)] - [InlineDataAttribute(TiffType.Short, false, new byte[] { 0, 0, 2, 3 }, 0)] - [InlineDataAttribute(TiffType.Short, false, new byte[] { 0, 1, 2, 3 }, 1)] - [InlineDataAttribute(TiffType.Short, false, new byte[] { 1, 0, 2, 3 }, 256)] - [InlineDataAttribute(TiffType.Short, false, new byte[] { 1, 2, 2, 3 }, 258)] - [InlineDataAttribute(TiffType.Short, false, new byte[] { 255, 255, 2, 3 }, UInt16.MaxValue)] - [InlineDataAttribute(TiffType.Long, true, new byte[] { 0, 0, 0, 0 }, 0)] - [InlineDataAttribute(TiffType.Long, true, new byte[] { 1, 0, 0, 0 }, 1)] - [InlineDataAttribute(TiffType.Long, true, new byte[] { 0, 1, 0, 0 }, 256)] - [InlineDataAttribute(TiffType.Long, true, new byte[] { 0, 0, 1, 0 }, 256 * 256)] - [InlineDataAttribute(TiffType.Long, true, new byte[] { 0, 0, 0, 1 }, 256 * 256 * 256)] - [InlineDataAttribute(TiffType.Long, true, new byte[] { 1, 2, 3, 4 }, 67305985)] - [InlineDataAttribute(TiffType.Long, true, new byte[] { 255, 255, 255, 255 }, UInt32.MaxValue)] - [InlineDataAttribute(TiffType.Long, false, new byte[] { 0, 0, 0, 0 }, 0)] - [InlineDataAttribute(TiffType.Long, false, new byte[] { 0, 0, 0, 1 }, 1)] - [InlineDataAttribute(TiffType.Long, false, new byte[] { 0, 0, 1, 0 }, 256)] - [InlineDataAttribute(TiffType.Long, false, new byte[] { 0, 1, 0, 0 }, 256 * 256)] - [InlineDataAttribute(TiffType.Long, false, new byte[] { 1, 0, 0, 0 }, 256 * 256 * 256)] - [InlineDataAttribute(TiffType.Long, false, new byte[] { 4, 3, 2, 1 }, 67305985)] - [InlineDataAttribute(TiffType.Long, false, new byte[] { 255, 255, 255, 255 }, UInt32.MaxValue)] + [InlineDataAttribute((ushort)TiffType.Byte, true, new byte[] { 0, 1, 2, 3 }, 0)] + [InlineDataAttribute((ushort)TiffType.Byte, true, new byte[] { 1, 2, 3, 4 }, 1)] + [InlineDataAttribute((ushort)TiffType.Byte, true, new byte[] { 255, 2, 3, 4 }, 255)] + [InlineDataAttribute((ushort)TiffType.Byte, false, new byte[] { 0, 1, 2, 3 }, 0)] + [InlineDataAttribute((ushort)TiffType.Byte, false, new byte[] { 1, 2, 3, 4 }, 1)] + [InlineDataAttribute((ushort)TiffType.Byte, false, new byte[] { 255, 2, 3, 4 }, 255)] + [InlineDataAttribute((ushort)TiffType.Short, true, new byte[] { 0, 0, 2, 3 }, 0)] + [InlineDataAttribute((ushort)TiffType.Short, true, new byte[] { 1, 0, 2, 3 }, 1)] + [InlineDataAttribute((ushort)TiffType.Short, true, new byte[] { 0, 1, 2, 3 }, 256)] + [InlineDataAttribute((ushort)TiffType.Short, true, new byte[] { 2, 1, 2, 3 }, 258)] + [InlineDataAttribute((ushort)TiffType.Short, true, new byte[] { 255, 255, 2, 3 }, UInt16.MaxValue)] + [InlineDataAttribute((ushort)TiffType.Short, false, new byte[] { 0, 0, 2, 3 }, 0)] + [InlineDataAttribute((ushort)TiffType.Short, false, new byte[] { 0, 1, 2, 3 }, 1)] + [InlineDataAttribute((ushort)TiffType.Short, false, new byte[] { 1, 0, 2, 3 }, 256)] + [InlineDataAttribute((ushort)TiffType.Short, false, new byte[] { 1, 2, 2, 3 }, 258)] + [InlineDataAttribute((ushort)TiffType.Short, false, new byte[] { 255, 255, 2, 3 }, UInt16.MaxValue)] + [InlineDataAttribute((ushort)TiffType.Long, true, new byte[] { 0, 0, 0, 0 }, 0)] + [InlineDataAttribute((ushort)TiffType.Long, true, new byte[] { 1, 0, 0, 0 }, 1)] + [InlineDataAttribute((ushort)TiffType.Long, true, new byte[] { 0, 1, 0, 0 }, 256)] + [InlineDataAttribute((ushort)TiffType.Long, true, new byte[] { 0, 0, 1, 0 }, 256 * 256)] + [InlineDataAttribute((ushort)TiffType.Long, true, new byte[] { 0, 0, 0, 1 }, 256 * 256 * 256)] + [InlineDataAttribute((ushort)TiffType.Long, true, new byte[] { 1, 2, 3, 4 }, 67305985)] + [InlineDataAttribute((ushort)TiffType.Long, true, new byte[] { 255, 255, 255, 255 }, UInt32.MaxValue)] + [InlineDataAttribute((ushort)TiffType.Long, false, new byte[] { 0, 0, 0, 0 }, 0)] + [InlineDataAttribute((ushort)TiffType.Long, false, new byte[] { 0, 0, 0, 1 }, 1)] + [InlineDataAttribute((ushort)TiffType.Long, false, new byte[] { 0, 0, 1, 0 }, 256)] + [InlineDataAttribute((ushort)TiffType.Long, false, new byte[] { 0, 1, 0, 0 }, 256 * 256)] + [InlineDataAttribute((ushort)TiffType.Long, false, new byte[] { 1, 0, 0, 0 }, 256 * 256 * 256)] + [InlineDataAttribute((ushort)TiffType.Long, false, new byte[] { 4, 3, 2, 1 }, 67305985)] + [InlineDataAttribute((ushort)TiffType.Long, false, new byte[] { 255, 255, 255, 255 }, UInt32.MaxValue)] public void ReadUnsignedInteger_ReturnsValue(ushort type, bool isLittleEndian, byte[] bytes, uint expectedValue) { (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, 1, bytes), isLittleEndian); @@ -158,17 +158,17 @@ namespace ImageSharp.Tests } [Theory] - [InlineDataAttribute(TiffType.Ascii)] - [InlineDataAttribute(TiffType.Rational)] - [InlineDataAttribute(TiffType.SByte)] - [InlineDataAttribute(TiffType.Undefined)] - [InlineDataAttribute(TiffType.SShort)] - [InlineDataAttribute(TiffType.SLong)] - [InlineDataAttribute(TiffType.SRational)] - [InlineDataAttribute(TiffType.Float)] - [InlineDataAttribute(TiffType.Double)] - [InlineDataAttribute(TiffType.Ifd)] - [InlineDataAttribute((TiffType)99)] + [InlineDataAttribute((ushort)TiffType.Ascii)] + [InlineDataAttribute((ushort)TiffType.Rational)] + [InlineDataAttribute((ushort)TiffType.SByte)] + [InlineDataAttribute((ushort)TiffType.Undefined)] + [InlineDataAttribute((ushort)TiffType.SShort)] + [InlineDataAttribute((ushort)TiffType.SLong)] + [InlineDataAttribute((ushort)TiffType.SRational)] + [InlineDataAttribute((ushort)TiffType.Float)] + [InlineDataAttribute((ushort)TiffType.Double)] + [InlineDataAttribute((ushort)TiffType.Ifd)] + [InlineDataAttribute((ushort)99)] public void ReadUnsignedInteger_ThrowsExceptionIfInvalidType(ushort type) { (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, 1, new byte[4]), true); @@ -179,12 +179,12 @@ namespace ImageSharp.Tests } [Theory] - [InlineDataAttribute(TiffType.Byte, true)] - [InlineDataAttribute(TiffType.Short, true)] - [InlineDataAttribute(TiffType.Long, true)] - [InlineDataAttribute(TiffType.Byte, false)] - [InlineDataAttribute(TiffType.Short, false)] - [InlineDataAttribute(TiffType.Long, false)] + [InlineDataAttribute((ushort)TiffType.Byte, true)] + [InlineDataAttribute((ushort)TiffType.Short, true)] + [InlineDataAttribute((ushort)TiffType.Long, true)] + [InlineDataAttribute((ushort)TiffType.Byte, false)] + [InlineDataAttribute((ushort)TiffType.Short, false)] + [InlineDataAttribute((ushort)TiffType.Long, false)] public void ReadUnsignedInteger_ThrowsExceptionIfCountIsNotOne(ushort type, bool isLittleEndian) { (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, 2, new byte[4]), isLittleEndian); @@ -195,36 +195,36 @@ namespace ImageSharp.Tests } [Theory] - [InlineDataAttribute(TiffType.SByte, true, new byte[] { 0, 1, 2, 3 }, 0)] - [InlineDataAttribute(TiffType.SByte, true, new byte[] { 1, 2, 3, 4 }, 1)] - [InlineDataAttribute(TiffType.SByte, true, new byte[] { 255, 2, 3, 4 }, -1)] - [InlineDataAttribute(TiffType.SByte, false, new byte[] { 0, 1, 2, 3 }, 0)] - [InlineDataAttribute(TiffType.SByte, false, new byte[] { 1, 2, 3, 4 }, 1)] - [InlineDataAttribute(TiffType.SByte, false, new byte[] { 255, 2, 3, 4 }, -1)] - [InlineDataAttribute(TiffType.SShort, true, new byte[] { 0, 0, 2, 3 }, 0)] - [InlineDataAttribute(TiffType.SShort, true, new byte[] { 1, 0, 2, 3 }, 1)] - [InlineDataAttribute(TiffType.SShort, true, new byte[] { 0, 1, 2, 3 }, 256)] - [InlineDataAttribute(TiffType.SShort, true, new byte[] { 2, 1, 2, 3 }, 258)] - [InlineDataAttribute(TiffType.SShort, true, new byte[] { 255, 255, 2, 3 }, -1)] - [InlineDataAttribute(TiffType.SShort, false, new byte[] { 0, 0, 2, 3 }, 0)] - [InlineDataAttribute(TiffType.SShort, false, new byte[] { 0, 1, 2, 3 }, 1)] - [InlineDataAttribute(TiffType.SShort, false, new byte[] { 1, 0, 2, 3 }, 256)] - [InlineDataAttribute(TiffType.SShort, false, new byte[] { 1, 2, 2, 3 }, 258)] - [InlineDataAttribute(TiffType.SShort, false, new byte[] { 255, 255, 2, 3 }, -1)] - [InlineDataAttribute(TiffType.SLong, true, new byte[] { 0, 0, 0, 0 }, 0)] - [InlineDataAttribute(TiffType.SLong, true, new byte[] { 1, 0, 0, 0 }, 1)] - [InlineDataAttribute(TiffType.SLong, true, new byte[] { 0, 1, 0, 0 }, 256)] - [InlineDataAttribute(TiffType.SLong, true, new byte[] { 0, 0, 1, 0 }, 256 * 256)] - [InlineDataAttribute(TiffType.SLong, true, new byte[] { 0, 0, 0, 1 }, 256 * 256 * 256)] - [InlineDataAttribute(TiffType.SLong, true, new byte[] { 1, 2, 3, 4 }, 67305985)] - [InlineDataAttribute(TiffType.SLong, true, new byte[] { 255, 255, 255, 255 }, -1)] - [InlineDataAttribute(TiffType.SLong, false, new byte[] { 0, 0, 0, 0 }, 0)] - [InlineDataAttribute(TiffType.SLong, false, new byte[] { 0, 0, 0, 1 }, 1)] - [InlineDataAttribute(TiffType.SLong, false, new byte[] { 0, 0, 1, 0 }, 256)] - [InlineDataAttribute(TiffType.SLong, false, new byte[] { 0, 1, 0, 0 }, 256 * 256)] - [InlineDataAttribute(TiffType.SLong, false, new byte[] { 1, 0, 0, 0 }, 256 * 256 * 256)] - [InlineDataAttribute(TiffType.SLong, false, new byte[] { 4, 3, 2, 1 }, 67305985)] - [InlineDataAttribute(TiffType.SLong, false, new byte[] { 255, 255, 255, 255 }, -1)] + [InlineDataAttribute((ushort)TiffType.SByte, true, new byte[] { 0, 1, 2, 3 }, 0)] + [InlineDataAttribute((ushort)TiffType.SByte, true, new byte[] { 1, 2, 3, 4 }, 1)] + [InlineDataAttribute((ushort)TiffType.SByte, true, new byte[] { 255, 2, 3, 4 }, -1)] + [InlineDataAttribute((ushort)TiffType.SByte, false, new byte[] { 0, 1, 2, 3 }, 0)] + [InlineDataAttribute((ushort)TiffType.SByte, false, new byte[] { 1, 2, 3, 4 }, 1)] + [InlineDataAttribute((ushort)TiffType.SByte, false, new byte[] { 255, 2, 3, 4 }, -1)] + [InlineDataAttribute((ushort)TiffType.SShort, true, new byte[] { 0, 0, 2, 3 }, 0)] + [InlineDataAttribute((ushort)TiffType.SShort, true, new byte[] { 1, 0, 2, 3 }, 1)] + [InlineDataAttribute((ushort)TiffType.SShort, true, new byte[] { 0, 1, 2, 3 }, 256)] + [InlineDataAttribute((ushort)TiffType.SShort, true, new byte[] { 2, 1, 2, 3 }, 258)] + [InlineDataAttribute((ushort)TiffType.SShort, true, new byte[] { 255, 255, 2, 3 }, -1)] + [InlineDataAttribute((ushort)TiffType.SShort, false, new byte[] { 0, 0, 2, 3 }, 0)] + [InlineDataAttribute((ushort)TiffType.SShort, false, new byte[] { 0, 1, 2, 3 }, 1)] + [InlineDataAttribute((ushort)TiffType.SShort, false, new byte[] { 1, 0, 2, 3 }, 256)] + [InlineDataAttribute((ushort)TiffType.SShort, false, new byte[] { 1, 2, 2, 3 }, 258)] + [InlineDataAttribute((ushort)TiffType.SShort, false, new byte[] { 255, 255, 2, 3 }, -1)] + [InlineDataAttribute((ushort)TiffType.SLong, true, new byte[] { 0, 0, 0, 0 }, 0)] + [InlineDataAttribute((ushort)TiffType.SLong, true, new byte[] { 1, 0, 0, 0 }, 1)] + [InlineDataAttribute((ushort)TiffType.SLong, true, new byte[] { 0, 1, 0, 0 }, 256)] + [InlineDataAttribute((ushort)TiffType.SLong, true, new byte[] { 0, 0, 1, 0 }, 256 * 256)] + [InlineDataAttribute((ushort)TiffType.SLong, true, new byte[] { 0, 0, 0, 1 }, 256 * 256 * 256)] + [InlineDataAttribute((ushort)TiffType.SLong, true, new byte[] { 1, 2, 3, 4 }, 67305985)] + [InlineDataAttribute((ushort)TiffType.SLong, true, new byte[] { 255, 255, 255, 255 }, -1)] + [InlineDataAttribute((ushort)TiffType.SLong, false, new byte[] { 0, 0, 0, 0 }, 0)] + [InlineDataAttribute((ushort)TiffType.SLong, false, new byte[] { 0, 0, 0, 1 }, 1)] + [InlineDataAttribute((ushort)TiffType.SLong, false, new byte[] { 0, 0, 1, 0 }, 256)] + [InlineDataAttribute((ushort)TiffType.SLong, false, new byte[] { 0, 1, 0, 0 }, 256 * 256)] + [InlineDataAttribute((ushort)TiffType.SLong, false, new byte[] { 1, 0, 0, 0 }, 256 * 256 * 256)] + [InlineDataAttribute((ushort)TiffType.SLong, false, new byte[] { 4, 3, 2, 1 }, 67305985)] + [InlineDataAttribute((ushort)TiffType.SLong, false, new byte[] { 255, 255, 255, 255 }, -1)] public void ReadSignedInteger_ReturnsValue(ushort type, bool isLittleEndian, byte[] bytes, int expectedValue) { (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, 1, bytes), isLittleEndian); @@ -235,17 +235,17 @@ namespace ImageSharp.Tests } [Theory] - [InlineDataAttribute(TiffType.Byte)] - [InlineDataAttribute(TiffType.Ascii)] - [InlineDataAttribute(TiffType.Short)] - [InlineDataAttribute(TiffType.Long)] - [InlineDataAttribute(TiffType.Rational)] - [InlineDataAttribute(TiffType.Undefined)] - [InlineDataAttribute(TiffType.SRational)] - [InlineDataAttribute(TiffType.Float)] - [InlineDataAttribute(TiffType.Double)] - [InlineDataAttribute(TiffType.Ifd)] - [InlineDataAttribute((TiffType)99)] + [InlineDataAttribute((ushort)TiffType.Byte)] + [InlineDataAttribute((ushort)TiffType.Ascii)] + [InlineDataAttribute((ushort)TiffType.Short)] + [InlineDataAttribute((ushort)TiffType.Long)] + [InlineDataAttribute((ushort)TiffType.Rational)] + [InlineDataAttribute((ushort)TiffType.Undefined)] + [InlineDataAttribute((ushort)TiffType.SRational)] + [InlineDataAttribute((ushort)TiffType.Float)] + [InlineDataAttribute((ushort)TiffType.Double)] + [InlineDataAttribute((ushort)TiffType.Ifd)] + [InlineDataAttribute((ushort)99)] public void ReadSignedInteger_ThrowsExceptionIfInvalidType(ushort type) { (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, 1, new byte[4]), true); @@ -256,12 +256,12 @@ namespace ImageSharp.Tests } [Theory] - [InlineDataAttribute(TiffType.SByte, true)] - [InlineDataAttribute(TiffType.SShort, true)] - [InlineDataAttribute(TiffType.SLong, true)] - [InlineDataAttribute(TiffType.SByte, false)] - [InlineDataAttribute(TiffType.SShort, false)] - [InlineDataAttribute(TiffType.SLong, false)] + [InlineDataAttribute((ushort)TiffType.SByte, true)] + [InlineDataAttribute((ushort)TiffType.SShort, true)] + [InlineDataAttribute((ushort)TiffType.SLong, true)] + [InlineDataAttribute((ushort)TiffType.SByte, false)] + [InlineDataAttribute((ushort)TiffType.SShort, false)] + [InlineDataAttribute((ushort)TiffType.SLong, false)] public void ReadSignedInteger_ThrowsExceptionIfCountIsNotOne(ushort type, bool isLittleEndian) { (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, 2, new byte[4]), isLittleEndian); @@ -272,22 +272,22 @@ namespace ImageSharp.Tests } [Theory] - [InlineDataAttribute(TiffType.Byte, 1, true, new byte[] { 0, 1, 2, 3 }, new uint[] { 0 })] - [InlineDataAttribute(TiffType.Byte, 3, true, new byte[] { 0, 1, 2, 3 }, new uint[] { 0, 1, 2 })] - [InlineDataAttribute(TiffType.Byte, 7, true, new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8 }, new uint[] { 0, 1, 2, 3, 4, 5, 6 })] - [InlineDataAttribute(TiffType.Byte, 1, false, new byte[] { 0, 1, 2, 3 }, new uint[] { 0 })] - [InlineDataAttribute(TiffType.Byte, 3, false, new byte[] { 0, 1, 2, 3 }, new uint[] { 0, 1, 2 })] - [InlineDataAttribute(TiffType.Byte, 7, false, new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8 }, new uint[] { 0, 1, 2, 3, 4, 5, 6 })] - [InlineDataAttribute(TiffType.Short, 1, true, new byte[] { 1, 0, 3, 2 }, new uint[] { 1 })] - [InlineDataAttribute(TiffType.Short, 2, true, new byte[] { 1, 0, 3, 2 }, new uint[] { 1, 515 })] - [InlineDataAttribute(TiffType.Short, 3, true, new byte[] { 1, 0, 3, 2, 5, 4, 6, 7, 8 }, new uint[] { 1, 515, 1029 })] - [InlineDataAttribute(TiffType.Short, 1, false, new byte[] { 0, 1, 2, 3 }, new uint[] { 1 })] - [InlineDataAttribute(TiffType.Short, 2, false, new byte[] { 0, 1, 2, 3 }, new uint[] { 1, 515 })] - [InlineDataAttribute(TiffType.Short, 3, false, new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8 }, new uint[] { 1, 515, 1029 })] - [InlineDataAttribute(TiffType.Long, 1, true, new byte[] { 4, 3, 2, 1 }, new uint[] { 0x01020304 })] - [InlineDataAttribute(TiffType.Long, 2, true, new byte[] { 4, 3, 2, 1, 6, 5, 4, 3, 99, 99 }, new uint[] { 0x01020304, 0x03040506 })] - [InlineDataAttribute(TiffType.Long, 1, false, new byte[] { 1, 2, 3, 4 }, new uint[] { 0x01020304 })] - [InlineDataAttribute(TiffType.Long, 2, false, new byte[] { 1, 2, 3, 4, 3, 4, 5, 6, 99, 99 }, new uint[] { 0x01020304, 0x03040506 })] + [InlineDataAttribute((ushort)TiffType.Byte, 1, true, new byte[] { 0, 1, 2, 3 }, new uint[] { 0 })] + [InlineDataAttribute((ushort)TiffType.Byte, 3, true, new byte[] { 0, 1, 2, 3 }, new uint[] { 0, 1, 2 })] + [InlineDataAttribute((ushort)TiffType.Byte, 7, true, new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8 }, new uint[] { 0, 1, 2, 3, 4, 5, 6 })] + [InlineDataAttribute((ushort)TiffType.Byte, 1, false, new byte[] { 0, 1, 2, 3 }, new uint[] { 0 })] + [InlineDataAttribute((ushort)TiffType.Byte, 3, false, new byte[] { 0, 1, 2, 3 }, new uint[] { 0, 1, 2 })] + [InlineDataAttribute((ushort)TiffType.Byte, 7, false, new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8 }, new uint[] { 0, 1, 2, 3, 4, 5, 6 })] + [InlineDataAttribute((ushort)TiffType.Short, 1, true, new byte[] { 1, 0, 3, 2 }, new uint[] { 1 })] + [InlineDataAttribute((ushort)TiffType.Short, 2, true, new byte[] { 1, 0, 3, 2 }, new uint[] { 1, 515 })] + [InlineDataAttribute((ushort)TiffType.Short, 3, true, new byte[] { 1, 0, 3, 2, 5, 4, 6, 7, 8 }, new uint[] { 1, 515, 1029 })] + [InlineDataAttribute((ushort)TiffType.Short, 1, false, new byte[] { 0, 1, 2, 3 }, new uint[] { 1 })] + [InlineDataAttribute((ushort)TiffType.Short, 2, false, new byte[] { 0, 1, 2, 3 }, new uint[] { 1, 515 })] + [InlineDataAttribute((ushort)TiffType.Short, 3, false, new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8 }, new uint[] { 1, 515, 1029 })] + [InlineDataAttribute((ushort)TiffType.Long, 1, true, new byte[] { 4, 3, 2, 1 }, new uint[] { 0x01020304 })] + [InlineDataAttribute((ushort)TiffType.Long, 2, true, new byte[] { 4, 3, 2, 1, 6, 5, 4, 3, 99, 99 }, new uint[] { 0x01020304, 0x03040506 })] + [InlineDataAttribute((ushort)TiffType.Long, 1, false, new byte[] { 1, 2, 3, 4 }, new uint[] { 0x01020304 })] + [InlineDataAttribute((ushort)TiffType.Long, 2, false, new byte[] { 1, 2, 3, 4, 3, 4, 5, 6, 99, 99 }, new uint[] { 0x01020304, 0x03040506 })] public void ReadUnsignedIntegerArray_ReturnsValue(ushort type, int count, bool isLittleEndian, byte[] bytes, uint[] expectedValue) { (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, (uint)expectedValue.Length, bytes), isLittleEndian); @@ -298,17 +298,17 @@ namespace ImageSharp.Tests } [Theory] - [InlineDataAttribute(TiffType.Ascii)] - [InlineDataAttribute(TiffType.Rational)] - [InlineDataAttribute(TiffType.SByte)] - [InlineDataAttribute(TiffType.Undefined)] - [InlineDataAttribute(TiffType.SShort)] - [InlineDataAttribute(TiffType.SLong)] - [InlineDataAttribute(TiffType.SRational)] - [InlineDataAttribute(TiffType.Float)] - [InlineDataAttribute(TiffType.Double)] - [InlineDataAttribute(TiffType.Ifd)] - [InlineDataAttribute((TiffType)99)] + [InlineDataAttribute((ushort)TiffType.Ascii)] + [InlineDataAttribute((ushort)TiffType.Rational)] + [InlineDataAttribute((ushort)TiffType.SByte)] + [InlineDataAttribute((ushort)TiffType.Undefined)] + [InlineDataAttribute((ushort)TiffType.SShort)] + [InlineDataAttribute((ushort)TiffType.SLong)] + [InlineDataAttribute((ushort)TiffType.SRational)] + [InlineDataAttribute((ushort)TiffType.Float)] + [InlineDataAttribute((ushort)TiffType.Double)] + [InlineDataAttribute((ushort)TiffType.Ifd)] + [InlineDataAttribute((ushort)99)] public void ReadUnsignedIntegerArray_ThrowsExceptionIfInvalidType(ushort type) { (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, 1, new byte[4]), true); @@ -319,22 +319,22 @@ namespace ImageSharp.Tests } [Theory] - [InlineDataAttribute(TiffType.SByte, 1, true, new byte[] { 0, 1, 2, 3 }, new int[] { 0 })] - [InlineDataAttribute(TiffType.SByte, 3, true, new byte[] { 0, 255, 2, 3 }, new int[] { 0, -1, 2 })] - [InlineDataAttribute(TiffType.SByte, 7, true, new byte[] { 0, 255, 2, 3, 4, 5, 6, 7, 8 }, new int[] { 0, -1, 2, 3, 4, 5, 6 })] - [InlineDataAttribute(TiffType.SByte, 1, false, new byte[] { 0, 1, 2, 3 }, new int[] { 0 })] - [InlineDataAttribute(TiffType.SByte, 3, false, new byte[] { 0, 255, 2, 3 }, new int[] { 0, -1, 2 })] - [InlineDataAttribute(TiffType.SByte, 7, false, new byte[] { 0, 255, 2, 3, 4, 5, 6, 7, 8 }, new int[] { 0, -1, 2, 3, 4, 5, 6 })] - [InlineDataAttribute(TiffType.SShort, 1, true, new byte[] { 1, 0, 3, 2 }, new int[] { 1 })] - [InlineDataAttribute(TiffType.SShort, 2, true, new byte[] { 1, 0, 255, 255 }, new int[] { 1, -1 })] - [InlineDataAttribute(TiffType.SShort, 3, true, new byte[] { 1, 0, 255, 255, 5, 4, 6, 7, 8 }, new int[] { 1, -1, 1029 })] - [InlineDataAttribute(TiffType.SShort, 1, false, new byte[] { 0, 1, 2, 3 }, new int[] { 1 })] - [InlineDataAttribute(TiffType.SShort, 2, false, new byte[] { 0, 1, 255, 255 }, new int[] { 1, -1 })] - [InlineDataAttribute(TiffType.SShort, 3, false, new byte[] { 0, 1, 255, 255, 4, 5, 6, 7, 8 }, new int[] { 1, -1, 1029 })] - [InlineDataAttribute(TiffType.SLong, 1, true, new byte[] { 4, 3, 2, 1 }, new int[] { 0x01020304 })] - [InlineDataAttribute(TiffType.SLong, 2, true, new byte[] { 4, 3, 2, 1, 255, 255, 255, 255, 99, 99 }, new int[] { 0x01020304, -1 })] - [InlineDataAttribute(TiffType.SLong, 1, false, new byte[] { 1, 2, 3, 4 }, new int[] { 0x01020304 })] - [InlineDataAttribute(TiffType.SLong, 2, false, new byte[] { 1, 2, 3, 4, 255, 255, 255, 255, 99, 99 }, new int[] { 0x01020304, -1 })] + [InlineDataAttribute((ushort)TiffType.SByte, 1, true, new byte[] { 0, 1, 2, 3 }, new int[] { 0 })] + [InlineDataAttribute((ushort)TiffType.SByte, 3, true, new byte[] { 0, 255, 2, 3 }, new int[] { 0, -1, 2 })] + [InlineDataAttribute((ushort)TiffType.SByte, 7, true, new byte[] { 0, 255, 2, 3, 4, 5, 6, 7, 8 }, new int[] { 0, -1, 2, 3, 4, 5, 6 })] + [InlineDataAttribute((ushort)TiffType.SByte, 1, false, new byte[] { 0, 1, 2, 3 }, new int[] { 0 })] + [InlineDataAttribute((ushort)TiffType.SByte, 3, false, new byte[] { 0, 255, 2, 3 }, new int[] { 0, -1, 2 })] + [InlineDataAttribute((ushort)TiffType.SByte, 7, false, new byte[] { 0, 255, 2, 3, 4, 5, 6, 7, 8 }, new int[] { 0, -1, 2, 3, 4, 5, 6 })] + [InlineDataAttribute((ushort)TiffType.SShort, 1, true, new byte[] { 1, 0, 3, 2 }, new int[] { 1 })] + [InlineDataAttribute((ushort)TiffType.SShort, 2, true, new byte[] { 1, 0, 255, 255 }, new int[] { 1, -1 })] + [InlineDataAttribute((ushort)TiffType.SShort, 3, true, new byte[] { 1, 0, 255, 255, 5, 4, 6, 7, 8 }, new int[] { 1, -1, 1029 })] + [InlineDataAttribute((ushort)TiffType.SShort, 1, false, new byte[] { 0, 1, 2, 3 }, new int[] { 1 })] + [InlineDataAttribute((ushort)TiffType.SShort, 2, false, new byte[] { 0, 1, 255, 255 }, new int[] { 1, -1 })] + [InlineDataAttribute((ushort)TiffType.SShort, 3, false, new byte[] { 0, 1, 255, 255, 4, 5, 6, 7, 8 }, new int[] { 1, -1, 1029 })] + [InlineDataAttribute((ushort)TiffType.SLong, 1, true, new byte[] { 4, 3, 2, 1 }, new int[] { 0x01020304 })] + [InlineDataAttribute((ushort)TiffType.SLong, 2, true, new byte[] { 4, 3, 2, 1, 255, 255, 255, 255, 99, 99 }, new int[] { 0x01020304, -1 })] + [InlineDataAttribute((ushort)TiffType.SLong, 1, false, new byte[] { 1, 2, 3, 4 }, new int[] { 0x01020304 })] + [InlineDataAttribute((ushort)TiffType.SLong, 2, false, new byte[] { 1, 2, 3, 4, 255, 255, 255, 255, 99, 99 }, new int[] { 0x01020304, -1 })] public void ReadSignedIntegerArray_ReturnsValue(ushort type, int count, bool isLittleEndian, byte[] bytes, int[] expectedValue) { (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, (uint)expectedValue.Length, bytes), isLittleEndian); @@ -345,17 +345,17 @@ namespace ImageSharp.Tests } [Theory] - [InlineDataAttribute(TiffType.Byte)] - [InlineDataAttribute(TiffType.Ascii)] - [InlineDataAttribute(TiffType.Short)] - [InlineDataAttribute(TiffType.Long)] - [InlineDataAttribute(TiffType.Rational)] - [InlineDataAttribute(TiffType.Undefined)] - [InlineDataAttribute(TiffType.SRational)] - [InlineDataAttribute(TiffType.Float)] - [InlineDataAttribute(TiffType.Double)] - [InlineDataAttribute(TiffType.Ifd)] - [InlineDataAttribute((TiffType)99)] + [InlineDataAttribute((ushort)TiffType.Byte)] + [InlineDataAttribute((ushort)TiffType.Ascii)] + [InlineDataAttribute((ushort)TiffType.Short)] + [InlineDataAttribute((ushort)TiffType.Long)] + [InlineDataAttribute((ushort)TiffType.Rational)] + [InlineDataAttribute((ushort)TiffType.Undefined)] + [InlineDataAttribute((ushort)TiffType.SRational)] + [InlineDataAttribute((ushort)TiffType.Float)] + [InlineDataAttribute((ushort)TiffType.Double)] + [InlineDataAttribute((ushort)TiffType.Ifd)] + [InlineDataAttribute((ushort)99)] public void ReadSignedIntegerArray_ThrowsExceptionIfInvalidType(ushort type) { (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, 1, new byte[4]), true); @@ -384,19 +384,19 @@ namespace ImageSharp.Tests } [Theory] - [InlineDataAttribute(TiffType.Byte)] - [InlineDataAttribute(TiffType.Short)] - [InlineDataAttribute(TiffType.Long)] - [InlineDataAttribute(TiffType.Rational)] - [InlineDataAttribute(TiffType.SByte)] - [InlineDataAttribute(TiffType.Undefined)] - [InlineDataAttribute(TiffType.SShort)] - [InlineDataAttribute(TiffType.SLong)] - [InlineDataAttribute(TiffType.SRational)] - [InlineDataAttribute(TiffType.Float)] - [InlineDataAttribute(TiffType.Double)] - [InlineDataAttribute(TiffType.Ifd)] - [InlineDataAttribute((TiffType)99)] + [InlineDataAttribute((ushort)TiffType.Byte)] + [InlineDataAttribute((ushort)TiffType.Short)] + [InlineDataAttribute((ushort)TiffType.Long)] + [InlineDataAttribute((ushort)TiffType.Rational)] + [InlineDataAttribute((ushort)TiffType.SByte)] + [InlineDataAttribute((ushort)TiffType.Undefined)] + [InlineDataAttribute((ushort)TiffType.SShort)] + [InlineDataAttribute((ushort)TiffType.SLong)] + [InlineDataAttribute((ushort)TiffType.SRational)] + [InlineDataAttribute((ushort)TiffType.Float)] + [InlineDataAttribute((ushort)TiffType.Double)] + [InlineDataAttribute((ushort)TiffType.Ifd)] + [InlineDataAttribute((ushort)99)] public void ReadString_ThrowsExceptionIfInvalidType(ushort type) { (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, 1, new byte[4]), true); @@ -493,19 +493,19 @@ namespace ImageSharp.Tests } [Theory] - [InlineDataAttribute(TiffType.Byte)] - [InlineDataAttribute(TiffType.Ascii)] - [InlineDataAttribute(TiffType.Short)] - [InlineDataAttribute(TiffType.Long)] - [InlineDataAttribute(TiffType.SByte)] - [InlineDataAttribute(TiffType.Undefined)] - [InlineDataAttribute(TiffType.SShort)] - [InlineDataAttribute(TiffType.SLong)] - [InlineDataAttribute(TiffType.SRational)] - [InlineDataAttribute(TiffType.Float)] - [InlineDataAttribute(TiffType.Double)] - [InlineDataAttribute(TiffType.Ifd)] - [InlineDataAttribute((TiffType)99)] + [InlineDataAttribute((ushort)TiffType.Byte)] + [InlineDataAttribute((ushort)TiffType.Ascii)] + [InlineDataAttribute((ushort)TiffType.Short)] + [InlineDataAttribute((ushort)TiffType.Long)] + [InlineDataAttribute((ushort)TiffType.SByte)] + [InlineDataAttribute((ushort)TiffType.Undefined)] + [InlineDataAttribute((ushort)TiffType.SShort)] + [InlineDataAttribute((ushort)TiffType.SLong)] + [InlineDataAttribute((ushort)TiffType.SRational)] + [InlineDataAttribute((ushort)TiffType.Float)] + [InlineDataAttribute((ushort)TiffType.Double)] + [InlineDataAttribute((ushort)TiffType.Ifd)] + [InlineDataAttribute((ushort)99)] public void ReadUnsignedRational_ThrowsExceptionIfInvalidType(ushort type) { (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, 1, new byte[4]), true); @@ -516,19 +516,19 @@ namespace ImageSharp.Tests } [Theory] - [InlineDataAttribute(TiffType.Byte)] - [InlineDataAttribute(TiffType.Ascii)] - [InlineDataAttribute(TiffType.Short)] - [InlineDataAttribute(TiffType.Long)] - [InlineDataAttribute(TiffType.SByte)] - [InlineDataAttribute(TiffType.Rational)] - [InlineDataAttribute(TiffType.Undefined)] - [InlineDataAttribute(TiffType.SShort)] - [InlineDataAttribute(TiffType.SLong)] - [InlineDataAttribute(TiffType.Float)] - [InlineDataAttribute(TiffType.Double)] - [InlineDataAttribute(TiffType.Ifd)] - [InlineDataAttribute((TiffType)99)] + [InlineDataAttribute((ushort)TiffType.Byte)] + [InlineDataAttribute((ushort)TiffType.Ascii)] + [InlineDataAttribute((ushort)TiffType.Short)] + [InlineDataAttribute((ushort)TiffType.Long)] + [InlineDataAttribute((ushort)TiffType.SByte)] + [InlineDataAttribute((ushort)TiffType.Rational)] + [InlineDataAttribute((ushort)TiffType.Undefined)] + [InlineDataAttribute((ushort)TiffType.SShort)] + [InlineDataAttribute((ushort)TiffType.SLong)] + [InlineDataAttribute((ushort)TiffType.Float)] + [InlineDataAttribute((ushort)TiffType.Double)] + [InlineDataAttribute((ushort)TiffType.Ifd)] + [InlineDataAttribute((ushort)99)] public void ReadSignedRational_ThrowsExceptionIfInvalidType(ushort type) { (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, 1, new byte[4]), true); @@ -539,19 +539,19 @@ namespace ImageSharp.Tests } [Theory] - [InlineDataAttribute(TiffType.Byte)] - [InlineDataAttribute(TiffType.Ascii)] - [InlineDataAttribute(TiffType.Short)] - [InlineDataAttribute(TiffType.Long)] - [InlineDataAttribute(TiffType.SByte)] - [InlineDataAttribute(TiffType.Undefined)] - [InlineDataAttribute(TiffType.SShort)] - [InlineDataAttribute(TiffType.SLong)] - [InlineDataAttribute(TiffType.SRational)] - [InlineDataAttribute(TiffType.Float)] - [InlineDataAttribute(TiffType.Double)] - [InlineDataAttribute(TiffType.Ifd)] - [InlineDataAttribute((TiffType)99)] + [InlineDataAttribute((ushort)TiffType.Byte)] + [InlineDataAttribute((ushort)TiffType.Ascii)] + [InlineDataAttribute((ushort)TiffType.Short)] + [InlineDataAttribute((ushort)TiffType.Long)] + [InlineDataAttribute((ushort)TiffType.SByte)] + [InlineDataAttribute((ushort)TiffType.Undefined)] + [InlineDataAttribute((ushort)TiffType.SShort)] + [InlineDataAttribute((ushort)TiffType.SLong)] + [InlineDataAttribute((ushort)TiffType.SRational)] + [InlineDataAttribute((ushort)TiffType.Float)] + [InlineDataAttribute((ushort)TiffType.Double)] + [InlineDataAttribute((ushort)TiffType.Ifd)] + [InlineDataAttribute((ushort)99)] public void ReadUnsignedRationalArray_ThrowsExceptionIfInvalidType(ushort type) { (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, 1, new byte[4]), true); @@ -562,19 +562,19 @@ namespace ImageSharp.Tests } [Theory] - [InlineDataAttribute(TiffType.Byte)] - [InlineDataAttribute(TiffType.Ascii)] - [InlineDataAttribute(TiffType.Short)] - [InlineDataAttribute(TiffType.Long)] - [InlineDataAttribute(TiffType.Rational)] - [InlineDataAttribute(TiffType.SByte)] - [InlineDataAttribute(TiffType.Undefined)] - [InlineDataAttribute(TiffType.SShort)] - [InlineDataAttribute(TiffType.SLong)] - [InlineDataAttribute(TiffType.Float)] - [InlineDataAttribute(TiffType.Double)] - [InlineDataAttribute(TiffType.Ifd)] - [InlineDataAttribute((TiffType)99)] + [InlineDataAttribute((ushort)TiffType.Byte)] + [InlineDataAttribute((ushort)TiffType.Ascii)] + [InlineDataAttribute((ushort)TiffType.Short)] + [InlineDataAttribute((ushort)TiffType.Long)] + [InlineDataAttribute((ushort)TiffType.Rational)] + [InlineDataAttribute((ushort)TiffType.SByte)] + [InlineDataAttribute((ushort)TiffType.Undefined)] + [InlineDataAttribute((ushort)TiffType.SShort)] + [InlineDataAttribute((ushort)TiffType.SLong)] + [InlineDataAttribute((ushort)TiffType.Float)] + [InlineDataAttribute((ushort)TiffType.Double)] + [InlineDataAttribute((ushort)TiffType.Ifd)] + [InlineDataAttribute((ushort)99)] public void ReadSignedRationalArray_ThrowsExceptionIfInvalidType(ushort type) { (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, 1, new byte[4]), true); @@ -631,19 +631,19 @@ namespace ImageSharp.Tests } [Theory] - [InlineDataAttribute(TiffType.Byte)] - [InlineDataAttribute(TiffType.Ascii)] - [InlineDataAttribute(TiffType.Short)] - [InlineDataAttribute(TiffType.Long)] - [InlineDataAttribute(TiffType.Rational)] - [InlineDataAttribute(TiffType.SByte)] - [InlineDataAttribute(TiffType.Undefined)] - [InlineDataAttribute(TiffType.SShort)] - [InlineDataAttribute(TiffType.SLong)] - [InlineDataAttribute(TiffType.SRational)] - [InlineDataAttribute(TiffType.Double)] - [InlineDataAttribute(TiffType.Ifd)] - [InlineDataAttribute((TiffType)99)] + [InlineDataAttribute((ushort)TiffType.Byte)] + [InlineDataAttribute((ushort)TiffType.Ascii)] + [InlineDataAttribute((ushort)TiffType.Short)] + [InlineDataAttribute((ushort)TiffType.Long)] + [InlineDataAttribute((ushort)TiffType.Rational)] + [InlineDataAttribute((ushort)TiffType.SByte)] + [InlineDataAttribute((ushort)TiffType.Undefined)] + [InlineDataAttribute((ushort)TiffType.SShort)] + [InlineDataAttribute((ushort)TiffType.SLong)] + [InlineDataAttribute((ushort)TiffType.SRational)] + [InlineDataAttribute((ushort)TiffType.Double)] + [InlineDataAttribute((ushort)TiffType.Ifd)] + [InlineDataAttribute((ushort)99)] public void ReadFloat_ThrowsExceptionIfInvalidType(ushort type) { (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, 1, new byte[4]), true); @@ -691,19 +691,19 @@ namespace ImageSharp.Tests } [Theory] - [InlineDataAttribute(TiffType.Byte)] - [InlineDataAttribute(TiffType.Ascii)] - [InlineDataAttribute(TiffType.Short)] - [InlineDataAttribute(TiffType.Long)] - [InlineDataAttribute(TiffType.Rational)] - [InlineDataAttribute(TiffType.SByte)] - [InlineDataAttribute(TiffType.Undefined)] - [InlineDataAttribute(TiffType.SShort)] - [InlineDataAttribute(TiffType.SLong)] - [InlineDataAttribute(TiffType.SRational)] - [InlineDataAttribute(TiffType.Double)] - [InlineDataAttribute(TiffType.Ifd)] - [InlineDataAttribute((TiffType)99)] + [InlineDataAttribute((ushort)TiffType.Byte)] + [InlineDataAttribute((ushort)TiffType.Ascii)] + [InlineDataAttribute((ushort)TiffType.Short)] + [InlineDataAttribute((ushort)TiffType.Long)] + [InlineDataAttribute((ushort)TiffType.Rational)] + [InlineDataAttribute((ushort)TiffType.SByte)] + [InlineDataAttribute((ushort)TiffType.Undefined)] + [InlineDataAttribute((ushort)TiffType.SShort)] + [InlineDataAttribute((ushort)TiffType.SLong)] + [InlineDataAttribute((ushort)TiffType.SRational)] + [InlineDataAttribute((ushort)TiffType.Double)] + [InlineDataAttribute((ushort)TiffType.Ifd)] + [InlineDataAttribute((ushort)99)] public void ReadFloatArray_ThrowsExceptionIfInvalidType(ushort type) { (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, 1, new byte[4]), true); @@ -740,19 +740,19 @@ namespace ImageSharp.Tests } [Theory] - [InlineDataAttribute(TiffType.Byte)] - [InlineDataAttribute(TiffType.Ascii)] - [InlineDataAttribute(TiffType.Short)] - [InlineDataAttribute(TiffType.Long)] - [InlineDataAttribute(TiffType.Rational)] - [InlineDataAttribute(TiffType.SByte)] - [InlineDataAttribute(TiffType.Undefined)] - [InlineDataAttribute(TiffType.SShort)] - [InlineDataAttribute(TiffType.SLong)] - [InlineDataAttribute(TiffType.SRational)] - [InlineDataAttribute(TiffType.Float)] - [InlineDataAttribute(TiffType.Ifd)] - [InlineDataAttribute((TiffType)99)] + [InlineDataAttribute((ushort)TiffType.Byte)] + [InlineDataAttribute((ushort)TiffType.Ascii)] + [InlineDataAttribute((ushort)TiffType.Short)] + [InlineDataAttribute((ushort)TiffType.Long)] + [InlineDataAttribute((ushort)TiffType.Rational)] + [InlineDataAttribute((ushort)TiffType.SByte)] + [InlineDataAttribute((ushort)TiffType.Undefined)] + [InlineDataAttribute((ushort)TiffType.SShort)] + [InlineDataAttribute((ushort)TiffType.SLong)] + [InlineDataAttribute((ushort)TiffType.SRational)] + [InlineDataAttribute((ushort)TiffType.Float)] + [InlineDataAttribute((ushort)TiffType.Ifd)] + [InlineDataAttribute((ushort)99)] public void ReadDouble_ThrowsExceptionIfInvalidType(ushort type) { (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, 1, new byte[4]), true); @@ -803,19 +803,19 @@ namespace ImageSharp.Tests } [Theory] - [InlineDataAttribute(TiffType.Byte)] - [InlineDataAttribute(TiffType.Ascii)] - [InlineDataAttribute(TiffType.Short)] - [InlineDataAttribute(TiffType.Long)] - [InlineDataAttribute(TiffType.Rational)] - [InlineDataAttribute(TiffType.SByte)] - [InlineDataAttribute(TiffType.Undefined)] - [InlineDataAttribute(TiffType.SShort)] - [InlineDataAttribute(TiffType.SLong)] - [InlineDataAttribute(TiffType.SRational)] - [InlineDataAttribute(TiffType.Float)] - [InlineDataAttribute(TiffType.Ifd)] - [InlineDataAttribute((TiffType)99)] + [InlineDataAttribute((ushort)TiffType.Byte)] + [InlineDataAttribute((ushort)TiffType.Ascii)] + [InlineDataAttribute((ushort)TiffType.Short)] + [InlineDataAttribute((ushort)TiffType.Long)] + [InlineDataAttribute((ushort)TiffType.Rational)] + [InlineDataAttribute((ushort)TiffType.SByte)] + [InlineDataAttribute((ushort)TiffType.Undefined)] + [InlineDataAttribute((ushort)TiffType.SShort)] + [InlineDataAttribute((ushort)TiffType.SLong)] + [InlineDataAttribute((ushort)TiffType.SRational)] + [InlineDataAttribute((ushort)TiffType.Float)] + [InlineDataAttribute((ushort)TiffType.Ifd)] + [InlineDataAttribute((ushort)99)] public void ReadDoubleArray_ThrowsExceptionIfInvalidType(ushort type) { (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, 1, new byte[4]), true); diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdTests.cs index a8d01cf27..fc557bf6f 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdTests.cs @@ -101,7 +101,7 @@ namespace ImageSharp.Tests TiffIfdEntry entry = ifd.Entries[1]; byte[] expectedData = isLittleEndian ? new byte[] { 210, 0, 0, 0 } : new byte[] { 0, 0, 0, 210 }; - Assert.NotNull(entry); + Assert.Equal(TiffTags.ImageLength, entry.Tag); Assert.Equal(TiffType.Long, entry.Type); Assert.Equal(1u, entry.Count); diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs index 3d6cf355a..efb02f318 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs @@ -68,16 +68,16 @@ namespace ImageSharp.Tests } [Theory] - [InlineData(false, TiffCompression.None, TiffCompressionType.None)] - [InlineData(true, TiffCompression.None, TiffCompressionType.None)] - [InlineData(false, TiffCompression.PackBits, TiffCompressionType.PackBits)] - [InlineData(true, TiffCompression.PackBits, TiffCompressionType.PackBits)] - [InlineData(false, TiffCompression.Deflate, TiffCompressionType.Deflate)] - [InlineData(true, TiffCompression.Deflate, TiffCompressionType.Deflate)] - [InlineData(false, TiffCompression.OldDeflate, TiffCompressionType.Deflate)] - [InlineData(true, TiffCompression.OldDeflate, TiffCompressionType.Deflate)] - [InlineData(false, TiffCompression.Lzw, TiffCompressionType.Lzw)] - [InlineData(true, TiffCompression.Lzw, TiffCompressionType.Lzw)] + [InlineData(false, (ushort)TiffCompression.None, (int)TiffCompressionType.None)] + [InlineData(true, (ushort)TiffCompression.None, (int)TiffCompressionType.None)] + [InlineData(false, (ushort)TiffCompression.PackBits, (int)TiffCompressionType.PackBits)] + [InlineData(true, (ushort)TiffCompression.PackBits, (int)TiffCompressionType.PackBits)] + [InlineData(false, (ushort)TiffCompression.Deflate, (int)TiffCompressionType.Deflate)] + [InlineData(true, (ushort)TiffCompression.Deflate, (int)TiffCompressionType.Deflate)] + [InlineData(false, (ushort)TiffCompression.OldDeflate, (int)TiffCompressionType.Deflate)] + [InlineData(true, (ushort)TiffCompression.OldDeflate, (int)TiffCompressionType.Deflate)] + [InlineData(false, (ushort)TiffCompression.Lzw, (int)TiffCompressionType.Lzw)] + [InlineData(true, (ushort)TiffCompression.Lzw, (int)TiffCompressionType.Lzw)] public void ReadImageFormat_DeterminesCorrectCompressionImplementation(bool isLittleEndian, ushort compression, int compressionType) { Stream stream = CreateTiffGenIfd() @@ -92,21 +92,21 @@ namespace ImageSharp.Tests } [Theory] - [InlineData(false, TiffCompression.Ccitt1D)] - [InlineData(false, TiffCompression.CcittGroup3Fax)] - [InlineData(false, TiffCompression.CcittGroup4Fax)] - [InlineData(false, TiffCompression.ItuTRecT43)] - [InlineData(false, TiffCompression.ItuTRecT82)] - [InlineData(false, TiffCompression.Jpeg)] - [InlineData(false, TiffCompression.OldJpeg)] + [InlineData(false, (ushort)TiffCompression.Ccitt1D)] + [InlineData(false, (ushort)TiffCompression.CcittGroup3Fax)] + [InlineData(false, (ushort)TiffCompression.CcittGroup4Fax)] + [InlineData(false, (ushort)TiffCompression.ItuTRecT43)] + [InlineData(false, (ushort)TiffCompression.ItuTRecT82)] + [InlineData(false, (ushort)TiffCompression.Jpeg)] + [InlineData(false, (ushort)TiffCompression.OldJpeg)] [InlineData(false, 999)] - [InlineData(true, TiffCompression.Ccitt1D)] - [InlineData(true, TiffCompression.CcittGroup3Fax)] - [InlineData(true, TiffCompression.CcittGroup4Fax)] - [InlineData(true, TiffCompression.ItuTRecT43)] - [InlineData(true, TiffCompression.ItuTRecT82)] - [InlineData(true, TiffCompression.Jpeg)] - [InlineData(true, TiffCompression.OldJpeg)] + [InlineData(true, (ushort)TiffCompression.Ccitt1D)] + [InlineData(true, (ushort)TiffCompression.CcittGroup3Fax)] + [InlineData(true, (ushort)TiffCompression.CcittGroup4Fax)] + [InlineData(true, (ushort)TiffCompression.ItuTRecT43)] + [InlineData(true, (ushort)TiffCompression.ItuTRecT82)] + [InlineData(true, (ushort)TiffCompression.Jpeg)] + [InlineData(true, (ushort)TiffCompression.OldJpeg)] [InlineData(true, 999)] public void ReadImageFormat_ThrowsExceptionForUnsupportedCompression(bool isLittleEndian, ushort compression) { @@ -123,34 +123,34 @@ namespace ImageSharp.Tests } [Theory] - [InlineData(false, TiffPhotometricInterpretation.WhiteIsZero, new[] { 3 }, TiffColorType.WhiteIsZero)] - [InlineData(true, TiffPhotometricInterpretation.WhiteIsZero, new[] { 3 }, TiffColorType.WhiteIsZero)] - [InlineData(false, TiffPhotometricInterpretation.WhiteIsZero, new[] { 8 }, TiffColorType.WhiteIsZero8)] - [InlineData(true, TiffPhotometricInterpretation.WhiteIsZero, new[] { 8 }, TiffColorType.WhiteIsZero8)] - [InlineData(false, TiffPhotometricInterpretation.WhiteIsZero, new[] { 4 }, TiffColorType.WhiteIsZero4)] - [InlineData(true, TiffPhotometricInterpretation.WhiteIsZero, new[] { 4 }, TiffColorType.WhiteIsZero4)] - [InlineData(false, TiffPhotometricInterpretation.WhiteIsZero, new[] { 1 }, TiffColorType.WhiteIsZero1)] - [InlineData(true, TiffPhotometricInterpretation.WhiteIsZero, new[] { 1 }, TiffColorType.WhiteIsZero1)] - [InlineData(false, TiffPhotometricInterpretation.BlackIsZero, new[] { 3 }, TiffColorType.BlackIsZero)] - [InlineData(true, TiffPhotometricInterpretation.BlackIsZero, new[] { 3 }, TiffColorType.BlackIsZero)] - [InlineData(false, TiffPhotometricInterpretation.BlackIsZero, new[] { 8 }, TiffColorType.BlackIsZero8)] - [InlineData(true, TiffPhotometricInterpretation.BlackIsZero, new[] { 8 }, TiffColorType.BlackIsZero8)] - [InlineData(false, TiffPhotometricInterpretation.BlackIsZero, new[] { 4 }, TiffColorType.BlackIsZero4)] - [InlineData(true, TiffPhotometricInterpretation.BlackIsZero, new[] { 4 }, TiffColorType.BlackIsZero4)] - [InlineData(false, TiffPhotometricInterpretation.BlackIsZero, new[] { 1 }, TiffColorType.BlackIsZero1)] - [InlineData(true, TiffPhotometricInterpretation.BlackIsZero, new[] { 1 }, TiffColorType.BlackIsZero1)] - [InlineData(false, TiffPhotometricInterpretation.PaletteColor, new[] { 3 }, TiffColorType.PaletteColor)] - [InlineData(true, TiffPhotometricInterpretation.PaletteColor, new[] { 3 }, TiffColorType.PaletteColor)] - [InlineData(false, TiffPhotometricInterpretation.PaletteColor, new[] { 8 }, TiffColorType.PaletteColor)] - [InlineData(true, TiffPhotometricInterpretation.PaletteColor, new[] { 8 }, TiffColorType.PaletteColor)] - [InlineData(false, TiffPhotometricInterpretation.PaletteColor, new[] { 4 }, TiffColorType.PaletteColor)] - [InlineData(true, TiffPhotometricInterpretation.PaletteColor, new[] { 4 }, TiffColorType.PaletteColor)] - [InlineData(false, TiffPhotometricInterpretation.PaletteColor, new[] { 1 }, TiffColorType.PaletteColor)] - [InlineData(true, TiffPhotometricInterpretation.PaletteColor, new[] { 1 }, TiffColorType.PaletteColor)] - [InlineData(false, TiffPhotometricInterpretation.Rgb, new[] { 4, 4, 4 }, TiffColorType.Rgb)] - [InlineData(true, TiffPhotometricInterpretation.Rgb, new[] { 4, 4, 4 }, TiffColorType.Rgb)] - [InlineData(false, TiffPhotometricInterpretation.Rgb, new[] { 8, 8, 8 }, TiffColorType.Rgb888)] - [InlineData(true, TiffPhotometricInterpretation.Rgb, new[] { 8, 8, 8 }, TiffColorType.Rgb888)] + [InlineData(false, (ushort)TiffPhotometricInterpretation.WhiteIsZero, new[] { 3 }, (int)TiffColorType.WhiteIsZero)] + [InlineData(true, (ushort)TiffPhotometricInterpretation.WhiteIsZero, new[] { 3 }, (int)TiffColorType.WhiteIsZero)] + [InlineData(false, (ushort)TiffPhotometricInterpretation.WhiteIsZero, new[] { 8 }, (int)TiffColorType.WhiteIsZero8)] + [InlineData(true, (ushort)TiffPhotometricInterpretation.WhiteIsZero, new[] { 8 }, (int)TiffColorType.WhiteIsZero8)] + [InlineData(false, (ushort)TiffPhotometricInterpretation.WhiteIsZero, new[] { 4 }, (int)TiffColorType.WhiteIsZero4)] + [InlineData(true, (ushort)TiffPhotometricInterpretation.WhiteIsZero, new[] { 4 }, (int)TiffColorType.WhiteIsZero4)] + [InlineData(false, (ushort)TiffPhotometricInterpretation.WhiteIsZero, new[] { 1 }, (int)TiffColorType.WhiteIsZero1)] + [InlineData(true, (ushort)TiffPhotometricInterpretation.WhiteIsZero, new[] { 1 }, (int)TiffColorType.WhiteIsZero1)] + [InlineData(false, (ushort)TiffPhotometricInterpretation.BlackIsZero, new[] { 3 }, (int)TiffColorType.BlackIsZero)] + [InlineData(true, (ushort)TiffPhotometricInterpretation.BlackIsZero, new[] { 3 }, (int)TiffColorType.BlackIsZero)] + [InlineData(false, (ushort)TiffPhotometricInterpretation.BlackIsZero, new[] { 8 }, (int)TiffColorType.BlackIsZero8)] + [InlineData(true, (ushort)TiffPhotometricInterpretation.BlackIsZero, new[] { 8 }, (int)TiffColorType.BlackIsZero8)] + [InlineData(false, (ushort)TiffPhotometricInterpretation.BlackIsZero, new[] { 4 }, (int)TiffColorType.BlackIsZero4)] + [InlineData(true, (ushort)TiffPhotometricInterpretation.BlackIsZero, new[] { 4 }, (int)TiffColorType.BlackIsZero4)] + [InlineData(false, (ushort)TiffPhotometricInterpretation.BlackIsZero, new[] { 1 }, (int)TiffColorType.BlackIsZero1)] + [InlineData(true, (ushort)TiffPhotometricInterpretation.BlackIsZero, new[] { 1 }, (int)TiffColorType.BlackIsZero1)] + [InlineData(false, (ushort)TiffPhotometricInterpretation.PaletteColor, new[] { 3 }, (int)TiffColorType.PaletteColor)] + [InlineData(true, (ushort)TiffPhotometricInterpretation.PaletteColor, new[] { 3 }, (int)TiffColorType.PaletteColor)] + [InlineData(false, (ushort)TiffPhotometricInterpretation.PaletteColor, new[] { 8 }, (int)TiffColorType.PaletteColor)] + [InlineData(true, (ushort)TiffPhotometricInterpretation.PaletteColor, new[] { 8 }, (int)TiffColorType.PaletteColor)] + [InlineData(false, (ushort)TiffPhotometricInterpretation.PaletteColor, new[] { 4 }, (int)TiffColorType.PaletteColor)] + [InlineData(true, (ushort)TiffPhotometricInterpretation.PaletteColor, new[] { 4 }, (int)TiffColorType.PaletteColor)] + [InlineData(false, (ushort)TiffPhotometricInterpretation.PaletteColor, new[] { 1 }, (int)TiffColorType.PaletteColor)] + [InlineData(true, (ushort)TiffPhotometricInterpretation.PaletteColor, new[] { 1 }, (int)TiffColorType.PaletteColor)] + [InlineData(false, (ushort)TiffPhotometricInterpretation.Rgb, new[] { 4, 4, 4 }, (int)TiffColorType.Rgb)] + [InlineData(true, (ushort)TiffPhotometricInterpretation.Rgb, new[] { 4, 4, 4 }, (int)TiffColorType.Rgb)] + [InlineData(false, (ushort)TiffPhotometricInterpretation.Rgb, new[] { 8, 8, 8 }, (int)TiffColorType.Rgb888)] + [InlineData(true, (ushort)TiffPhotometricInterpretation.Rgb, new[] { 8, 8, 8 }, (int)TiffColorType.Rgb888)] public void ReadImageFormat_DeterminesCorrectColorImplementation_Chunky(bool isLittleEndian, ushort photometricInterpretation, int[] bitsPerSample, int colorType) { Stream stream = CreateTiffGenIfd() @@ -167,10 +167,10 @@ namespace ImageSharp.Tests } [Theory] - [InlineData(false, TiffPhotometricInterpretation.Rgb, new[] { 4, 4, 4 }, TiffColorType.RgbPlanar)] - [InlineData(true, TiffPhotometricInterpretation.Rgb, new[] { 4, 4, 4 }, TiffColorType.RgbPlanar)] - [InlineData(false, TiffPhotometricInterpretation.Rgb, new[] { 8, 8, 8 }, TiffColorType.RgbPlanar)] - [InlineData(true, TiffPhotometricInterpretation.Rgb, new[] { 8, 8, 8 }, TiffColorType.RgbPlanar)] + [InlineData(false, (ushort)TiffPhotometricInterpretation.Rgb, new[] { 4, 4, 4 }, (int)TiffColorType.RgbPlanar)] + [InlineData(true, (ushort)TiffPhotometricInterpretation.Rgb, new[] { 4, 4, 4 }, (int)TiffColorType.RgbPlanar)] + [InlineData(false, (ushort)TiffPhotometricInterpretation.Rgb, new[] { 8, 8, 8 }, (int)TiffColorType.RgbPlanar)] + [InlineData(true, (ushort)TiffPhotometricInterpretation.Rgb, new[] { 8, 8, 8 }, (int)TiffColorType.RgbPlanar)] public void ReadImageFormat_DeterminesCorrectColorImplementation_Planar(bool isLittleEndian, ushort photometricInterpretation, int[] bitsPerSample, int colorType) { Stream stream = CreateTiffGenIfd() @@ -187,10 +187,10 @@ namespace ImageSharp.Tests } [Theory] - [InlineData(false, TiffPhotometricInterpretation.WhiteIsZero, TiffColorType.WhiteIsZero1)] - [InlineData(true, TiffPhotometricInterpretation.WhiteIsZero, TiffColorType.WhiteIsZero1)] - [InlineData(false, TiffPhotometricInterpretation.BlackIsZero, TiffColorType.BlackIsZero1)] - [InlineData(true, TiffPhotometricInterpretation.BlackIsZero, TiffColorType.BlackIsZero1)] + [InlineData(false, (ushort)TiffPhotometricInterpretation.WhiteIsZero, (int)TiffColorType.WhiteIsZero1)] + [InlineData(true, (ushort)TiffPhotometricInterpretation.WhiteIsZero, (int)TiffColorType.WhiteIsZero1)] + [InlineData(false, (ushort)TiffPhotometricInterpretation.BlackIsZero, (int)TiffColorType.BlackIsZero1)] + [InlineData(true, (ushort)TiffPhotometricInterpretation.BlackIsZero, (int)TiffColorType.BlackIsZero1)] public void ReadImageFormat_DeterminesCorrectColorImplementation_DefaultsToBilevel(bool isLittleEndian, ushort photometricInterpretation, int colorType) { Stream stream = CreateTiffGenIfd() @@ -206,8 +206,8 @@ namespace ImageSharp.Tests } // [Theory] - // [InlineData(false, new[] { 8 }, TiffColorType.WhiteIsZero8)] - // [InlineData(true, new[] { 8 }, TiffColorType.WhiteIsZero8)] + // [InlineData(false, new[] { 8 }, (int)TiffColorType.WhiteIsZero8)] + // [InlineData(true, new[] { 8 }, (int)TiffColorType.WhiteIsZero8)] // public void ReadImageFormat_UsesDefaultColorImplementationForCcitt1D(bool isLittleEndian, int[] bitsPerSample, int colorType) // { // Stream stream = CreateTiffGenIfd() @@ -240,23 +240,23 @@ namespace ImageSharp.Tests } [Theory] - [InlineData(false, TiffPhotometricInterpretation.CieLab)] - [InlineData(false, TiffPhotometricInterpretation.ColorFilterArray)] - [InlineData(false, TiffPhotometricInterpretation.IccLab)] - [InlineData(false, TiffPhotometricInterpretation.ItuLab)] - [InlineData(false, TiffPhotometricInterpretation.LinearRaw)] - [InlineData(false, TiffPhotometricInterpretation.Separated)] - [InlineData(false, TiffPhotometricInterpretation.TransparencyMask)] - [InlineData(false, TiffPhotometricInterpretation.YCbCr)] + [InlineData(false, (ushort)TiffPhotometricInterpretation.CieLab)] + [InlineData(false, (ushort)TiffPhotometricInterpretation.ColorFilterArray)] + [InlineData(false, (ushort)TiffPhotometricInterpretation.IccLab)] + [InlineData(false, (ushort)TiffPhotometricInterpretation.ItuLab)] + [InlineData(false, (ushort)TiffPhotometricInterpretation.LinearRaw)] + [InlineData(false, (ushort)TiffPhotometricInterpretation.Separated)] + [InlineData(false, (ushort)TiffPhotometricInterpretation.TransparencyMask)] + [InlineData(false, (ushort)TiffPhotometricInterpretation.YCbCr)] [InlineData(false, 999)] - [InlineData(true, TiffPhotometricInterpretation.CieLab)] - [InlineData(true, TiffPhotometricInterpretation.ColorFilterArray)] - [InlineData(true, TiffPhotometricInterpretation.IccLab)] - [InlineData(true, TiffPhotometricInterpretation.ItuLab)] - [InlineData(true, TiffPhotometricInterpretation.LinearRaw)] - [InlineData(true, TiffPhotometricInterpretation.Separated)] - [InlineData(true, TiffPhotometricInterpretation.TransparencyMask)] - [InlineData(true, TiffPhotometricInterpretation.YCbCr)] + [InlineData(true, (ushort)TiffPhotometricInterpretation.CieLab)] + [InlineData(true, (ushort)TiffPhotometricInterpretation.ColorFilterArray)] + [InlineData(true, (ushort)TiffPhotometricInterpretation.IccLab)] + [InlineData(true, (ushort)TiffPhotometricInterpretation.ItuLab)] + [InlineData(true, (ushort)TiffPhotometricInterpretation.LinearRaw)] + [InlineData(true, (ushort)TiffPhotometricInterpretation.Separated)] + [InlineData(true, (ushort)TiffPhotometricInterpretation.TransparencyMask)] + [InlineData(true, (ushort)TiffPhotometricInterpretation.YCbCr)] [InlineData(true, 999)] public void ReadImageFormat_ThrowsExceptionForUnsupportedPhotometricInterpretation(bool isLittleEndian, ushort photometricInterpretation) { @@ -297,10 +297,10 @@ namespace ImageSharp.Tests } [Theory] - [InlineData(false, TiffPhotometricInterpretation.WhiteIsZero)] - [InlineData(true, TiffPhotometricInterpretation.WhiteIsZero)] - [InlineData(false, TiffPhotometricInterpretation.BlackIsZero)] - [InlineData(true, TiffPhotometricInterpretation.BlackIsZero)] + [InlineData(false, (ushort)TiffPhotometricInterpretation.WhiteIsZero)] + [InlineData(true, (ushort)TiffPhotometricInterpretation.WhiteIsZero)] + [InlineData(false, (ushort)TiffPhotometricInterpretation.BlackIsZero)] + [InlineData(true, (ushort)TiffPhotometricInterpretation.BlackIsZero)] public void ReadImageFormat_ReadsBitsPerSample_DefaultsToBilevel(bool isLittleEndian, ushort photometricInterpretation) { Stream stream = CreateTiffGenIfd() @@ -333,24 +333,24 @@ namespace ImageSharp.Tests } [Theory] - [InlineData(false, TiffPhotometricInterpretation.WhiteIsZero, new int[] { })] - [InlineData(true, TiffPhotometricInterpretation.WhiteIsZero, new int[] { })] - [InlineData(false, TiffPhotometricInterpretation.BlackIsZero, new int[] { })] - [InlineData(true, TiffPhotometricInterpretation.BlackIsZero, new int[] { })] - [InlineData(false, TiffPhotometricInterpretation.PaletteColor, new int[] { })] - [InlineData(true, TiffPhotometricInterpretation.PaletteColor, new int[] { })] - [InlineData(false, TiffPhotometricInterpretation.Rgb, new int[] { })] - [InlineData(true, TiffPhotometricInterpretation.Rgb, new int[] { })] - [InlineData(false, TiffPhotometricInterpretation.WhiteIsZero, new[] { 8, 8 })] - [InlineData(true, TiffPhotometricInterpretation.WhiteIsZero, new[] { 8, 8 })] - [InlineData(false, TiffPhotometricInterpretation.BlackIsZero, new[] { 8, 8 })] - [InlineData(true, TiffPhotometricInterpretation.BlackIsZero, new[] { 8, 8 })] - [InlineData(false, TiffPhotometricInterpretation.PaletteColor, new[] { 8, 8 })] - [InlineData(true, TiffPhotometricInterpretation.PaletteColor, new[] { 8, 8 })] - [InlineData(false, TiffPhotometricInterpretation.Rgb, new[] { 8 })] - [InlineData(true, TiffPhotometricInterpretation.Rgb, new[] { 8 })] - [InlineData(false, TiffPhotometricInterpretation.Rgb, new[] { 8, 8 })] - [InlineData(true, TiffPhotometricInterpretation.Rgb, new[] { 8, 8 })] + [InlineData(false, (ushort)TiffPhotometricInterpretation.WhiteIsZero, new int[] { })] + [InlineData(true, (ushort)TiffPhotometricInterpretation.WhiteIsZero, new int[] { })] + [InlineData(false, (ushort)TiffPhotometricInterpretation.BlackIsZero, new int[] { })] + [InlineData(true, (ushort)TiffPhotometricInterpretation.BlackIsZero, new int[] { })] + [InlineData(false, (ushort)TiffPhotometricInterpretation.PaletteColor, new int[] { })] + [InlineData(true, (ushort)TiffPhotometricInterpretation.PaletteColor, new int[] { })] + [InlineData(false, (ushort)TiffPhotometricInterpretation.Rgb, new int[] { })] + [InlineData(true, (ushort)TiffPhotometricInterpretation.Rgb, new int[] { })] + [InlineData(false, (ushort)TiffPhotometricInterpretation.WhiteIsZero, new[] { 8, 8 })] + [InlineData(true, (ushort)TiffPhotometricInterpretation.WhiteIsZero, new[] { 8, 8 })] + [InlineData(false, (ushort)TiffPhotometricInterpretation.BlackIsZero, new[] { 8, 8 })] + [InlineData(true, (ushort)TiffPhotometricInterpretation.BlackIsZero, new[] { 8, 8 })] + [InlineData(false, (ushort)TiffPhotometricInterpretation.PaletteColor, new[] { 8, 8 })] + [InlineData(true, (ushort)TiffPhotometricInterpretation.PaletteColor, new[] { 8, 8 })] + [InlineData(false, (ushort)TiffPhotometricInterpretation.Rgb, new[] { 8 })] + [InlineData(true, (ushort)TiffPhotometricInterpretation.Rgb, new[] { 8 })] + [InlineData(false, (ushort)TiffPhotometricInterpretation.Rgb, new[] { 8, 8 })] + [InlineData(true, (ushort)TiffPhotometricInterpretation.Rgb, new[] { 8, 8 })] public void ReadImageFormat_ThrowsExceptionForUnsupportedNumberOfSamples(bool isLittleEndian, ushort photometricInterpretation, int[] bitsPerSample) { Stream stream = CreateTiffGenIfd() @@ -400,10 +400,10 @@ namespace ImageSharp.Tests } [Theory] - [InlineData(false, TiffPlanarConfiguration.Chunky)] - [InlineData(true, TiffPlanarConfiguration.Chunky)] - [InlineData(false, TiffPlanarConfiguration.Planar)] - [InlineData(true, TiffPlanarConfiguration.Planar)] + [InlineData(false, (ushort)TiffPlanarConfiguration.Chunky)] + [InlineData(true, (ushort)TiffPlanarConfiguration.Chunky)] + [InlineData(false, (ushort)TiffPlanarConfiguration.Planar)] + [InlineData(true, (ushort)TiffPlanarConfiguration.Planar)] public void ReadImageFormat_ReadsPlanarConfiguration(bool isLittleEndian, int planarConfiguration) { Stream stream = CreateTiffGenIfd() @@ -437,21 +437,21 @@ namespace ImageSharp.Tests } [Theory] - [InlineData(TiffColorType.WhiteIsZero, new uint[] { 1 }, 160, 80, 20 * 80)] - [InlineData(TiffColorType.WhiteIsZero, new uint[] { 1 }, 153, 80, 20 * 80)] - [InlineData(TiffColorType.WhiteIsZero, new uint[] { 3 }, 100, 80, 38 * 80)] - [InlineData(TiffColorType.WhiteIsZero, new uint[] { 4 }, 100, 80, 50 * 80)] - [InlineData(TiffColorType.WhiteIsZero, new uint[] { 4 }, 99, 80, 50 * 80)] - [InlineData(TiffColorType.WhiteIsZero, new uint[] { 8 }, 100, 80, 100 * 80)] - [InlineData(TiffColorType.PaletteColor, new uint[] { 1 }, 160, 80, 20 * 80)] - [InlineData(TiffColorType.PaletteColor, new uint[] { 1 }, 153, 80, 20 * 80)] - [InlineData(TiffColorType.PaletteColor, new uint[] { 3 }, 100, 80, 38 * 80)] - [InlineData(TiffColorType.PaletteColor, new uint[] { 4 }, 100, 80, 50 * 80)] - [InlineData(TiffColorType.PaletteColor, new uint[] { 4 }, 99, 80, 50 * 80)] - [InlineData(TiffColorType.PaletteColor, new uint[] { 8 }, 100, 80, 100 * 80)] - [InlineData(TiffColorType.Rgb, new uint[] { 8, 8, 8 }, 100, 80, 300 * 80)] - [InlineData(TiffColorType.Rgb, new uint[] { 4, 4, 4 }, 100, 80, 150 * 80)] - [InlineData(TiffColorType.Rgb, new uint[] { 4, 8, 4 }, 100, 80, 200 * 80)] + [InlineData((ushort)TiffColorType.WhiteIsZero, new uint[] { 1 }, 160, 80, 20 * 80)] + [InlineData((ushort)TiffColorType.WhiteIsZero, new uint[] { 1 }, 153, 80, 20 * 80)] + [InlineData((ushort)TiffColorType.WhiteIsZero, new uint[] { 3 }, 100, 80, 38 * 80)] + [InlineData((ushort)TiffColorType.WhiteIsZero, new uint[] { 4 }, 100, 80, 50 * 80)] + [InlineData((ushort)TiffColorType.WhiteIsZero, new uint[] { 4 }, 99, 80, 50 * 80)] + [InlineData((ushort)TiffColorType.WhiteIsZero, new uint[] { 8 }, 100, 80, 100 * 80)] + [InlineData((ushort)TiffColorType.PaletteColor, new uint[] { 1 }, 160, 80, 20 * 80)] + [InlineData((ushort)TiffColorType.PaletteColor, new uint[] { 1 }, 153, 80, 20 * 80)] + [InlineData((ushort)TiffColorType.PaletteColor, new uint[] { 3 }, 100, 80, 38 * 80)] + [InlineData((ushort)TiffColorType.PaletteColor, new uint[] { 4 }, 100, 80, 50 * 80)] + [InlineData((ushort)TiffColorType.PaletteColor, new uint[] { 4 }, 99, 80, 50 * 80)] + [InlineData((ushort)TiffColorType.PaletteColor, new uint[] { 8 }, 100, 80, 100 * 80)] + [InlineData((ushort)TiffColorType.Rgb, new uint[] { 8, 8, 8 }, 100, 80, 300 * 80)] + [InlineData((ushort)TiffColorType.Rgb, new uint[] { 4, 4, 4 }, 100, 80, 150 * 80)] + [InlineData((ushort)TiffColorType.Rgb, new uint[] { 4, 8, 4 }, 100, 80, 200 * 80)] public void CalculateImageBufferSize_ReturnsCorrectSize_Chunky(ushort colorType, uint[] bitsPerSample, int width, int height, int expectedResult) { TiffDecoderCore decoder = new TiffDecoderCore(null, null); @@ -465,18 +465,18 @@ namespace ImageSharp.Tests } [Theory] - [InlineData(TiffColorType.Rgb, new uint[] { 8, 8, 8 }, 100, 80, 0, 100 * 80)] - [InlineData(TiffColorType.Rgb, new uint[] { 8, 8, 8 }, 100, 80, 1, 100 * 80)] - [InlineData(TiffColorType.Rgb, new uint[] { 8, 8, 8 }, 100, 80, 2, 100 * 80)] - [InlineData(TiffColorType.Rgb, new uint[] { 4, 4, 4 }, 100, 80, 0, 50 * 80)] - [InlineData(TiffColorType.Rgb, new uint[] { 4, 4, 4 }, 100, 80, 1, 50 * 80)] - [InlineData(TiffColorType.Rgb, new uint[] { 4, 4, 4 }, 100, 80, 2, 50 * 80)] - [InlineData(TiffColorType.Rgb, new uint[] { 4, 8, 4 }, 100, 80, 0, 50 * 80)] - [InlineData(TiffColorType.Rgb, new uint[] { 4, 8, 4 }, 100, 80, 1, 100 * 80)] - [InlineData(TiffColorType.Rgb, new uint[] { 4, 8, 4 }, 100, 80, 2, 50 * 80)] - [InlineData(TiffColorType.Rgb, new uint[] { 4, 8, 4 }, 99, 80, 0, 50 * 80)] - [InlineData(TiffColorType.Rgb, new uint[] { 4, 8, 4 }, 99, 80, 1, 99 * 80)] - [InlineData(TiffColorType.Rgb, new uint[] { 4, 8, 4 }, 99, 80, 2, 50 * 80)] + [InlineData((ushort)TiffColorType.Rgb, new uint[] { 8, 8, 8 }, 100, 80, 0, 100 * 80)] + [InlineData((ushort)TiffColorType.Rgb, new uint[] { 8, 8, 8 }, 100, 80, 1, 100 * 80)] + [InlineData((ushort)TiffColorType.Rgb, new uint[] { 8, 8, 8 }, 100, 80, 2, 100 * 80)] + [InlineData((ushort)TiffColorType.Rgb, new uint[] { 4, 4, 4 }, 100, 80, 0, 50 * 80)] + [InlineData((ushort)TiffColorType.Rgb, new uint[] { 4, 4, 4 }, 100, 80, 1, 50 * 80)] + [InlineData((ushort)TiffColorType.Rgb, new uint[] { 4, 4, 4 }, 100, 80, 2, 50 * 80)] + [InlineData((ushort)TiffColorType.Rgb, new uint[] { 4, 8, 4 }, 100, 80, 0, 50 * 80)] + [InlineData((ushort)TiffColorType.Rgb, new uint[] { 4, 8, 4 }, 100, 80, 1, 100 * 80)] + [InlineData((ushort)TiffColorType.Rgb, new uint[] { 4, 8, 4 }, 100, 80, 2, 50 * 80)] + [InlineData((ushort)TiffColorType.Rgb, new uint[] { 4, 8, 4 }, 99, 80, 0, 50 * 80)] + [InlineData((ushort)TiffColorType.Rgb, new uint[] { 4, 8, 4 }, 99, 80, 1, 99 * 80)] + [InlineData((ushort)TiffColorType.Rgb, new uint[] { 4, 8, 4 }, 99, 80, 2, 50 * 80)] public void CalculateImageBufferSize_ReturnsCorrectSize_Planar(ushort colorType, uint[] bitsPerSample, int width, int height, int plane, int expectedResult) { diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs index e418d0d67..ab2ab5f75 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs @@ -132,7 +132,7 @@ namespace ImageSharp.Tests decoder.ReadMetadata(ifd, image); var metadata = image.MetaData.Properties.FirstOrDefault(m => m.Name == metadataName)?.Value; - Assert.Equal(null, metadata); + Assert.Null(metadata); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs index 97e46ef4e..26ec20963 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs @@ -35,7 +35,7 @@ namespace ImageSharp.Tests TiffIfdEntry? entry = ifd.GetIfdEntry(30); - Assert.Equal(true, entry.HasValue); + Assert.True(entry.HasValue); Assert.Equal(30, entry.Value.Tag); } @@ -53,7 +53,7 @@ namespace ImageSharp.Tests TiffIfdEntry? entry = ifd.GetIfdEntry(25); - Assert.Equal(false, entry.HasValue); + Assert.False(entry.HasValue); } [Fact] @@ -70,7 +70,7 @@ namespace ImageSharp.Tests bool success = ifd.TryGetIfdEntry(30, out var entry); - Assert.Equal(true, success); + Assert.True(success); Assert.Equal(30, entry.Tag); } @@ -88,7 +88,7 @@ namespace ImageSharp.Tests bool success = ifd.TryGetIfdEntry(25, out var entry); - Assert.Equal(false, success); + Assert.False(success); Assert.Equal(0, entry.Tag); } } diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Utils/SubStreamTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Utils/SubStreamTests.cs index b57a77c74..34997a90c 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Utils/SubStreamTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Utils/SubStreamTests.cs @@ -316,6 +316,7 @@ namespace ImageSharp.Tests Assert.Equal("Invalid seek origin.", e.Message); } + [Fact] public void SetLength_ThrowsNotSupportedException() { Stream innerStream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); diff --git a/tests/ImageSharp.Formats.Tiff.Tests/ImageSharp.Formats.Tiff.Tests.csproj b/tests/ImageSharp.Formats.Tiff.Tests/ImageSharp.Formats.Tiff.Tests.csproj index 3e861f778..8d3d1db36 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/ImageSharp.Formats.Tiff.Tests.csproj +++ b/tests/ImageSharp.Formats.Tiff.Tests/ImageSharp.Formats.Tiff.Tests.csproj @@ -8,8 +8,8 @@ - - + + From bace18e4abf5233758e4fc727ca4d6e9343d4a14 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Wed, 5 Jul 2017 13:14:38 +0100 Subject: [PATCH 057/275] Use new configuration API for TIFF codec --- .../Formats/Tiff/Constants/TiffConstants.cs | 12 ++ .../Formats/Tiff/ITiffDecoderOptions.cs | 18 +++ .../Formats/Tiff/ITiffEncoderOptions.cs | 2 +- .../Formats/Tiff/ImageExtensions.cs | 8 +- .../Formats/Tiff/TiffConfigurationModule.cs | 21 ++++ src/ImageSharp/Formats/Tiff/TiffDecoder.cs | 12 +- .../Formats/Tiff/TiffDecoderCore.cs | 22 ++-- src/ImageSharp/Formats/Tiff/TiffEncoder.cs | 16 +-- .../Formats/Tiff/TiffEncoderCore.cs | 7 +- .../Formats/Tiff/TiffEncoderOptions.cs | 40 ------- src/ImageSharp/Formats/Tiff/TiffFormat.cs | 23 +--- .../Formats/Tiff/TiffImageFormatDetector.cs | 36 ++++++ src/ImageSharp/ImageFormats.cs | 5 + .../Formats/Tiff/TiffDecoderHeaderTests.cs | 6 +- .../Formats/Tiff/TiffDecoderMetadataTests.cs | 4 +- .../Formats/Tiff/TiffFormatTests.cs | 104 +----------------- .../Tiff/TiffImageFormatDetectorTests.cs | 89 +++++++++++++++ .../ImageSharp.Formats.Tiff.Tests.csproj | 2 +- 18 files changed, 227 insertions(+), 200 deletions(-) create mode 100644 src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs create mode 100644 src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs delete mode 100644 src/ImageSharp/Formats/Tiff/TiffEncoderOptions.cs create mode 100644 src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs create mode 100644 tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffImageFormatDetectorTests.cs diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs index 5c03d33b0..cd88ccee7 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs @@ -5,6 +5,8 @@ namespace ImageSharp.Formats.Tiff { + using System.Collections.Generic; + /// /// Defines constants defined in the TIFF specification. /// @@ -69,5 +71,15 @@ namespace ImageSharp.Formats.Tiff /// Size (in bytes) of the Double data type /// public const int SizeOfDouble = 8; + + /// + /// The list of mimetypes that equate to a tiff. + /// + public static readonly IEnumerable MimeTypes = new[] { "image/tiff", "image/tiff-fx" }; + + /// + /// The list of file extensions that equate to a tiff. + /// + public static readonly IEnumerable FileExtensions = new[] { "tiff", "tif" }; } } diff --git a/src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs b/src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs new file mode 100644 index 000000000..9f4562134 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs @@ -0,0 +1,18 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + /// + /// Encapsulates the options for the . + /// + public interface ITiffDecoderOptions + { + /// + /// Gets a value indicating whether the metadata should be ignored when the image is being decoded. + /// + bool IgnoreMetadata { get; } + } +} diff --git a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs index df2ad7770..eefb484c2 100644 --- a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs +++ b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs @@ -8,7 +8,7 @@ namespace ImageSharp.Formats /// /// Encapsulates the options for the . /// - public interface ITiffEncoderOptions : IEncoderOptions + public interface ITiffEncoderOptions { } } diff --git a/src/ImageSharp/Formats/Tiff/ImageExtensions.cs b/src/ImageSharp/Formats/Tiff/ImageExtensions.cs index db160bd9f..470c09c72 100644 --- a/src/ImageSharp/Formats/Tiff/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Tiff/ImageExtensions.cs @@ -36,16 +36,16 @@ namespace ImageSharp /// The pixel format. /// The image this method extends. /// The stream to save the image to. - /// The options for the encoder. + /// The options for the encoder. /// Thrown if the stream is null. /// /// The . /// - public static Image SaveAsTiff(this Image source, Stream stream, ITiffEncoderOptions options) + public static Image SaveAsTiff(this Image source, Stream stream, TiffEncoder encoder) where TPixel : struct, IPixel { - TiffEncoder encoder = new TiffEncoder(); - encoder.Encode(source, stream, options); + encoder = encoder ?? new TiffEncoder(); + encoder.Encode(source, stream); return source; } diff --git a/src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs b/src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs new file mode 100644 index 000000000..3e280ce83 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs @@ -0,0 +1,21 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + /// + /// Registers the image encoders, decoders and mime type detectors for the TIFF format. + /// + public sealed class TiffConfigurationModule : IConfigurationModule + { + /// + public void Configure(Configuration host) + { + host.SetEncoder(ImageFormats.Tiff, new TiffEncoder()); + host.SetDecoder(ImageFormats.Tiff, new TiffDecoder()); + host.AddImageFormatDetector(new TiffImageFormatDetector()); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs index 6a605c878..250b02915 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs @@ -6,20 +6,26 @@ namespace ImageSharp.Formats { using System.IO; + using ImageSharp.Formats.Tiff; using ImageSharp.PixelFormats; /// /// Image decoder for generating an image out of a TIFF stream. /// - public class TiffDecoder : IImageDecoder + public class TiffDecoder : IImageDecoder, ITiffDecoderOptions { + /// + /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. + /// + public bool IgnoreMetadata { get; set; } + /// - public Image Decode(Configuration configuration, Stream stream, IDecoderOptions options) + public Image Decode(Configuration configuration, Stream stream) where TPixel : struct, IPixel { Guard.NotNull(stream, "stream"); - using (TiffDecoderCore decoder = new TiffDecoderCore(options, configuration)) + using (TiffDecoderCore decoder = new TiffDecoderCore(configuration, this)) { return decoder.Decode(stream); } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index d2446bb76..de42a0345 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -18,24 +18,26 @@ namespace ImageSharp.Formats internal class TiffDecoderCore : IDisposable { /// - /// The decoder options. + /// The global configuration /// - private readonly IDecoderOptions options; + private readonly Configuration configuration; /// - /// The global configuration + /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. /// - private readonly Configuration configuration; + private bool ignoreMetadata; /// /// Initializes a new instance of the class. /// - /// The decoder options. /// The configuration. - public TiffDecoderCore(IDecoderOptions options, Configuration configuration) + /// The decoder options. + public TiffDecoderCore(Configuration configuration, ITiffDecoderOptions options) { + options = options ?? new TiffDecoder(); + this.configuration = configuration ?? Configuration.Default; - this.options = options ?? new DecoderOptions(); + this.ignoreMetadata = options.IgnoreMetadata; } /// @@ -45,8 +47,8 @@ namespace ImageSharp.Formats /// A flag indicating if the file is encoded in little-endian or big-endian format. /// The decoder options. /// The configuration. - public TiffDecoderCore(Stream stream, bool isLittleEndian, IDecoderOptions options, Configuration configuration) - : this(options, configuration) + public TiffDecoderCore(Stream stream, bool isLittleEndian, Configuration configuration, ITiffDecoderOptions options) + : this(configuration, options) { this.InputStream = stream; this.IsLittleEndian = isLittleEndian; @@ -252,7 +254,7 @@ namespace ImageSharp.Formats } } - if (!this.options.IgnoreMetadata) + if (!this.ignoreMetadata) { if (ifd.TryGetIfdEntry(TiffTags.Artist, out TiffIfdEntry artistEntry)) { diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs index 75ff7dcd4..6f84bd852 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs @@ -12,28 +12,18 @@ namespace ImageSharp.Formats /// /// Encoder for writing the data image to a stream in TIFF format. /// - public class TiffEncoder : IImageEncoder + public class TiffEncoder : IImageEncoder, ITiffEncoderOptions { - /// - public void Encode(Image image, Stream stream, IEncoderOptions options) - where TPixel : struct, IPixel - { - ITiffEncoderOptions tiffOptions = TiffEncoderOptions.Create(options); - - this.Encode(image, stream, tiffOptions); - } - /// /// Encodes the image to the specified stream from the . /// /// The pixel format. /// The to encode from. /// The to encode the image data to. - /// The options for the encoder. - public void Encode(Image image, Stream stream, ITiffEncoderOptions options) + public void Encode(Image image, Stream stream) where TPixel : struct, IPixel { - var encode = new TiffEncoderCore(options); + var encode = new TiffEncoderCore(this); encode.Encode(image, stream); } } diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 5f1148ade..d04b221d8 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -25,18 +25,13 @@ namespace ImageSharp.Formats /// internal sealed class TiffEncoderCore { - /// - /// The options for the encoder. - /// - private readonly ITiffEncoderOptions options; - /// /// Initializes a new instance of the class. /// /// The options for the encoder. public TiffEncoderCore(ITiffEncoderOptions options) { - this.options = options ?? new TiffEncoderOptions(); + options = options ?? new TiffEncoder(); } /// diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderOptions.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderOptions.cs deleted file mode 100644 index 3a9ae8aa2..000000000 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderOptions.cs +++ /dev/null @@ -1,40 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Formats -{ - /// - /// Encapsulates the options for the . - /// - public sealed class TiffEncoderOptions : EncoderOptions, ITiffEncoderOptions - { - /// - /// Initializes a new instance of the class. - /// - public TiffEncoderOptions() - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The options for the encoder. - private TiffEncoderOptions(IEncoderOptions options) - : base(options) - { - } - - /// - /// Converts the options to a instance with a - /// cast or by creating a new instance with the specfied options. - /// - /// The options for the encoder. - /// The options for the . - internal static ITiffEncoderOptions Create(IEncoderOptions options) - { - return options as ITiffEncoderOptions ?? new TiffEncoderOptions(options); - } - } -} diff --git a/src/ImageSharp/Formats/Tiff/TiffFormat.cs b/src/ImageSharp/Formats/Tiff/TiffFormat.cs index 20090a289..c12134c37 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFormat.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFormat.cs @@ -6,6 +6,7 @@ namespace ImageSharp.Formats { using System.Collections.Generic; + using ImageSharp.Formats.Tiff; /// /// Encapsulates the means to encode and decode Tiff images. @@ -13,29 +14,15 @@ namespace ImageSharp.Formats public class TiffFormat : IImageFormat { /// - public string MimeType => "image/tiff"; + public string Name => "TIFF"; /// - public string Extension => "tif"; + public string DefaultMimeType => "image/tiff"; /// - public IEnumerable SupportedExtensions => new string[] { "tif", "tiff" }; + public IEnumerable MimeTypes => TiffConstants.MimeTypes; /// - public IImageDecoder Decoder => new TiffDecoder(); - - /// - public IImageEncoder Encoder => new TiffEncoder(); - - /// - public int HeaderSize => 4; - - /// - public bool IsSupportedFileFormat(byte[] header) - { - return header.Length >= this.HeaderSize && - ((header[0] == 0x49 && header[1] == 0x49 && header[2] == 0x2A && header[3] == 0x00) || // Little-endian - (header[0] == 0x4D && header[1] == 0x4D && header[2] == 0x00 && header[3] == 0x2A)); // Big-endian - } + public IEnumerable FileExtensions => TiffConstants.FileExtensions; } } diff --git a/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs b/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs new file mode 100644 index 000000000..fd53080f5 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs @@ -0,0 +1,36 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + using System; + + /// + /// Detects tiff file headers + /// + public sealed class TiffImageFormatDetector : IImageFormatDetector + { + /// + public int HeaderSize => 4; + + /// + public IImageFormat DetectFormat(ReadOnlySpan header) + { + if (this.IsSupportedFileFormat(header)) + { + return ImageFormats.Tiff; + } + + return null; + } + + private bool IsSupportedFileFormat(ReadOnlySpan header) + { + return header.Length >= this.HeaderSize && + ((header[0] == 0x49 && header[1] == 0x49 && header[2] == 0x2A && header[3] == 0x00) || // Little-endian + (header[0] == 0x4D && header[1] == 0x4D && header[2] == 0x00 && header[3] == 0x2A)); // Big-endian + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/ImageFormats.cs b/src/ImageSharp/ImageFormats.cs index f79191eae..bc6e0f40f 100644 --- a/src/ImageSharp/ImageFormats.cs +++ b/src/ImageSharp/ImageFormats.cs @@ -31,5 +31,10 @@ namespace ImageSharp /// The format details for the bitmaps. /// public static readonly IImageFormat Bitmap = new BmpFormat(); + + /// + /// The format details for the tiffs. + /// + public static readonly IImageFormat Tiff = new TiffFormat(); } } \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs index 0f03c3207..43a349bac 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs @@ -68,7 +68,7 @@ namespace ImageSharp.Tests TiffDecoder decoder = new TiffDecoder(); - ImageFormatException e = Assert.Throws(() => { decoder.Decode(Configuration.Default, stream, null); }); + ImageFormatException e = Assert.Throws(() => { decoder.Decode(Configuration.Default, stream); }); Assert.Equal("Invalid TIFF file header.", e.Message); } @@ -86,7 +86,7 @@ namespace ImageSharp.Tests TiffDecoder decoder = new TiffDecoder(); - ImageFormatException e = Assert.Throws(() => { decoder.Decode(Configuration.Default, stream, null); }); + ImageFormatException e = Assert.Throws(() => { decoder.Decode(Configuration.Default, stream); }); Assert.Equal("Invalid TIFF file header.", e.Message); } @@ -103,7 +103,7 @@ namespace ImageSharp.Tests TiffDecoder decoder = new TiffDecoder(); - ImageFormatException e = Assert.Throws(() => { decoder.Decode(Configuration.Default, stream, null); }); + ImageFormatException e = Assert.Throws(() => { decoder.Decode(Configuration.Default, stream); }); Assert.Equal("Invalid TIFF file header.", e.Message); } diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs index ab2ab5f75..593733f73 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs @@ -124,8 +124,8 @@ namespace ImageSharp.Tests } .ToStream(isLittleEndian); - DecoderOptions options = new DecoderOptions() { IgnoreMetadata = true }; - TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, options, null); + TiffDecoder options = new TiffDecoder() { IgnoreMetadata = true }; + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null, options); TiffIfd ifd = decoder.ReadIfd(0); Image image = new Image(null, 20, 20); diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffFormatTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffFormatTests.cs index 265787546..09d90bb19 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffFormatTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffFormatTests.cs @@ -12,110 +12,16 @@ namespace ImageSharp.Tests public class TiffFormatTests { - public static object[][] IsLittleEndianValues = new[] { new object[] { false }, - new object[] { true } }; - [Fact] public void FormatProperties_AreAsExpected() { TiffFormat tiffFormat = new TiffFormat(); - Assert.Equal("image/tiff", tiffFormat.MimeType); - Assert.Equal("tif", tiffFormat.Extension); - Assert.Contains("tif", tiffFormat.SupportedExtensions); - Assert.Contains("tiff", tiffFormat.SupportedExtensions); - } - - [Theory] - [MemberData(nameof(IsLittleEndianValues))] - public void IsSupportedFileFormat_ReturnsTrue_ForValidFile(bool isLittleEndian) - { - byte[] bytes = new TiffGenHeader() - { - FirstIfd = new TiffGenIfd() - } - .ToBytes(isLittleEndian); - - TiffFormat tiffFormat = new TiffFormat(); - byte[] headerBytes = bytes.Take(tiffFormat.HeaderSize).ToArray(); - bool isSupported = tiffFormat.IsSupportedFileFormat(headerBytes); - - Assert.True(isSupported); - } - - [Theory] - [MemberData(nameof(IsLittleEndianValues))] - public void IsSupportedFileFormat_ReturnsFalse_WithInvalidByteOrderMarkers(bool isLittleEndian) - { - byte[] bytes = new TiffGenHeader() - { - FirstIfd = new TiffGenIfd(), - ByteOrderMarker = 0x1234 - } - .ToBytes(isLittleEndian); - - TiffFormat tiffFormat = new TiffFormat(); - byte[] headerBytes = bytes.Take(tiffFormat.HeaderSize).ToArray(); - bool isSupported = tiffFormat.IsSupportedFileFormat(headerBytes); - - Assert.False(isSupported); - } - - [Theory] - [MemberData(nameof(IsLittleEndianValues))] - public void IsSupportedFileFormat_ReturnsFalse_WithIncorrectMagicNumber(bool isLittleEndian) - { - byte[] bytes = new TiffGenHeader() - { - FirstIfd = new TiffGenIfd(), - MagicNumber = 32 - } - .ToBytes(isLittleEndian); - - TiffFormat tiffFormat = new TiffFormat(); - byte[] headerBytes = bytes.Take(tiffFormat.HeaderSize).ToArray(); - bool isSupported = tiffFormat.IsSupportedFileFormat(headerBytes); - - Assert.False(isSupported); - } - - [Theory] - [MemberData(nameof(IsLittleEndianValues))] - public void IsSupportedFileFormat_ReturnsFalse_WithShortHeader(bool isLittleEndian) - { - byte[] bytes = new TiffGenHeader() - { - FirstIfd = new TiffGenIfd() - } - .ToBytes(isLittleEndian); - - TiffFormat tiffFormat = new TiffFormat(); - byte[] headerBytes = bytes.Take(tiffFormat.HeaderSize - 1).ToArray(); - bool isSupported = tiffFormat.IsSupportedFileFormat(headerBytes); - - Assert.False(isSupported); - } - - [Fact] - public void Decoder_ReturnsTiffDecoder() - { - TiffFormat tiffFormat = new TiffFormat(); - - var decoder = tiffFormat.Decoder; - - Assert.NotNull(decoder); - Assert.IsType(decoder); - } - - [Fact] - public void Encoder_ReturnsTiffEncoder() - { - TiffFormat tiffFormat = new TiffFormat(); - - var encoder = tiffFormat.Encoder; - - Assert.NotNull(encoder); - Assert.IsType(encoder); + Assert.Equal("TIFF", tiffFormat.Name); + Assert.Equal("image/tiff", tiffFormat.DefaultMimeType); + Assert.Contains("image/tiff", tiffFormat.MimeTypes); + Assert.Contains("tif", tiffFormat.FileExtensions); + Assert.Contains("tiff", tiffFormat.FileExtensions); } } } diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffImageFormatDetectorTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffImageFormatDetectorTests.cs new file mode 100644 index 000000000..c7b049e11 --- /dev/null +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffImageFormatDetectorTests.cs @@ -0,0 +1,89 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System.Linq; + using Xunit; + + using ImageSharp.Formats; + + public class TiffImageFormatDetectorTests + { + public static object[][] IsLittleEndianValues = new[] { new object[] { false }, + new object[] { true } }; + + [Theory] + [MemberData(nameof(IsLittleEndianValues))] + public void DetectFormat_ReturnsTiffFormat_ForValidFile(bool isLittleEndian) + { + byte[] bytes = new TiffGenHeader() + { + FirstIfd = new TiffGenIfd() + } + .ToBytes(isLittleEndian); + + TiffImageFormatDetector formatDetector = new TiffImageFormatDetector(); + byte[] headerBytes = bytes.Take(formatDetector.HeaderSize).ToArray(); + var format = formatDetector.DetectFormat(headerBytes); + + Assert.NotNull(format); + Assert.IsType(format); + } + + [Theory] + [MemberData(nameof(IsLittleEndianValues))] + public void DetectFormat_ReturnsNull_WithInvalidByteOrderMarkers(bool isLittleEndian) + { + byte[] bytes = new TiffGenHeader() + { + FirstIfd = new TiffGenIfd(), + ByteOrderMarker = 0x1234 + } + .ToBytes(isLittleEndian); + + TiffImageFormatDetector formatDetector = new TiffImageFormatDetector(); + byte[] headerBytes = bytes.Take(formatDetector.HeaderSize).ToArray(); + var format = formatDetector.DetectFormat(headerBytes); + + Assert.Null(format); + } + + [Theory] + [MemberData(nameof(IsLittleEndianValues))] + public void DetectFormat_ReturnsNull_WithIncorrectMagicNumber(bool isLittleEndian) + { + byte[] bytes = new TiffGenHeader() + { + FirstIfd = new TiffGenIfd(), + MagicNumber = 32 + } + .ToBytes(isLittleEndian); + + TiffImageFormatDetector formatDetector = new TiffImageFormatDetector(); + byte[] headerBytes = bytes.Take(formatDetector.HeaderSize).ToArray(); + var format = formatDetector.DetectFormat(headerBytes); + + Assert.Null(format); + } + + [Theory] + [MemberData(nameof(IsLittleEndianValues))] + public void DetectFormat_ReturnsNull_WithShortHeader(bool isLittleEndian) + { + byte[] bytes = new TiffGenHeader() + { + FirstIfd = new TiffGenIfd() + } + .ToBytes(isLittleEndian); + + TiffImageFormatDetector formatDetector = new TiffImageFormatDetector(); + byte[] headerBytes = bytes.Take(formatDetector.HeaderSize - 1).ToArray(); + var format = formatDetector.DetectFormat(headerBytes); + + Assert.Null(format); + } + } +} diff --git a/tests/ImageSharp.Formats.Tiff.Tests/ImageSharp.Formats.Tiff.Tests.csproj b/tests/ImageSharp.Formats.Tiff.Tests/ImageSharp.Formats.Tiff.Tests.csproj index 8d3d1db36..5ecbcc5b9 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/ImageSharp.Formats.Tiff.Tests.csproj +++ b/tests/ImageSharp.Formats.Tiff.Tests/ImageSharp.Formats.Tiff.Tests.csproj @@ -7,7 +7,7 @@ - + From 10b814c079280ce612f272b2d8ccf820f297b637 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 4 Mar 2018 21:20:35 +1100 Subject: [PATCH 058/275] Update codebase to catch up with changes to main repo. --- ImageSharp.sln | 50 ++----------------- .../Compression/DeflateTiffCompression.cs | 17 +++---- .../Tiff/Compression/LzwTiffCompression.cs | 15 ++---- .../Tiff/Compression/NoneTiffCompression.cs | 12 ++--- .../Compression/PackBitsTiffCompression.cs | 16 +++--- .../Tiff/Compression/TiffCompressionType.cs | 6 +-- .../Formats/Tiff/Constants/TiffCompression.cs | 6 +-- .../Formats/Tiff/Constants/TiffConstants.cs | 10 ++-- .../Tiff/Constants/TiffExtraSamples.cs | 6 +-- .../Formats/Tiff/Constants/TiffFillOrder.cs | 6 +-- .../Tiff/Constants/TiffNewSubfileType.cs | 10 ++-- .../Formats/Tiff/Constants/TiffOrientation.cs | 6 +-- .../TiffPhotometricInterpretation.cs | 6 +-- .../Tiff/Constants/TiffPlanarConfiguration.cs | 6 +-- .../Tiff/Constants/TiffResolutionUnit.cs | 6 +-- .../Formats/Tiff/Constants/TiffSubfileType.cs | 6 +-- .../Formats/Tiff/Constants/TiffTags.cs | 6 +-- .../Tiff/Constants/TiffThreshholding.cs | 6 +-- .../Formats/Tiff/Constants/TiffType.cs | 6 +-- .../Formats/Tiff/ITiffDecoderOptions.cs | 6 +-- .../Formats/Tiff/ITiffEncoderOptions.cs | 6 +-- .../Formats/Tiff/ImageExtensions.cs | 14 +++--- .../BlackIsZero1TiffColor.cs | 16 +++--- .../BlackIsZero4TiffColor.cs | 14 +++--- .../BlackIsZero8TiffColor.cs | 14 +++--- .../BlackIsZeroTiffColor.cs | 18 +++---- .../PaletteTiffColor.cs | 18 +++---- .../Rgb888TiffColor.cs | 16 +++--- .../RgbPlanarTiffColor.cs | 18 +++---- .../PhotometricInterpretation/RgbTiffColor.cs | 18 +++---- .../TiffColorType.cs | 6 +-- .../WhiteIsZero1TiffColor.cs | 16 +++--- .../WhiteIsZero4TiffColor.cs | 14 +++--- .../WhiteIsZero8TiffColor.cs | 14 +++--- .../WhiteIsZeroTiffColor.cs | 18 +++---- .../Formats/Tiff/TiffConfigurationModule.cs | 12 ++--- src/ImageSharp/Formats/Tiff/TiffDecoder.cs | 14 +++--- .../Formats/Tiff/TiffDecoderCore.cs | 24 ++++----- src/ImageSharp/Formats/Tiff/TiffEncoder.cs | 13 ++--- .../Formats/Tiff/TiffEncoderCore.cs | 37 ++++++-------- src/ImageSharp/Formats/Tiff/TiffFormat.cs | 12 ++--- .../Formats/Tiff/TiffIfd/TiffIfd.cs | 8 ++- .../Formats/Tiff/TiffIfd/TiffIfdEntry.cs | 8 ++- .../Tiff/TiffIfd/TiffIfdEntryCreator.cs | 15 +++--- .../Formats/Tiff/TiffImageFormatDetector.cs | 10 ++-- .../Formats/Tiff/TiffMetadataNames.cs | 6 +-- .../Formats/Tiff/Utils/BitReader.cs | 9 ++-- .../Formats/Tiff/Utils/SubStream.cs | 17 +++---- .../Formats/Tiff/Utils/TiffLzwDecoder.cs | 15 +++--- .../Formats/Tiff/Utils/TiffLzwEncoder.cs | 15 +++--- .../Formats/Tiff/Utils/TiffUtils.cs | 11 ++-- .../Formats/Tiff/Utils/TiffWriter.cs | 19 ++++--- src/Shared/AssemblyInfo.Common.cs | 2 - .../ImageSharp.Benchmarks/Image/DecodeTiff.cs | 21 ++++---- .../ImageSharp.Formats.Tiff.Tests.csproj | 20 -------- .../DeflateTiffCompressionTests.cs | 8 +-- .../Compression/LzwTiffCompressionTests.cs | 8 +-- .../Compression/NoneTiffCompressionTests.cs | 6 +-- .../PackBitsTiffCompressionTests.cs | 6 +-- .../BlackIsZeroTiffColorTests.cs | 6 +-- .../PaletteTiffColorTests.cs | 7 +-- .../PhotometricInterpretationTestBase.cs | 8 ++- .../RgbPlanarTiffColorTests.cs | 6 +-- .../RgbTiffColorTests.cs | 6 +-- .../WhiteIsZeroTiffColorTests.cs | 6 +-- .../Formats/Tiff/TiffDecoderHeaderTests.cs | 6 +-- .../Formats/Tiff/TiffDecoderIfdEntryTests.cs | 8 +-- .../Formats/Tiff/TiffDecoderIfdTests.cs | 6 +-- .../Formats/Tiff/TiffDecoderImageTests.cs | 6 +-- .../Formats/Tiff/TiffDecoderMetadataTests.cs | 7 +-- .../Formats/Tiff/TiffEncoderHeaderTests.cs | 10 ++-- .../Formats/Tiff/TiffEncoderIfdTests.cs | 8 ++- .../Formats/Tiff/TiffEncoderMetadataTests.cs | 12 ++--- .../Formats/Tiff/TiffFormatTests.cs | 7 +-- .../Tiff/TiffIfd/TiffIfdEntryCreatorTests.cs | 10 ++-- .../Formats/Tiff/TiffIfd/TiffIfdEntryTests.cs | 6 +-- .../Formats/Tiff/TiffIfd/TiffIfdTests.cs | 6 +-- .../Tiff/TiffImageFormatDetectorTests.cs | 6 +-- .../Formats/Tiff/Utils/SubStreamTests.cs | 6 +-- .../Formats/Tiff/Utils/TiffWriterTests.cs | 7 +-- .../ImageSharp.Tests/ImageSharp.Tests.csproj | 7 --- .../TestUtilities/ByteArrayUtility.cs | 6 +-- .../TestUtilities/ByteBuffer.cs | 6 +-- .../TestUtilities/Tiff/ITiffGenDataSource.cs | 6 +-- .../TestUtilities/Tiff/TiffGenDataBlock.cs | 6 +-- .../Tiff/TiffGenDataReference.cs | 6 +-- .../TestUtilities/Tiff/TiffGenEntry.cs | 6 +-- .../TestUtilities/Tiff/TiffGenExtensions.cs | 6 +-- .../TestUtilities/Tiff/TiffGenHeader.cs | 6 +-- .../TestUtilities/Tiff/TiffGenIfd.cs | 6 +-- .../Tiff/TiffGenIfdExtensions.cs | 6 +-- .../TestUtilities/Tiff/TiffIfdParser.cs | 9 ++-- 92 files changed, 357 insertions(+), 613 deletions(-) delete mode 100644 tests/ImageSharp.Formats.Tiff.Tests/ImageSharp.Formats.Tiff.Tests.csproj rename tests/{ImageSharp.Formats.Tiff.Tests => ImageSharp.Tests}/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs (88%) rename tests/{ImageSharp.Formats.Tiff.Tests => ImageSharp.Tests}/Formats/Tiff/Compression/LzwTiffCompressionTests.cs (86%) rename tests/{ImageSharp.Formats.Tiff.Tests => ImageSharp.Tests}/Formats/Tiff/Compression/NoneTiffCompressionTests.cs (81%) rename tests/{ImageSharp.Formats.Tiff.Tests => ImageSharp.Tests}/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs (89%) rename tests/{ImageSharp.Formats.Tiff.Tests => ImageSharp.Tests}/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs (98%) rename tests/{ImageSharp.Formats.Tiff.Tests => ImageSharp.Tests}/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs (97%) rename tests/{ImageSharp.Formats.Tiff.Tests => ImageSharp.Tests}/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs (89%) rename tests/{ImageSharp.Formats.Tiff.Tests => ImageSharp.Tests}/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs (98%) rename tests/{ImageSharp.Formats.Tiff.Tests => ImageSharp.Tests}/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs (98%) rename tests/{ImageSharp.Formats.Tiff.Tests => ImageSharp.Tests}/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs (98%) rename tests/{ImageSharp.Formats.Tiff.Tests => ImageSharp.Tests}/Formats/Tiff/TiffDecoderHeaderTests.cs (95%) rename tests/{ImageSharp.Formats.Tiff.Tests => ImageSharp.Tests}/Formats/Tiff/TiffDecoderIfdEntryTests.cs (99%) rename tests/{ImageSharp.Formats.Tiff.Tests => ImageSharp.Tests}/Formats/Tiff/TiffDecoderIfdTests.cs (96%) rename tests/{ImageSharp.Formats.Tiff.Tests => ImageSharp.Tests}/Formats/Tiff/TiffDecoderImageTests.cs (99%) rename tests/{ImageSharp.Formats.Tiff.Tests => ImageSharp.Tests}/Formats/Tiff/TiffDecoderMetadataTests.cs (97%) rename tests/{ImageSharp.Formats.Tiff.Tests => ImageSharp.Tests}/Formats/Tiff/TiffEncoderHeaderTests.cs (82%) rename tests/{ImageSharp.Formats.Tiff.Tests => ImageSharp.Tests}/Formats/Tiff/TiffEncoderIfdTests.cs (98%) rename tests/{ImageSharp.Formats.Tiff.Tests => ImageSharp.Tests}/Formats/Tiff/TiffEncoderMetadataTests.cs (92%) rename tests/{ImageSharp.Formats.Tiff.Tests => ImageSharp.Tests}/Formats/Tiff/TiffFormatTests.cs (75%) rename tests/{ImageSharp.Formats.Tiff.Tests => ImageSharp.Tests}/Formats/Tiff/TiffIfd/TiffIfdEntryCreatorTests.cs (98%) rename tests/{ImageSharp.Formats.Tiff.Tests => ImageSharp.Tests}/Formats/Tiff/TiffIfd/TiffIfdEntryTests.cs (76%) rename tests/{ImageSharp.Formats.Tiff.Tests => ImageSharp.Tests}/Formats/Tiff/TiffIfd/TiffIfdTests.cs (94%) rename tests/{ImageSharp.Formats.Tiff.Tests => ImageSharp.Tests}/Formats/Tiff/TiffImageFormatDetectorTests.cs (94%) rename tests/{ImageSharp.Formats.Tiff.Tests => ImageSharp.Tests}/Formats/Tiff/Utils/SubStreamTests.cs (98%) rename tests/{ImageSharp.Formats.Tiff.Tests => ImageSharp.Tests}/Formats/Tiff/Utils/TiffWriterTests.cs (95%) rename tests/{ImageSharp.Formats.Tiff.Tests => ImageSharp.Tests}/TestUtilities/ByteArrayUtility.cs (77%) rename tests/{ImageSharp.Formats.Tiff.Tests => ImageSharp.Tests}/TestUtilities/ByteBuffer.cs (83%) rename tests/{ImageSharp.Formats.Tiff.Tests => ImageSharp.Tests}/TestUtilities/Tiff/ITiffGenDataSource.cs (67%) rename tests/{ImageSharp.Formats.Tiff.Tests => ImageSharp.Tests}/TestUtilities/Tiff/TiffGenDataBlock.cs (81%) rename tests/{ImageSharp.Formats.Tiff.Tests => ImageSharp.Tests}/TestUtilities/Tiff/TiffGenDataReference.cs (72%) rename tests/{ImageSharp.Formats.Tiff.Tests => ImageSharp.Tests}/TestUtilities/Tiff/TiffGenEntry.cs (97%) rename tests/{ImageSharp.Formats.Tiff.Tests => ImageSharp.Tests}/TestUtilities/Tiff/TiffGenExtensions.cs (89%) rename tests/{ImageSharp.Formats.Tiff.Tests => ImageSharp.Tests}/TestUtilities/Tiff/TiffGenHeader.cs (88%) rename tests/{ImageSharp.Formats.Tiff.Tests => ImageSharp.Tests}/TestUtilities/Tiff/TiffGenIfd.cs (94%) rename tests/{ImageSharp.Formats.Tiff.Tests => ImageSharp.Tests}/TestUtilities/Tiff/TiffGenIfdExtensions.cs (81%) rename tests/{ImageSharp.Formats.Tiff.Tests => ImageSharp.Tests}/TestUtilities/Tiff/TiffIfdParser.cs (93%) diff --git a/ImageSharp.sln b/ImageSharp.sln index 51b63325b..535e4d084 100644 --- a/ImageSharp.sln +++ b/ImageSharp.sln @@ -1,7 +1,6 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26730.3 +VisualStudioVersion = 15.0.27130.2036 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{C317F1B1-D75E-4C6D-83EB-80367343E0D7}" ProjectSection(SolutionItems) = preProject @@ -44,12 +43,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Benchmarks", "te EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Sandbox46", "tests\ImageSharp.Sandbox46\ImageSharp.Sandbox46.csproj", "{561B880A-D9EE-44EF-90F5-817C54A9D9AB}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageSharp.Formats.Tiff.Tests", "tests\ImageSharp.Formats.Tiff.Tests\ImageSharp.Formats.Tiff.Tests.csproj", "{F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC}" -EndProject Global - GlobalSection(Performance) = preSolution - HasPerformanceSessions = true - EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 @@ -107,42 +101,6 @@ Global {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release|x64.Build.0 = Release|Any CPU {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release|x86.ActiveCfg = Release|Any CPU {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release|x86.Build.0 = Release|Any CPU - {F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC}.Debug|x64.ActiveCfg = Debug|x64 - {F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC}.Debug|x64.Build.0 = Debug|x64 - {F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC}.Debug|x86.ActiveCfg = Debug|x86 - {F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC}.Debug|x86.Build.0 = Debug|x86 - {F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC}.Release|Any CPU.Build.0 = Release|Any CPU - {F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC}.Release|x64.ActiveCfg = Release|x64 - {F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC}.Release|x64.Build.0 = Release|x64 - {F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC}.Release|x86.ActiveCfg = Release|x86 - {F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC}.Release|x86.Build.0 = Release|x86 - {844FC582-4E78-4371-847D-EFD4D1103578}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {844FC582-4E78-4371-847D-EFD4D1103578}.Debug|Any CPU.Build.0 = Debug|Any CPU - {844FC582-4E78-4371-847D-EFD4D1103578}.Debug|x64.ActiveCfg = Debug|Any CPU - {844FC582-4E78-4371-847D-EFD4D1103578}.Debug|x64.Build.0 = Debug|Any CPU - {844FC582-4E78-4371-847D-EFD4D1103578}.Debug|x86.ActiveCfg = Debug|Any CPU - {844FC582-4E78-4371-847D-EFD4D1103578}.Debug|x86.Build.0 = Debug|Any CPU - {844FC582-4E78-4371-847D-EFD4D1103578}.Release|Any CPU.ActiveCfg = Release|Any CPU - {844FC582-4E78-4371-847D-EFD4D1103578}.Release|Any CPU.Build.0 = Release|Any CPU - {844FC582-4E78-4371-847D-EFD4D1103578}.Release|x64.ActiveCfg = Release|Any CPU - {844FC582-4E78-4371-847D-EFD4D1103578}.Release|x64.Build.0 = Release|Any CPU - {844FC582-4E78-4371-847D-EFD4D1103578}.Release|x86.ActiveCfg = Release|Any CPU - {844FC582-4E78-4371-847D-EFD4D1103578}.Release|x86.Build.0 = Release|Any CPU - {07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Debug|x64.ActiveCfg = Debug|Any CPU - {07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Debug|x64.Build.0 = Debug|Any CPU - {07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Debug|x86.ActiveCfg = Debug|Any CPU - {07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Debug|x86.Build.0 = Debug|Any CPU - {07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Release|Any CPU.Build.0 = Release|Any CPU - {07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Release|x64.ActiveCfg = Release|Any CPU - {07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Release|x64.Build.0 = Release|Any CPU - {07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Release|x86.ActiveCfg = Release|Any CPU - {07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Release|x86.Build.0 = Release|Any CPU {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Debug|Any CPU.Build.0 = Debug|Any CPU {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -165,12 +123,12 @@ Global {2E33181E-6E28-4662-A801-E2E7DC206029} = {815C0625-CD3D-440F-9F80-2D83856AB7AE} {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} {2BF743D8-2A06-412D-96D7-F448F00C5EA5} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} - {F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} - {844FC582-4E78-4371-847D-EFD4D1103578} = {7CC6D57E-B916-43B8-B315-A0BB92F260A2} - {07EE511D-4BAB-4323-BAFC-3AF2BF9366F0} = {7CC6D57E-B916-43B8-B315-A0BB92F260A2} {561B880A-D9EE-44EF-90F5-817C54A9D9AB} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {5F8B9D1F-CD8B-4CC5-8216-D531E25BD795} EndGlobalSection + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection EndGlobal diff --git a/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs index 1af8362a0..00d69dbd5 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff -{ - using System; - using System.Buffers; - using System.IO; - using System.IO.Compression; - using System.Runtime.CompilerServices; +using System; +using System.IO; +using System.IO.Compression; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.Formats.Tiff +{ /// /// Class to handle cases where TIFF image data is compressed using Deflate compression. /// diff --git a/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs index ae4d22a71..5de966554 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs @@ -1,16 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff -{ - using System; - using System.Buffers; - using System.IO; - using System.IO.Compression; - using System.Runtime.CompilerServices; +using System.IO; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.Formats.Tiff +{ /// /// Class to handle cases where TIFF image data is compressed using LZW compression. /// diff --git a/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs index 6bc8a308f..a9587d199 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff -{ - using System.IO; - using System.Runtime.CompilerServices; +using System.IO; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.Formats.Tiff +{ /// /// Class to handle cases where TIFF image data is not compressed. /// diff --git a/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs index 0ac30e8f1..a6cd8f88d 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff -{ - using System; - using System.Buffers; - using System.IO; - using System.Runtime.CompilerServices; +using System; +using System.Buffers; +using System.IO; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.Formats.Tiff +{ /// /// Class to handle cases where TIFF image data is compressed using PackBits compression. /// diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs index 3ea9270c8..4121f90b2 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Provides enumeration of the various TIFF compression types. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs index acb0685db..e5ee8b195 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Enumeration representing the compression formats defined by the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs index cd88ccee7..a2044314a 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff -{ - using System.Collections.Generic; +using System.Collections.Generic; +namespace SixLabors.ImageSharp.Formats.Tiff +{ /// /// Defines constants defined in the TIFF specification. /// diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffExtraSamples.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffExtraSamples.cs index 545ae4c39..d34d999b9 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffExtraSamples.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffExtraSamples.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Enumeration representing the possible uses of extra components in TIFF format files. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffFillOrder.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffFillOrder.cs index 7edf0eeaa..e4d30a324 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffFillOrder.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffFillOrder.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Enumeration representing the fill orders defined by the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs index 20bf16c63..b881ac209 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff -{ - using System; +using System; +namespace SixLabors.ImageSharp.Formats.Tiff +{ /// /// Enumeration representing the sub-file types defined by the Tiff file-format. /// diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffOrientation.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffOrientation.cs index 9ffa5cf81..035f88809 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffOrientation.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffOrientation.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Enumeration representing the image orientations defined by the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs index 35d1a291c..dd4d923b8 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Enumeration representing the photometric interpretation formats defined by the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffPlanarConfiguration.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffPlanarConfiguration.cs index ef0b72236..4fc0aa4c8 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffPlanarConfiguration.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffPlanarConfiguration.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Enumeration representing how the components of each pixel are stored the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs index 4bb7c15ba..7bb3dbd6e 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Enumeration representing the resolution units defined by the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffSubfileType.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffSubfileType.cs index 050af238c..4039ae9e2 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffSubfileType.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffSubfileType.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Enumeration representing the sub-file types defined by the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffTags.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffTags.cs index 7d9343515..38cf4280e 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffTags.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffTags.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Constants representing tag IDs in the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffThreshholding.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffThreshholding.cs index 0e9444302..0a398d231 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffThreshholding.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffThreshholding.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Enumeration representing the threshholding applied to image data defined by the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffType.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffType.cs index 1a1fc3108..8e55d80cc 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffType.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffType.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Enumeration representing the data types understood by the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs b/src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs index 9f4562134..c718102b8 100644 --- a/src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs +++ b/src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats { /// /// Encapsulates the options for the . diff --git a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs index eefb484c2..e10396d5f 100644 --- a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs +++ b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats { /// /// Encapsulates the options for the . diff --git a/src/ImageSharp/Formats/Tiff/ImageExtensions.cs b/src/ImageSharp/Formats/Tiff/ImageExtensions.cs index 470c09c72..3414f84d3 100644 --- a/src/ImageSharp/Formats/Tiff/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Tiff/ImageExtensions.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System.IO; - using Formats; - using ImageSharp.PixelFormats; +using System.IO; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs index a4de21874..48a3a1098 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff -{ - using System; - using System.Runtime.CompilerServices; - using ImageSharp; - using ImageSharp.PixelFormats; +using System; +using System.Runtime.CompilerServices; + +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Tiff +{ /// /// Implements the 'BlackIsZero' photometric interpretation (optimised for bilevel images). /// diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs index 42d829ef8..5a9a0fc97 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff -{ - using System.Runtime.CompilerServices; - using ImageSharp; - using ImageSharp.PixelFormats; +using System.Runtime.CompilerServices; + +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Tiff +{ /// /// Implements the 'BlackIsZero' photometric interpretation (optimised for 4-bit grayscale images). /// diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs index b30cbe264..d712e89bd 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff -{ - using System.Runtime.CompilerServices; - using ImageSharp; - using ImageSharp.PixelFormats; +using System.Runtime.CompilerServices; + +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Tiff +{ /// /// Implements the 'BlackIsZero' photometric interpretation (optimised for 8-bit grayscale images). /// diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs index 18654f271..5a8d633fe 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; - using ImageSharp; - using ImageSharp.PixelFormats; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Tiff +{ /// /// Implements the 'BlackIsZero' photometric interpretation (for all bit depths). /// diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs index 4f4536331..cc90aa405 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; - using ImageSharp; - using ImageSharp.PixelFormats; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Tiff +{ /// /// Implements the 'PaletteTiffColor' photometric interpretation (for all bit depths). /// diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs index a4c1c8ad5..820c73d2a 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs @@ -1,16 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; - using ImageSharp; - using ImageSharp.PixelFormats; +using System.Runtime.CompilerServices; + +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Tiff +{ /// /// Implements the 'RGB' photometric interpretation (optimised for 8-bit full color images). /// diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs index bcd8e171b..74c05fcd5 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; - using ImageSharp; - using ImageSharp.PixelFormats; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Tiff +{ /// /// Implements the 'RGB' photometric interpretation with 'Planar' layout (for all bit depths). /// diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs index e62ee7dc9..51915de46 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; - using ImageSharp; - using ImageSharp.PixelFormats; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Tiff +{ /// /// Implements the 'RGB' photometric interpretation (for all bit depths). /// diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs index 36e00edf4..7aea15885 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Provides enumeration of the various TIFF photometric interpretation implementation types. diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs index 25d01a2fb..227aef5a4 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff -{ - using System; - using System.Runtime.CompilerServices; - using ImageSharp; - using ImageSharp.PixelFormats; +using System; +using System.Runtime.CompilerServices; + +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Tiff +{ /// /// Implements the 'WhiteIsZero' photometric interpretation (optimised for bilevel images). /// diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs index 8aef89dc5..053f5165b 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff -{ - using System.Runtime.CompilerServices; - using ImageSharp; - using ImageSharp.PixelFormats; +using System.Runtime.CompilerServices; + +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Tiff +{ /// /// Implements the 'WhiteIsZero' photometric interpretation (optimised for 4-bit grayscale images). /// diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs index 469767510..74a1b2de9 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff -{ - using System.Runtime.CompilerServices; - using ImageSharp; - using ImageSharp.PixelFormats; +using System.Runtime.CompilerServices; + +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Tiff +{ /// /// Implements the 'WhiteIsZero' photometric interpretation (optimised for 8-bit grayscale images). /// diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs index 876ea8789..f85d858e7 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; - using ImageSharp; - using ImageSharp.PixelFormats; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Tiff +{ /// /// Implements the 'WhiteIsZero' photometric interpretation (for all bit depths). /// diff --git a/src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs b/src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs index 3e280ce83..868cbdef6 100644 --- a/src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs +++ b/src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats { /// /// Registers the image encoders, decoders and mime type detectors for the TIFF format. @@ -13,9 +11,9 @@ namespace ImageSharp.Formats /// public void Configure(Configuration host) { - host.SetEncoder(ImageFormats.Tiff, new TiffEncoder()); - host.SetDecoder(ImageFormats.Tiff, new TiffDecoder()); - host.AddImageFormatDetector(new TiffImageFormatDetector()); + host.ImageFormatsManager.SetEncoder(ImageFormats.Tiff, new TiffEncoder()); + host.ImageFormatsManager.SetDecoder(ImageFormats.Tiff, new TiffDecoder()); + host.ImageFormatsManager.AddImageFormatDetector(new TiffImageFormatDetector()); } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs index 250b02915..1d4521b0b 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System.IO; - using ImageSharp.Formats.Tiff; - using ImageSharp.PixelFormats; +using System.IO; + +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats +{ /// /// Image decoder for generating an image out of a TIFF stream. /// diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index de42a0345..d9928c52e 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -1,17 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Buffers; - using System.IO; - using System.Text; - using ImageSharp.Formats.Tiff; - using ImageSharp.PixelFormats; +using System; +using System.Buffers; +using System.IO; +using System.Text; +using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats +{ /// /// Performs the tiff decoding operation. /// @@ -80,7 +80,7 @@ namespace ImageSharp.Formats public Stream InputStream { get; private set; } /// - /// A flag indicating if the file is encoded in little-endian or big-endian format. + /// Gets a value indicating whether the file is encoded in little-endian or big-endian format. /// public bool IsLittleEndian { get; private set; } diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs index 6f84bd852..63886a075 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs @@ -1,14 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.IO; - using ImageSharp.PixelFormats; +using System.IO; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats +{ /// /// Encoder for writing the data image to a stream in TIFF format. /// diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index d04b221d8..600698446 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -1,25 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Buffers; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Runtime.CompilerServices; - using System.Text; - using ImageSharp.Formats.Tiff; - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - - using Quantizers; - - using static ComparableExtensions; +using System; +using System.Collections.Generic; +using System.IO; +using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats +{ /// /// Performs the TIFF encoding operation. /// @@ -48,7 +39,7 @@ namespace ImageSharp.Formats /// Encodes the image to the specified stream from the . /// /// The pixel format. - /// The to encode from. + /// The to encode from. /// The to encode the image data to. public void Encode(Image image, Stream stream) where TPixel : struct, IPixel @@ -138,7 +129,7 @@ namespace ImageSharp.Formats /// /// The pixel format. /// The to write data to. - /// The to encode from. + /// The to encode from. /// The marker to write this IFD offset. /// The marker to write the next IFD offset (if present). public long WriteImage(TiffWriter writer, Image image, long ifdOffset) @@ -159,7 +150,7 @@ namespace ImageSharp.Formats /// Adds image metadata to the specified IFD. /// /// The pixel format. - /// The to encode from. + /// The to encode from. /// The metadata entries to add to the IFD. public void AddMetadata(Image image, List ifdEntries) where TPixel : struct, IPixel @@ -227,7 +218,7 @@ namespace ImageSharp.Formats /// Adds image format information to the specified IFD. /// /// The pixel format. - /// The to encode from. + /// The to encode from. /// The image format entries to add to the IFD. public void AddImageFormat(Image image, List ifdEntries) where TPixel : struct, IPixel diff --git a/src/ImageSharp/Formats/Tiff/TiffFormat.cs b/src/ImageSharp/Formats/Tiff/TiffFormat.cs index c12134c37..6c0279ab5 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFormat.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFormat.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System.Collections.Generic; - using ImageSharp.Formats.Tiff; +using System.Collections.Generic; +using SixLabors.ImageSharp.Formats.Tiff; +namespace SixLabors.ImageSharp.Formats +{ /// /// Encapsulates the means to encode and decode Tiff images. /// diff --git a/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfd.cs b/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfd.cs index f666f371d..a6534c155 100644 --- a/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfd.cs +++ b/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfd.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Data structure for holding details of each TIFF IFD. @@ -21,7 +19,7 @@ namespace ImageSharp.Formats.Tiff public uint NextIfdOffset; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the struct. /// /// An array of the entries within the IFD. /// Offset (in bytes) to the next IFD, or zero if this is the last IFD. diff --git a/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntry.cs b/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntry.cs index d9c1722c8..de5974035 100644 --- a/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntry.cs +++ b/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntry.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Data structure for holding details of each TIFF IFD entry. @@ -31,7 +29,7 @@ namespace ImageSharp.Formats.Tiff public byte[] Value; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the struct. /// /// The Tag ID for this entry. /// The data-type of this entry. diff --git a/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntryCreator.cs b/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntryCreator.cs index c30ce5c8a..bd9a52939 100644 --- a/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntryCreator.cs +++ b/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntryCreator.cs @@ -1,14 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff -{ - using System; - using System.Collections.Generic; - using System.Text; +using System; +using System.Collections.Generic; +using System.Text; +using SixLabors.ImageSharp.MetaData.Profiles.Exif; +namespace SixLabors.ImageSharp.Formats.Tiff +{ /// /// Utility class for generating TIFF IFD entries. /// diff --git a/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs b/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs index fd53080f5..447f523e9 100644 --- a/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs +++ b/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; +using System; +namespace SixLabors.ImageSharp.Formats +{ /// /// Detects tiff file headers /// diff --git a/src/ImageSharp/Formats/Tiff/TiffMetadataNames.cs b/src/ImageSharp/Formats/Tiff/TiffMetadataNames.cs index 4591986b0..10f558b29 100644 --- a/src/ImageSharp/Formats/Tiff/TiffMetadataNames.cs +++ b/src/ImageSharp/Formats/Tiff/TiffMetadataNames.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats { /// /// Defines constants for each of the supported TIFF metadata types. diff --git a/src/ImageSharp/Formats/Tiff/Utils/BitReader.cs b/src/ImageSharp/Formats/Tiff/Utils/BitReader.cs index f330690f9..cbd7256ed 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/BitReader.cs @@ -1,11 +1,8 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff -{ - using System.IO; +namespace SixLabors.ImageSharp.Formats.Tiff +{ /// /// Utility class to read a sequence of bits from an array /// diff --git a/src/ImageSharp/Formats/Tiff/Utils/SubStream.cs b/src/ImageSharp/Formats/Tiff/Utils/SubStream.cs index 3bab41edb..aaf9af23a 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/SubStream.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/SubStream.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff -{ - using System; - using System.IO; +using System; +using System.IO; + +namespace SixLabors.ImageSharp.Formats.Tiff +{ /// /// Utility class to encapsulate a sub-portion of another . /// @@ -22,7 +21,7 @@ namespace ImageSharp.Formats.Tiff private long length; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The underlying to wrap. /// The length of the sub-stream. @@ -39,7 +38,7 @@ namespace ImageSharp.Formats.Tiff } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The underlying to wrap. /// The offset of the sub-stream within the underlying . diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs index f6ad7b3a4..6ac09f391 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs @@ -1,14 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff -{ - using System; - using System.Buffers; - using System.IO; +using System; +using System.Buffers; +using System.IO; +using SixLabors.ImageSharp.Formats.Gif; +namespace SixLabors.ImageSharp.Formats.Tiff +{ /// /// Decompresses and decodes data using the dynamic LZW algorithms. /// diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs index 1ad7c3128..e024b59fa 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs @@ -1,14 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff -{ - using System; - using System.Buffers; - using System.IO; +using System; +using System.Buffers; +using System.IO; +using SixLabors.ImageSharp.Formats.Gif; +namespace SixLabors.ImageSharp.Formats.Tiff +{ /// /// Encodes and compresses the image data using dynamic Lempel-Ziv compression. /// diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs index 59b249105..7842a71c1 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs @@ -1,11 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff -{ - using System.IO; +using System.IO; + +namespace SixLabors.ImageSharp.Formats.Tiff +{ /// /// TIFF specific utilities and extension methods. /// diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs index 201e7b4da..5aa59c108 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -1,13 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Tiff -{ - using System; - using System.Collections.Generic; - using System.IO; +using System; +using System.Collections.Generic; +using System.IO; + +namespace SixLabors.ImageSharp.Formats.Tiff +{ /// /// Utility class for writing TIFF data to a . /// @@ -27,12 +26,12 @@ namespace ImageSharp.Formats.Tiff } /// - /// Gets a flag indicating whether the architecture is little-endian. + /// Gets a value indicating whether the architecture is little-endian. /// public bool IsLittleEndian => BitConverter.IsLittleEndian; /// - /// Returns the current position within the stream. + /// Gets the current position within the stream. /// public long Position => this.output.Position; diff --git a/src/Shared/AssemblyInfo.Common.cs b/src/Shared/AssemblyInfo.Common.cs index cf4077b70..327d3abd7 100644 --- a/src/Shared/AssemblyInfo.Common.cs +++ b/src/Shared/AssemblyInfo.Common.cs @@ -34,10 +34,8 @@ using System.Runtime.CompilerServices; // Ensure the internals can be built and tested. [assembly: InternalsVisibleTo("SixLabors.ImageSharp.Drawing")] [assembly: InternalsVisibleTo("ImageSharp.Benchmarks")] -[assembly: InternalsVisibleTo("ImageSharp.Formats.Tiff.Tests")] [assembly: InternalsVisibleTo("SixLabors.ImageSharp.Tests")] [assembly: InternalsVisibleTo("SixLabors.ImageSharp.Sandbox46")] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKeyToken=null")] - diff --git a/tests/ImageSharp.Benchmarks/Image/DecodeTiff.cs b/tests/ImageSharp.Benchmarks/Image/DecodeTiff.cs index 3c57e5fd2..fd02b9876 100644 --- a/tests/ImageSharp.Benchmarks/Image/DecodeTiff.cs +++ b/tests/ImageSharp.Benchmarks/Image/DecodeTiff.cs @@ -1,19 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Benchmarks.Image -{ - using System.Drawing; - using System.IO; - - using BenchmarkDotNet.Attributes; +using System.Drawing; +using System.IO; - using CoreImage = ImageSharp.Image; +using BenchmarkDotNet.Attributes; - using CoreSize = ImageSharp.Size; +using CoreImage = SixLabors.ImageSharp.Image; +using CoreSize = SixLabors.Primitives.Size; +namespace SixLabors.ImageSharp.Benchmarks.Image +{ public class DecodeTiff : BenchmarkBase { private byte[] tiffBytes; @@ -32,7 +29,7 @@ namespace ImageSharp.Benchmarks.Image { using (MemoryStream memoryStream = new MemoryStream(this.tiffBytes)) { - using (Image image = Image.FromStream(memoryStream)) + using (var image = System.Drawing.Image.FromStream(memoryStream)) { return image.Size; } diff --git a/tests/ImageSharp.Formats.Tiff.Tests/ImageSharp.Formats.Tiff.Tests.csproj b/tests/ImageSharp.Formats.Tiff.Tests/ImageSharp.Formats.Tiff.Tests.csproj deleted file mode 100644 index 5ecbcc5b9..000000000 --- a/tests/ImageSharp.Formats.Tiff.Tests/ImageSharp.Formats.Tiff.Tests.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - - Exe - netcoreapp1.1 - - - - - - - - - - - - - - - diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs similarity index 88% rename from tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs rename to tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs index 7021684d5..c739adcaf 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { using System.IO; using Xunit; @@ -11,6 +9,8 @@ namespace ImageSharp.Tests using ImageSharp.Formats; using ImageSharp.Formats.Tiff; + using SixLabors.ImageSharp.Formats.Png.Zlib; + public class DeflateTiffCompressionTests { [Theory] diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs similarity index 86% rename from tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs rename to tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs index e54d0dd5d..3f379f8f6 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs @@ -1,14 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { using System.IO; using Xunit; - - using ImageSharp.Formats; using ImageSharp.Formats.Tiff; public class LzwTiffCompressionTests diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs similarity index 81% rename from tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs rename to tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs index 40348cfed..6f638cf9e 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { using System.IO; using Xunit; diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs similarity index 89% rename from tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs rename to tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs index 85a7bd729..b60524025 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { using System.IO; using Xunit; diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs similarity index 98% rename from tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs rename to tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs index 8c4f78846..a6692a4e0 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { using System.Collections.Generic; using Xunit; diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs similarity index 97% rename from tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs rename to tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs index 8a77c67f0..0470cb55e 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs @@ -1,15 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { using System.Collections.Generic; using Xunit; using ImageSharp.Formats.Tiff; - using ImageSharp.PixelFormats; public class PaletteTiffColorTests : PhotometricInterpretationTestBase { diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs similarity index 89% rename from tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs rename to tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs index 7b3663573..0e18fd020 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { using System; using Xunit; @@ -46,7 +44,7 @@ namespace ImageSharp.Tests int resultWidth = expectedResult[0].Length; int resultHeight = expectedResult.Length; Image image = new Image(resultWidth, resultHeight); - image.Fill(DefaultColor); + image.Mutate(x => x.Fill(DefaultColor)); using (PixelAccessor pixels = image.Lock()) { diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs similarity index 98% rename from tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs rename to tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs index 2b06a8af5..abf8171d7 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { using System.Collections.Generic; using Xunit; diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs similarity index 98% rename from tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs rename to tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs index 06122484b..affb009c8 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { using System.Collections.Generic; using Xunit; diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs similarity index 98% rename from tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs rename to tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs index e9d9556bd..ff30862d2 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { using System.Collections.Generic; using Xunit; diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs similarity index 95% rename from tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs rename to tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs index 43a349bac..bde5c323b 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { using System.IO; using Xunit; diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs similarity index 99% rename from tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs rename to tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs index 8cd7e4e9e..068812987 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { using System; using System.IO; @@ -13,6 +11,8 @@ namespace ImageSharp.Tests using ImageSharp.Formats; using ImageSharp.Formats.Tiff; + using SixLabors.ImageSharp.MetaData.Profiles.Exif; + public class TiffDecoderIfdEntryTests { [Theory] diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderIfdTests.cs similarity index 96% rename from tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdTests.cs rename to tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderIfdTests.cs index fc557bf6f..6accdf995 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderIfdTests.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { using System.IO; using Xunit; diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderImageTests.cs similarity index 99% rename from tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs rename to tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderImageTests.cs index efb02f318..9a17ffd86 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderImageTests.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { using System; using System.IO; diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs similarity index 97% rename from tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs rename to tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs index 593733f73..e40822c73 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs @@ -1,11 +1,8 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { - using System; using System.IO; using System.Linq; using Xunit; diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs similarity index 82% rename from tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs rename to tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs index 76d15f6a1..667d4e232 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs @@ -1,18 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { - using System; using System.IO; - using System.Linq; + using Xunit; using ImageSharp.Formats; using ImageSharp.Formats.Tiff; - using System.Text; public class TiffEncoderHeaderTests { diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderIfdTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderIfdTests.cs similarity index 98% rename from tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderIfdTests.cs rename to tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderIfdTests.cs index c4c4fb84b..c059a99da 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderIfdTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderIfdTests.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { using System; using System.IO; @@ -12,7 +10,7 @@ namespace ImageSharp.Tests using ImageSharp.Formats; using ImageSharp.Formats.Tiff; - using System.Text; + using System.Collections.Generic; public class TiffEncoderIfdTests diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderMetadataTests.cs similarity index 92% rename from tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderMetadataTests.cs rename to tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderMetadataTests.cs index 4dec7630c..a9a223126 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderMetadataTests.cs @@ -1,19 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { - using System; - using System.IO; - using System.Linq; using Xunit; using ImageSharp.Formats; using ImageSharp.Formats.Tiff; using System.Collections.Generic; + using SixLabors.ImageSharp.MetaData; + using SixLabors.ImageSharp.MetaData.Profiles.Exif; + public class TiffEncoderMetadataTests { public static object[][] BaselineMetadataValues = new[] { new object[] { TiffTags.Artist, TiffMetadataNames.Artist, "My Artist Name" }, diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffFormatTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffFormatTests.cs similarity index 75% rename from tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffFormatTests.cs rename to tests/ImageSharp.Tests/Formats/Tiff/TiffFormatTests.cs index 09d90bb19..43428a0e9 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffFormatTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffFormatTests.cs @@ -1,11 +1,8 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { - using System.Linq; using Xunit; using ImageSharp.Formats; diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryCreatorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryCreatorTests.cs similarity index 98% rename from tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryCreatorTests.cs rename to tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryCreatorTests.cs index 036ab4621..f9f3adfe8 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryCreatorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryCreatorTests.cs @@ -1,19 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { using System; using System.Collections.Generic; - using System.IO; using System.Linq; using Xunit; - using ImageSharp.Formats; using ImageSharp.Formats.Tiff; + using SixLabors.ImageSharp.MetaData.Profiles.Exif; + public class TiffIfdEntryCreatorTests { [Theory] diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryTests.cs similarity index 76% rename from tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryTests.cs rename to tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryTests.cs index efca357f9..627042f42 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryTests.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { using Xunit; diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs similarity index 94% rename from tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs rename to tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs index 26ec20963..f6a3c90b7 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { using Xunit; diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffImageFormatDetectorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffImageFormatDetectorTests.cs similarity index 94% rename from tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffImageFormatDetectorTests.cs rename to tests/ImageSharp.Tests/Formats/Tiff/TiffImageFormatDetectorTests.cs index c7b049e11..9800567f5 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffImageFormatDetectorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffImageFormatDetectorTests.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { using System.Linq; using Xunit; diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Utils/SubStreamTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Utils/SubStreamTests.cs similarity index 98% rename from tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Utils/SubStreamTests.cs rename to tests/ImageSharp.Tests/Formats/Tiff/Utils/SubStreamTests.cs index 34997a90c..1f8f74664 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Utils/SubStreamTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Utils/SubStreamTests.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { using System; using System.IO; diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Utils/TiffWriterTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs similarity index 95% rename from tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Utils/TiffWriterTests.cs rename to tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs index 31582fb6d..ce09cd72e 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Utils/TiffWriterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs @@ -1,11 +1,8 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { - using System; using System.IO; using Xunit; diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index 878ce1a0d..3261836c8 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -13,13 +13,6 @@ - - - - - - - diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/ByteArrayUtility.cs b/tests/ImageSharp.Tests/TestUtilities/ByteArrayUtility.cs similarity index 77% rename from tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/ByteArrayUtility.cs rename to tests/ImageSharp.Tests/TestUtilities/ByteArrayUtility.cs index 0a3c9fc34..dbe1d4755 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/ByteArrayUtility.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ByteArrayUtility.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { using System; diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/ByteBuffer.cs b/tests/ImageSharp.Tests/TestUtilities/ByteBuffer.cs similarity index 83% rename from tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/ByteBuffer.cs rename to tests/ImageSharp.Tests/TestUtilities/ByteBuffer.cs index 290c942f8..f646ab5be 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/ByteBuffer.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ByteBuffer.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { using System; using System.Collections.Generic; diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/ITiffGenDataSource.cs b/tests/ImageSharp.Tests/TestUtilities/Tiff/ITiffGenDataSource.cs similarity index 67% rename from tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/ITiffGenDataSource.cs rename to tests/ImageSharp.Tests/TestUtilities/Tiff/ITiffGenDataSource.cs index 75025f3e7..3b84dbbc2 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/ITiffGenDataSource.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tiff/ITiffGenDataSource.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { using System.Collections.Generic; diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenDataBlock.cs b/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenDataBlock.cs similarity index 81% rename from tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenDataBlock.cs rename to tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenDataBlock.cs index 0b412f7fe..8764b4d51 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenDataBlock.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenDataBlock.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { using System.Collections.Generic; diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenDataReference.cs b/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenDataReference.cs similarity index 72% rename from tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenDataReference.cs rename to tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenDataReference.cs index 24d03bece..f72f56b2c 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenDataReference.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenDataReference.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { /// /// A utility data structure to represent a reference from one block of data to another in a Tiff file. diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs b/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenEntry.cs similarity index 97% rename from tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs rename to tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenEntry.cs index 0cdfac5cb..cf4892ede 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenEntry.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { using System; using System.Collections.Generic; diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenExtensions.cs similarity index 89% rename from tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenExtensions.cs rename to tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenExtensions.cs index 21c3b0844..cd1382c3b 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenExtensions.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { using System; using System.IO; diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenHeader.cs b/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenHeader.cs similarity index 88% rename from tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenHeader.cs rename to tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenHeader.cs index 946faedf9..e22128f77 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenHeader.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenHeader.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { using System.Collections.Generic; using System.Linq; diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfd.cs b/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenIfd.cs similarity index 94% rename from tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfd.cs rename to tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenIfd.cs index ee560b18f..4736a6fdf 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfd.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenIfd.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { using System; using System.Collections.Generic; diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfdExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenIfdExtensions.cs similarity index 81% rename from tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfdExtensions.cs rename to tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenIfdExtensions.cs index 4b62b9803..e03f6ae3a 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfdExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenIfdExtensions.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { using System.Linq; diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffIfdParser.cs b/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffIfdParser.cs similarity index 93% rename from tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffIfdParser.cs rename to tests/ImageSharp.Tests/TestUtilities/Tiff/TiffIfdParser.cs index f8d744037..4fc8bca84 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffIfdParser.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffIfdParser.cs @@ -1,15 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { using System; using System.Collections.Generic; using System.Linq; using System.Text; using ImageSharp.Formats.Tiff; + + using SixLabors.ImageSharp.MetaData.Profiles.Exif; + using Xunit; /// From 1628863e056a67ea05fbadd03fb06f79056d3a10 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 4 Mar 2018 22:22:42 +1100 Subject: [PATCH 059/275] Use environment specific newline in test --- tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderIfdTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderIfdTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderIfdTests.cs index c059a99da..edcf5eb4e 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderIfdTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderIfdTests.cs @@ -289,7 +289,7 @@ namespace SixLabors.ImageSharp.Tests { ArgumentException e = Assert.Throws(() => { encoder.WriteIfd(writer, entries); }); - Assert.Equal("There must be at least one entry per IFD.\r\nParameter name: entries", e.Message); + Assert.Equal($"There must be at least one entry per IFD.{Environment.NewLine}Parameter name: entries", e.Message); Assert.Equal("entries", e.ParamName); } } From 01fbd076251a4cb3dd871eed5c8cf05873ed242b Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 14 Mar 2018 19:08:31 +1100 Subject: [PATCH 060/275] Fix namespace reference in tests --- tests/ImageSharp.Benchmarks/Image/DecodeTiff.cs | 2 ++ .../PhotometricInterpretation/BlackIsZeroTiffColorTests.cs | 2 ++ .../Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs | 4 +++- .../PhotometricInterpretationTestBase.cs | 4 ++++ .../Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs | 2 ++ .../Tiff/PhotometricInterpretation/RgbTiffColorTests.cs | 2 ++ .../PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs | 2 ++ tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs | 2 ++ tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderImageTests.cs | 2 ++ .../ImageSharp.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs | 2 ++ .../ImageSharp.Tests/Formats/Tiff/TiffEncoderMetadataTests.cs | 2 ++ 11 files changed, 25 insertions(+), 1 deletion(-) diff --git a/tests/ImageSharp.Benchmarks/Image/DecodeTiff.cs b/tests/ImageSharp.Benchmarks/Image/DecodeTiff.cs index fd02b9876..1ddd3e91c 100644 --- a/tests/ImageSharp.Benchmarks/Image/DecodeTiff.cs +++ b/tests/ImageSharp.Benchmarks/Image/DecodeTiff.cs @@ -6,6 +6,8 @@ using System.IO; using BenchmarkDotNet.Attributes; +using SixLabors.ImageSharp.PixelFormats; + using CoreImage = SixLabors.ImageSharp.Image; using CoreSize = SixLabors.Primitives.Size; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs index a6692a4e0..70ebd2133 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.PixelFormats; + namespace SixLabors.ImageSharp.Tests { using System.Collections.Generic; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs index 0470cb55e..56e3a0598 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.PixelFormats; + namespace SixLabors.ImageSharp.Tests { using System.Collections.Generic; @@ -122,7 +124,7 @@ namespace SixLabors.ImageSharp.Tests private static Rgba32[][] GenerateResult(uint[][] colorPalette, int[][] pixelLookup) { - Rgba32[][] result = new Rgba32[pixelLookup.Length][]; + var result = new Rgba32[pixelLookup.Length][]; for (int y = 0; y < pixelLookup.Length; y++) { diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs index 0e18fd020..29e9f50ae 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs @@ -1,6 +1,10 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Drawing; + namespace SixLabors.ImageSharp.Tests { using System; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs index abf8171d7..09f2af0d1 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.PixelFormats; + namespace SixLabors.ImageSharp.Tests { using System.Collections.Generic; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs index affb009c8..7d5cb1782 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.PixelFormats; + namespace SixLabors.ImageSharp.Tests { using System.Collections.Generic; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs index ff30862d2..cddf05393 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.PixelFormats; + namespace SixLabors.ImageSharp.Tests { using System.Collections.Generic; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs index bde5c323b..73f2a8862 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.PixelFormats; + namespace SixLabors.ImageSharp.Tests { using System.IO; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderImageTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderImageTests.cs index 9a17ffd86..3b1717705 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderImageTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderImageTests.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.PixelFormats; + namespace SixLabors.ImageSharp.Tests { using System; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs index e40822c73..598d924b6 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.PixelFormats; + namespace SixLabors.ImageSharp.Tests { using System.IO; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderMetadataTests.cs index a9a223126..bb1e35104 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderMetadataTests.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.PixelFormats; + namespace SixLabors.ImageSharp.Tests { using Xunit; From 3fd86a23925b87a63eca55ece287bca9776c674d Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 18 Oct 2018 09:52:11 +0100 Subject: [PATCH 061/275] Update tests/Images/External --- tests/Images/External | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Images/External b/tests/Images/External index fcf311bf1..ee90e5f32 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit fcf311bf15bea061e552e4cc357cafe2d4f4bd70 +Subproject commit ee90e5f32218027744b5d40058b587cc1047b76f From a5b1e677826d02bab292f88645b092340426256c Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 7 Dec 2018 05:47:28 +1100 Subject: [PATCH 062/275] Update IPixel method calls to match new signatures --- .../Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs | 2 +- .../Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs | 6 +++--- .../Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs | 2 +- .../Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs | 2 +- .../Tiff/PhotometricInterpretation/PaletteTiffColor.cs | 2 +- .../Tiff/PhotometricInterpretation/Rgb888TiffColor.cs | 2 +- .../Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs | 2 +- .../Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs | 2 +- .../Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs | 2 +- .../Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs | 6 +++--- .../Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs | 2 +- .../Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs | 2 +- 12 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs index bc82ba6bf..e317a7af7 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs @@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff { int bit = (b >> (7 - shift)) & 1; byte intensity = (bit == 1) ? (byte)255 : (byte)0; - color.PackFromRgba32(new Rgba32(intensity, intensity, intensity, 255)); + color.FromRgba32(new Rgba32(intensity, intensity, intensity, 255)); pixels[x + shift, y] = color; } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs index 5deb38fbd..62fff4737 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs @@ -38,11 +38,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff byte byteData = data[offset++]; byte intensity1 = (byte)(((byteData & 0xF0) >> 4) * 17); - color.PackFromRgba32(new Rgba32(intensity1, intensity1, intensity1, 255)); + color.FromRgba32(new Rgba32(intensity1, intensity1, intensity1, 255)); pixels[x, y] = color; byte intensity2 = (byte)((byteData & 0x0F) * 17); - color.PackFromRgba32(new Rgba32(intensity2, intensity2, intensity2, 255)); + color.FromRgba32(new Rgba32(intensity2, intensity2, intensity2, 255)); pixels[x + 1, y] = color; } @@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff byte byteData = data[offset++]; byte intensity1 = (byte)(((byteData & 0xF0) >> 4) * 17); - color.PackFromRgba32(new Rgba32(intensity1, intensity1, intensity1, 255)); + color.FromRgba32(new Rgba32(intensity1, intensity1, intensity1, 255)); pixels[left + width - 1, y] = color; } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs index a31868980..949549d17 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs @@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff for (int x = left; x < left + width; x++) { byte intensity = data[offset++]; - color.PackFromRgba32(new Rgba32(intensity, intensity, intensity, 255)); + color.FromRgba32(new Rgba32(intensity, intensity, intensity, 255)); pixels[x, y] = color; } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs index 39e3f8104..689a305ca 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs @@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff { int value = bitReader.ReadBits(bitsPerSample[0]); float intensity = ((float)value) / factor; - color.PackFromVector4(new Vector4(intensity, intensity, intensity, 1.0f)); + color.FromVector4(new Vector4(intensity, intensity, intensity, 1.0f)); pixels[x, y] = color; } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs index 11913c89a..6c4bb5612 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs @@ -62,7 +62,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff float r = colorMap[rOffset + i] / 65535F; float g = colorMap[gOffset + i] / 65535F; float b = colorMap[bOffset + i] / 65535F; - palette[i].PackFromVector4(new Vector4(r, g, b, 1.0f)); + palette[i].FromVector4(new Vector4(r, g, b, 1.0f)); } return palette; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs index b39ae92ca..7582220f7 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs @@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff byte r = data[offset++]; byte g = data[offset++]; byte b = data[offset++]; - color.PackFromRgba32(new Rgba32(r, g, b, 255)); + color.FromRgba32(new Rgba32(r, g, b, 255)); pixels[x, y] = color; } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs index 5ea36dffa..df7671d76 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs @@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff float r = ((float)rBitReader.ReadBits(bitsPerSample[0])) / rFactor; float g = ((float)gBitReader.ReadBits(bitsPerSample[1])) / gFactor; float b = ((float)bBitReader.ReadBits(bitsPerSample[2])) / bFactor; - color.PackFromVector4(new Vector4(r, g, b, 1.0f)); + color.FromVector4(new Vector4(r, g, b, 1.0f)); pixels[x, y] = color; } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs index 75675dd9a..ec3341799 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs @@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff float r = ((float)bitReader.ReadBits(bitsPerSample[0])) / rFactor; float g = ((float)bitReader.ReadBits(bitsPerSample[1])) / gFactor; float b = ((float)bitReader.ReadBits(bitsPerSample[2])) / bFactor; - color.PackFromVector4(new Vector4(r, g, b, 1.0f)); + color.FromVector4(new Vector4(r, g, b, 1.0f)); pixels[x, y] = color; } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs index 84cc26228..2d9914de7 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs @@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff { int bit = (b >> (7 - shift)) & 1; byte intensity = (bit == 1) ? (byte)0 : (byte)255; - color.PackFromRgba32(new Rgba32(intensity, intensity, intensity, 255)); + color.FromRgba32(new Rgba32(intensity, intensity, intensity, 255)); pixels[x + shift, y] = color; } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs index 8e9eaaa47..965abec81 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs @@ -38,11 +38,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff byte byteData = data[offset++]; byte intensity1 = (byte)((15 - ((byteData & 0xF0) >> 4)) * 17); - color.PackFromRgba32(new Rgba32(intensity1, intensity1, intensity1, 255)); + color.FromRgba32(new Rgba32(intensity1, intensity1, intensity1, 255)); pixels[x, y] = color; byte intensity2 = (byte)((15 - (byteData & 0x0F)) * 17); - color.PackFromRgba32(new Rgba32(intensity2, intensity2, intensity2, 255)); + color.FromRgba32(new Rgba32(intensity2, intensity2, intensity2, 255)); pixels[x + 1, y] = color; } @@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff byte byteData = data[offset++]; byte intensity1 = (byte)((15 - ((byteData & 0xF0) >> 4)) * 17); - color.PackFromRgba32(new Rgba32(intensity1, intensity1, intensity1, 255)); + color.FromRgba32(new Rgba32(intensity1, intensity1, intensity1, 255)); pixels[left + width - 1, y] = color; } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs index 410c920e1..fb209cecb 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs @@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff for (int x = left; x < left + width; x++) { byte intensity = (byte)(255 - data[offset++]); - color.PackFromRgba32(new Rgba32(intensity, intensity, intensity, 255)); + color.FromRgba32(new Rgba32(intensity, intensity, intensity, 255)); pixels[x, y] = color; } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs index 1da647d99..8bb720bb9 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs @@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff { int value = bitReader.ReadBits(bitsPerSample[0]); float intensity = 1.0f - (((float)value) / factor); - color.PackFromVector4(new Vector4(intensity, intensity, intensity, 1.0f)); + color.FromVector4(new Vector4(intensity, intensity, intensity, 1.0f)); pixels[x, y] = color; } From 879051a0546837658b8c891d4b925393869ab193 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 7 Dec 2018 06:36:23 +1100 Subject: [PATCH 063/275] Update external references --- tests/Images/External | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Images/External b/tests/Images/External index ee90e5f32..1edb0f3e0 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit ee90e5f32218027744b5d40058b587cc1047b76f +Subproject commit 1edb0f3e04c18974821a3012a87f7c2e073c8019 From 7a0461b1bc66d4108cf6282bb5894cbc5f7fdc41 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Fri, 21 Aug 2020 17:13:27 +0300 Subject: [PATCH 064/275] Compilation errors fixes. Temporarily disable obsolete metadata related methods and tests. --- .../Formats/Tiff/ImageExtensions.cs | 4 +- .../BlackIsZero1TiffColor.cs | 2 +- .../BlackIsZero4TiffColor.cs | 2 +- .../BlackIsZero8TiffColor.cs | 2 +- .../BlackIsZeroTiffColor.cs | 2 +- .../PaletteTiffColor.cs | 4 +- .../Rgb888TiffColor.cs | 2 +- .../RgbPlanarTiffColor.cs | 2 +- .../PhotometricInterpretation/RgbTiffColor.cs | 2 +- .../WhiteIsZero1TiffColor.cs | 2 +- .../WhiteIsZero4TiffColor.cs | 2 +- .../WhiteIsZero8TiffColor.cs | 2 +- .../WhiteIsZeroTiffColor.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffDecoder.cs | 24 ++++++++++-- .../Formats/Tiff/TiffDecoderCore.cs | 39 +++++++++---------- src/ImageSharp/Formats/Tiff/TiffEncoder.cs | 20 ++++++---- .../Formats/Tiff/TiffEncoderCore.cs | 22 +++++------ .../Tiff/TiffIfd/TiffIfdEntryCreator.cs | 4 +- .../Codecs/DecodeTiff.cs | 6 +-- .../DeflateTiffCompressionTests.cs | 4 +- .../PhotometricInterpretationTestBase.cs | 4 +- .../Formats/Tiff/TiffDecoderIfdEntryTests.cs | 5 +-- .../Formats/Tiff/TiffDecoderMetadataTests.cs | 16 ++++---- .../Formats/Tiff/TiffEncoderMetadataTests.cs | 16 ++++---- .../Tiff/TiffIfd/TiffIfdEntryCreatorTests.cs | 5 +-- .../TestUtilities/Tiff/TiffIfdParser.cs | 5 +-- 26 files changed, 107 insertions(+), 93 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/ImageExtensions.cs b/src/ImageSharp/Formats/Tiff/ImageExtensions.cs index 3414f84d3..8355a4f26 100644 --- a/src/ImageSharp/Formats/Tiff/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Tiff/ImageExtensions.cs @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp /// The . /// public static Image SaveAsTiff(this Image source, Stream stream) - where TPixel : struct, IPixel + where TPixel : unmanaged, IPixel { return SaveAsTiff(source, stream, null); } @@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp /// The . /// public static Image SaveAsTiff(this Image source, Stream stream, TiffEncoder encoder) - where TPixel : struct, IPixel + where TPixel : unmanaged, IPixel { encoder = encoder ?? new TiffEncoder(); encoder.Encode(source, stream); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs index e317a7af7..16659d9a5 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The height of the image block. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) - where TPixel : struct, IPixel + where TPixel : unmanaged, IPixel { TPixel color = default(TPixel); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs index 62fff4737..22a765465 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The height of the image block. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) - where TPixel : struct, IPixel + where TPixel : unmanaged, IPixel { TPixel color = default(TPixel); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs index 949549d17..5b9c30068 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The height of the image block. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) - where TPixel : struct, IPixel + where TPixel : unmanaged, IPixel { TPixel color = default(TPixel); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs index 689a305ca..a403602ea 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs @@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The height of the image block. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Decode(byte[] data, uint[] bitsPerSample, Buffer2D pixels, int left, int top, int width, int height) - where TPixel : struct, IPixel + where TPixel : unmanaged, IPixel { TPixel color = default(TPixel); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs index 6c4bb5612..6ed0f196e 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs @@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The height of the image block. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Decode(byte[] data, uint[] bitsPerSample, uint[] colorMap, Buffer2D pixels, int left, int top, int width, int height) - where TPixel : struct, IPixel + where TPixel : unmanaged, IPixel { int colorCount = (int)Math.Pow(2, bitsPerSample[0]); TPixel[] palette = GeneratePalette(colorMap, colorCount); @@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff [MethodImpl(MethodImplOptions.AggressiveInlining)] private static TPixel[] GeneratePalette(uint[] colorMap, int colorCount) - where TPixel : struct, IPixel + where TPixel : unmanaged, IPixel { TPixel[] palette = new TPixel[colorCount]; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs index 7582220f7..68e1c986c 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The height of the image block. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) - where TPixel : struct, IPixel + where TPixel : unmanaged, IPixel { TPixel color = default(TPixel); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs index df7671d76..a96d29851 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs @@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The height of the image block. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Decode(byte[][] data, uint[] bitsPerSample, Buffer2D pixels, int left, int top, int width, int height) - where TPixel : struct, IPixel + where TPixel : unmanaged, IPixel { TPixel color = default(TPixel); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs index ec3341799..4dea7909a 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs @@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The height of the image block. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Decode(byte[] data, uint[] bitsPerSample, Buffer2D pixels, int left, int top, int width, int height) - where TPixel : struct, IPixel + where TPixel : unmanaged, IPixel { TPixel color = default(TPixel); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs index 2d9914de7..022b8b6f8 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The height of the image block. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) - where TPixel : struct, IPixel + where TPixel : unmanaged, IPixel { TPixel color = default(TPixel); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs index 965abec81..268d8fe10 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The height of the image block. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) - where TPixel : struct, IPixel + where TPixel : unmanaged, IPixel { TPixel color = default(TPixel); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs index fb209cecb..bf238dcd8 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The height of the image block. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) - where TPixel : struct, IPixel + where TPixel : unmanaged, IPixel { TPixel color = default(TPixel); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs index 8bb720bb9..850ad418d 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs @@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The height of the image block. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Decode(byte[] data, uint[] bitsPerSample, Buffer2D pixels, int left, int top, int width, int height) - where TPixel : struct, IPixel + where TPixel : unmanaged, IPixel { TPixel color = default(TPixel); diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs index 1d4521b0b..16dc8600e 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs @@ -1,8 +1,9 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System.IO; - +using System.Threading; +using System.Threading.Tasks; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats @@ -19,7 +20,7 @@ namespace SixLabors.ImageSharp.Formats /// public Image Decode(Configuration configuration, Stream stream) - where TPixel : struct, IPixel + where TPixel : unmanaged, IPixel { Guard.NotNull(stream, "stream"); @@ -28,5 +29,22 @@ namespace SixLabors.ImageSharp.Formats return decoder.Decode(stream); } } + + /// + public Image Decode(Configuration configuration, Stream stream) + { + throw new System.NotImplementedException(); + } + + /// + public Task> DecodeAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel + { + throw new System.NotImplementedException(); + } + + public Task DecodeAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken) + { + throw new System.NotImplementedException(); + } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 608c2e558..6da36210c 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -7,10 +7,7 @@ using System.IO; using System.Text; using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.MetaData; -using SixLabors.ImageSharp.MetaData.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Formats { @@ -106,7 +103,7 @@ namespace SixLabors.ImageSharp.Formats /// The stream, where the image should be. /// The decoded image. public Image Decode(Stream stream) - where TPixel : struct, IPixel + where TPixel : unmanaged, IPixel { this.InputStream = stream; @@ -199,7 +196,7 @@ namespace SixLabors.ImageSharp.Formats /// The IFD to read the image from. /// The decoded image. public Image DecodeImage(TiffIfd ifd) - where TPixel : struct, IPixel + where TPixel : unmanaged, IPixel { if (!ifd.TryGetIfdEntry(TiffTags.ImageLength, out TiffIfdEntry imageLengthEntry) || !ifd.TryGetIfdEntry(TiffTags.ImageWidth, out TiffIfdEntry imageWidthEntry)) @@ -235,7 +232,7 @@ namespace SixLabors.ImageSharp.Formats /// The IFD to read the image from. /// The image to write the metadata to. public void ReadMetadata(TiffIfd ifd, Image image) - where TPixel : struct, IPixel + where TPixel : unmanaged, IPixel { TiffResolutionUnit resolutionUnit = (TiffResolutionUnit)this.ReadUnsignedInteger(ifd, TiffTags.ResolutionUnit, (uint)TiffResolutionUnit.Inch); @@ -246,57 +243,59 @@ namespace SixLabors.ImageSharp.Formats if (ifd.TryGetIfdEntry(TiffTags.XResolution, out TiffIfdEntry xResolutionEntry)) { Rational xResolution = this.ReadUnsignedRational(ref xResolutionEntry); - image.MetaData.HorizontalResolution = xResolution.ToDouble() * resolutionUnitFactor; + image.Metadata.HorizontalResolution = xResolution.ToDouble() * resolutionUnitFactor; } if (ifd.TryGetIfdEntry(TiffTags.YResolution, out TiffIfdEntry yResolutionEntry)) { Rational yResolution = this.ReadUnsignedRational(ref yResolutionEntry); - image.MetaData.VerticalResolution = yResolution.ToDouble() * resolutionUnitFactor; + image.Metadata.VerticalResolution = yResolution.ToDouble() * resolutionUnitFactor; } } if (!this.ignoreMetadata) { + /* if (ifd.TryGetIfdEntry(TiffTags.Artist, out TiffIfdEntry artistEntry)) { - image.MetaData.Properties.Add(new ImageProperty(TiffMetadataNames.Artist, this.ReadString(ref artistEntry))); + image.Metadata.Properties.Add(new ImageProperty(TiffMetadataNames.Artist, this.ReadString(ref artistEntry))); } if (ifd.TryGetIfdEntry(TiffTags.Copyright, out TiffIfdEntry copyrightEntry)) { - image.MetaData.Properties.Add(new ImageProperty(TiffMetadataNames.Copyright, this.ReadString(ref copyrightEntry))); + image.Metadata.Properties.Add(new ImageProperty(TiffMetadataNames.Copyright, this.ReadString(ref copyrightEntry))); } if (ifd.TryGetIfdEntry(TiffTags.DateTime, out TiffIfdEntry dateTimeEntry)) { - image.MetaData.Properties.Add(new ImageProperty(TiffMetadataNames.DateTime, this.ReadString(ref dateTimeEntry))); + image.Metadata.Properties.Add(new ImageProperty(TiffMetadataNames.DateTime, this.ReadString(ref dateTimeEntry))); } if (ifd.TryGetIfdEntry(TiffTags.HostComputer, out TiffIfdEntry hostComputerEntry)) { - image.MetaData.Properties.Add(new ImageProperty(TiffMetadataNames.HostComputer, this.ReadString(ref hostComputerEntry))); + image.Metadata.Properties.Add(new ImageProperty(TiffMetadataNames.HostComputer, this.ReadString(ref hostComputerEntry))); } if (ifd.TryGetIfdEntry(TiffTags.ImageDescription, out TiffIfdEntry imageDescriptionEntry)) { - image.MetaData.Properties.Add(new ImageProperty(TiffMetadataNames.ImageDescription, this.ReadString(ref imageDescriptionEntry))); + image.Metadata.Properties.Add(new ImageProperty(TiffMetadataNames.ImageDescription, this.ReadString(ref imageDescriptionEntry))); } if (ifd.TryGetIfdEntry(TiffTags.Make, out TiffIfdEntry makeEntry)) { - image.MetaData.Properties.Add(new ImageProperty(TiffMetadataNames.Make, this.ReadString(ref makeEntry))); + image.Metadata.Properties.Add(new ImageProperty(TiffMetadataNames.Make, this.ReadString(ref makeEntry))); } if (ifd.TryGetIfdEntry(TiffTags.Model, out TiffIfdEntry modelEntry)) { - image.MetaData.Properties.Add(new ImageProperty(TiffMetadataNames.Model, this.ReadString(ref modelEntry))); + image.Metadata.Properties.Add(new ImageProperty(TiffMetadataNames.Model, this.ReadString(ref modelEntry))); } if (ifd.TryGetIfdEntry(TiffTags.Software, out TiffIfdEntry softwareEntry)) { - image.MetaData.Properties.Add(new ImageProperty(TiffMetadataNames.Software, this.ReadString(ref softwareEntry))); + image.Metadata.Properties.Add(new ImageProperty(TiffMetadataNames.Software, this.ReadString(ref softwareEntry))); } + */ } } @@ -588,7 +587,7 @@ namespace SixLabors.ImageSharp.Formats /// The width of the image block. /// The height of the image block. public void ProcessImageBlockChunky(byte[] data, Buffer2D pixels, int left, int top, int width, int height) - where TPixel : struct, IPixel + where TPixel : unmanaged, IPixel { switch (this.ColorType) { @@ -641,7 +640,7 @@ namespace SixLabors.ImageSharp.Formats /// The width of the image block. /// The height of the image block. public void ProcessImageBlockPlanar(byte[][] data, Buffer2D pixels, int left, int top, int width, int height) - where TPixel : struct, IPixel + where TPixel : unmanaged, IPixel { switch (this.ColorType) { @@ -1232,7 +1231,7 @@ namespace SixLabors.ImageSharp.Formats /// An array of byte offsets to each strip in the image. /// An array of the size of each strip (in bytes). private void DecodeImageStrips(Image image, int rowsPerStrip, uint[] stripOffsets, uint[] stripByteCounts) - where TPixel : struct, IPixel + where TPixel : unmanaged, IPixel { int stripsPerPixel = this.PlanarConfiguration == TiffPlanarConfiguration.Chunky ? 1 : this.BitsPerSample.Length; int stripsPerPlane = stripOffsets.Length / stripsPerPixel; diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs index 63886a075..613f216ea 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs @@ -1,7 +1,9 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System.IO; +using System.Threading; +using System.Threading.Tasks; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats @@ -11,17 +13,19 @@ namespace SixLabors.ImageSharp.Formats /// public class TiffEncoder : IImageEncoder, ITiffEncoderOptions { - /// - /// Encodes the image to the specified stream from the . - /// - /// The pixel format. - /// The to encode from. - /// The to encode the image data to. + /// public void Encode(Image image, Stream stream) - where TPixel : struct, IPixel + where TPixel : unmanaged, IPixel { var encode = new TiffEncoderCore(this); encode.Encode(image, stream); } + + /// + public Task EncodeAsync(Image image, Stream stream, CancellationToken cancellationToken) + where TPixel : unmanaged, IPixel + { + throw new System.NotImplementedException(); + } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 9ab72f316..8354d0f05 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -5,10 +5,7 @@ using System; using System.Collections.Generic; using System.IO; using SixLabors.ImageSharp.Formats.Tiff; -using SixLabors.ImageSharp.MetaData; -using SixLabors.ImageSharp.MetaData.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Formats { @@ -43,7 +40,7 @@ namespace SixLabors.ImageSharp.Formats /// The to encode from. /// The to encode the image data to. public void Encode(Image image, Stream stream) - where TPixel : struct, IPixel + where TPixel : unmanaged, IPixel { Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); @@ -134,7 +131,7 @@ namespace SixLabors.ImageSharp.Formats /// The marker to write this IFD offset. /// The marker to write the next IFD offset (if present). public long WriteImage(TiffWriter writer, Image image, long ifdOffset) - where TPixel : struct, IPixel + where TPixel : unmanaged, IPixel { List ifdEntries = new List(); @@ -154,13 +151,14 @@ namespace SixLabors.ImageSharp.Formats /// The to encode from. /// The metadata entries to add to the IFD. public void AddMetadata(Image image, List ifdEntries) - where TPixel : struct, IPixel + where TPixel : unmanaged, IPixel { - ifdEntries.AddUnsignedRational(TiffTags.XResolution, new Rational(image.MetaData.HorizontalResolution)); - ifdEntries.AddUnsignedRational(TiffTags.YResolution, new Rational(image.MetaData.VerticalResolution)); + ifdEntries.AddUnsignedRational(TiffTags.XResolution, new Rational(image.Metadata.HorizontalResolution)); + ifdEntries.AddUnsignedRational(TiffTags.YResolution, new Rational(image.Metadata.VerticalResolution)); ifdEntries.AddUnsignedShort(TiffTags.ResolutionUnit, (uint)TiffResolutionUnit.Inch); - foreach (ImageProperty metadata in image.MetaData.Properties) + /* + foreach (ImageProperty metadata in image.Metadata.Properties) { switch (metadata.Name) { @@ -212,7 +210,7 @@ namespace SixLabors.ImageSharp.Formats break; } } - } + } */ } /// @@ -222,9 +220,9 @@ namespace SixLabors.ImageSharp.Formats /// The to encode from. /// The image format entries to add to the IFD. public void AddImageFormat(Image image, List ifdEntries) - where TPixel : struct, IPixel + where TPixel : unmanaged, IPixel { throw new NotImplementedException(); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntryCreator.cs b/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntryCreator.cs index 35517d190..e0a15fd05 100644 --- a/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntryCreator.cs +++ b/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntryCreator.cs @@ -4,8 +4,6 @@ using System; using System.Collections.Generic; using System.Text; -using SixLabors.ImageSharp.MetaData.Profiles.Exif; -using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Formats.Tiff { @@ -351,4 +349,4 @@ namespace SixLabors.ImageSharp.Formats.Tiff bytes[offset + 3] = (byte)(value >> 24); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Benchmarks/Codecs/DecodeTiff.cs b/tests/ImageSharp.Benchmarks/Codecs/DecodeTiff.cs index dd6fc34b3..6c11615ca 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/DecodeTiff.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/DecodeTiff.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System.Drawing; @@ -9,7 +9,7 @@ using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; using CoreImage = SixLabors.ImageSharp.Image; -using CoreSize = SixLabors.Primitives.Size; +using CoreSize = SixLabors.ImageSharp.Size; namespace SixLabors.ImageSharp.Benchmarks.Codecs { @@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs } [Benchmark(Baseline = true, Description = "System.Drawing Tiff")] - public Size TiffSystemDrawing() + public System.Drawing.Size TiffSystemDrawing() { using (MemoryStream memoryStream = new MemoryStream(this.tiffBytes)) { diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs index c739adcaf..a8a127093 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs @@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Tests Stream compressedStream = new MemoryStream(); using (Stream uncompressedStream = new MemoryStream(data), - deflateStream = new ZlibDeflateStream(compressedStream, 6)) + deflateStream = new ZlibDeflateStream(Configuration.Default.MemoryAllocator, compressedStream, ImageSharp.Formats.Png.PngCompressionLevel.Level6)) { uncompressedStream.CopyTo(deflateStream); } @@ -45,4 +45,4 @@ namespace SixLabors.ImageSharp.Tests return compressedStream; } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs index 1c8341fad..4b096abbb 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs @@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.Tests int resultWidth = expectedResult[0].Length; int resultHeight = expectedResult.Length; Image image = new Image(resultWidth, resultHeight); - image.Mutate(x => x.Fill(DefaultColor)); + image.Mutate(x => x.BackgroundColor(DefaultColor)); Buffer2D pixels = image.GetRootFramePixelBuffer(); decodeAction(pixels); @@ -63,4 +63,4 @@ namespace SixLabors.ImageSharp.Tests } } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs index 64a5b9516..f0a9fe42e 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs @@ -11,8 +11,7 @@ namespace SixLabors.ImageSharp.Tests using ImageSharp.Formats; using ImageSharp.Formats.Tiff; - using SixLabors.ImageSharp.MetaData.Profiles.Exif; - using SixLabors.ImageSharp.Primitives; + using SixLabors.ImageSharp.Metadata.Profiles.Exif; public class TiffDecoderIfdEntryTests { @@ -843,4 +842,4 @@ namespace SixLabors.ImageSharp.Tests return (decoder, ifdEntry); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs index 6766ba80f..3ddd0e9a1 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs @@ -77,10 +77,10 @@ namespace SixLabors.ImageSharp.Tests decoder.ReadMetadata(ifd, image); - Assert.Equal(expectedHorizonalResolution, image.MetaData.HorizontalResolution, 10); - Assert.Equal(expectedVerticalResolution, image.MetaData.VerticalResolution, 10); + Assert.Equal(expectedHorizonalResolution, image.Metadata.HorizontalResolution, 10); + Assert.Equal(expectedVerticalResolution, image.Metadata.VerticalResolution, 10); } - + /* [Theory] [MemberData(nameof(BaselineMetadataValues))] public void ReadMetadata_SetsAsciiMetadata(bool isLittleEndian, ushort tag, string metadataName, string metadataValue) @@ -102,10 +102,10 @@ namespace SixLabors.ImageSharp.Tests Image image = new Image(null, 20, 20); decoder.ReadMetadata(ifd, image); - var metadata = image.MetaData.Properties.FirstOrDefault(m => m.Name == metadataName).Value; + var metadata = image.Metadata.Properties.FirstOrDefault(m => m.Name == metadataName).Value; Assert.Equal(metadataValue, metadata); - } + } [Theory] [MemberData(nameof(BaselineMetadataValues))] @@ -129,9 +129,9 @@ namespace SixLabors.ImageSharp.Tests Image image = new Image(null, 20, 20); decoder.ReadMetadata(ifd, image); - var metadata = image.MetaData.Properties.FirstOrDefault(m => m.Name == metadataName).Value; + var metadata = image.Metadata.Properties.FirstOrDefault(m => m.Name == metadataName).Value; Assert.Null(metadata); - } + } */ } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderMetadataTests.cs index c90d77f4a..31ff39c97 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderMetadataTests.cs @@ -11,9 +11,8 @@ namespace SixLabors.ImageSharp.Tests using ImageSharp.Formats.Tiff; using System.Collections.Generic; - using SixLabors.ImageSharp.MetaData; - using SixLabors.ImageSharp.MetaData.Profiles.Exif; - using SixLabors.ImageSharp.Primitives; + using SixLabors.ImageSharp.Metadata; + using SixLabors.ImageSharp.Metadata.Profiles.Exif; public class TiffEncoderMetadataTests { @@ -30,8 +29,8 @@ namespace SixLabors.ImageSharp.Tests public void AddMetadata_SetsImageResolution() { Image image = new Image(100, 100); - image.MetaData.HorizontalResolution = 40.0; - image.MetaData.VerticalResolution = 50.5; + image.Metadata.HorizontalResolution = 40.0; + image.Metadata.VerticalResolution = 50.5; TiffEncoderCore encoder = new TiffEncoderCore(null); List ifdEntries = new List(); @@ -42,18 +41,19 @@ namespace SixLabors.ImageSharp.Tests Assert.Equal(TiffResolutionUnit.Inch, (TiffResolutionUnit?)ifdEntries.GetInteger(TiffTags.ResolutionUnit)); } + /* [Theory] [MemberData(nameof(BaselineMetadataValues))] public void AddMetadata_SetsAsciiMetadata(ushort tag, string metadataName, string metadataValue) { Image image = new Image(100, 100); - image.MetaData.Properties.Add(new ImageProperty(metadataName, metadataValue)); + image.Metadata.Properties.Add(new ImageProperty(metadataName, metadataValue)); TiffEncoderCore encoder = new TiffEncoderCore(null); List ifdEntries = new List(); encoder.AddMetadata(image, ifdEntries); Assert.Equal(metadataValue + "\0", ifdEntries.GetAscii(tag)); - } + } */ } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryCreatorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryCreatorTests.cs index 7c0f55ee7..e203cc9f1 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryCreatorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryCreatorTests.cs @@ -10,8 +10,7 @@ namespace SixLabors.ImageSharp.Tests using ImageSharp.Formats.Tiff; - using SixLabors.ImageSharp.MetaData.Profiles.Exif; - using SixLabors.ImageSharp.Primitives; + using SixLabors.ImageSharp.Metadata.Profiles.Exif; public class TiffIfdEntryCreatorTests { @@ -406,4 +405,4 @@ namespace SixLabors.ImageSharp.Tests Assert.Equal(bytes, entry.Value); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffIfdParser.cs b/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffIfdParser.cs index 55e95dc7f..d58e27340 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffIfdParser.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffIfdParser.cs @@ -9,8 +9,7 @@ namespace SixLabors.ImageSharp.Tests using System.Text; using ImageSharp.Formats.Tiff; - using SixLabors.ImageSharp.MetaData.Profiles.Exif; - using SixLabors.ImageSharp.Primitives; + using SixLabors.ImageSharp.Metadata.Profiles.Exif; using Xunit; /// @@ -75,4 +74,4 @@ namespace SixLabors.ImageSharp.Tests return Encoding.UTF8.GetString(entry.Value, 0, (int)entry.Count); } } -} \ No newline at end of file +} From 712104a786d4238790a1b1b173b1b77b530ab566 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Fri, 21 Aug 2020 17:14:20 +0300 Subject: [PATCH 065/275] Update license --- .../Formats/Tiff/Compression/DeflateTiffCompression.cs | 2 +- src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs | 2 +- .../Formats/Tiff/Compression/NoneTiffCompression.cs | 2 +- .../Formats/Tiff/Compression/PackBitsTiffCompression.cs | 2 +- .../Formats/Tiff/Compression/TiffCompressionType.cs | 2 +- src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs | 2 +- src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs | 2 +- src/ImageSharp/Formats/Tiff/Constants/TiffExtraSamples.cs | 2 +- src/ImageSharp/Formats/Tiff/Constants/TiffFillOrder.cs | 2 +- src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs | 2 +- src/ImageSharp/Formats/Tiff/Constants/TiffOrientation.cs | 2 +- .../Formats/Tiff/Constants/TiffPhotometricInterpretation.cs | 2 +- .../Formats/Tiff/Constants/TiffPlanarConfiguration.cs | 2 +- src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs | 2 +- src/ImageSharp/Formats/Tiff/Constants/TiffSubfileType.cs | 2 +- src/ImageSharp/Formats/Tiff/Constants/TiffTags.cs | 2 +- src/ImageSharp/Formats/Tiff/Constants/TiffThreshholding.cs | 2 +- src/ImageSharp/Formats/Tiff/Constants/TiffType.cs | 2 +- src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs | 2 +- src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs | 2 +- src/ImageSharp/Formats/Tiff/ImageExtensions.cs | 2 +- .../Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs | 2 +- .../Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs | 2 +- .../Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs | 2 +- .../Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs | 2 +- .../Tiff/PhotometricInterpretation/PaletteTiffColor.cs | 2 +- .../Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs | 2 +- .../Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs | 2 +- .../Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs | 2 +- .../Formats/Tiff/PhotometricInterpretation/TiffColorType.cs | 2 +- .../Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs | 2 +- .../Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs | 2 +- .../Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs | 2 +- .../Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffDecoder.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffEncoder.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffFormat.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfd.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntry.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntryCreator.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffMetadataNames.cs | 2 +- src/ImageSharp/Formats/Tiff/Utils/BitReader.cs | 2 +- src/ImageSharp/Formats/Tiff/Utils/SubStream.cs | 2 +- src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs | 2 +- src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs | 2 +- src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs | 2 +- src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs | 2 +- tests/ImageSharp.Benchmarks/Codecs/DecodeTiff.cs | 2 +- .../Formats/Tiff/Compression/DeflateTiffCompressionTests.cs | 2 +- .../Formats/Tiff/Compression/LzwTiffCompressionTests.cs | 2 +- .../Formats/Tiff/Compression/NoneTiffCompressionTests.cs | 2 +- .../Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs | 2 +- .../PhotometricInterpretation/BlackIsZeroTiffColorTests.cs | 2 +- .../Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs | 2 +- .../PhotometricInterpretationTestBase.cs | 2 +- .../Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs | 2 +- .../Tiff/PhotometricInterpretation/RgbTiffColorTests.cs | 2 +- .../PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs | 2 +- tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs | 2 +- .../ImageSharp.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs | 2 +- tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderIfdTests.cs | 2 +- tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderImageTests.cs | 4 ++-- .../ImageSharp.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs | 2 +- tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs | 2 +- tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderIfdTests.cs | 2 +- .../ImageSharp.Tests/Formats/Tiff/TiffEncoderMetadataTests.cs | 2 +- tests/ImageSharp.Tests/Formats/Tiff/TiffFormatTests.cs | 2 +- .../Formats/Tiff/TiffIfd/TiffIfdEntryCreatorTests.cs | 2 +- .../Formats/Tiff/TiffIfd/TiffIfdEntryTests.cs | 2 +- tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs | 2 +- .../Formats/Tiff/TiffImageFormatDetectorTests.cs | 2 +- tests/ImageSharp.Tests/Formats/Tiff/Utils/SubStreamTests.cs | 2 +- tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs | 2 +- tests/ImageSharp.Tests/TestUtilities/ByteArrayUtility.cs | 2 +- tests/ImageSharp.Tests/TestUtilities/ByteBuffer.cs | 2 +- .../ImageSharp.Tests/TestUtilities/Tiff/ITiffGenDataSource.cs | 2 +- tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenDataBlock.cs | 2 +- .../TestUtilities/Tiff/TiffGenDataReference.cs | 2 +- tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenEntry.cs | 2 +- .../ImageSharp.Tests/TestUtilities/Tiff/TiffGenExtensions.cs | 2 +- tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenHeader.cs | 2 +- tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenIfd.cs | 2 +- .../TestUtilities/Tiff/TiffGenIfdExtensions.cs | 2 +- tests/ImageSharp.Tests/TestUtilities/Tiff/TiffIfdParser.cs | 2 +- 88 files changed, 89 insertions(+), 89 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs index 00d69dbd5..b42ac9ee1 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System; diff --git a/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs index 5de966554..2c244b606 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System.IO; diff --git a/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs index a9587d199..c78e22b41 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System.IO; diff --git a/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs index a6cd8f88d..9d5f041bf 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System; diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs index 4121f90b2..b62def1ea 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Formats.Tiff diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs index e5ee8b195..f8a661c1b 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Formats.Tiff diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs index a2044314a..c19072ef0 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System.Collections.Generic; diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffExtraSamples.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffExtraSamples.cs index d34d999b9..b306105db 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffExtraSamples.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffExtraSamples.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Formats.Tiff diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffFillOrder.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffFillOrder.cs index e4d30a324..024a41911 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffFillOrder.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffFillOrder.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Formats.Tiff diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs index b881ac209..495b499f8 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System; diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffOrientation.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffOrientation.cs index 035f88809..bb61b51bd 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffOrientation.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffOrientation.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Formats.Tiff diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs index dd4d923b8..c197965a6 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Formats.Tiff diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffPlanarConfiguration.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffPlanarConfiguration.cs index 4fc0aa4c8..e0535be9d 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffPlanarConfiguration.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffPlanarConfiguration.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Formats.Tiff diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs index 7bb3dbd6e..51c3a72ce 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Formats.Tiff diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffSubfileType.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffSubfileType.cs index 4039ae9e2..4b6b2061f 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffSubfileType.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffSubfileType.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Formats.Tiff diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffTags.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffTags.cs index 38cf4280e..fe9b49ef7 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffTags.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffTags.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Formats.Tiff diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffThreshholding.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffThreshholding.cs index 0a398d231..9002df978 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffThreshholding.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffThreshholding.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Formats.Tiff diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffType.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffType.cs index 8e55d80cc..f1d0adfc2 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffType.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffType.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Formats.Tiff diff --git a/src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs b/src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs index c718102b8..fb0c93a60 100644 --- a/src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs +++ b/src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Formats diff --git a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs index e10396d5f..2c8e222e8 100644 --- a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs +++ b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Formats diff --git a/src/ImageSharp/Formats/Tiff/ImageExtensions.cs b/src/ImageSharp/Formats/Tiff/ImageExtensions.cs index 8355a4f26..117ad2a9c 100644 --- a/src/ImageSharp/Formats/Tiff/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Tiff/ImageExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System.IO; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs index 16659d9a5..224da447c 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs index 22a765465..1e59624b1 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System.Runtime.CompilerServices; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs index 5b9c30068..e34346fd4 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System.Runtime.CompilerServices; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs index a403602ea..e5414da8b 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs index 6ed0f196e..509965641 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs index 68e1c986c..c34d4f087 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System.Runtime.CompilerServices; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs index a96d29851..b8a62b3ae 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs index 4dea7909a..584beb365 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs index 7aea15885..f58b37431 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Formats.Tiff diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs index 022b8b6f8..79784cbd9 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs index 268d8fe10..061624349 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System.Runtime.CompilerServices; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs index bf238dcd8..eb8608173 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System.Runtime.CompilerServices; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs index 850ad418d..f8492a510 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System; diff --git a/src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs b/src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs index 0da193239..4347b0dae 100644 --- a/src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs +++ b/src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Formats diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs index 16dc8600e..ea93258c6 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System.IO; diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 6da36210c..8e986f847 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System; diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs index 613f216ea..319b1465b 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System.IO; diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 8354d0f05..d07e5319f 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System; diff --git a/src/ImageSharp/Formats/Tiff/TiffFormat.cs b/src/ImageSharp/Formats/Tiff/TiffFormat.cs index 3f2807a06..e4acdad14 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFormat.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFormat.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System.Collections.Generic; diff --git a/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfd.cs b/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfd.cs index a6534c155..65d53e153 100644 --- a/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfd.cs +++ b/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfd.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Formats.Tiff diff --git a/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntry.cs b/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntry.cs index de5974035..b98c3b862 100644 --- a/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntry.cs +++ b/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntry.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Formats.Tiff diff --git a/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntryCreator.cs b/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntryCreator.cs index e0a15fd05..e98a73b84 100644 --- a/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntryCreator.cs +++ b/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntryCreator.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System; diff --git a/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs b/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs index 12ffd5ed5..15e2bb2fb 100644 --- a/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs +++ b/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System; diff --git a/src/ImageSharp/Formats/Tiff/TiffMetadataNames.cs b/src/ImageSharp/Formats/Tiff/TiffMetadataNames.cs index 10f558b29..a25882302 100644 --- a/src/ImageSharp/Formats/Tiff/TiffMetadataNames.cs +++ b/src/ImageSharp/Formats/Tiff/TiffMetadataNames.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Formats diff --git a/src/ImageSharp/Formats/Tiff/Utils/BitReader.cs b/src/ImageSharp/Formats/Tiff/Utils/BitReader.cs index cbd7256ed..2c1b25000 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/BitReader.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Formats.Tiff diff --git a/src/ImageSharp/Formats/Tiff/Utils/SubStream.cs b/src/ImageSharp/Formats/Tiff/Utils/SubStream.cs index aaf9af23a..1a4da9a31 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/SubStream.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/SubStream.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System; diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs index 6ac09f391..7e33f186b 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System; diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs index e024b59fa..a18a820a3 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System; diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs index 7842a71c1..823724409 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System.IO; diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs index 5aa59c108..7501e314a 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System; diff --git a/tests/ImageSharp.Benchmarks/Codecs/DecodeTiff.cs b/tests/ImageSharp.Benchmarks/Codecs/DecodeTiff.cs index 6c11615ca..2a01c60a1 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/DecodeTiff.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/DecodeTiff.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System.Drawing; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs index a8a127093..faf61f872 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Tests diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs index 3f379f8f6..af853d6c9 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Tests diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs index 6f638cf9e..39fc3e605 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Tests diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs index b60524025..707dad1e0 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Tests diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs index 70ebd2133..113f6928e 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs index 56e3a0598..895a8af92 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs index 4b096abbb..8c40a7bdd 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs index 09f2af0d1..810e8e731 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs index 7d5cb1782..6523a9a19 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs index cddf05393..054368e21 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs index 73f2a8862..d038b69cd 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs index f0a9fe42e..b39cecb18 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Tests diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderIfdTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderIfdTests.cs index 6accdf995..0b018dfc5 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderIfdTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderIfdTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Tests diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderImageTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderImageTests.cs index 3b1717705..8e40015a5 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderImageTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderImageTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; @@ -509,4 +509,4 @@ namespace SixLabors.ImageSharp.Tests }; } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs index 3ddd0e9a1..ecc510461 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs index 667d4e232..e1086e735 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Tests diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderIfdTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderIfdTests.cs index edcf5eb4e..3b9bce493 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderIfdTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderIfdTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Tests diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderMetadataTests.cs index 31ff39c97..dd52317a9 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderMetadataTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffFormatTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffFormatTests.cs index a6bcfb9ef..e3d51e76d 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffFormatTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffFormatTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Tests diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryCreatorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryCreatorTests.cs index e203cc9f1..d5ce048ea 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryCreatorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryCreatorTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Tests diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryTests.cs index 627042f42..105eab250 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Tests diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs index f6a3c90b7..b1374a0ae 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Tests diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffImageFormatDetectorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffImageFormatDetectorTests.cs index 9800567f5..89de19fda 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffImageFormatDetectorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffImageFormatDetectorTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Tests diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Utils/SubStreamTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Utils/SubStreamTests.cs index 1f8f74664..2b57a5a70 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Utils/SubStreamTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Utils/SubStreamTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Tests diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs index ce09cd72e..f18611acf 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Tests diff --git a/tests/ImageSharp.Tests/TestUtilities/ByteArrayUtility.cs b/tests/ImageSharp.Tests/TestUtilities/ByteArrayUtility.cs index dbe1d4755..27dfdb8a2 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ByteArrayUtility.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ByteArrayUtility.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Tests diff --git a/tests/ImageSharp.Tests/TestUtilities/ByteBuffer.cs b/tests/ImageSharp.Tests/TestUtilities/ByteBuffer.cs index f646ab5be..92b59271f 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ByteBuffer.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ByteBuffer.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Tests diff --git a/tests/ImageSharp.Tests/TestUtilities/Tiff/ITiffGenDataSource.cs b/tests/ImageSharp.Tests/TestUtilities/Tiff/ITiffGenDataSource.cs index 3b84dbbc2..4e66879b9 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tiff/ITiffGenDataSource.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tiff/ITiffGenDataSource.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Tests diff --git a/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenDataBlock.cs b/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenDataBlock.cs index 8764b4d51..1dd70b57b 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenDataBlock.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenDataBlock.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Tests diff --git a/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenDataReference.cs b/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenDataReference.cs index f72f56b2c..4c044ac52 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenDataReference.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenDataReference.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Tests diff --git a/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenEntry.cs b/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenEntry.cs index cf4892ede..a652ba01b 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenEntry.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenEntry.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Tests diff --git a/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenExtensions.cs index cd1382c3b..edef6c064 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Tests diff --git a/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenHeader.cs b/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenHeader.cs index e22128f77..be2784fca 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenHeader.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenHeader.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Tests diff --git a/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenIfd.cs b/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenIfd.cs index 4736a6fdf..99ef0d133 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenIfd.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenIfd.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Tests diff --git a/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenIfdExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenIfdExtensions.cs index e03f6ae3a..bfa74c3d0 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenIfdExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenIfdExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Tests diff --git a/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffIfdParser.cs b/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffIfdParser.cs index d58e27340..51890204f 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffIfdParser.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffIfdParser.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Tests From 2457d20bfc0187ceb2648d2a02abcb52ecd8d5a0 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sun, 11 Aug 2019 12:44:53 +0300 Subject: [PATCH 066/275] Move tiff classes into Formats.Tiff namespace. Mark tests with category. --- src/ImageSharp/Configuration.cs | 1 + .../Formats/Tiff/ITiffDecoderOptions.cs | 2 +- .../Formats/Tiff/ITiffEncoderOptions.cs | 2 +- .../Formats/Tiff/ImageExtensions.cs | 2 +- .../Formats/Tiff/TiffConfigurationModule.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffDecoder.cs | 2 +- .../Formats/Tiff/TiffDecoderCore.cs | 5 ++--- src/ImageSharp/Formats/Tiff/TiffEncoder.cs | 2 +- .../Formats/Tiff/TiffEncoderCore.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffFormat.cs | 2 +- .../Formats/Tiff/TiffImageFormatDetector.cs | 2 +- .../Formats/Tiff/TiffMetadataNames.cs | 2 +- .../DeflateTiffCompressionTests.cs | 16 ++++++-------- .../Compression/LzwTiffCompressionTests.cs | 13 ++++++----- .../Compression/NoneTiffCompressionTests.cs | 14 ++++++------ .../PackBitsTiffCompressionTests.cs | 14 ++++++------ .../BlackIsZeroTiffColorTests.cs | 12 +++++----- .../PaletteTiffColorTests.cs | 12 +++++----- .../PhotometricInterpretationTestBase.cs | 13 ++++++----- .../RgbPlanarTiffColorTests.cs | 12 +++++----- .../RgbTiffColorTests.cs | 12 +++++----- .../WhiteIsZeroTiffColorTests.cs | 12 +++++----- .../Formats/Tiff/TiffDecoderHeaderTests.cs | 13 +++++------ .../Formats/Tiff/TiffDecoderIfdEntryTests.cs | 19 +++++++--------- .../Formats/Tiff/TiffDecoderIfdTests.cs | 15 ++++++------- .../Formats/Tiff/TiffDecoderImageTests.cs | 14 +++++------- .../Formats/Tiff/TiffDecoderMetadataTests.cs | 13 +++++------ .../Formats/Tiff/TiffEncoderHeaderTests.cs | 16 ++++++-------- .../Formats/Tiff/TiffEncoderIfdTests.cs | 22 +++++++++---------- .../Formats/Tiff/TiffEncoderMetadataTests.cs | 15 +++++-------- .../Formats/Tiff/TiffFormatTests.cs | 12 +++++----- .../Tiff/TiffIfd/TiffIfdEntryCreatorTests.cs | 18 +++++++-------- .../Formats/Tiff/TiffIfd/TiffIfdEntryTests.cs | 12 +++++----- .../Formats/Tiff/TiffIfd/TiffIfdTests.cs | 12 +++++----- .../Tiff/TiffImageFormatDetectorTests.cs | 14 ++++++------ .../Formats/Tiff/Utils/SubStreamTests.cs | 16 +++++++------- .../Formats/Tiff/Utils/TiffWriterTests.cs | 14 ++++++------ 37 files changed, 175 insertions(+), 206 deletions(-) diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index 062bcb229..3ed61aa6c 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -10,6 +10,7 @@ using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Tga; +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Processing; diff --git a/src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs b/src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs index fb0c93a60..cee66694b 100644 --- a/src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs +++ b/src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Encapsulates the options for the . diff --git a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs index 2c8e222e8..98efa52cd 100644 --- a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs +++ b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Encapsulates the options for the . diff --git a/src/ImageSharp/Formats/Tiff/ImageExtensions.cs b/src/ImageSharp/Formats/Tiff/ImageExtensions.cs index 117ad2a9c..611f99538 100644 --- a/src/ImageSharp/Formats/Tiff/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Tiff/ImageExtensions.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System.IO; -using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp diff --git a/src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs b/src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs index 4347b0dae..e96dba207 100644 --- a/src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs +++ b/src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Registers the image encoders, decoders and mime type detectors for the TIFF format. diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs index ea93258c6..ba52e4266 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs @@ -6,7 +6,7 @@ using System.Threading; using System.Threading.Tasks; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Image decoder for generating an image out of a TIFF stream. diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 8e986f847..f134376ff 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -5,11 +5,10 @@ using System; using System.Buffers; using System.IO; using System.Text; -using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Performs the tiff decoding operation. @@ -24,7 +23,7 @@ namespace SixLabors.ImageSharp.Formats /// /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. /// - private bool ignoreMetadata; + private readonly bool ignoreMetadata; /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs index 319b1465b..4c0d5dff8 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs @@ -6,7 +6,7 @@ using System.Threading; using System.Threading.Tasks; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Encoder for writing the data image to a stream in TIFF format. diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index d07e5319f..2fa7e7925 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -7,7 +7,7 @@ using System.IO; using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Performs the TIFF encoding operation. diff --git a/src/ImageSharp/Formats/Tiff/TiffFormat.cs b/src/ImageSharp/Formats/Tiff/TiffFormat.cs index e4acdad14..1ee5c89cc 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFormat.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFormat.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using SixLabors.ImageSharp.Formats.Tiff; -namespace SixLabors.ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Encapsulates the means to encode and decode Tiff images. diff --git a/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs b/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs index 15e2bb2fb..f7e6f7a99 100644 --- a/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs +++ b/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Detects tiff file headers diff --git a/src/ImageSharp/Formats/Tiff/TiffMetadataNames.cs b/src/ImageSharp/Formats/Tiff/TiffMetadataNames.cs index a25882302..a610df814 100644 --- a/src/ImageSharp/Formats/Tiff/TiffMetadataNames.cs +++ b/src/ImageSharp/Formats/Tiff/TiffMetadataNames.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Defines constants for each of the supported TIFF metadata types. diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs index faf61f872..66868d3dc 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs @@ -1,16 +1,14 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Tests -{ - using System.IO; - using Xunit; - - using ImageSharp.Formats; - using ImageSharp.Formats.Tiff; - - using SixLabors.ImageSharp.Formats.Png.Zlib; +using System.IO; +using SixLabors.ImageSharp.Formats.Png.Zlib; +using SixLabors.ImageSharp.Formats.Tiff; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Formats.Tiff +{ + [Trait("Category", "Tiff")] public class DeflateTiffCompressionTests { [Theory] diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs index af853d6c9..de4d5f46d 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs @@ -1,12 +1,13 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Tests -{ - using System.IO; - using Xunit; - using ImageSharp.Formats.Tiff; +using System.IO; +using SixLabors.ImageSharp.Formats.Tiff; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Formats.Tiff +{ + [Trait("Category", "Tiff")] public class LzwTiffCompressionTests { [Theory] @@ -40,4 +41,4 @@ namespace SixLabors.ImageSharp.Tests return compressedStream; } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs index 39fc3e605..8e0d81fa4 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs @@ -1,13 +1,13 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Tests -{ - using System.IO; - using Xunit; - - using ImageSharp.Formats.Tiff; +using System.IO; +using SixLabors.ImageSharp.Formats.Tiff; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Formats.Tiff +{ + [Trait("Category", "Tiff")] public class NoneTiffCompressionTests { [Theory] @@ -23,4 +23,4 @@ namespace SixLabors.ImageSharp.Tests Assert.Equal(expectedResult, buffer); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs index 707dad1e0..3888f6bf9 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs @@ -1,13 +1,13 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Tests -{ - using System.IO; - using Xunit; - - using ImageSharp.Formats.Tiff; +using System.IO; +using SixLabors.ImageSharp.Formats.Tiff; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Formats.Tiff +{ + [Trait("Category", "Tiff")] public class PackBitsTiffCompressionTests { [Theory] @@ -30,4 +30,4 @@ namespace SixLabors.ImageSharp.Tests Assert.Equal(expectedResult, buffer); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs index 113f6928e..20fd53f41 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs @@ -1,15 +1,13 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System.Collections.Generic; +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.PixelFormats; +using Xunit; -namespace SixLabors.ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.Formats.Tiff { - using System.Collections.Generic; - using Xunit; - - using ImageSharp.Formats.Tiff; - public class BlackIsZeroTiffColorTests : PhotometricInterpretationTestBase { private static Rgba32 Gray000 = new Rgba32(0, 0, 0, 255); @@ -161,4 +159,4 @@ namespace SixLabors.ImageSharp.Tests }); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs index 895a8af92..4f6bef0cf 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs @@ -1,15 +1,13 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System.Collections.Generic; +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.PixelFormats; +using Xunit; -namespace SixLabors.ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.Formats.Tiff { - using System.Collections.Generic; - using Xunit; - - using ImageSharp.Formats.Tiff; - public class PaletteTiffColorTests : PhotometricInterpretationTestBase { public static uint[][] Palette4_ColorPalette { get => GeneratePalette(16); } @@ -140,4 +138,4 @@ namespace SixLabors.ImageSharp.Tests return result; } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs index 8c40a7bdd..da48086bb 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs @@ -1,16 +1,17 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; +using System.Collections.Generic; +using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; +using Xunit; -namespace SixLabors.ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.Formats.Tiff { - using System; - using Xunit; - using ImageSharp; - using SixLabors.ImageSharp.Memory; - + [Trait("Category", "Tiff")] public abstract class PhotometricInterpretationTestBase { public static Rgba32 DefaultColor = new Rgba32(42, 96, 18, 128); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs index 810e8e731..cc025a452 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs @@ -1,15 +1,13 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System.Collections.Generic; +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.PixelFormats; +using Xunit; -namespace SixLabors.ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.Formats.Tiff { - using System.Collections.Generic; - using Xunit; - - using ImageSharp.Formats.Tiff; - public class RgbPlanarTiffColorTests : PhotometricInterpretationTestBase { private static Rgba32 Rgb4_000 = new Rgba32(0, 0, 0, 255); @@ -196,4 +194,4 @@ namespace SixLabors.ImageSharp.Tests }); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs index 6523a9a19..5683e4752 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs @@ -1,15 +1,13 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System.Collections.Generic; +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.PixelFormats; +using Xunit; -namespace SixLabors.ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.Formats.Tiff { - using System.Collections.Generic; - using Xunit; - - using ImageSharp.Formats.Tiff; - public class RgbTiffColorTests : PhotometricInterpretationTestBase { private static Rgba32 Rgb4_000 = new Rgba32(0, 0, 0, 255); @@ -158,4 +156,4 @@ namespace SixLabors.ImageSharp.Tests }); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs index 054368e21..5334e2984 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs @@ -1,15 +1,13 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System.Collections.Generic; +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.PixelFormats; +using Xunit; -namespace SixLabors.ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.Formats.Tiff { - using System.Collections.Generic; - using Xunit; - - using ImageSharp.Formats.Tiff; - public class WhiteIsZeroTiffColorTests : PhotometricInterpretationTestBase { private static Rgba32 Gray000 = new Rgba32(255, 255, 255, 255); @@ -161,4 +159,4 @@ namespace SixLabors.ImageSharp.Tests }); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs index d038b69cd..275da7da1 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs @@ -1,15 +1,14 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System.IO; +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.PixelFormats; +using Xunit; -namespace SixLabors.ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.Formats.Tiff { - using System.IO; - using Xunit; - - using ImageSharp.Formats; - + [Trait("Category", "Tiff")] public class TiffDecoderHeaderTests { public static object[][] IsLittleEndianValues = new[] { new object[] { false }, @@ -108,4 +107,4 @@ namespace SixLabors.ImageSharp.Tests Assert.Equal("Invalid TIFF file header.", e.Message); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs index b39cecb18..f8d41cb86 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs @@ -1,18 +1,15 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Tests -{ - using System; - using System.IO; - using System.Linq; - using Xunit; - - using ImageSharp.Formats; - using ImageSharp.Formats.Tiff; - - using SixLabors.ImageSharp.Metadata.Profiles.Exif; +using System; +using System.IO; +using System.Linq; +using SixLabors.ImageSharp.Formats.Tiff; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Formats.Tiff +{ + [Trait("Category", "Tiff")] public class TiffDecoderIfdEntryTests { [Theory] diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderIfdTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderIfdTests.cs index 0b018dfc5..97ace8bed 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderIfdTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderIfdTests.cs @@ -1,14 +1,13 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Tests -{ - using System.IO; - using Xunit; - - using ImageSharp.Formats; - using ImageSharp.Formats.Tiff; +using System.IO; +using SixLabors.ImageSharp.Formats.Tiff; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Formats.Tiff +{ + [Trait("Category", "Tiff")] public class TiffDecoderIfdTests { public static object[][] IsLittleEndianValues = new[] { new object[] { false }, @@ -106,4 +105,4 @@ namespace SixLabors.ImageSharp.Tests Assert.Equal(expectedData, entry.Value); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderImageTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderImageTests.cs index 8e40015a5..52f321a47 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderImageTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderImageTests.cs @@ -1,17 +1,15 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; +using System.IO; +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.PixelFormats; +using Xunit; -namespace SixLabors.ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.Formats.Tiff { - using System; - using System.IO; - using Xunit; - - using ImageSharp.Formats; - using ImageSharp.Formats.Tiff; - + [Trait("Category", "Tiff")] public class TiffDecoderImageTests { public const int ImageWidth = 200; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs index ecc510461..b36d4e02f 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs @@ -1,17 +1,14 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System.IO; +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.PixelFormats; +using Xunit; -namespace SixLabors.ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.Formats.Tiff { - using System.IO; - using System.Linq; - using Xunit; - - using ImageSharp.Formats; - using ImageSharp.Formats.Tiff; - + [Trait("Category", "Tiff")] public class TiffDecoderMetadataTests { public static object[][] BaselineMetadataValues = new[] { new object[] { false, TiffTags.Artist, TiffMetadataNames.Artist, "My Artist Name" }, diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs index e1086e735..2af1b5225 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs @@ -1,15 +1,13 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Tests -{ - using System.IO; - - using Xunit; - - using ImageSharp.Formats; - using ImageSharp.Formats.Tiff; +using System.IO; +using SixLabors.ImageSharp.Formats.Tiff; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Formats.Tiff +{ + [Trait("Category", "Tiff")] public class TiffEncoderHeaderTests { [Fact] @@ -39,4 +37,4 @@ namespace SixLabors.ImageSharp.Tests } } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderIfdTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderIfdTests.cs index 3b9bce493..1dfa7dc16 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderIfdTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderIfdTests.cs @@ -1,18 +1,16 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Tests +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using SixLabors.ImageSharp.Formats.Tiff; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Formats.Tiff { - using System; - using System.IO; - using System.Linq; - using Xunit; - - using ImageSharp.Formats; - using ImageSharp.Formats.Tiff; - - using System.Collections.Generic; - + [Trait("Category", "Tiff")] public class TiffEncoderIfdTests { [Fact] @@ -294,4 +292,4 @@ namespace SixLabors.ImageSharp.Tests } } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderMetadataTests.cs index dd52317a9..ec90381ab 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderMetadataTests.cs @@ -1,19 +1,14 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System.Collections.Generic; +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.PixelFormats; +using Xunit; -namespace SixLabors.ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.Formats.Tiff { - using Xunit; - - using ImageSharp.Formats; - using ImageSharp.Formats.Tiff; - using System.Collections.Generic; - - using SixLabors.ImageSharp.Metadata; - using SixLabors.ImageSharp.Metadata.Profiles.Exif; - + [Trait("Category", "Tiff")] public class TiffEncoderMetadataTests { public static object[][] BaselineMetadataValues = new[] { new object[] { TiffTags.Artist, TiffMetadataNames.Artist, "My Artist Name" }, diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffFormatTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffFormatTests.cs index e3d51e76d..312c84308 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffFormatTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffFormatTests.cs @@ -1,12 +1,12 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Tests -{ - using Xunit; - - using ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Tiff; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Formats.Tiff +{ + [Trait("Category", "Tiff")] public class TiffFormatTests { [Fact] diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryCreatorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryCreatorTests.cs index d5ce048ea..c3c108e9f 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryCreatorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryCreatorTests.cs @@ -1,17 +1,15 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Tests -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Xunit; - - using ImageSharp.Formats.Tiff; - - using SixLabors.ImageSharp.Metadata.Profiles.Exif; +using System; +using System.Collections.Generic; +using System.Linq; +using SixLabors.ImageSharp.Formats.Tiff; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Formats.Tiff +{ + [Trait("Category", "Tiff")] public class TiffIfdEntryCreatorTests { [Theory] diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryTests.cs index 105eab250..f5f44b322 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryTests.cs @@ -1,12 +1,12 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Tests -{ - using Xunit; - - using ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Tiff; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Formats.Tiff +{ + [Trait("Category", "Tiff")] public class TiffIfdEntryTests { [Fact] @@ -20,4 +20,4 @@ namespace SixLabors.ImageSharp.Tests Assert.Equal(new byte[] { 2, 4, 6, 8 }, entry.Value); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs index b1374a0ae..88ae28961 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs @@ -1,12 +1,12 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Tests -{ - using Xunit; - - using ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Tiff; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Formats.Tiff +{ + [Trait("Category", "Tiff")] public class TiffIfdTests { [Fact] @@ -90,4 +90,4 @@ namespace SixLabors.ImageSharp.Tests Assert.Equal(0, entry.Tag); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffImageFormatDetectorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffImageFormatDetectorTests.cs index 89de19fda..7d8cad56b 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffImageFormatDetectorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffImageFormatDetectorTests.cs @@ -1,13 +1,13 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Tests -{ - using System.Linq; - using Xunit; - - using ImageSharp.Formats; +using System.Linq; +using SixLabors.ImageSharp.Formats.Tiff; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Formats.Tiff +{ + [Trait("Category", "Tiff")] public class TiffImageFormatDetectorTests { public static object[][] IsLittleEndianValues = new[] { new object[] { false }, diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Utils/SubStreamTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Utils/SubStreamTests.cs index 2b57a5a70..dbb053b90 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Utils/SubStreamTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Utils/SubStreamTests.cs @@ -1,14 +1,14 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Tests -{ - using System; - using System.IO; - using Xunit; - - using ImageSharp.Formats.Tiff; +using System; +using System.IO; +using SixLabors.ImageSharp.Formats.Tiff; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Formats.Tiff +{ + [Trait("Category", "Tiff")] public class SubStreamTests { [Fact] @@ -323,4 +323,4 @@ namespace SixLabors.ImageSharp.Tests Assert.Throws(() => stream.SetLength(5)); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs index f18611acf..a3e865519 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs @@ -1,13 +1,13 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Tests -{ - using System.IO; - using Xunit; - - using ImageSharp.Formats.Tiff; +using System.IO; +using SixLabors.ImageSharp.Formats.Tiff; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Formats.Tiff +{ + [Trait("Category", "Tiff")] public class TiffWriterTests { [Fact] @@ -129,4 +129,4 @@ namespace SixLabors.ImageSharp.Tests 0x44, 0x44, 0x44, 0x44 }, stream.ToArray()); } } -} \ No newline at end of file +} From 25a8203bc03ccdc5264dfcacdcebbd78510964cf Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Wed, 21 Aug 2019 21:04:10 +0300 Subject: [PATCH 067/275] Add Tiff to configurations. Implement simple unit-tests for Tiff decoder, add test files. Add benchmarks. Report decoder bugs. --- .gitattributes | 2 + src/ImageSharp/Advanced/AotCompilerTools.cs | 2 + src/ImageSharp/Configuration.cs | 4 +- src/ImageSharp/Formats/Tiff/TiffDecoder.cs | 6 +- .../Codecs/DecodeTiff.cs | 37 +++++---- .../Codecs/DecodeTiffBig.cs | 76 +++++++++++++++++++ tests/ImageSharp.Tests/FileTestBase.cs | 10 ++- .../Formats/Tiff/TiffDecoderTests.cs | 35 +++++++++ tests/ImageSharp.Tests/TestImages.cs | 49 ++++++++++++ .../TestUtilities/TestEnvironment.Formats.cs | 5 +- ...arks.Codecs.DecodeTiffBig-report-github.md | 69 +++++++++++++++++ ...enchmarks.Codecs.DecodeTiffBig-report.html | 69 +++++++++++++++++ tests/Images/Input/Tiff/Benchmarks/gen.bat | 2 + .../Images/Input/Tiff/Benchmarks/gen_big.ps1 | 12 +++ .../Input/Tiff/Benchmarks/gen_medium.ps1 | 12 +++ .../Input/Tiff/Benchmarks}/genimages.ps1 | 0 .../Images/Input/Tiff/Benchmarks/jpeg444.jpg | 3 + .../Input/Tiff/Benchmarks/jpeg444_big.jpg | 3 + .../Input/Tiff/Benchmarks/jpeg444_medium.jpg | 3 + .../Calliphora_grayscale_uncompressed.tiff | 0 .../Tiff/Calliphora_palette_uncompressed.tiff | 0 .../Input}/Tiff/Calliphora_rgb_deflate.tiff | 0 .../Input}/Tiff/Calliphora_rgb_jpeg.tiff | 0 .../Input}/Tiff/Calliphora_rgb_lzw.tiff | 0 .../Input}/Tiff/Calliphora_rgb_packbits.tiff | 0 .../Tiff/Calliphora_rgb_uncompressed.tiff | 0 .../Tiff/grayscale_deflate_multistrip.tiff | 3 + .../Input/Tiff/grayscale_uncompressed.tiff | 3 + .../Decode_Rgba32_Calliphora_rgb_deflate.png | 3 + .../Decode_Rgba32_Calliphora_rgb_lzw.png | 3 + .../net472/Decode_Rgba32_metadata_sample.png | 3 + .../net472/Decode_Rgba32_multipage_lzw.png | 3 + .../net472/Decode_Rgba32_rgb_deflate.png | 3 + .../Decode_Rgba32_rgb_lzw_multistrip.png | 3 + .../Decode_Rgba32_Calliphora_rgb_deflate.png | 3 + .../Decode_Rgba32_Calliphora_rgb_lzw.png | 3 + .../Decode_Rgba32_metadata_sample.png | 3 + .../Decode_Rgba32_multipage_lzw.png | 3 + .../Decode_Rgba32_rgb_deflate.png | 3 + .../Decode_Rgba32_rgb_lzw_multistrip.png | 3 + .../Decode_Rgba32_Calliphora_rgb_deflate.png | 3 + .../Decode_Rgba32_Calliphora_rgb_lzw.png | 3 + .../Decode_Rgba32_metadata_sample.png | 3 + .../Decode_Rgba32_multipage_lzw.png | 3 + .../Decode_Rgba32_rgb_deflate.png | 3 + .../netcoreapp3.1/Decode_Rgba32_rgb_lzw.png | 3 + .../Decode_Rgba32_rgb_lzw_multistrip.png | 3 + tests/Images/Input/Tiff/issues/readme.md | 2 + tests/Images/Input/Tiff/metadata_sample.tiff | 3 + ...page_ withPreview_differentSize_tiled.tiff | 3 + .../Tiff/multipage_deflate_withPreview.tiff | 3 + .../Input/Tiff/multipage_differentSize.tiff | 3 + .../Tiff/multipage_differentVariants.tiff | 3 + tests/Images/Input/Tiff/multipage_lzw.tiff | 3 + .../palette_grayscale_deflate_multistrip.tiff | 3 + .../Input/Tiff/palette_uncompressed.tiff | 3 + tests/Images/Input/Tiff/rgb_deflate.tiff | 3 + .../Input/Tiff/rgb_deflate_multistrip.tiff | 3 + tests/Images/Input/Tiff/rgb_jpeg.tiff | 3 + tests/Images/Input/Tiff/rgb_lzw.tiff | 3 + .../Images/Input/Tiff/rgb_lzw_multistrip.tiff | 3 + tests/Images/Input/Tiff/rgb_packbits.tiff | 3 + .../Input/Tiff/rgb_packbits_multistrip.tiff | 3 + tests/Images/Input/Tiff/rgb_uncompressed.tiff | 3 + .../Input/Tiff/rgb_uncompressed_tiled.tiff | 3 + 65 files changed, 491 insertions(+), 24 deletions(-) create mode 100644 tests/ImageSharp.Benchmarks/Codecs/DecodeTiffBig.cs create mode 100644 tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs create mode 100644 tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiffBig-report-github.md create mode 100644 tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiffBig-report.html create mode 100644 tests/Images/Input/Tiff/Benchmarks/gen.bat create mode 100644 tests/Images/Input/Tiff/Benchmarks/gen_big.ps1 create mode 100644 tests/Images/Input/Tiff/Benchmarks/gen_medium.ps1 rename tests/{ImageSharp.Tests/TestImages/Formats/Tiff => Images/Input/Tiff/Benchmarks}/genimages.ps1 (100%) create mode 100644 tests/Images/Input/Tiff/Benchmarks/jpeg444.jpg create mode 100644 tests/Images/Input/Tiff/Benchmarks/jpeg444_big.jpg create mode 100644 tests/Images/Input/Tiff/Benchmarks/jpeg444_medium.jpg rename tests/{ImageSharp.Tests/TestImages/Formats => Images/Input}/Tiff/Calliphora_grayscale_uncompressed.tiff (100%) rename tests/{ImageSharp.Tests/TestImages/Formats => Images/Input}/Tiff/Calliphora_palette_uncompressed.tiff (100%) rename tests/{ImageSharp.Tests/TestImages/Formats => Images/Input}/Tiff/Calliphora_rgb_deflate.tiff (100%) rename tests/{ImageSharp.Tests/TestImages/Formats => Images/Input}/Tiff/Calliphora_rgb_jpeg.tiff (100%) rename tests/{ImageSharp.Tests/TestImages/Formats => Images/Input}/Tiff/Calliphora_rgb_lzw.tiff (100%) rename tests/{ImageSharp.Tests/TestImages/Formats => Images/Input}/Tiff/Calliphora_rgb_packbits.tiff (100%) rename tests/{ImageSharp.Tests/TestImages/Formats => Images/Input}/Tiff/Calliphora_rgb_uncompressed.tiff (100%) create mode 100644 tests/Images/Input/Tiff/grayscale_deflate_multistrip.tiff create mode 100644 tests/Images/Input/Tiff/grayscale_uncompressed.tiff create mode 100644 tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_Calliphora_rgb_deflate.png create mode 100644 tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_Calliphora_rgb_lzw.png create mode 100644 tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_metadata_sample.png create mode 100644 tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_multipage_lzw.png create mode 100644 tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_rgb_deflate.png create mode 100644 tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_rgb_lzw_multistrip.png create mode 100644 tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_Calliphora_rgb_deflate.png create mode 100644 tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_Calliphora_rgb_lzw.png create mode 100644 tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_metadata_sample.png create mode 100644 tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_multipage_lzw.png create mode 100644 tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_rgb_deflate.png create mode 100644 tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_rgb_lzw_multistrip.png create mode 100644 tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_Calliphora_rgb_deflate.png create mode 100644 tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_Calliphora_rgb_lzw.png create mode 100644 tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_metadata_sample.png create mode 100644 tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_multipage_lzw.png create mode 100644 tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_deflate.png create mode 100644 tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_lzw.png create mode 100644 tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_lzw_multistrip.png create mode 100644 tests/Images/Input/Tiff/issues/readme.md create mode 100644 tests/Images/Input/Tiff/metadata_sample.tiff create mode 100644 tests/Images/Input/Tiff/multipage_ withPreview_differentSize_tiled.tiff create mode 100644 tests/Images/Input/Tiff/multipage_deflate_withPreview.tiff create mode 100644 tests/Images/Input/Tiff/multipage_differentSize.tiff create mode 100644 tests/Images/Input/Tiff/multipage_differentVariants.tiff create mode 100644 tests/Images/Input/Tiff/multipage_lzw.tiff create mode 100644 tests/Images/Input/Tiff/palette_grayscale_deflate_multistrip.tiff create mode 100644 tests/Images/Input/Tiff/palette_uncompressed.tiff create mode 100644 tests/Images/Input/Tiff/rgb_deflate.tiff create mode 100644 tests/Images/Input/Tiff/rgb_deflate_multistrip.tiff create mode 100644 tests/Images/Input/Tiff/rgb_jpeg.tiff create mode 100644 tests/Images/Input/Tiff/rgb_lzw.tiff create mode 100644 tests/Images/Input/Tiff/rgb_lzw_multistrip.tiff create mode 100644 tests/Images/Input/Tiff/rgb_packbits.tiff create mode 100644 tests/Images/Input/Tiff/rgb_packbits_multistrip.tiff create mode 100644 tests/Images/Input/Tiff/rgb_uncompressed.tiff create mode 100644 tests/Images/Input/Tiff/rgb_uncompressed_tiled.tiff diff --git a/.gitattributes b/.gitattributes index c0bff6e18..f605a871c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -82,6 +82,8 @@ *.tga binary *.ttc binary *.ttf binary +*.tif binary +*.tiff binary *.webp binary *.woff binary *.woff2 binary diff --git a/src/ImageSharp/Advanced/AotCompilerTools.cs b/src/ImageSharp/Advanced/AotCompilerTools.cs index 2ea456286..7cd6dac09 100644 --- a/src/ImageSharp/Advanced/AotCompilerTools.cs +++ b/src/ImageSharp/Advanced/AotCompilerTools.cs @@ -94,6 +94,8 @@ namespace SixLabors.ImageSharp.Advanced AotCodec(new Formats.Bmp.BmpDecoder(), new Formats.Bmp.BmpEncoder()); AotCodec(new Formats.Gif.GifDecoder(), new Formats.Gif.GifEncoder()); AotCodec(new Formats.Jpeg.JpegDecoder(), new Formats.Jpeg.JpegEncoder()); + AotCodec(new Formats.Tga.TgaDecoder(), new Formats.Tga.TgaEncoder()); + AotCodec(new Formats.Tiff.TiffDecoder(), new Formats.Tiff.TiffEncoder()); // TODO: Do the discovery work to figure out what works and what doesn't. } diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index 3ed61aa6c..854a5d69c 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -181,6 +181,7 @@ namespace SixLabors.ImageSharp /// /// . /// . + /// /// /// The default configuration of . internal static Configuration CreateDefaultInstance() @@ -190,7 +191,8 @@ namespace SixLabors.ImageSharp new JpegConfigurationModule(), new GifConfigurationModule(), new BmpConfigurationModule(), - new TgaConfigurationModule()); + new TgaConfigurationModule(), + new TiffConfigurationModule()); } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs index ba52e4266..e4bfc666a 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff { Guard.NotNull(stream, "stream"); - using (TiffDecoderCore decoder = new TiffDecoderCore(configuration, this)) + using (var decoder = new TiffDecoderCore(configuration, this)) { return decoder.Decode(stream); } @@ -37,11 +37,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff } /// - public Task> DecodeAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel + public Task> DecodeAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken) + where TPixel : unmanaged, IPixel { throw new System.NotImplementedException(); } + /// public Task DecodeAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken) { throw new System.NotImplementedException(); diff --git a/tests/ImageSharp.Benchmarks/Codecs/DecodeTiff.cs b/tests/ImageSharp.Benchmarks/Codecs/DecodeTiff.cs index 2a01c60a1..7e42f1bee 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/DecodeTiff.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/DecodeTiff.cs @@ -1,52 +1,51 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using System.Drawing; using System.IO; - using BenchmarkDotNet.Attributes; - using SixLabors.ImageSharp.PixelFormats; - -using CoreImage = SixLabors.ImageSharp.Image; -using CoreSize = SixLabors.ImageSharp.Size; +using SixLabors.ImageSharp.Tests; +using SDImage = System.Drawing.Image; +using SDSize = System.Drawing.Size; namespace SixLabors.ImageSharp.Benchmarks.Codecs { + [Config(typeof(Config.ShortClr))] public class DecodeTiff : BenchmarkBase { private byte[] tiffBytes; + private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); + + [Params(TestImages.Tiff.RgbLzw)] + public string TestImage { get; set; } + [GlobalSetup] public void ReadImages() { if (this.tiffBytes == null) { - this.tiffBytes = File.ReadAllBytes("../ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_rgb_uncompressed.tiff"); + this.tiffBytes = File.ReadAllBytes(this.TestImageFullPath); } } [Benchmark(Baseline = true, Description = "System.Drawing Tiff")] - public System.Drawing.Size TiffSystemDrawing() + public SDSize TiffSystemDrawing() { - using (MemoryStream memoryStream = new MemoryStream(this.tiffBytes)) + using (var memoryStream = new MemoryStream(this.tiffBytes)) + using (var image = SDImage.FromStream(memoryStream)) { - using (var image = System.Drawing.Image.FromStream(memoryStream)) - { - return image.Size; - } + return image.Size; } } [Benchmark(Description = "ImageSharp Tiff")] - public CoreSize TiffCore() + public Size TiffCore() { - using (MemoryStream memoryStream = new MemoryStream(this.tiffBytes)) + using (var memoryStream = new MemoryStream(this.tiffBytes)) + using (var image = Image.Load(memoryStream)) { - using (Image image = CoreImage.Load(memoryStream)) - { - return new CoreSize(image.Width, image.Height); - } + return image.Size(); } } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/DecodeTiffBig.cs b/tests/ImageSharp.Benchmarks/Codecs/DecodeTiffBig.cs new file mode 100644 index 000000000..c7c1020ef --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Codecs/DecodeTiffBig.cs @@ -0,0 +1,76 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System.IO; + +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Environments; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Reports; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests; +using SDImage = System.Drawing.Image; +using SDSize = System.Drawing.Size; + +namespace SixLabors.ImageSharp.Benchmarks.Codecs +{ + [Config(typeof(DecodeTiffBig.Config.LongClr))] + public class DecodeTiffBig : BenchmarkBase + { + private class Config : SixLabors.ImageSharp.Benchmarks.Config + { + public class LongClr : Config + { + public LongClr() + { + this.Add( + Job.Default.With(ClrRuntime.Net472).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(5), + Job.Default.With(CoreRuntime.Core31).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(5), + Job.Default.With(CoreRuntime.Core21).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(5)); + + this.SummaryStyle = SummaryStyle.Default.WithMaxParameterColumnWidth(60); + } + } + } + + private string prevImage = null; + + private byte[] data; + + private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); + + [Params(TestImages.Tiff.Benchmark_GrayscaleUncompressed, TestImages.Tiff.Benchmark_PaletteUncompressed, TestImages.Tiff.Benchmark_RgbDeflate, TestImages.Tiff.Benchmark_RgbLzw, TestImages.Tiff.Benchmark_RgbPackbits, TestImages.Tiff.Benchmark_RgbUncompressed)] + // [Params(TestImages.Tiff.GrayscaleUncompressed, TestImages.Tiff.PaletteUncompressed, TestImages.Tiff.RgbDeflate, TestImages.Tiff.RgbLzw, TestImages.Tiff.RgbPackbits, TestImages.Tiff.RgbUncompressed)] + public string TestImage { get; set; } + + [IterationSetup] + public void ReadImages() + { + if (this.prevImage != this.TestImage) + { + this.data = File.ReadAllBytes(this.TestImageFullPath); + this.prevImage = this.TestImage; + } + } + + [Benchmark(Baseline = true, Description = "System.Drawing Tiff")] + public SDSize TiffSystemDrawing() + { + using (var memoryStream = new MemoryStream(this.data)) + using (var image = SDImage.FromStream(memoryStream)) + { + return image.Size; + } + } + + [Benchmark(Description = "ImageSharp Tiff")] + public Size TiffCore() + { + using (var ms = new MemoryStream(this.data)) + using (var image = Image.Load(ms)) + { + return image.Size(); + } + } + } +} diff --git a/tests/ImageSharp.Tests/FileTestBase.cs b/tests/ImageSharp.Tests/FileTestBase.cs index 93024197b..cf5f87e66 100644 --- a/tests/ImageSharp.Tests/FileTestBase.cs +++ b/tests/ImageSharp.Tests/FileTestBase.cs @@ -22,7 +22,9 @@ namespace SixLabors.ImageSharp.Tests TestImages.Bmp.Car, TestImages.Jpeg.Baseline.Calliphora, TestImages.Png.Splash, - TestImages.Gif.Trans + TestImages.Gif.Trans, + TestImages.Tga.Bit24PalRleTopRight, + TestImages.Tiff.RgbLzw, }; /// @@ -64,6 +66,10 @@ namespace SixLabors.ImageSharp.Tests public const string Png = "png"; public const string Gif = "gif"; + + public const string Tga = "tga"; + + public const string Tiff = "tiff"; } /// @@ -109,6 +115,8 @@ namespace SixLabors.ImageSharp.Tests // TestFile.Create(TestImages.Gif.Trans), // Perf: Enable for local testing only // TestFile.Create(TestImages.Gif.Cheers), // Perf: Enable for local testing only // TestFile.Create(TestImages.Gif.Giphy) // Perf: Enable for local testing only + TestFile.Create(TestImages.Tga.Bit24PalRleTopRight), + TestFile.Create(TestImages.Tiff.RgbLzw), }; #pragma warning restore SA1515 // Single-line comment should be preceded by blank line } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs new file mode 100644 index 000000000..b4d2a4894 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -0,0 +1,35 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +// ReSharper disable InconsistentNaming + + +using System.IO; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; +using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Formats.Tiff +{ + [Trait("Category", "Tiff.BlackBox")] + public class TiffDecoderTests + { + public static readonly string[] CommonTestImages = TestImages.Tiff.All; + + [Theory] + [WithFileCollection(nameof(CommonTestImages), PixelTypes.Rgba32)] + public void Decode(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(new TiffDecoder())) + { + image.DebugSave(provider); + image.CompareToOriginal(provider, ImageComparer.Exact, new MagickReferenceDecoder()); + } + } + } +} diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index fd5296c37..74967f3ec 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -490,5 +490,54 @@ namespace SixLabors.ImageSharp.Tests public const string NoAlphaBits32Bit = "Tga/32bit_no_alphabits.tga"; public const string NoAlphaBits32BitRle = "Tga/32bit_rle_no_alphabits.tga"; } + + public static class Tiff + { + public const string Benchmark_GrayscaleUncompressed = "Tiff/Benchmarks/jpeg444_big_grayscale_uncompressed.tiff"; + public const string Benchmark_PaletteUncompressed = "Tiff/Benchmarks/jpeg444_big_palette_uncompressed.tiff"; + public const string Benchmark_RgbDeflate = "Tiff/Benchmarks/jpeg444_big_rgb_deflate.tiff"; + public const string Benchmark_RgbLzw = "Tiff/Benchmarks/jpeg444_big_rgb_lzw.tiff"; + public const string Benchmark_RgbPackbits = "Tiff/Benchmarks/jpeg444_big_rgb_packbits.tiff"; + public const string Benchmark_RgbUncompressed = "Tiff/Benchmarks/jpeg444_big_rgb_uncompressed.tiff"; + + public const string Calliphora_GrayscaleUncompressed = "Tiff/Calliphora_grayscale_uncompressed.tiff"; + public const string Calliphora_PaletteUncompressed = "Tiff/Calliphora_palette_uncompressed.tiff"; + public const string Calliphora_RgbDeflate = "Tiff/Calliphora_rgb_deflate.tiff"; + public const string Calliphora_RgbJpeg = "Tiff/Calliphora_rgb_jpeg.tiff"; + public const string Calliphora_RgbLzw = "Tiff/Calliphora_rgb_lzw.tiff"; + public const string Calliphora_RgbPackbits = "Tiff/Calliphora_rgb_packbits.tiff"; + public const string Calliphora_RgbUncompressed = "Tiff/Calliphora_rgb_uncompressed.tiff"; + + public const string GrayscaleDeflateMultistrip = "Tiff/grayscale_deflate_multistrip.tiff"; + public const string GrayscaleUncompressed = "Tiff/grayscale_uncompressed.tiff"; + public const string PaletteDeflateMultistrip = "Tiff/palette_grayscale_deflate_multistrip.tiff"; + public const string PaletteUncompressed = "Tiff/palette_uncompressed.tiff"; + public const string RgbDeflate = "Tiff/rgb_deflate.tiff"; + public const string RgbDeflateMultistrip = "Tiff/rgb_deflate_multistrip.tiff"; + public const string RgbJpeg = "Tiff/rgb_jpeg.tiff"; + public const string RgbLzw = "Tiff/rgb_lzw.tiff"; + public const string RgbLzwMultistrip = "Tiff/rgb_lzw_multistrip.tiff"; + public const string RgbPackbits = "Tiff/rgb_packbits.tiff"; + public const string RgbPackbitsMultistrip = "Tiff/rgb_packbits_multistrip.tiff"; + public const string RgbUncompressed = "Tiff/rgb_uncompressed.tiff"; + + public const string RgbUncompressedTiled = "Tiff/rgb_uncompressed_tiled.tiff"; + public const string MultiframeDifferentSizeTiled = "Tiff/multipage_ withPreview_differentSize_tiled.tiff"; + + public const string MultiframeLzw = "Tiff/multipage_lzw.tiff"; + public const string MultiframeDeflateWithPreview = "Tiff/multipage_deflate_withPreview.tiff"; + public const string MultiframeDifferentSize = "Tiff/multipage_differentSize.tiff"; + public const string MultiframeDifferentVariants = "Tiff/multipage_differentVariants.tiff"; + + public const string SampleMetadata = "Tiff/metadata_sample.tiff"; + + public static readonly string[] All = { Calliphora_GrayscaleUncompressed, Calliphora_PaletteUncompressed, Calliphora_RgbDeflate, Calliphora_RgbLzw, Calliphora_RgbPackbits, Calliphora_RgbUncompressed, GrayscaleDeflateMultistrip, GrayscaleUncompressed, PaletteDeflateMultistrip, PaletteUncompressed, RgbDeflate, RgbDeflateMultistrip, /*RgbJpeg,*/ RgbLzw, RgbLzwMultistrip, RgbPackbits, RgbPackbitsMultistrip, RgbUncompressed, MultiframeLzw, /*MultiFrameDifferentVariants,*/ SampleMetadata, }; + + public static readonly string[] Multiframes = { MultiframeLzw, MultiframeDeflateWithPreview /*MultiFrameDifferentSize, MultiframeDifferentSizeTiled, MultiFrameDifferentVariants,*/ }; + + public static readonly string[] Metadata = { SampleMetadata }; + + public static readonly string[] NotSupported = { Calliphora_RgbJpeg, RgbJpeg, RgbUncompressedTiled, MultiframeDifferentSize, MultiframeDifferentVariants }; + } } } diff --git a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs index 6e204e2d4..1bcacc4de 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs @@ -9,6 +9,7 @@ using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Tga; +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; namespace SixLabors.ImageSharp.Tests @@ -55,7 +56,9 @@ namespace SixLabors.ImageSharp.Tests var cfg = new Configuration( new JpegConfigurationModule(), new GifConfigurationModule(), - new TgaConfigurationModule()); + new TgaConfigurationModule(), + new TiffConfigurationModule() + ); // Magick codecs should work on all platforms IImageEncoder pngEncoder = IsWindows ? (IImageEncoder)SystemDrawingReferenceEncoder.Png : new PngEncoder(); diff --git a/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiffBig-report-github.md b/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiffBig-report-github.md new file mode 100644 index 000000000..68b149c50 --- /dev/null +++ b/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiffBig-report-github.md @@ -0,0 +1,69 @@ +``` ini + +BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.450 (2004/?/20H1) +Intel Core i7-3610QM CPU 2.30GHz (Ivy Bridge), 1 CPU, 8 logical and 4 physical cores +.NET Core SDK=3.1.401 + [Host] : .NET Core 3.1.7 (CoreCLR 4.700.20.36602, CoreFX 4.700.20.37001), X64 RyuJIT + Job-MTZTUC : .NET Framework 4.8 (4.8.4200.0), X64 RyuJIT + Job-BGVYTJ : .NET Core 2.1.21 (CoreCLR 4.6.29130.01, CoreFX 4.6.29130.02), X64 RyuJIT + Job-ZDUDFU : .NET Core 3.1.7 (CoreCLR 4.700.20.36602, CoreFX 4.700.20.37001), X64 RyuJIT + +InvocationCount=1 IterationCount=5 LaunchCount=1 +UnrollFactor=1 WarmupCount=3 + +``` +| Method | Job | Runtime | TestImage | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | +|---------------------- |----------- |-------------- |-------------------------------------------------------- |------------:|------------:|------------:|-------:|--------:|------------:|----------:|----------:|-------------:| +| **'System.Drawing Tiff'** | **Job-MTZTUC** | **.NET 4.7.2** | **Tiff/Benchmarks/jpeg444_big_grayscale_uncompressed.tiff** | **180.2 ms** | **15.21 ms** | **2.35 ms** | **1.00** | **0.00** | **85000.0000** | **-** | **-** | **269221840 B** | +| 'ImageSharp Tiff' | Job-MTZTUC | .NET 4.7.2 | Tiff/Benchmarks/jpeg444_big_grayscale_uncompressed.tiff | 31,527.8 ms | 4,371.70 ms | 1,135.32 ms | 176.11 | 8.81 | 1000.0000 | 1000.0000 | 1000.0000 | 1342029912 B | +| | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-BGVYTJ | .NET Core 2.1 | Tiff/Benchmarks/jpeg444_big_grayscale_uncompressed.tiff | 185.5 ms | 15.88 ms | 2.46 ms | 1.00 | 0.00 | 85000.0000 | - | - | 268813936 B | +| 'ImageSharp Tiff' | Job-BGVYTJ | .NET Core 2.1 | Tiff/Benchmarks/jpeg444_big_grayscale_uncompressed.tiff | 17,768.7 ms | 116.03 ms | 30.13 ms | 95.84 | 1.13 | 1000.0000 | 1000.0000 | 1000.0000 | 1342016464 B | +| | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-ZDUDFU | .NET Core 3.1 | Tiff/Benchmarks/jpeg444_big_grayscale_uncompressed.tiff | 149.9 ms | 8.23 ms | 1.27 ms | 1.00 | 0.00 | - | - | - | 176 B | +| 'ImageSharp Tiff' | Job-ZDUDFU | .NET Core 3.1 | Tiff/Benchmarks/jpeg444_big_grayscale_uncompressed.tiff | 16,782.2 ms | 718.14 ms | 111.13 ms | 111.94 | 0.80 | 1000.0000 | 1000.0000 | 1000.0000 | 1342016440 B | +| | | | | | | | | | | | | | +| **'System.Drawing Tiff'** | **Job-MTZTUC** | **.NET 4.7.2** | **Tiff/Benchmarks/jpeg444_big_palette_uncompressed.tiff** | **178.0 ms** | **7.07 ms** | **1.83 ms** | **1.00** | **0.00** | **85000.0000** | **-** | **-** | **269221840 B** | +| 'ImageSharp Tiff' | Job-MTZTUC | .NET 4.7.2 | Tiff/Benchmarks/jpeg444_big_palette_uncompressed.tiff | 33,721.9 ms | 78.03 ms | 12.08 ms | 188.96 | 1.80 | 1000.0000 | 1000.0000 | 1000.0000 | 1342023280 B | +| | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-BGVYTJ | .NET Core 2.1 | Tiff/Benchmarks/jpeg444_big_palette_uncompressed.tiff | 180.1 ms | 8.81 ms | 2.29 ms | 1.00 | 0.00 | 85000.0000 | - | - | 268815616 B | +| 'ImageSharp Tiff' | Job-BGVYTJ | .NET Core 2.1 | Tiff/Benchmarks/jpeg444_big_palette_uncompressed.tiff | 22,941.4 ms | 728.12 ms | 189.09 ms | 127.37 | 1.07 | 1000.0000 | 1000.0000 | 1000.0000 | 1342022368 B | +| | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-ZDUDFU | .NET Core 3.1 | Tiff/Benchmarks/jpeg444_big_palette_uncompressed.tiff | 145.5 ms | 3.20 ms | 0.50 ms | 1.00 | 0.00 | - | - | - | 176 B | +| 'ImageSharp Tiff' | Job-ZDUDFU | .NET Core 3.1 | Tiff/Benchmarks/jpeg444_big_palette_uncompressed.tiff | 21,485.0 ms | 711.10 ms | 184.67 ms | 148.04 | 0.66 | 1000.0000 | 1000.0000 | 1000.0000 | 1342025632 B | +| | | | | | | | | | | | | | +| **'System.Drawing Tiff'** | **Job-MTZTUC** | **.NET 4.7.2** | **Tiff/Benchmarks/jpeg444_big_rgb_deflate.tiff** | **2,518.2 ms** | **76.22 ms** | **19.79 ms** | **1.00** | **0.00** | **6000.0000** | **-** | **-** | **29598616 B** | +| 'ImageSharp Tiff' | Job-MTZTUC | .NET 4.7.2 | Tiff/Benchmarks/jpeg444_big_rgb_deflate.tiff | 29,327.2 ms | 102.72 ms | 26.68 ms | 11.65 | 0.10 | 1000.0000 | 1000.0000 | 1000.0000 | 1124088224 B | +| | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-BGVYTJ | .NET Core 2.1 | Tiff/Benchmarks/jpeg444_big_rgb_deflate.tiff | 2,500.3 ms | 67.24 ms | 10.41 ms | 1.00 | 0.00 | 6000.0000 | - | - | 29528752 B | +| 'ImageSharp Tiff' | Job-BGVYTJ | .NET Core 2.1 | Tiff/Benchmarks/jpeg444_big_rgb_deflate.tiff | 18,974.7 ms | 199.58 ms | 30.89 ms | 7.59 | 0.04 | 1000.0000 | 1000.0000 | 1000.0000 | 1123947608 B | +| | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-ZDUDFU | .NET Core 3.1 | Tiff/Benchmarks/jpeg444_big_rgb_deflate.tiff | 2,541.1 ms | 21.36 ms | 5.55 ms | 1.00 | 0.00 | - | - | - | 176 B | +| 'ImageSharp Tiff' | Job-ZDUDFU | .NET Core 3.1 | Tiff/Benchmarks/jpeg444_big_rgb_deflate.tiff | 17,974.8 ms | 751.73 ms | 116.33 ms | 7.07 | 0.04 | 1000.0000 | 1000.0000 | 1000.0000 | 1123949960 B | +| | | | | | | | | | | | | | +| **'System.Drawing Tiff'** | **Job-MTZTUC** | **.NET 4.7.2** | **Tiff/Benchmarks/jpeg444_big_rgb_lzw.tiff** | **3,368.4 ms** | **40.71 ms** | **6.30 ms** | **1.00** | **0.00** | **4000.0000** | **-** | **-** | **22835824 B** | +| 'ImageSharp Tiff' | Job-MTZTUC | .NET 4.7.2 | Tiff/Benchmarks/jpeg444_big_rgb_lzw.tiff | 28,919.9 ms | 705.58 ms | 183.24 ms | 8.57 | 0.04 | 1000.0000 | 1000.0000 | 1000.0000 | 1123956384 B | +| | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-BGVYTJ | .NET Core 2.1 | Tiff/Benchmarks/jpeg444_big_rgb_lzw.tiff | 3,365.1 ms | 36.93 ms | 5.72 ms | 1.00 | 0.00 | 4000.0000 | - | - | 22789840 B | +| 'ImageSharp Tiff' | Job-BGVYTJ | .NET Core 2.1 | Tiff/Benchmarks/jpeg444_big_rgb_lzw.tiff | 17,905.1 ms | 40.08 ms | 10.41 ms | 5.32 | 0.01 | 1000.0000 | 1000.0000 | 1000.0000 | 1123949072 B | +| | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-ZDUDFU | .NET Core 3.1 | Tiff/Benchmarks/jpeg444_big_rgb_lzw.tiff | 3,377.6 ms | 125.36 ms | 32.56 ms | 1.00 | 0.00 | - | - | - | 176 B | +| 'ImageSharp Tiff' | Job-ZDUDFU | .NET Core 3.1 | Tiff/Benchmarks/jpeg444_big_rgb_lzw.tiff | 16,998.0 ms | 460.59 ms | 119.61 ms | 5.03 | 0.07 | 1000.0000 | 1000.0000 | 1000.0000 | 1123952144 B | +| | | | | | | | | | | | | | +| **'System.Drawing Tiff'** | **Job-MTZTUC** | **.NET 4.7.2** | **Tiff/Benchmarks/jpeg444_big_rgb_packbits.tiff** | **1,849.3 ms** | **43.52 ms** | **11.30 ms** | **1.00** | **0.00** | **255000.0000** | **-** | **-** | **812350880 B** | +| 'ImageSharp Tiff' | Job-MTZTUC | .NET 4.7.2 | Tiff/Benchmarks/jpeg444_big_rgb_packbits.tiff | 29,360.0 ms | 157.78 ms | 40.98 ms | 15.88 | 0.12 | - | - | - | 2690323752 B | +| | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-BGVYTJ | .NET Core 2.1 | Tiff/Benchmarks/jpeg444_big_rgb_packbits.tiff | 1,882.7 ms | 64.85 ms | 16.84 ms | 1.00 | 0.00 | 255000.0000 | - | - | 811943568 B | +| 'ImageSharp Tiff' | Job-BGVYTJ | .NET Core 2.1 | Tiff/Benchmarks/jpeg444_big_rgb_packbits.tiff | 18,967.7 ms | 445.86 ms | 115.79 ms | 10.08 | 0.09 | - | - | - | 2690318648 B | +| | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-ZDUDFU | .NET Core 3.1 | Tiff/Benchmarks/jpeg444_big_rgb_packbits.tiff | 1,743.2 ms | 78.50 ms | 20.39 ms | 1.00 | 0.00 | - | - | - | 176 B | +| 'ImageSharp Tiff' | Job-ZDUDFU | .NET Core 3.1 | Tiff/Benchmarks/jpeg444_big_rgb_packbits.tiff | 17,379.6 ms | 243.53 ms | 63.24 ms | 9.97 | 0.10 | - | - | - | 2690321912 B | +| | | | | | | | | | | | | | +| **'System.Drawing Tiff'** | **Job-MTZTUC** | **.NET 4.7.2** | **Tiff/Benchmarks/jpeg444_big_rgb_uncompressed.tiff** | **758.5 ms** | **9.75 ms** | **2.53 ms** | **1.00** | **0.00** | **255000.0000** | **-** | **-** | **806059984 B** | +| 'ImageSharp Tiff' | Job-MTZTUC | .NET 4.7.2 | Tiff/Benchmarks/jpeg444_big_rgb_uncompressed.tiff | 29,198.2 ms | 677.81 ms | 176.03 ms | 38.50 | 0.19 | - | - | - | 1878827096 B | +| | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-BGVYTJ | .NET Core 2.1 | Tiff/Benchmarks/jpeg444_big_rgb_uncompressed.tiff | 760.1 ms | 15.95 ms | 2.47 ms | 1.00 | 0.00 | 255000.0000 | - | - | 805652192 B | +| 'ImageSharp Tiff' | Job-BGVYTJ | .NET Core 2.1 | Tiff/Benchmarks/jpeg444_big_rgb_uncompressed.tiff | 18,457.2 ms | 35.60 ms | 5.51 ms | 24.28 | 0.08 | - | - | - | 1878821992 B | +| | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-ZDUDFU | .NET Core 3.1 | Tiff/Benchmarks/jpeg444_big_rgb_uncompressed.tiff | 629.5 ms | 11.40 ms | 2.96 ms | 1.00 | 0.00 | - | - | - | 176 B | +| 'ImageSharp Tiff' | Job-ZDUDFU | .NET Core 3.1 | Tiff/Benchmarks/jpeg444_big_rgb_uncompressed.tiff | 17,579.8 ms | 371.72 ms | 96.54 ms | 27.93 | 0.11 | - | - | - | 1878825256 B | diff --git a/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiffBig-report.html b/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiffBig-report.html new file mode 100644 index 000000000..406b72819 --- /dev/null +++ b/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiffBig-report.html @@ -0,0 +1,69 @@ + + + + +SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiffBig-20200824-095044 + + + + +

+BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.450 (2004/?/20H1)
+Intel Core i7-3610QM CPU 2.30GHz (Ivy Bridge), 1 CPU, 8 logical and 4 physical cores
+.NET Core SDK=3.1.401
+  [Host]     : .NET Core 3.1.7 (CoreCLR 4.700.20.36602, CoreFX 4.700.20.37001), X64 RyuJIT
+  Job-MTZTUC : .NET Framework 4.8 (4.8.4200.0), X64 RyuJIT
+  Job-BGVYTJ : .NET Core 2.1.21 (CoreCLR 4.6.29130.01, CoreFX 4.6.29130.02), X64 RyuJIT
+  Job-ZDUDFU : .NET Core 3.1.7 (CoreCLR 4.700.20.36602, CoreFX 4.700.20.37001), X64 RyuJIT
+
+
InvocationCount=1  IterationCount=5  LaunchCount=1  
+UnrollFactor=1  WarmupCount=3  
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Method JobRuntime TestImage MeanErrorStdDevRatioRatioSDGen 0Gen 1Gen 2Allocated
'System.Drawing Tiff'Job-MTZTUC.NET 4.7.2Tiff/Benchmarks/jpeg444_big_grayscale_uncompressed.tiff180.2 ms15.21 ms2.35 ms1.000.0085000.0000--269221840 B
'ImageSharp Tiff'Job-MTZTUC.NET 4.7.2Tiff/Benchmarks/jpeg444_big_grayscale_uncompressed.tiff31,527.8 ms4,371.70 ms1,135.32 ms176.118.811000.00001000.00001000.00001342029912 B
'System.Drawing Tiff'Job-BGVYTJ.NET Core 2.1Tiff/Benchmarks/jpeg444_big_grayscale_uncompressed.tiff185.5 ms15.88 ms2.46 ms1.000.0085000.0000--268813936 B
'ImageSharp Tiff'Job-BGVYTJ.NET Core 2.1Tiff/Benchmarks/jpeg444_big_grayscale_uncompressed.tiff17,768.7 ms116.03 ms30.13 ms95.841.131000.00001000.00001000.00001342016464 B
'System.Drawing Tiff'Job-ZDUDFU.NET Core 3.1Tiff/Benchmarks/jpeg444_big_grayscale_uncompressed.tiff149.9 ms8.23 ms1.27 ms1.000.00---176 B
'ImageSharp Tiff'Job-ZDUDFU.NET Core 3.1Tiff/Benchmarks/jpeg444_big_grayscale_uncompressed.tiff16,782.2 ms718.14 ms111.13 ms111.940.801000.00001000.00001000.00001342016440 B
'System.Drawing Tiff'Job-MTZTUC.NET 4.7.2Tiff/Benchmarks/jpeg444_big_palette_uncompressed.tiff178.0 ms7.07 ms1.83 ms1.000.0085000.0000--269221840 B
'ImageSharp Tiff'Job-MTZTUC.NET 4.7.2Tiff/Benchmarks/jpeg444_big_palette_uncompressed.tiff33,721.9 ms78.03 ms12.08 ms188.961.801000.00001000.00001000.00001342023280 B
'System.Drawing Tiff'Job-BGVYTJ.NET Core 2.1Tiff/Benchmarks/jpeg444_big_palette_uncompressed.tiff180.1 ms8.81 ms2.29 ms1.000.0085000.0000--268815616 B
'ImageSharp Tiff'Job-BGVYTJ.NET Core 2.1Tiff/Benchmarks/jpeg444_big_palette_uncompressed.tiff22,941.4 ms728.12 ms189.09 ms127.371.071000.00001000.00001000.00001342022368 B
'System.Drawing Tiff'Job-ZDUDFU.NET Core 3.1Tiff/Benchmarks/jpeg444_big_palette_uncompressed.tiff145.5 ms3.20 ms0.50 ms1.000.00---176 B
'ImageSharp Tiff'Job-ZDUDFU.NET Core 3.1Tiff/Benchmarks/jpeg444_big_palette_uncompressed.tiff21,485.0 ms711.10 ms184.67 ms148.040.661000.00001000.00001000.00001342025632 B
'System.Drawing Tiff'Job-MTZTUC.NET 4.7.2Tiff/Benchmarks/jpeg444_big_rgb_deflate.tiff2,518.2 ms76.22 ms19.79 ms1.000.006000.0000--29598616 B
'ImageSharp Tiff'Job-MTZTUC.NET 4.7.2Tiff/Benchmarks/jpeg444_big_rgb_deflate.tiff29,327.2 ms102.72 ms26.68 ms11.650.101000.00001000.00001000.00001124088224 B
'System.Drawing Tiff'Job-BGVYTJ.NET Core 2.1Tiff/Benchmarks/jpeg444_big_rgb_deflate.tiff2,500.3 ms67.24 ms10.41 ms1.000.006000.0000--29528752 B
'ImageSharp Tiff'Job-BGVYTJ.NET Core 2.1Tiff/Benchmarks/jpeg444_big_rgb_deflate.tiff18,974.7 ms199.58 ms30.89 ms7.590.041000.00001000.00001000.00001123947608 B
'System.Drawing Tiff'Job-ZDUDFU.NET Core 3.1Tiff/Benchmarks/jpeg444_big_rgb_deflate.tiff2,541.1 ms21.36 ms5.55 ms1.000.00---176 B
'ImageSharp Tiff'Job-ZDUDFU.NET Core 3.1Tiff/Benchmarks/jpeg444_big_rgb_deflate.tiff17,974.8 ms751.73 ms116.33 ms7.070.041000.00001000.00001000.00001123949960 B
'System.Drawing Tiff'Job-MTZTUC.NET 4.7.2Tiff/Benchmarks/jpeg444_big_rgb_lzw.tiff3,368.4 ms40.71 ms6.30 ms1.000.004000.0000--22835824 B
'ImageSharp Tiff'Job-MTZTUC.NET 4.7.2Tiff/Benchmarks/jpeg444_big_rgb_lzw.tiff28,919.9 ms705.58 ms183.24 ms8.570.041000.00001000.00001000.00001123956384 B
'System.Drawing Tiff'Job-BGVYTJ.NET Core 2.1Tiff/Benchmarks/jpeg444_big_rgb_lzw.tiff3,365.1 ms36.93 ms5.72 ms1.000.004000.0000--22789840 B
'ImageSharp Tiff'Job-BGVYTJ.NET Core 2.1Tiff/Benchmarks/jpeg444_big_rgb_lzw.tiff17,905.1 ms40.08 ms10.41 ms5.320.011000.00001000.00001000.00001123949072 B
'System.Drawing Tiff'Job-ZDUDFU.NET Core 3.1Tiff/Benchmarks/jpeg444_big_rgb_lzw.tiff3,377.6 ms125.36 ms32.56 ms1.000.00---176 B
'ImageSharp Tiff'Job-ZDUDFU.NET Core 3.1Tiff/Benchmarks/jpeg444_big_rgb_lzw.tiff16,998.0 ms460.59 ms119.61 ms5.030.071000.00001000.00001000.00001123952144 B
'System.Drawing Tiff'Job-MTZTUC.NET 4.7.2Tiff/Benchmarks/jpeg444_big_rgb_packbits.tiff1,849.3 ms43.52 ms11.30 ms1.000.00255000.0000--812350880 B
'ImageSharp Tiff'Job-MTZTUC.NET 4.7.2Tiff/Benchmarks/jpeg444_big_rgb_packbits.tiff29,360.0 ms157.78 ms40.98 ms15.880.12---2690323752 B
'System.Drawing Tiff'Job-BGVYTJ.NET Core 2.1Tiff/Benchmarks/jpeg444_big_rgb_packbits.tiff1,882.7 ms64.85 ms16.84 ms1.000.00255000.0000--811943568 B
'ImageSharp Tiff'Job-BGVYTJ.NET Core 2.1Tiff/Benchmarks/jpeg444_big_rgb_packbits.tiff18,967.7 ms445.86 ms115.79 ms10.080.09---2690318648 B
'System.Drawing Tiff'Job-ZDUDFU.NET Core 3.1Tiff/Benchmarks/jpeg444_big_rgb_packbits.tiff1,743.2 ms78.50 ms20.39 ms1.000.00---176 B
'ImageSharp Tiff'Job-ZDUDFU.NET Core 3.1Tiff/Benchmarks/jpeg444_big_rgb_packbits.tiff17,379.6 ms243.53 ms63.24 ms9.970.10---2690321912 B
'System.Drawing Tiff'Job-MTZTUC.NET 4.7.2Tiff/Benchmarks/jpeg444_big_rgb_uncompressed.tiff758.5 ms9.75 ms2.53 ms1.000.00255000.0000--806059984 B
'ImageSharp Tiff'Job-MTZTUC.NET 4.7.2Tiff/Benchmarks/jpeg444_big_rgb_uncompressed.tiff29,198.2 ms677.81 ms176.03 ms38.500.19---1878827096 B
'System.Drawing Tiff'Job-BGVYTJ.NET Core 2.1Tiff/Benchmarks/jpeg444_big_rgb_uncompressed.tiff760.1 ms15.95 ms2.47 ms1.000.00255000.0000--805652192 B
'ImageSharp Tiff'Job-BGVYTJ.NET Core 2.1Tiff/Benchmarks/jpeg444_big_rgb_uncompressed.tiff18,457.2 ms35.60 ms5.51 ms24.280.08---1878821992 B
'System.Drawing Tiff'Job-ZDUDFU.NET Core 3.1Tiff/Benchmarks/jpeg444_big_rgb_uncompressed.tiff629.5 ms11.40 ms2.96 ms1.000.00---176 B
'ImageSharp Tiff'Job-ZDUDFU.NET Core 3.1Tiff/Benchmarks/jpeg444_big_rgb_uncompressed.tiff17,579.8 ms371.72 ms96.54 ms27.930.11---1878825256 B
+ + diff --git a/tests/Images/Input/Tiff/Benchmarks/gen.bat b/tests/Images/Input/Tiff/Benchmarks/gen.bat new file mode 100644 index 000000000..3a0a032c1 --- /dev/null +++ b/tests/Images/Input/Tiff/Benchmarks/gen.bat @@ -0,0 +1,2 @@ +powershell -executionpolicy RemoteSigned -file gen_big.ps1 +powershell -executionpolicy RemoteSigned -file gen_medium.ps1 diff --git a/tests/Images/Input/Tiff/Benchmarks/gen_big.ps1 b/tests/Images/Input/Tiff/Benchmarks/gen_big.ps1 new file mode 100644 index 000000000..b824bd463 --- /dev/null +++ b/tests/Images/Input/Tiff/Benchmarks/gen_big.ps1 @@ -0,0 +1,12 @@ +$Gm_Exe = "C:\Program Files\ImageMagick-7.0.8-Q16\magick.exe" +$Source_Image = ".\jpeg444_big.jpg" +$Output_Prefix = ".\jpeg444_big" + +& $Gm_Exe convert $Source_Image -compress None -type TrueColor $Output_Prefix"_rgb_uncompressed.tiff" +& $Gm_Exe convert $Source_Image -compress LZW -type TrueColor $Output_Prefix"_rgb_lzw.tiff" +& $Gm_Exe convert $Source_Image -compress RLE -type TrueColor $Output_Prefix"_rgb_packbits.tiff" +& $Gm_Exe convert $Source_Image -compress JPEG -type TrueColor $Output_Prefix"_rgb_jpeg.tiff" +& $Gm_Exe convert $Source_Image -compress Zip -type TrueColor $Output_Prefix"_rgb_deflate.tiff" + +& $Gm_Exe convert $Source_Image -compress None -type Grayscale $Output_Prefix"_grayscale_uncompressed.tiff" +& $Gm_Exe convert $Source_Image -compress None -colors 256 $Output_Prefix"_palette_uncompressed.tiff" \ No newline at end of file diff --git a/tests/Images/Input/Tiff/Benchmarks/gen_medium.ps1 b/tests/Images/Input/Tiff/Benchmarks/gen_medium.ps1 new file mode 100644 index 000000000..2cd631c28 --- /dev/null +++ b/tests/Images/Input/Tiff/Benchmarks/gen_medium.ps1 @@ -0,0 +1,12 @@ +$Gm_Exe = "C:\Program Files\ImageMagick-7.0.8-Q16\magick.exe" +$Source_Image = ".\jpeg444_medium.jpg" +$Output_Prefix = ".\jpeg444_medium" + +& $Gm_Exe convert $Source_Image -compress None -type TrueColor $Output_Prefix"_rgb_uncompressed.tiff" +& $Gm_Exe convert $Source_Image -compress LZW -type TrueColor $Output_Prefix"_rgb_lzw.tiff" +& $Gm_Exe convert $Source_Image -compress RLE -type TrueColor $Output_Prefix"_rgb_packbits.tiff" +& $Gm_Exe convert $Source_Image -compress JPEG -type TrueColor $Output_Prefix"_rgb_jpeg.tiff" +& $Gm_Exe convert $Source_Image -compress Zip -type TrueColor $Output_Prefix"_rgb_deflate.tiff" + +& $Gm_Exe convert $Source_Image -compress None -type Grayscale $Output_Prefix"_grayscale_uncompressed.tiff" +& $Gm_Exe convert $Source_Image -compress None -colors 256 $Output_Prefix"_palette_uncompressed.tiff" \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Tiff/genimages.ps1 b/tests/Images/Input/Tiff/Benchmarks/genimages.ps1 similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Tiff/genimages.ps1 rename to tests/Images/Input/Tiff/Benchmarks/genimages.ps1 diff --git a/tests/Images/Input/Tiff/Benchmarks/jpeg444.jpg b/tests/Images/Input/Tiff/Benchmarks/jpeg444.jpg new file mode 100644 index 000000000..1887fa4a5 --- /dev/null +++ b/tests/Images/Input/Tiff/Benchmarks/jpeg444.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:89c632bbc42bc917f81e7c47595c95cb914a619604ac07b8cebf6fd4d1d744ca +size 5667 diff --git a/tests/Images/Input/Tiff/Benchmarks/jpeg444_big.jpg b/tests/Images/Input/Tiff/Benchmarks/jpeg444_big.jpg new file mode 100644 index 000000000..650aff92b --- /dev/null +++ b/tests/Images/Input/Tiff/Benchmarks/jpeg444_big.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cf48ba72885b98b9d05f1e4bed2e85f5db1db04b0206fc8160a9da2367f4467c +size 1984946 diff --git a/tests/Images/Input/Tiff/Benchmarks/jpeg444_medium.jpg b/tests/Images/Input/Tiff/Benchmarks/jpeg444_medium.jpg new file mode 100644 index 000000000..0300c67ab --- /dev/null +++ b/tests/Images/Input/Tiff/Benchmarks/jpeg444_medium.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:75cdf78efd14c880f26d5009e087df06e772b000edddbb404e7098177f895ac1 +size 525610 diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_grayscale_uncompressed.tiff b/tests/Images/Input/Tiff/Calliphora_grayscale_uncompressed.tiff similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_grayscale_uncompressed.tiff rename to tests/Images/Input/Tiff/Calliphora_grayscale_uncompressed.tiff diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_palette_uncompressed.tiff b/tests/Images/Input/Tiff/Calliphora_palette_uncompressed.tiff similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_palette_uncompressed.tiff rename to tests/Images/Input/Tiff/Calliphora_palette_uncompressed.tiff diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_rgb_deflate.tiff b/tests/Images/Input/Tiff/Calliphora_rgb_deflate.tiff similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_rgb_deflate.tiff rename to tests/Images/Input/Tiff/Calliphora_rgb_deflate.tiff diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_rgb_jpeg.tiff b/tests/Images/Input/Tiff/Calliphora_rgb_jpeg.tiff similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_rgb_jpeg.tiff rename to tests/Images/Input/Tiff/Calliphora_rgb_jpeg.tiff diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_rgb_lzw.tiff b/tests/Images/Input/Tiff/Calliphora_rgb_lzw.tiff similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_rgb_lzw.tiff rename to tests/Images/Input/Tiff/Calliphora_rgb_lzw.tiff diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_rgb_packbits.tiff b/tests/Images/Input/Tiff/Calliphora_rgb_packbits.tiff similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_rgb_packbits.tiff rename to tests/Images/Input/Tiff/Calliphora_rgb_packbits.tiff diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_rgb_uncompressed.tiff b/tests/Images/Input/Tiff/Calliphora_rgb_uncompressed.tiff similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Tiff/Calliphora_rgb_uncompressed.tiff rename to tests/Images/Input/Tiff/Calliphora_rgb_uncompressed.tiff diff --git a/tests/Images/Input/Tiff/grayscale_deflate_multistrip.tiff b/tests/Images/Input/Tiff/grayscale_deflate_multistrip.tiff new file mode 100644 index 000000000..6e57bb56e --- /dev/null +++ b/tests/Images/Input/Tiff/grayscale_deflate_multistrip.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aaee95d80f1e9eb9afbb7447da78a685f29359181ce71c045cff3aacda28a916 +size 14530 diff --git a/tests/Images/Input/Tiff/grayscale_uncompressed.tiff b/tests/Images/Input/Tiff/grayscale_uncompressed.tiff new file mode 100644 index 000000000..570edfc6d --- /dev/null +++ b/tests/Images/Input/Tiff/grayscale_uncompressed.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fefe6f6e1daf270546848c23ef437cfd072abb812e539fbab1006d74d416e9a4 +size 65758 diff --git a/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_Calliphora_rgb_deflate.png b/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_Calliphora_rgb_deflate.png new file mode 100644 index 000000000..e49bf1073 --- /dev/null +++ b/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_Calliphora_rgb_deflate.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:087d7479ebe3bdd95281584cf4c9582603d90e157d136cf4233dcdefd909ba73 +size 1696927 diff --git a/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_Calliphora_rgb_lzw.png b/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_Calliphora_rgb_lzw.png new file mode 100644 index 000000000..891a2ace6 --- /dev/null +++ b/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_Calliphora_rgb_lzw.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:14f7b8e8275b4488418e4403c31e1a5c7565bf062fbd962f09f7a665468e2481 +size 7358 diff --git a/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_metadata_sample.png b/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_metadata_sample.png new file mode 100644 index 000000000..9eb1808f2 --- /dev/null +++ b/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_metadata_sample.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:91c87bc3d75b1386b30990513fab2da26bad065b977108904e866c850d66a7e5 +size 197 diff --git a/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_multipage_lzw.png b/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_multipage_lzw.png new file mode 100644 index 000000000..a6642e716 --- /dev/null +++ b/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_multipage_lzw.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:727356bf611957750a0427bda4582f9ecc0f8935884f2158e8a2d5e65c3469b4 +size 18278 diff --git a/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_rgb_deflate.png b/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_rgb_deflate.png new file mode 100644 index 000000000..9e95173bc --- /dev/null +++ b/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_rgb_deflate.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b077fb63012967c39c5a0b1b515ada33ad5470160ad0fa1aa89581aa77238c82 +size 18237 diff --git a/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_rgb_lzw_multistrip.png b/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_rgb_lzw_multistrip.png new file mode 100644 index 000000000..9dcd861ec --- /dev/null +++ b/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_rgb_lzw_multistrip.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6d82fec289ce819c51fcb00d7eee662afc2d7357c940154e651e9e5ebf8a0287 +size 91898 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_Calliphora_rgb_deflate.png b/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_Calliphora_rgb_deflate.png new file mode 100644 index 000000000..415f73d87 --- /dev/null +++ b/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_Calliphora_rgb_deflate.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:beb1b3f0229c9a1ed78d4c1ab3cd786d96d70b904398ba008f1aa4157862554c +size 1696925 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_Calliphora_rgb_lzw.png b/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_Calliphora_rgb_lzw.png new file mode 100644 index 000000000..36dcfd9e8 --- /dev/null +++ b/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_Calliphora_rgb_lzw.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c74fae7a00dbf00bc259851b1e9c774a10fac2bd8581397d8680ebc47a7d0340 +size 31982 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_metadata_sample.png b/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_metadata_sample.png new file mode 100644 index 000000000..96cbbca22 --- /dev/null +++ b/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_metadata_sample.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5727c4007787bf0d6af78763c94125029255e41ebe570a6b8f3cbdb65e2a4d5f +size 1488 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_multipage_lzw.png b/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_multipage_lzw.png new file mode 100644 index 000000000..575e6cd56 --- /dev/null +++ b/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_multipage_lzw.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9d788e1facd4ee5cea5c57caa8b38a26a45fc8423b9967e9348f0514d734524f +size 18278 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_rgb_deflate.png b/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_rgb_deflate.png new file mode 100644 index 000000000..82d582e77 --- /dev/null +++ b/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_rgb_deflate.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9adbd0ce357c08c7b5ef543ff81bc32d227f9c2a016965f110f68c032f640ff1 +size 18237 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_rgb_lzw_multistrip.png b/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_rgb_lzw_multistrip.png new file mode 100644 index 000000000..9dcd861ec --- /dev/null +++ b/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_rgb_lzw_multistrip.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6d82fec289ce819c51fcb00d7eee662afc2d7357c940154e651e9e5ebf8a0287 +size 91898 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_Calliphora_rgb_deflate.png b/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_Calliphora_rgb_deflate.png new file mode 100644 index 000000000..415f73d87 --- /dev/null +++ b/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_Calliphora_rgb_deflate.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:beb1b3f0229c9a1ed78d4c1ab3cd786d96d70b904398ba008f1aa4157862554c +size 1696925 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_Calliphora_rgb_lzw.png b/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_Calliphora_rgb_lzw.png new file mode 100644 index 000000000..a79ae6096 --- /dev/null +++ b/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_Calliphora_rgb_lzw.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4acb6da968aa5bfc7af57b41fe9aefe13e7b2d3ee4379867b83548209fbc94eb +size 8108 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_metadata_sample.png b/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_metadata_sample.png new file mode 100644 index 000000000..249f68831 --- /dev/null +++ b/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_metadata_sample.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:977c552ee08788244fa4579c23ca59dfcb6e91df706774dc8167286a4ce5a536 +size 821 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_multipage_lzw.png b/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_multipage_lzw.png new file mode 100644 index 000000000..575e6cd56 --- /dev/null +++ b/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_multipage_lzw.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9d788e1facd4ee5cea5c57caa8b38a26a45fc8423b9967e9348f0514d734524f +size 18278 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_deflate.png b/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_deflate.png new file mode 100644 index 000000000..82d582e77 --- /dev/null +++ b/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_deflate.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9adbd0ce357c08c7b5ef543ff81bc32d227f9c2a016965f110f68c032f640ff1 +size 18237 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_lzw.png b/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_lzw.png new file mode 100644 index 000000000..c0da2eb59 --- /dev/null +++ b/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_lzw.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:24fe217e11cbc2944e7a8aba0411e37314822024e90fdd873cd9e3feb0e55898 +size 77527 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_lzw_multistrip.png b/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_lzw_multistrip.png new file mode 100644 index 000000000..61d88337b --- /dev/null +++ b/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_lzw_multistrip.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:82f30f2936880eacc3248fbc759f84adac7d625b974259d61aeed16bc00f01b9 +size 64056 diff --git a/tests/Images/Input/Tiff/issues/readme.md b/tests/Images/Input/Tiff/issues/readme.md new file mode 100644 index 000000000..1616a432c --- /dev/null +++ b/tests/Images/Input/Tiff/issues/readme.md @@ -0,0 +1,2 @@ +SixLabors.ImageSharp.Tests.Formats.Tiff.TiffDecoderTests.Decode +damaged output files \ No newline at end of file diff --git a/tests/Images/Input/Tiff/metadata_sample.tiff b/tests/Images/Input/Tiff/metadata_sample.tiff new file mode 100644 index 000000000..aac5fe2c4 --- /dev/null +++ b/tests/Images/Input/Tiff/metadata_sample.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ea7bc7404614a90da555637f6fed88defe5c6be8b5d1da2ff5980c39d249a01b +size 8833 diff --git a/tests/Images/Input/Tiff/multipage_ withPreview_differentSize_tiled.tiff b/tests/Images/Input/Tiff/multipage_ withPreview_differentSize_tiled.tiff new file mode 100644 index 000000000..164740c4a --- /dev/null +++ b/tests/Images/Input/Tiff/multipage_ withPreview_differentSize_tiled.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5e5ef9ccff292ed33a352cd040326c1ceeefc2cd68aedf0598dbff8326deecf6 +size 113784 diff --git a/tests/Images/Input/Tiff/multipage_deflate_withPreview.tiff b/tests/Images/Input/Tiff/multipage_deflate_withPreview.tiff new file mode 100644 index 000000000..4a4db4524 --- /dev/null +++ b/tests/Images/Input/Tiff/multipage_deflate_withPreview.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:63360eb1e383d0d3830155a269c4b5f4b07f3a2cb386f18427ea1c5ae5f1817a +size 160015 diff --git a/tests/Images/Input/Tiff/multipage_differentSize.tiff b/tests/Images/Input/Tiff/multipage_differentSize.tiff new file mode 100644 index 000000000..7caa44710 --- /dev/null +++ b/tests/Images/Input/Tiff/multipage_differentSize.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8476813a4c68883872a8a196f567a567d2351802f86114aef0c64e9786a7d8b9 +size 210533 diff --git a/tests/Images/Input/Tiff/multipage_differentVariants.tiff b/tests/Images/Input/Tiff/multipage_differentVariants.tiff new file mode 100644 index 000000000..9bbb84d8b --- /dev/null +++ b/tests/Images/Input/Tiff/multipage_differentVariants.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e54681a90093f0f613299eabf01db60ac971c96d26d281db01873b2cb9fb2d09 +size 483062 diff --git a/tests/Images/Input/Tiff/multipage_lzw.tiff b/tests/Images/Input/Tiff/multipage_lzw.tiff new file mode 100644 index 000000000..d2595dcdc --- /dev/null +++ b/tests/Images/Input/Tiff/multipage_lzw.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:da86a6d5fa61609edb54ef9118be16d89488f0cfce0acd16990e68b685f76094 +size 43432 diff --git a/tests/Images/Input/Tiff/palette_grayscale_deflate_multistrip.tiff b/tests/Images/Input/Tiff/palette_grayscale_deflate_multistrip.tiff new file mode 100644 index 000000000..d68c53483 --- /dev/null +++ b/tests/Images/Input/Tiff/palette_grayscale_deflate_multistrip.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a6b269e44539ea2e36f6357941c97a158ee288a7b44dab35338c241de69b5d37 +size 16078 diff --git a/tests/Images/Input/Tiff/palette_uncompressed.tiff b/tests/Images/Input/Tiff/palette_uncompressed.tiff new file mode 100644 index 000000000..b282d65b5 --- /dev/null +++ b/tests/Images/Input/Tiff/palette_uncompressed.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:75f1bcaff7dc09ddbe6ded7b764b8c0b17bffc3392bafdc7bc7a4c7d616a38e5 +size 67394 diff --git a/tests/Images/Input/Tiff/rgb_deflate.tiff b/tests/Images/Input/Tiff/rgb_deflate.tiff new file mode 100644 index 000000000..97623cd5b --- /dev/null +++ b/tests/Images/Input/Tiff/rgb_deflate.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a1db70e0cfb056cfc675db3a2b85a1f226c53cd70275808773ff580c738b3db1 +size 3158 diff --git a/tests/Images/Input/Tiff/rgb_deflate_multistrip.tiff b/tests/Images/Input/Tiff/rgb_deflate_multistrip.tiff new file mode 100644 index 000000000..dc9b36a9f --- /dev/null +++ b/tests/Images/Input/Tiff/rgb_deflate_multistrip.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eb8375584d0ba70626f0026bf91306c423a6c00f511362a3ce523cefb1e65d56 +size 68058 diff --git a/tests/Images/Input/Tiff/rgb_jpeg.tiff b/tests/Images/Input/Tiff/rgb_jpeg.tiff new file mode 100644 index 000000000..b198d1aba --- /dev/null +++ b/tests/Images/Input/Tiff/rgb_jpeg.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aa7f77e16bc51a55f5d2cb8d162801ea9edc620e16ec7ab43323f3c994830399 +size 5736 diff --git a/tests/Images/Input/Tiff/rgb_lzw.tiff b/tests/Images/Input/Tiff/rgb_lzw.tiff new file mode 100644 index 000000000..a1d3d77f4 --- /dev/null +++ b/tests/Images/Input/Tiff/rgb_lzw.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8fe0ad0f383136e32f45c922de530ebcbce055f99ca81ef1d50608e241ea621c +size 25806 diff --git a/tests/Images/Input/Tiff/rgb_lzw_multistrip.tiff b/tests/Images/Input/Tiff/rgb_lzw_multistrip.tiff new file mode 100644 index 000000000..6cfc803bb --- /dev/null +++ b/tests/Images/Input/Tiff/rgb_lzw_multistrip.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:55ecb81526b238ca4a43b559a33a3b393b9776fe32fd2f3b78a1b460780f7ba9 +size 26962 diff --git a/tests/Images/Input/Tiff/rgb_packbits.tiff b/tests/Images/Input/Tiff/rgb_packbits.tiff new file mode 100644 index 000000000..28310cade --- /dev/null +++ b/tests/Images/Input/Tiff/rgb_packbits.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8c5c7996d78fb97a43bdd6d9fec7c1cb6bdea546d73c21f5a068edc602ff3aa8 +size 198460 diff --git a/tests/Images/Input/Tiff/rgb_packbits_multistrip.tiff b/tests/Images/Input/Tiff/rgb_packbits_multistrip.tiff new file mode 100644 index 000000000..fa9a8f2ae --- /dev/null +++ b/tests/Images/Input/Tiff/rgb_packbits_multistrip.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:79ea79362ddad668b28cb919c91dc793b4246f33e411e3794cbe587c5461367a +size 198402 diff --git a/tests/Images/Input/Tiff/rgb_uncompressed.tiff b/tests/Images/Input/Tiff/rgb_uncompressed.tiff new file mode 100644 index 000000000..c9602763d --- /dev/null +++ b/tests/Images/Input/Tiff/rgb_uncompressed.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:87134a685bcd816d77cae664b415f1b6a25b78933953c128a742fba653eca9fa +size 196924 diff --git a/tests/Images/Input/Tiff/rgb_uncompressed_tiled.tiff b/tests/Images/Input/Tiff/rgb_uncompressed_tiled.tiff new file mode 100644 index 000000000..0f4912136 --- /dev/null +++ b/tests/Images/Input/Tiff/rgb_uncompressed_tiled.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:165d85d8e3be6b44309855474c73ae6c14267393945d287fc20be4fcadc0e3f3 +size 3337 From 768ff6b582d157039d6bbd9ac21a97ff43105d60 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Tue, 25 Aug 2020 21:40:10 +0300 Subject: [PATCH 068/275] Deep refactoring and improves for tiff classes. In particular: * Support multi framing. * Using Metadata\Profiles\Exif\* types instead their duplicate tag types (moved to __obsolete directory). * Implement useful Metadata classes. * Add extensions. * Test coverage. --- .../Formats/ImageExtensions.Save.cs | 106 +- .../Formats/ImageExtensions.Save.tt | 3 +- .../Tiff/Compression/CompressionFactory.cs | 42 + .../Compression/DeflateTiffCompression.cs | 2 +- .../Formats/Tiff/Constants/TiffByteOrder.cs | 21 + .../Formats/Tiff/Constants/TiffCompression.cs | 4 +- .../Formats/Tiff/Constants/TiffFillOrder.cs | 4 +- .../Tiff/Constants/TiffNewSubfileType.cs | 4 +- .../TiffPhotometricInterpretation.cs | 4 +- .../Tiff/Constants/TiffPlanarConfiguration.cs | 4 +- .../Tiff/Constants/TiffResolutionUnit.cs | 4 +- .../Formats/Tiff/Constants/TiffSubfileType.cs | 4 +- .../Formats/Tiff/MetadataExtensions.cs | 28 + .../BlackIsZero1TiffColor.cs | 15 +- .../BlackIsZero4TiffColor.cs | 15 +- .../BlackIsZero8TiffColor.cs | 15 +- .../BlackIsZeroTiffColor.cs | 26 +- .../PaletteTiffColor.cs | 34 +- .../Rgb888TiffColor.cs | 15 +- .../RgbPlanarTiffColor.cs | 50 +- .../PhotometricInterpretation/RgbTiffColor.cs | 44 +- .../TiffColorDecoder.cs | 52 + .../TiffColorDecoderFactory.cs | 95 ++ .../TiffColorType.cs | 2 +- .../WhiteIsZero1TiffColor.cs | 14 +- .../WhiteIsZero4TiffColor.cs | 15 +- .../WhiteIsZero8TiffColor.cs | 15 +- .../WhiteIsZeroTiffColor.cs | 27 +- .../Tiff/Streams/TiffBigEndianStream.cs | 88 ++ .../Tiff/Streams/TiffLittleEndianStream.cs | 88 ++ .../Formats/Tiff/Streams/TiffStream.cs | 94 ++ .../Formats/Tiff/Streams/TiffStreamFactory.cs | 59 + src/ImageSharp/Formats/Tiff/TiffDecoder.cs | 23 +- .../Formats/Tiff/TiffDecoderCore.cs | 1205 ++--------------- .../Formats/Tiff/TiffDecoderHelpers.cs | 344 +++++ .../Formats/Tiff/TiffEncoderCore.cs | 113 +- src/ImageSharp/Formats/Tiff/TiffFormat.cs | 11 +- .../Formats/Tiff/TiffFrameMetadata.cs | 324 +++++ .../Formats/Tiff/TiffIfd/TiffIfd.cs | 125 +- .../Formats/Tiff/TiffIfd/TiffIfdEntry.cs | 328 ++++- src/ImageSharp/Formats/Tiff/TiffMetadata.cs | 44 + .../TiffIfdEntryCreator.cs | 0 .../{ => __obsolete}/TiffMetadataNames.cs | 0 .../TiffTags.cs => __obsolete/TiffTagId.cs} | 0 .../TiffType.cs => __obsolete/TiffTagType.cs} | 0 src/ImageSharp/ImageSharp.csproj | 6 + .../Metadata/Profiles/Exif/ExifDataType.cs | 7 +- .../Metadata/Profiles/Exif/ExifWriter.cs | 22 +- .../Profiles/Exif/Tags/ExifTag.ByteArray.cs | 10 + .../Profiles/Exif/Tags/ExifTag.Long.cs | 5 + .../Profiles/Exif/Tags/ExifTag.LongArray.cs | 6 + .../Profiles/Exif/Tags/ExifTagValue.cs | 287 +++- .../Profiles/Exif/Values/ExifValues.cs | 6 +- .../Formats/Tiff/ImageExtensionsTest.cs | 172 +++ .../BlackIsZeroTiffColorTests.cs | 28 +- .../PaletteTiffColorTests.cs | 30 +- .../PhotometricInterpretationTestBase.cs | 24 +- .../RgbPlanarTiffColorTests.cs | 46 +- .../RgbTiffColorTests.cs | 50 +- .../WhiteIsZeroTiffColorTests.cs | 28 +- .../Formats/Tiff/TiffDecoderTests.cs | 43 +- .../Formats/Tiff/TiffMetadataTests.cs | 105 ++ .../TestUtilities/Tiff/ITiffGenDataSource.cs | 0 .../TestUtilities/Tiff/TiffGenDataBlock.cs | 0 .../Tiff/TiffGenDataReference.cs | 0 .../TestUtilities/Tiff/TiffGenEntry.cs | 0 .../TestUtilities/Tiff/TiffGenExtensions.cs | 0 .../TestUtilities/Tiff/TiffGenHeader.cs | 0 .../TestUtilities/Tiff/TiffGenIfd.cs | 0 .../Tiff/TiffGenIfdExtensions.cs | 0 .../TestUtilities/Tiff/TiffIfdParser.cs | 0 .../TiffDecoderHeaderTests.cs | 0 .../TiffDecoderIfdEntryTests.cs | 0 .../{ => __obsolete}/TiffDecoderIfdTests.cs | 0 .../{ => __obsolete}/TiffDecoderImageTests.cs | 0 .../TiffDecoderMetadataTests.cs | 0 .../{ => __obsolete}/TiffEncoderIfdTests.cs | 0 .../TiffEncoderMetadataTests.cs | 0 .../TiffIfd/TiffIfdEntryCreatorTests.cs | 0 .../TiffIfd/TiffIfdEntryTests.cs | 0 .../{ => __obsolete}/TiffIfd/TiffIfdTests.cs | 0 .../TiffImageFormatDetectorTests.cs | 0 .../ImageSharp.Tests/ImageSharp.Tests.csproj | 6 + .../ReferenceCodecs/MagickReferenceDecoder.cs | 45 +- .../TestUtilities/TestImageExtensions.cs | 25 + 85 files changed, 2875 insertions(+), 1586 deletions(-) create mode 100644 src/ImageSharp/Formats/Tiff/Compression/CompressionFactory.cs create mode 100644 src/ImageSharp/Formats/Tiff/Constants/TiffByteOrder.cs create mode 100644 src/ImageSharp/Formats/Tiff/MetadataExtensions.cs create mode 100644 src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoder.cs create mode 100644 src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory.cs create mode 100644 src/ImageSharp/Formats/Tiff/Streams/TiffBigEndianStream.cs create mode 100644 src/ImageSharp/Formats/Tiff/Streams/TiffLittleEndianStream.cs create mode 100644 src/ImageSharp/Formats/Tiff/Streams/TiffStream.cs create mode 100644 src/ImageSharp/Formats/Tiff/Streams/TiffStreamFactory.cs create mode 100644 src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs create mode 100644 src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs create mode 100644 src/ImageSharp/Formats/Tiff/TiffMetadata.cs rename src/ImageSharp/Formats/Tiff/{TiffIfd => __obsolete}/TiffIfdEntryCreator.cs (100%) rename src/ImageSharp/Formats/Tiff/{ => __obsolete}/TiffMetadataNames.cs (100%) rename src/ImageSharp/Formats/Tiff/{Constants/TiffTags.cs => __obsolete/TiffTagId.cs} (100%) rename src/ImageSharp/Formats/Tiff/{Constants/TiffType.cs => __obsolete/TiffTagType.cs} (100%) create mode 100644 tests/ImageSharp.Tests/Formats/Tiff/ImageExtensionsTest.cs create mode 100644 tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs rename tests/ImageSharp.Tests/{ => Formats/Tiff/__obsolete}/TestUtilities/Tiff/ITiffGenDataSource.cs (100%) rename tests/ImageSharp.Tests/{ => Formats/Tiff/__obsolete}/TestUtilities/Tiff/TiffGenDataBlock.cs (100%) rename tests/ImageSharp.Tests/{ => Formats/Tiff/__obsolete}/TestUtilities/Tiff/TiffGenDataReference.cs (100%) rename tests/ImageSharp.Tests/{ => Formats/Tiff/__obsolete}/TestUtilities/Tiff/TiffGenEntry.cs (100%) rename tests/ImageSharp.Tests/{ => Formats/Tiff/__obsolete}/TestUtilities/Tiff/TiffGenExtensions.cs (100%) rename tests/ImageSharp.Tests/{ => Formats/Tiff/__obsolete}/TestUtilities/Tiff/TiffGenHeader.cs (100%) rename tests/ImageSharp.Tests/{ => Formats/Tiff/__obsolete}/TestUtilities/Tiff/TiffGenIfd.cs (100%) rename tests/ImageSharp.Tests/{ => Formats/Tiff/__obsolete}/TestUtilities/Tiff/TiffGenIfdExtensions.cs (100%) rename tests/ImageSharp.Tests/{ => Formats/Tiff/__obsolete}/TestUtilities/Tiff/TiffIfdParser.cs (100%) rename tests/ImageSharp.Tests/Formats/Tiff/{ => __obsolete}/TiffDecoderHeaderTests.cs (100%) rename tests/ImageSharp.Tests/Formats/Tiff/{ => __obsolete}/TiffDecoderIfdEntryTests.cs (100%) rename tests/ImageSharp.Tests/Formats/Tiff/{ => __obsolete}/TiffDecoderIfdTests.cs (100%) rename tests/ImageSharp.Tests/Formats/Tiff/{ => __obsolete}/TiffDecoderImageTests.cs (100%) rename tests/ImageSharp.Tests/Formats/Tiff/{ => __obsolete}/TiffDecoderMetadataTests.cs (100%) rename tests/ImageSharp.Tests/Formats/Tiff/{ => __obsolete}/TiffEncoderIfdTests.cs (100%) rename tests/ImageSharp.Tests/Formats/Tiff/{ => __obsolete}/TiffEncoderMetadataTests.cs (100%) rename tests/ImageSharp.Tests/Formats/Tiff/{ => __obsolete}/TiffIfd/TiffIfdEntryCreatorTests.cs (100%) rename tests/ImageSharp.Tests/Formats/Tiff/{ => __obsolete}/TiffIfd/TiffIfdEntryTests.cs (100%) rename tests/ImageSharp.Tests/Formats/Tiff/{ => __obsolete}/TiffIfd/TiffIfdTests.cs (100%) rename tests/ImageSharp.Tests/Formats/Tiff/{ => __obsolete}/TiffImageFormatDetectorTests.cs (100%) diff --git a/src/ImageSharp/Formats/ImageExtensions.Save.cs b/src/ImageSharp/Formats/ImageExtensions.Save.cs index 075c708b6..0f8b1e16d 100644 --- a/src/ImageSharp/Formats/ImageExtensions.Save.cs +++ b/src/ImageSharp/Formats/ImageExtensions.Save.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. // @@ -12,6 +12,7 @@ using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Tga; +using SixLabors.ImageSharp.Formats.Tiff; namespace SixLabors.ImageSharp { @@ -535,5 +536,108 @@ namespace SixLabors.ImageSharp encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TgaFormat.Instance), cancellationToken); + /// + /// Saves the image to the given stream with the Tiff format. + /// + /// The image this method extends. + /// The file path to save the image to. + /// Thrown if the path is null. + public static void SaveAsTiff(this Image source, string path) => SaveAsTiff(source, path, null); + + /// + /// Saves the image to the given stream with the Tiff format. + /// + /// The image this method extends. + /// The file path to save the image to. + /// Thrown if the path is null. + /// A representing the asynchronous operation. + public static Task SaveAsTiffAsync(this Image source, string path) => SaveAsTiffAsync(source, path, null); + + /// + /// Saves the image to the given stream with the Tiff format. + /// + /// The image this method extends. + /// The file path to save the image to. + /// The token to monitor for cancellation requests. + /// Thrown if the path is null. + /// A representing the asynchronous operation. + public static Task SaveAsTiffAsync(this Image source, string path, CancellationToken cancellationToken) + => SaveAsTiffAsync(source, path, null, cancellationToken); + + /// + /// Saves the image to the given stream with the Tiff format. + /// + /// The image this method extends. + /// The file path to save the image to. + /// The encoder to save the image with. + /// Thrown if the path is null. + public static void SaveAsTiff(this Image source, string path, TiffEncoder encoder) => + source.Save( + path, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TiffFormat.Instance)); + + /// + /// Saves the image to the given stream with the Tiff format. + /// + /// The image this method extends. + /// The file path to save the image to. + /// The encoder to save the image with. + /// The token to monitor for cancellation requests. + /// Thrown if the path is null. + /// A representing the asynchronous operation. + public static Task SaveAsTiffAsync(this Image source, string path, TiffEncoder encoder, CancellationToken cancellationToken = default) => + source.SaveAsync( + path, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TiffFormat.Instance), + cancellationToken); + + /// + /// Saves the image to the given stream with the Tiff format. + /// + /// The image this method extends. + /// The stream to save the image to. + /// Thrown if the stream is null. + public static void SaveAsTiff(this Image source, Stream stream) + => SaveAsTiff(source, stream, null); + + /// + /// Saves the image to the given stream with the Tiff format. + /// + /// The image this method extends. + /// The stream to save the image to. + /// The token to monitor for cancellation requests. + /// Thrown if the stream is null. + /// A representing the asynchronous operation. + public static Task SaveAsTiffAsync(this Image source, Stream stream, CancellationToken cancellationToken = default) + => SaveAsTiffAsync(source, stream, null, cancellationToken); + + /// + /// Saves the image to the given stream with the Tiff format. + /// + /// The image this method extends. + /// The stream to save the image to. + /// The encoder to save the image with. + /// Thrown if the stream is null. + /// A representing the asynchronous operation. + public static void SaveAsTiff(this Image source, Stream stream, TiffEncoder encoder) + => source.Save( + stream, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TiffFormat.Instance)); + + /// + /// Saves the image to the given stream with the Tiff format. + /// + /// The image this method extends. + /// The stream to save the image to. + /// The encoder to save the image with. + /// The token to monitor for cancellation requests. + /// Thrown if the stream is null. + /// A representing the asynchronous operation. + public static Task SaveAsTiffAsync(this Image source, Stream stream, TiffEncoder encoder, CancellationToken cancellationToken = default) => + source.SaveAsync( + stream, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TiffFormat.Instance), + cancellationToken); + } } diff --git a/src/ImageSharp/Formats/ImageExtensions.Save.tt b/src/ImageSharp/Formats/ImageExtensions.Save.tt index 63b404cc4..af9531225 100644 --- a/src/ImageSharp/Formats/ImageExtensions.Save.tt +++ b/src/ImageSharp/Formats/ImageExtensions.Save.tt @@ -1,4 +1,4 @@ -<#@ template language="C#" #> +<#@ template language="C#" #> <#@ import namespace="System.Text" #> <#@ import namespace="System.Collections.Generic" #> // Copyright (c) Six Labors. @@ -17,6 +17,7 @@ using SixLabors.ImageSharp.Advanced; "Jpeg", "Png", "Tga", + "Tiff", }; foreach (string fmt in formats) diff --git a/src/ImageSharp/Formats/Tiff/Compression/CompressionFactory.cs b/src/ImageSharp/Formats/Tiff/Compression/CompressionFactory.cs new file mode 100644 index 000000000..f65b3caf3 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Compression/CompressionFactory.cs @@ -0,0 +1,42 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; + +namespace SixLabors.ImageSharp.Formats.Tiff +{ + internal static class CompressionFactory + { + /// + /// Decompresses an image block from the input stream into the specified buffer. + /// + /// The input stream. + /// Type of the compression. + /// The offset within the file of the image block. + /// The size (in bytes) of the compressed data. + /// The buffer to write the uncompressed data. + public static void DecompressImageBlock(Stream stream, TiffCompressionType compressionType, uint offset, uint byteCount, byte[] buffer) + { + stream.Seek(offset, SeekOrigin.Begin); + + switch (compressionType) + { + case TiffCompressionType.None: + NoneTiffCompression.Decompress(stream, (int)byteCount, buffer); + break; + case TiffCompressionType.PackBits: + PackBitsTiffCompression.Decompress(stream, (int)byteCount, buffer); + break; + case TiffCompressionType.Deflate: + DeflateTiffCompression.Decompress(stream, (int)byteCount, buffer); + break; + case TiffCompressionType.Lzw: + LzwTiffCompression.Decompress(stream, (int)byteCount, buffer); + break; + default: + throw new InvalidOperationException(); + } + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs index b42ac9ee1..f5295de4a 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs @@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff // The subsequent data is the Deflate compressed data (except for the last four bytes of checksum) int headerLength = fdict ? 10 : 6; SubStream subStream = new SubStream(stream, byteCount - headerLength); - using (DeflateStream deflateStream = new DeflateStream(stream, CompressionMode.Decompress, true)) + using (DeflateStream deflateStream = new DeflateStream(subStream, CompressionMode.Decompress, true)) { deflateStream.ReadFull(buffer); } diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffByteOrder.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffByteOrder.cs new file mode 100644 index 000000000..b6418d11d --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffByteOrder.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Tiff +{ + /// + /// The tiff data stream byte order enum. + /// + public enum TiffByteOrder + { + /// + /// The big-endian byte order (Motorola). + /// + BigEndian, + + /// + /// The little-endian byte order (Intel). + /// + LittleEndian + } +} diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs index f8a661c1b..a8a46409f 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Enumeration representing the compression formats defined by the Tiff file-format. /// - internal enum TiffCompression + public enum TiffCompression : ushort { /// /// No compression. @@ -68,4 +68,4 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// ItuTRecT43 = 10 } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffFillOrder.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffFillOrder.cs index 024a41911..3febf2a96 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffFillOrder.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffFillOrder.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Enumeration representing the fill orders defined by the Tiff file-format. /// - internal enum TiffFillOrder + internal enum TiffFillOrder : ushort { /// /// Pixels with lower column values are stored in the higher-order bits of the byte. @@ -18,4 +18,4 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// LeastSignificantBitFirst = 2 } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs index 495b499f8..35c4439cb 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// Enumeration representing the sub-file types defined by the Tiff file-format. ///
[Flags] - internal enum TiffNewSubfileType + public enum TiffNewSubfileType : uint { /// /// A full-resolution image. @@ -41,4 +41,4 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// MixedRasterContent = 0x0008 } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs index c197965a6..dc8225a7a 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Enumeration representing the photometric interpretation formats defined by the Tiff file-format. /// - internal enum TiffPhotometricInterpretation + public enum TiffPhotometricInterpretation : ushort { /// /// Bilevel and grayscale: 0 is imaged as white. The maximum value is imaged as black. @@ -68,4 +68,4 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// LinearRaw = 34892 } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffPlanarConfiguration.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffPlanarConfiguration.cs index e0535be9d..e04e100e6 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffPlanarConfiguration.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffPlanarConfiguration.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Enumeration representing how the components of each pixel are stored the Tiff file-format. /// - internal enum TiffPlanarConfiguration + public enum TiffPlanarConfiguration : ushort { /// /// Chunky format. @@ -18,4 +18,4 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// Planar = 2 } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs index 51c3a72ce..a523f0bc2 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Enumeration representing the resolution units defined by the Tiff file-format. /// - internal enum TiffResolutionUnit + public enum TiffResolutionUnit : ushort { /// /// No absolute unit of measurement. @@ -23,4 +23,4 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// Centimeter = 3 } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffSubfileType.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffSubfileType.cs index 4b6b2061f..b66b72ce9 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffSubfileType.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffSubfileType.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Enumeration representing the sub-file types defined by the Tiff file-format. /// - internal enum TiffSubfileType + public enum TiffSubfileType : uint { /// /// Full-resolution image data. @@ -23,4 +23,4 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// SinglePage = 3 } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Tiff/MetadataExtensions.cs b/src/ImageSharp/Formats/Tiff/MetadataExtensions.cs new file mode 100644 index 000000000..b9da86fc4 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/MetadataExtensions.cs @@ -0,0 +1,28 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Metadata; + +namespace SixLabors.ImageSharp +{ + /// + /// Extension methods for the type. + /// + public static partial class MetadataExtensions + { + /// + /// Gets the tiff format specific metadata for the image. + /// + /// The metadata this method extends. + /// The . + public static TiffMetadata GetTiffMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(TiffFormat.Instance); + + /// + /// Gets the tiff format specific metadata for the image frame. + /// + /// The metadata this method extends. + /// The . + public static TiffFrameMetadata GetTiffMetadata(this ImageFrameMetadata metadata) => metadata.GetFormatMetadata(TiffFormat.Instance); + } +} diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs index 224da447c..a19cd6d44 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -11,21 +10,25 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Implements the 'BlackIsZero' photometric interpretation (optimised for bilevel images). /// - internal static class BlackIsZero1TiffColor + /// The pixel format. + internal class BlackIsZero1TiffColor : TiffColorDecoder + where TPixel : unmanaged, IPixel { + public BlackIsZero1TiffColor() + : base(null, null) + { + } + /// /// Decodes pixel data using the current photometric interpretation. /// - /// The pixel format. /// The buffer to read image data from. /// The image buffer to write pixels to. /// The x-coordinate of the left-hand side of the image block. /// The y-coordinate of the top of the image block. /// The width of the image block. /// The height of the image block. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) - where TPixel : unmanaged, IPixel + public override void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) { TPixel color = default(TPixel); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs index 1e59624b1..059de1a3e 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using System.Runtime.CompilerServices; +using System; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -10,21 +10,24 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Implements the 'BlackIsZero' photometric interpretation (optimised for 4-bit grayscale images). /// - internal static class BlackIsZero4TiffColor + internal class BlackIsZero4TiffColor : TiffColorDecoder + where TPixel : unmanaged, IPixel { + public BlackIsZero4TiffColor() + : base(null, null) + { + } + /// /// Decodes pixel data using the current photometric interpretation. /// - /// The pixel format. /// The buffer to read image data from. /// The image buffer to write pixels to. /// The x-coordinate of the left-hand side of the image block. /// The y-coordinate of the top of the image block. /// The width of the image block. /// The height of the image block. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) - where TPixel : unmanaged, IPixel + public override void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) { TPixel color = default(TPixel); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs index e34346fd4..5d50600d9 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using System.Runtime.CompilerServices; +using System; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -10,21 +10,24 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Implements the 'BlackIsZero' photometric interpretation (optimised for 8-bit grayscale images). /// - internal static class BlackIsZero8TiffColor + internal class BlackIsZero8TiffColor : TiffColorDecoder + where TPixel : unmanaged, IPixel { + public BlackIsZero8TiffColor() + : base(null, null) + { + } + /// /// Decodes pixel data using the current photometric interpretation. /// - /// The pixel format. /// The buffer to read image data from. /// The image buffer to write pixels to. /// The x-coordinate of the left-hand side of the image block. /// The y-coordinate of the top of the image block. /// The width of the image block. /// The height of the image block. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) - where TPixel : unmanaged, IPixel + public override void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) { TPixel color = default(TPixel); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs index e5414da8b..7de303536 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs @@ -3,7 +3,6 @@ using System; using System.Numerics; -using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -12,34 +11,41 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Implements the 'BlackIsZero' photometric interpretation (for all bit depths). /// - internal static class BlackIsZeroTiffColor + internal class BlackIsZeroTiffColor : TiffColorDecoder + where TPixel : unmanaged, IPixel { + private readonly ushort bitsPerSample0; + + private readonly float factor; + + public BlackIsZeroTiffColor(ushort[] bitsPerSample) + : base(bitsPerSample, null) + { + this.bitsPerSample0 = bitsPerSample[0]; + this.factor = (float)Math.Pow(2, this.bitsPerSample0) - 1.0f; + } + /// /// Decodes pixel data using the current photometric interpretation. /// - /// The pixel format. /// The buffer to read image data from. - /// The number of bits per sample for each pixel. /// The image buffer to write pixels to. /// The x-coordinate of the left-hand side of the image block. /// The y-coordinate of the top of the image block. /// The width of the image block. /// The height of the image block. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decode(byte[] data, uint[] bitsPerSample, Buffer2D pixels, int left, int top, int width, int height) - where TPixel : unmanaged, IPixel + public override void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) { TPixel color = default(TPixel); BitReader bitReader = new BitReader(data); - float factor = (float)Math.Pow(2, bitsPerSample[0]) - 1.0f; for (int y = top; y < top + height; y++) { for (int x = left; x < left + width; x++) { - int value = bitReader.ReadBits(bitsPerSample[0]); - float intensity = ((float)value) / factor; + int value = bitReader.ReadBits(this.bitsPerSample0); + float intensity = ((float)value) / this.factor; color.FromVector4(new Vector4(intensity, intensity, intensity, 1.0f)); pixels[x, y] = color; } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs index 509965641..7b4f50e80 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs @@ -12,35 +12,40 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Implements the 'PaletteTiffColor' photometric interpretation (for all bit depths). /// - internal static class PaletteTiffColor + internal class PaletteTiffColor : TiffColorDecoder + where TPixel : unmanaged, IPixel { + private readonly ushort bitsPerSample0; + + private readonly TPixel[] palette; + + public PaletteTiffColor(ushort[] bitsPerSample, ushort[] colorMap) + : base(bitsPerSample, colorMap) + { + this.bitsPerSample0 = bitsPerSample[0]; + int colorCount = (int)Math.Pow(2, this.bitsPerSample0); + this.palette = GeneratePalette(colorMap, colorCount); + } + /// /// Decodes pixel data using the current photometric interpretation. /// - /// The pixel format. /// The buffer to read image data from. - /// The number of bits per sample for each pixel. - /// The RGB color lookup table to use for decoding the image. /// The image buffer to write pixels to. /// The x-coordinate of the left-hand side of the image block. /// The y-coordinate of the top of the image block. /// The width of the image block. /// The height of the image block. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decode(byte[] data, uint[] bitsPerSample, uint[] colorMap, Buffer2D pixels, int left, int top, int width, int height) - where TPixel : unmanaged, IPixel + public override void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) { - int colorCount = (int)Math.Pow(2, bitsPerSample[0]); - TPixel[] palette = GeneratePalette(colorMap, colorCount); - BitReader bitReader = new BitReader(data); for (int y = top; y < top + height; y++) { for (int x = left; x < left + width; x++) { - int index = bitReader.ReadBits(bitsPerSample[0]); - pixels[x, y] = palette[index]; + int index = bitReader.ReadBits(this.bitsPerSample0); + pixels[x, y] = this.palette[index]; } bitReader.NextRow(); @@ -48,10 +53,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static TPixel[] GeneratePalette(uint[] colorMap, int colorCount) - where TPixel : unmanaged, IPixel + private static TPixel[] GeneratePalette(ushort[] colorMap, int colorCount) { - TPixel[] palette = new TPixel[colorCount]; + var palette = new TPixel[colorCount]; int rOffset = 0; int gOffset = colorCount; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs index c34d4f087..352c1e26c 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using System.Runtime.CompilerServices; +using System; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -10,21 +10,24 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Implements the 'RGB' photometric interpretation (optimised for 8-bit full color images). /// - internal static class Rgb888TiffColor + internal class Rgb888TiffColor : TiffColorDecoder + where TPixel : unmanaged, IPixel { + public Rgb888TiffColor() + : base(null, null) + { + } + /// /// Decodes pixel data using the current photometric interpretation. /// - /// The pixel format. /// The buffer to read image data from. /// The image buffer to write pixels to. /// The x-coordinate of the left-hand side of the image block. /// The y-coordinate of the top of the image block. /// The width of the image block. /// The height of the image block. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) - where TPixel : unmanaged, IPixel + public override void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) { TPixel color = default(TPixel); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs index b8a62b3ae..23f05c35f 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs @@ -3,7 +3,6 @@ using System; using System.Numerics; -using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -12,39 +11,58 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Implements the 'RGB' photometric interpretation with 'Planar' layout (for all bit depths). /// - internal static class RgbPlanarTiffColor + internal class RgbPlanarTiffColor /* : TiffColorDecoder */ + where TPixel : unmanaged, IPixel { + private readonly float rFactor; + + private readonly float gFactor; + + private readonly float bFactor; + + private readonly uint bitsPerSampleR; + + private readonly uint bitsPerSampleG; + + private readonly uint bitsPerSampleB; + + public RgbPlanarTiffColor(ushort[] bitsPerSample) + /* : base(bitsPerSample, null) */ + { + this.bitsPerSampleR = bitsPerSample[0]; + this.bitsPerSampleG = bitsPerSample[1]; + this.bitsPerSampleB = bitsPerSample[2]; + + this.rFactor = (float)Math.Pow(2, this.bitsPerSampleR) - 1.0f; + this.gFactor = (float)Math.Pow(2, this.bitsPerSampleG) - 1.0f; + this.bFactor = (float)Math.Pow(2, this.bitsPerSampleB) - 1.0f; + } + /// /// Decodes pixel data using the current photometric interpretation. /// - /// The pixel format. /// The buffers to read image data from. - /// The number of bits per sample for each pixel. /// The image buffer to write pixels to. /// The x-coordinate of the left-hand side of the image block. /// The y-coordinate of the top of the image block. /// The width of the image block. /// The height of the image block. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decode(byte[][] data, uint[] bitsPerSample, Buffer2D pixels, int left, int top, int width, int height) - where TPixel : unmanaged, IPixel + public void Decode(byte[][] data, Buffer2D pixels, int left, int top, int width, int height) { TPixel color = default(TPixel); - BitReader rBitReader = new BitReader(data[0]); - BitReader gBitReader = new BitReader(data[1]); - BitReader bBitReader = new BitReader(data[2]); - float rFactor = (float)Math.Pow(2, bitsPerSample[0]) - 1.0f; - float gFactor = (float)Math.Pow(2, bitsPerSample[1]) - 1.0f; - float bFactor = (float)Math.Pow(2, bitsPerSample[2]) - 1.0f; + var rBitReader = new BitReader(data[0]); + var gBitReader = new BitReader(data[1]); + var bBitReader = new BitReader(data[2]); for (int y = top; y < top + height; y++) { for (int x = left; x < left + width; x++) { - float r = ((float)rBitReader.ReadBits(bitsPerSample[0])) / rFactor; - float g = ((float)gBitReader.ReadBits(bitsPerSample[1])) / gFactor; - float b = ((float)bBitReader.ReadBits(bitsPerSample[2])) / bFactor; + float r = ((float)rBitReader.ReadBits(this.bitsPerSampleR)) / this.rFactor; + float g = ((float)gBitReader.ReadBits(this.bitsPerSampleG)) / this.gFactor; + float b = ((float)bBitReader.ReadBits(this.bitsPerSampleB)) / this.bFactor; + color.FromVector4(new Vector4(r, g, b, 1.0f)); pixels[x, y] = color; } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs index 584beb365..35b51fd95 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs @@ -3,7 +3,6 @@ using System; using System.Numerics; -using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -12,37 +11,56 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Implements the 'RGB' photometric interpretation (for all bit depths). /// - internal static class RgbTiffColor + internal class RgbTiffColor : TiffColorDecoder + where TPixel : unmanaged, IPixel { + private readonly float rFactor; + + private readonly float gFactor; + + private readonly float bFactor; + + private readonly uint bitsPerSampleR; + + private readonly uint bitsPerSampleG; + + private readonly uint bitsPerSampleB; + + public RgbTiffColor(ushort[] bitsPerSample) + : base(bitsPerSample, null) + { + this.bitsPerSampleR = bitsPerSample[0]; + this.bitsPerSampleG = bitsPerSample[1]; + this.bitsPerSampleB = bitsPerSample[2]; + + this.rFactor = (float)Math.Pow(2, this.bitsPerSampleR) - 1.0f; + this.gFactor = (float)Math.Pow(2, this.bitsPerSampleG) - 1.0f; + this.bFactor = (float)Math.Pow(2, this.bitsPerSampleB) - 1.0f; + } + /// /// Decodes pixel data using the current photometric interpretation. /// - /// The pixel format. /// The buffer to read image data from. - /// The number of bits per sample for each pixel. /// The image buffer to write pixels to. /// The x-coordinate of the left-hand side of the image block. /// The y-coordinate of the top of the image block. /// The width of the image block. /// The height of the image block. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decode(byte[] data, uint[] bitsPerSample, Buffer2D pixels, int left, int top, int width, int height) - where TPixel : unmanaged, IPixel + public override void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) { TPixel color = default(TPixel); BitReader bitReader = new BitReader(data); - float rFactor = (float)Math.Pow(2, bitsPerSample[0]) - 1.0f; - float gFactor = (float)Math.Pow(2, bitsPerSample[1]) - 1.0f; - float bFactor = (float)Math.Pow(2, bitsPerSample[2]) - 1.0f; for (int y = top; y < top + height; y++) { for (int x = left; x < left + width; x++) { - float r = ((float)bitReader.ReadBits(bitsPerSample[0])) / rFactor; - float g = ((float)bitReader.ReadBits(bitsPerSample[1])) / gFactor; - float b = ((float)bitReader.ReadBits(bitsPerSample[2])) / bFactor; + float r = ((float)bitReader.ReadBits(this.bitsPerSampleR)) / this.rFactor; + float g = ((float)bitReader.ReadBits(this.bitsPerSampleG)) / this.gFactor; + float b = ((float)bitReader.ReadBits(this.bitsPerSampleB)) / this.bFactor; + color.FromVector4(new Vector4(r, g, b, 1.0f)); pixels[x, y] = color; } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoder.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoder.cs new file mode 100644 index 000000000..8c4f7e9b5 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoder.cs @@ -0,0 +1,52 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Tiff +{ + /// + /// The base class for photometric interpretation decoders. + /// + /// The pixel format. + internal abstract class TiffColorDecoder + where TPixel : unmanaged, IPixel + { + private readonly ushort[] bitsPerSample; + + private readonly ushort[] colorMap; + + /// + /// Initializes a new instance of the class. + /// + /// The number of bits per sample for each pixel. + /// The RGB color lookup table to use for decoding the image. + protected TiffColorDecoder(ushort[] bitsPerSample, ushort[] colorMap) + { + this.bitsPerSample = bitsPerSample; + this.colorMap = colorMap; + } + + /* + /// + /// Gets the photometric interpretation value. + /// + /// + /// The photometric interpretation value. + /// + public TiffColorType ColorType { get; } + */ + + /// + /// Decodes source raw pixel data using the current photometric interpretation. + /// + /// The buffer to read image data from. + /// The image buffer to write pixels to. + /// The x-coordinate of the left-hand side of the image block. + /// The y-coordinate of the top of the image block. + /// The width of the image block. + /// The height of the image block. [MethodImpl(MethodImplOptions.AggressiveInlining)] + public abstract void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height); + } +} diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory.cs new file mode 100644 index 000000000..37bc96ef4 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory.cs @@ -0,0 +1,95 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Tiff +{ + internal static class TiffColorDecoderFactory + where TPixel : unmanaged, IPixel + { + public static TiffColorDecoder Create(TiffColorType colorType, ushort[] bitsPerSample, ushort[] colorMap) + { + switch (colorType) + { + case TiffColorType.WhiteIsZero: + DebugGuard.IsTrue(bitsPerSample.Length == 1, "bitsPerSample"); + DebugGuard.IsTrue(colorMap == null, "colorMap"); + return new WhiteIsZeroTiffColor(bitsPerSample); + + case TiffColorType.WhiteIsZero1: + DebugGuard.IsTrue(bitsPerSample.Length == 1 && bitsPerSample[0] == 1, "bitsPerSample"); + DebugGuard.IsTrue(colorMap == null, "colorMap"); + return new WhiteIsZero1TiffColor(); + + case TiffColorType.WhiteIsZero4: + DebugGuard.IsTrue(bitsPerSample.Length == 1 && bitsPerSample[0] == 4, "bitsPerSample"); + DebugGuard.IsTrue(colorMap == null, "colorMap"); + return new WhiteIsZero4TiffColor(); + + case TiffColorType.WhiteIsZero8: + DebugGuard.IsTrue(bitsPerSample.Length == 1 && bitsPerSample[0] == 8, "bitsPerSample"); + DebugGuard.IsTrue(colorMap == null, "colorMap"); + return new WhiteIsZero8TiffColor(); + + case TiffColorType.BlackIsZero: + DebugGuard.IsTrue(bitsPerSample.Length == 1, "bitsPerSample"); + DebugGuard.IsTrue(colorMap == null, "colorMap"); + return new BlackIsZeroTiffColor(bitsPerSample); + + case TiffColorType.BlackIsZero1: + DebugGuard.IsTrue(bitsPerSample.Length == 1 && bitsPerSample[0] == 1, "bitsPerSample"); + DebugGuard.IsTrue(colorMap == null, "colorMap"); + return new BlackIsZero1TiffColor(); + + case TiffColorType.BlackIsZero4: + DebugGuard.IsTrue(bitsPerSample.Length == 1 && bitsPerSample[0] == 4, "bitsPerSample"); + DebugGuard.IsTrue(colorMap == null, "colorMap"); + return new BlackIsZero4TiffColor(); + + case TiffColorType.BlackIsZero8: + DebugGuard.IsTrue(bitsPerSample.Length == 1 && bitsPerSample[0] == 8, "bitsPerSample"); + DebugGuard.IsTrue(colorMap == null, "colorMap"); + return new BlackIsZero8TiffColor(); + + case TiffColorType.Rgb: + DebugGuard.NotNull(bitsPerSample, "bitsPerSample"); + DebugGuard.IsTrue(colorMap == null, "colorMap"); + return new RgbTiffColor(bitsPerSample); + + case TiffColorType.Rgb888: + DebugGuard.IsTrue( + bitsPerSample.Length == 3 + && bitsPerSample[0] == 8 + && bitsPerSample[1] == 8 + && bitsPerSample[2] == 8, + "bitsPerSample"); + DebugGuard.IsTrue(colorMap == null, "colorMap"); + return new Rgb888TiffColor(); + + case TiffColorType.PaletteColor: + DebugGuard.NotNull(bitsPerSample, "bitsPerSample"); + DebugGuard.NotNull(colorMap, "colorMap"); + return new PaletteTiffColor(bitsPerSample, colorMap); + + default: + throw new InvalidOperationException(); + } + } + + public static RgbPlanarTiffColor CreatePlanar(TiffColorType colorType, ushort[] bitsPerSample, ushort[] colorMap) + { + switch (colorType) + { + case TiffColorType.RgbPlanar: + DebugGuard.NotNull(bitsPerSample, "bitsPerSample"); + DebugGuard.IsTrue(colorMap == null, "colorMap"); + return new RgbPlanarTiffColor(bitsPerSample); + + default: + throw new InvalidOperationException(); + } + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs index f58b37431..c86a5e76c 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Formats.Tiff diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs index 79784cbd9..666d03f9c 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -11,21 +10,24 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Implements the 'WhiteIsZero' photometric interpretation (optimised for bilevel images). /// - internal static class WhiteIsZero1TiffColor + internal class WhiteIsZero1TiffColor : TiffColorDecoder + where TPixel : unmanaged, IPixel { + public WhiteIsZero1TiffColor() + : base(null, null) + { + } + /// /// Decodes pixel data using the current photometric interpretation. /// - /// The pixel format. /// The buffer to read image data from. /// The image buffer to write pixels to. /// The x-coordinate of the left-hand side of the image block. /// The y-coordinate of the top of the image block. /// The width of the image block. /// The height of the image block. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) - where TPixel : unmanaged, IPixel + public override void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) { TPixel color = default(TPixel); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs index 061624349..9e5ce1f59 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using System.Runtime.CompilerServices; +using System; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -10,21 +10,24 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Implements the 'WhiteIsZero' photometric interpretation (optimised for 4-bit grayscale images). /// - internal static class WhiteIsZero4TiffColor + internal class WhiteIsZero4TiffColor : TiffColorDecoder + where TPixel : unmanaged, IPixel { + public WhiteIsZero4TiffColor() + : base(null, null) + { + } + /// /// Decodes pixel data using the current photometric interpretation. /// - /// The pixel format. /// The buffer to read image data from. /// The image buffer to write pixels to. /// The x-coordinate of the left-hand side of the image block. /// The y-coordinate of the top of the image block. /// The width of the image block. /// The height of the image block. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) - where TPixel : unmanaged, IPixel + public override void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) { TPixel color = default(TPixel); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs index eb8608173..4e3a0e443 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using System.Runtime.CompilerServices; +using System; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -10,21 +10,24 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Implements the 'WhiteIsZero' photometric interpretation (optimised for 8-bit grayscale images). /// - internal static class WhiteIsZero8TiffColor + internal class WhiteIsZero8TiffColor : TiffColorDecoder + where TPixel : unmanaged, IPixel { + public WhiteIsZero8TiffColor() + : base(null, null) + { + } + /// /// Decodes pixel data using the current photometric interpretation. /// - /// The pixel format. /// The buffer to read image data from. /// The image buffer to write pixels to. /// The x-coordinate of the left-hand side of the image block. /// The y-coordinate of the top of the image block. /// The width of the image block. /// The height of the image block. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) - where TPixel : unmanaged, IPixel + public override void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) { TPixel color = default(TPixel); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs index f8492a510..329454e9d 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs @@ -3,7 +3,6 @@ using System; using System.Numerics; -using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -12,34 +11,42 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Implements the 'WhiteIsZero' photometric interpretation (for all bit depths). /// - internal static class WhiteIsZeroTiffColor + internal class WhiteIsZeroTiffColor : TiffColorDecoder + where TPixel : unmanaged, IPixel { + private readonly ushort bitsPerSample0; + + private readonly float factor; + + public WhiteIsZeroTiffColor(ushort[] bitsPerSample) + : base(bitsPerSample, null) + { + this.bitsPerSample0 = bitsPerSample[0]; + this.factor = (float)Math.Pow(2, this.bitsPerSample0) - 1.0f; + } + /// /// Decodes pixel data using the current photometric interpretation. /// - /// The pixel format. /// The buffer to read image data from. - /// The number of bits per sample for each pixel. /// The image buffer to write pixels to. /// The x-coordinate of the left-hand side of the image block. /// The y-coordinate of the top of the image block. /// The width of the image block. /// The height of the image block. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decode(byte[] data, uint[] bitsPerSample, Buffer2D pixels, int left, int top, int width, int height) - where TPixel : unmanaged, IPixel + public override void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) { TPixel color = default(TPixel); BitReader bitReader = new BitReader(data); - float factor = (float)Math.Pow(2, bitsPerSample[0]) - 1.0f; for (int y = top; y < top + height; y++) { for (int x = left; x < left + width; x++) { - int value = bitReader.ReadBits(bitsPerSample[0]); - float intensity = 1.0f - (((float)value) / factor); + int value = bitReader.ReadBits(this.bitsPerSample0); + float intensity = 1.0f - (((float)value) / this.factor); + color.FromVector4(new Vector4(intensity, intensity, intensity, 1.0f)); pixels[x, y] = color; } diff --git a/src/ImageSharp/Formats/Tiff/Streams/TiffBigEndianStream.cs b/src/ImageSharp/Formats/Tiff/Streams/TiffBigEndianStream.cs new file mode 100644 index 000000000..157937055 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Streams/TiffBigEndianStream.cs @@ -0,0 +1,88 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; + +namespace SixLabors.ImageSharp.Formats.Tiff +{ + internal class TiffBigEndianStream : TiffStream + { + public TiffBigEndianStream(Stream stream) + : base(stream) + { + } + + public override TiffByteOrder ByteOrder => TiffByteOrder.BigEndian; + + /// + /// Converts buffer data into an using the correct endianness. + /// + /// The converted value. + public override short ReadInt16() + { + byte[] bytes = this.ReadBytes(2); + return (short)((bytes[0] << 8) | bytes[1]); + } + + /// + /// Converts buffer data into an using the correct endianness. + /// + /// The converted value. + public override int ReadInt32() + { + byte[] bytes = this.ReadBytes(4); + return (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]; + } + + /// + /// Converts buffer data into a using the correct endianness. + /// + /// The converted value. + public override uint ReadUInt32() + { + return (uint)this.ReadInt32(); + } + + /// + /// Converts buffer data into a using the correct endianness. + /// + /// The converted value. + public override ushort ReadUInt16() + { + return (ushort)this.ReadInt16(); + } + + /// + /// Converts buffer data into a using the correct endianness. + /// + /// The converted value. + public override float ReadSingle() + { + byte[] bytes = this.ReadBytes(4); + + if (BitConverter.IsLittleEndian) + { + Array.Reverse(bytes); + } + + return BitConverter.ToSingle(bytes, 0); + } + + /// + /// Converts buffer data into a using the correct endianness. + /// + /// The converted value. + public override double ReadDouble() + { + byte[] bytes = this.ReadBytes(8); + + if (BitConverter.IsLittleEndian) + { + Array.Reverse(bytes); + } + + return BitConverter.ToDouble(bytes, 0); + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/Streams/TiffLittleEndianStream.cs b/src/ImageSharp/Formats/Tiff/Streams/TiffLittleEndianStream.cs new file mode 100644 index 000000000..da6b8b8ef --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Streams/TiffLittleEndianStream.cs @@ -0,0 +1,88 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; + +namespace SixLabors.ImageSharp.Formats.Tiff +{ + internal class TiffLittleEndianStream : TiffStream + { + public TiffLittleEndianStream(Stream stream) + : base(stream) + { + } + + public override TiffByteOrder ByteOrder => TiffByteOrder.LittleEndian; + + /// + /// Converts buffer data into an using the correct endianness. + /// + /// The converted value. + public override short ReadInt16() + { + byte[] bytes = this.ReadBytes(2); + return (short)(bytes[0] | (bytes[1] << 8)); + } + + /// + /// Converts buffer data into an using the correct endianness. + /// + /// The converted value. + public override int ReadInt32() + { + byte[] bytes = this.ReadBytes(4); + return bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24); + } + + /// + /// Converts buffer data into a using the correct endianness. + /// + /// The converted value. + public override uint ReadUInt32() + { + return (uint)this.ReadInt32(); + } + + /// + /// Converts buffer data into a using the correct endianness. + /// + /// The converted value. + public override ushort ReadUInt16() + { + return (ushort)this.ReadInt16(); + } + + /// + /// Converts buffer data into a using the correct endianness. + /// + /// The converted value. + public override float ReadSingle() + { + byte[] bytes = this.ReadBytes(4); + + if (!BitConverter.IsLittleEndian) + { + Array.Reverse(bytes); + } + + return BitConverter.ToSingle(bytes, 0); + } + + /// + /// Converts buffer data into a using the correct endianness. + /// + /// The converted value. + public override double ReadDouble() + { + byte[] bytes = this.ReadBytes(8); + + if (!BitConverter.IsLittleEndian) + { + Array.Reverse(bytes); + } + + return BitConverter.ToDouble(bytes, 0); + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/Streams/TiffStream.cs b/src/ImageSharp/Formats/Tiff/Streams/TiffStream.cs new file mode 100644 index 000000000..0c62c01c3 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Streams/TiffStream.cs @@ -0,0 +1,94 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System.IO; + +namespace SixLabors.ImageSharp.Formats.Tiff +{ + /// + /// The tiff data stream base class. + /// + internal abstract class TiffStream + { + /// + /// The input stream. + /// + private readonly Stream stream; + + /// + /// Initializes a new instance of the class. + /// + /// The stream. + protected TiffStream(Stream stream) + { + this.stream = stream; + } + + /// + /// Gets a value indicating whether the file is encoded in little-endian or big-endian format. + /// + public abstract TiffByteOrder ByteOrder { get; } + + /// + /// Gets the input stream. + /// + public Stream InputStream => this.stream; + + /// + /// Gets the stream position. + /// + public long Position => this.stream.Position; + + public void Seek(uint offset) + { + this.stream.Seek(offset, SeekOrigin.Begin); + } + + public void Skip(uint offset) + { + this.stream.Seek(offset, SeekOrigin.Current); + } + + public void Skip(int offset) + { + this.stream.Seek(offset, SeekOrigin.Current); + } + + /// + /// Converts buffer data into a using the correct endianness. + /// + /// The converted value. + public byte ReadByte() + { + return (byte)this.stream.ReadByte(); + } + + /// + /// Converts buffer data into an using the correct endianness. + /// + /// The converted value. + public sbyte ReadSByte() + { + return (sbyte)this.stream.ReadByte(); + } + + public byte[] ReadBytes(uint count) + { + byte[] buf = new byte[count]; + this.stream.Read(buf, 0, buf.Length); + return buf; + } + + public abstract short ReadInt16(); + + public abstract int ReadInt32(); + + public abstract uint ReadUInt32(); + + public abstract ushort ReadUInt16(); + + public abstract float ReadSingle(); + + public abstract double ReadDouble(); + } +} diff --git a/src/ImageSharp/Formats/Tiff/Streams/TiffStreamFactory.cs b/src/ImageSharp/Formats/Tiff/Streams/TiffStreamFactory.cs new file mode 100644 index 000000000..7c9715380 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Streams/TiffStreamFactory.cs @@ -0,0 +1,59 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; + +namespace SixLabors.ImageSharp.Formats.Tiff +{ + /// + /// The tiff data stream factory class. + /// + internal static class TiffStreamFactory + { + public static TiffStream CreateBySignature(Stream stream) + { + TiffByteOrder order = ReadByteOrder(stream); + return Create(order, stream); + } + + /// + /// Creates the specified byte order. + /// + /// The byte order. + /// The stream. + public static TiffStream Create(TiffByteOrder byteOrder, Stream stream) + { + if (byteOrder == TiffByteOrder.BigEndian) + { + return new TiffBigEndianStream(stream); + } + else if (byteOrder == TiffByteOrder.LittleEndian) + { + return new TiffLittleEndianStream(stream); + } + + throw new ArgumentOutOfRangeException(nameof(byteOrder)); + } + + /// + /// Reads the byte order of stream. + /// + /// The stream. + private static TiffByteOrder ReadByteOrder(Stream stream) + { + byte[] headerBytes = new byte[2]; + stream.Read(headerBytes, 0, 2); + if (headerBytes[0] == TiffConstants.ByteOrderLittleEndian && headerBytes[1] == TiffConstants.ByteOrderLittleEndian) + { + return TiffByteOrder.LittleEndian; + } + else if (headerBytes[0] == TiffConstants.ByteOrderBigEndian && headerBytes[1] == TiffConstants.ByteOrderBigEndian) + { + return TiffByteOrder.BigEndian; + } + + throw new ImageFormatException("Invalid TIFF file header."); + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs index e4bfc666a..74d11f1d4 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Image decoder for generating an image out of a TIFF stream. /// - public class TiffDecoder : IImageDecoder, ITiffDecoderOptions + public class TiffDecoder : IImageDecoder, ITiffDecoderOptions, IImageInfoDetector { /// /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. @@ -24,17 +24,14 @@ namespace SixLabors.ImageSharp.Formats.Tiff { Guard.NotNull(stream, "stream"); - using (var decoder = new TiffDecoderCore(configuration, this)) + using (var decoder = new TiffDecoderCore(stream, configuration, this)) { - return decoder.Decode(stream); + return decoder.Decode(); } } /// - public Image Decode(Configuration configuration, Stream stream) - { - throw new System.NotImplementedException(); - } + public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); /// public Task> DecodeAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken) @@ -48,5 +45,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff { throw new System.NotImplementedException(); } + + /// + public IImageInfo Identify(Configuration configuration, Stream stream) + { + throw new System.NotImplementedException(); + } + + /// + public Task IdentifyAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken) + { + throw new System.NotImplementedException(); + } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index f134376ff..0d089287f 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -3,9 +3,14 @@ using System; using System.Buffers; +using System.Collections.Generic; using System.IO; -using System.Text; +using System.Linq; +using System.Threading; +using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Metadata; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Tiff @@ -13,7 +18,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Performs the tiff decoding operation. /// - internal class TiffDecoderCore : IDisposable + internal class TiffDecoderCore : IImageDecoderInternals, IDisposable { /// /// The global configuration @@ -30,7 +35,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// The configuration. /// The decoder options. - public TiffDecoderCore(Configuration configuration, ITiffDecoderOptions options) + private TiffDecoderCore(Configuration configuration, ITiffDecoderOptions options) { options = options ?? new TiffDecoder(); @@ -41,26 +46,42 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Initializes a new instance of the class. /// - /// The input stream. - /// A flag indicating if the file is encoded in little-endian or big-endian format. + /// The stream. + /// The configuration. /// The decoder options. + public TiffDecoderCore(Stream stream, Configuration configuration, ITiffDecoderOptions options) + : this(configuration, options) + { + this.Stream = TiffStreamFactory.CreateBySignature(stream); + } + + /// + /// Initializes a new instance of the class. + /// + /// The byte order. + /// The input stream. /// The configuration. - public TiffDecoderCore(Stream stream, bool isLittleEndian, Configuration configuration, ITiffDecoderOptions options) + /// The decoder options. + public TiffDecoderCore(TiffByteOrder byteOrder, Stream stream, Configuration configuration, ITiffDecoderOptions options) : this(configuration, options) { - this.InputStream = stream; - this.IsLittleEndian = isLittleEndian; + this.Stream = TiffStreamFactory.Create(byteOrder, stream); } + /// + /// Gets the input stream. + /// + public TiffStream Stream { get; } + /// /// Gets or sets the number of bits for each sample of the pixel format used to encode the image. /// - public uint[] BitsPerSample { get; set; } + public ushort[] BitsPerSample { get; set; } /// /// Gets or sets the lookup table for RGB palette colored images. /// - public uint[] ColorMap { get; set; } + public ushort[] ColorMap { get; set; } /// /// Gets or sets the photometric interpretation implementation to use when decoding the image. @@ -72,451 +93,94 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// public TiffCompressionType CompressionType { get; set; } - /// - /// Gets the input stream. - /// - public Stream InputStream { get; private set; } - - /// - /// Gets a value indicating whether the file is encoded in little-endian or big-endian format. - /// - public bool IsLittleEndian { get; private set; } - /// /// Gets or sets the planar configuration type to use when decoding the image. /// public TiffPlanarConfiguration PlanarConfiguration { get; set; } /// - /// Calculates the size (in bytes) of the data contained within an IFD entry. + /// Gets or sets the photometric interpretation. /// - /// The IFD entry to calculate the size for. - /// The size of the data (in bytes). - public static uint GetSizeOfData(TiffIfdEntry entry) => SizeOfDataType(entry.Type) * entry.Count; + public TiffPhotometricInterpretation PhotometricInterpretation { get; set; } + + /// + public Configuration Configuration => this.configuration; + + /// + public Size Dimensions { get; private set; } /// /// Decodes the image from the specified and sets /// the data to image. /// /// The pixel format. - /// The stream, where the image should be. /// The decoded image. - public Image Decode(Stream stream) + public Image Decode() where TPixel : unmanaged, IPixel { - this.InputStream = stream; - - uint firstIfdOffset = this.ReadHeader(); - TiffIfd firstIfd = this.ReadIfd(firstIfdOffset); - Image image = this.DecodeImage(firstIfd); - - return image; - } - - /// - /// Dispose - /// - public void Dispose() - { - } + var reader = new DirectoryReader(this.Stream); + IEnumerable directories = reader.Read(); - /// - /// Reads the TIFF header from the input stream. - /// - /// The byte offset to the first IFD in the file. - /// - /// Thrown if the TIFF file header is invalid. - /// - public uint ReadHeader() - { - byte[] headerBytes = new byte[TiffConstants.SizeOfTiffHeader]; - this.InputStream.ReadFull(headerBytes, TiffConstants.SizeOfTiffHeader); - - if (headerBytes[0] == TiffConstants.ByteOrderLittleEndian && headerBytes[1] == TiffConstants.ByteOrderLittleEndian) - { - this.IsLittleEndian = true; - } - else if (headerBytes[0] != TiffConstants.ByteOrderBigEndian && headerBytes[1] != TiffConstants.ByteOrderBigEndian) + var frames = new List>(); + foreach (IExifValue[] ifd in directories) { - throw new ImageFormatException("Invalid TIFF file header."); + ImageFrame frame = this.DecodeFrame(ifd); + frames.Add(frame); } - if (this.ToUInt16(headerBytes, 2) != TiffConstants.HeaderMagicNumber) - { - throw new ImageFormatException("Invalid TIFF file header."); - } + ImageMetadata metadata = frames.CreateMetadata(this.ignoreMetadata, this.Stream.ByteOrder); - uint firstIfdOffset = this.ToUInt32(headerBytes, 4); - if (firstIfdOffset == 0) + // todo: tiff frames can have different sizes { - throw new ImageFormatException("Invalid TIFF file header."); - } - - return firstIfdOffset; - } - - /// - /// Reads a from the input stream. - /// - /// The byte offset within the file to find the IFD. - /// A containing the retrieved data. - public TiffIfd ReadIfd(uint offset) - { - this.InputStream.Seek(offset, SeekOrigin.Begin); - - byte[] buffer = new byte[TiffConstants.SizeOfIfdEntry]; - - this.InputStream.ReadFull(buffer, 2); - ushort entryCount = this.ToUInt16(buffer, 0); - - TiffIfdEntry[] entries = new TiffIfdEntry[entryCount]; - for (int i = 0; i < entryCount; i++) - { - this.InputStream.ReadFull(buffer, TiffConstants.SizeOfIfdEntry); - - ushort tag = this.ToUInt16(buffer, 0); - TiffType type = (TiffType)this.ToUInt16(buffer, 2); - uint count = this.ToUInt32(buffer, 4); - byte[] value = new byte[] { buffer[8], buffer[9], buffer[10], buffer[11] }; - - entries[i] = new TiffIfdEntry(tag, type, count, value); + var root = frames.First(); + this.Dimensions = root.Size(); + foreach (var frame in frames) + { + if (frame.Size() != root.Size()) + { + throw new NotSupportedException("Images with different sizes are not supported"); + } + } } - this.InputStream.ReadFull(buffer, 4); - uint nextIfdOffset = this.ToUInt32(buffer, 0); + var image = new Image(this.configuration, metadata, frames); - return new TiffIfd(entries, nextIfdOffset); + return image; } /// - /// Decodes the image data from a specified IFD. + /// Dispose /// - /// The pixel format. - /// The IFD to read the image from. - /// The decoded image. - public Image DecodeImage(TiffIfd ifd) - where TPixel : unmanaged, IPixel + public void Dispose() { - if (!ifd.TryGetIfdEntry(TiffTags.ImageLength, out TiffIfdEntry imageLengthEntry) - || !ifd.TryGetIfdEntry(TiffTags.ImageWidth, out TiffIfdEntry imageWidthEntry)) - { - throw new ImageFormatException("The TIFF IFD does not specify the image dimensions."); - } - - int width = (int)this.ReadUnsignedInteger(ref imageWidthEntry); - int height = (int)this.ReadUnsignedInteger(ref imageLengthEntry); - - Image image = new Image(this.configuration, width, height); - - this.ReadMetadata(ifd, image); - this.ReadImageFormat(ifd); - - if (ifd.TryGetIfdEntry(TiffTags.RowsPerStrip, out TiffIfdEntry rowsPerStripEntry) - && ifd.TryGetIfdEntry(TiffTags.StripOffsets, out TiffIfdEntry stripOffsetsEntry) - && ifd.TryGetIfdEntry(TiffTags.StripByteCounts, out TiffIfdEntry stripByteCountsEntry)) - { - int rowsPerStrip = (int)this.ReadUnsignedInteger(ref rowsPerStripEntry); - uint[] stripOffsets = this.ReadUnsignedIntegerArray(ref stripOffsetsEntry); - uint[] stripByteCounts = this.ReadUnsignedIntegerArray(ref stripByteCountsEntry); - this.DecodeImageStrips(image, rowsPerStrip, stripOffsets, stripByteCounts); - } - - return image; + // nothing } /// - /// Reads the image metadata from a specified IFD. + /// Decodes the image data from a specified IFD. /// /// The pixel format. - /// The IFD to read the image from. - /// The image to write the metadata to. - public void ReadMetadata(TiffIfd ifd, Image image) + /// The IFD tags. + private ImageFrame DecodeFrame(IExifValue[] tags) where TPixel : unmanaged, IPixel { - TiffResolutionUnit resolutionUnit = (TiffResolutionUnit)this.ReadUnsignedInteger(ifd, TiffTags.ResolutionUnit, (uint)TiffResolutionUnit.Inch); - - if (resolutionUnit != TiffResolutionUnit.None) - { - double resolutionUnitFactor = resolutionUnit == TiffResolutionUnit.Centimeter ? 2.54 : 1.0; - - if (ifd.TryGetIfdEntry(TiffTags.XResolution, out TiffIfdEntry xResolutionEntry)) - { - Rational xResolution = this.ReadUnsignedRational(ref xResolutionEntry); - image.Metadata.HorizontalResolution = xResolution.ToDouble() * resolutionUnitFactor; - } - - if (ifd.TryGetIfdEntry(TiffTags.YResolution, out TiffIfdEntry yResolutionEntry)) - { - Rational yResolution = this.ReadUnsignedRational(ref yResolutionEntry); - image.Metadata.VerticalResolution = yResolution.ToDouble() * resolutionUnitFactor; - } - } + var coreMetadata = new ImageFrameMetadata(); + TiffFrameMetadata metadata = coreMetadata.GetTiffMetadata(); + metadata.Tags = tags; - if (!this.ignoreMetadata) - { - /* - if (ifd.TryGetIfdEntry(TiffTags.Artist, out TiffIfdEntry artistEntry)) - { - image.Metadata.Properties.Add(new ImageProperty(TiffMetadataNames.Artist, this.ReadString(ref artistEntry))); - } + this.VerifyAndParseOptions(metadata); - if (ifd.TryGetIfdEntry(TiffTags.Copyright, out TiffIfdEntry copyrightEntry)) - { - image.Metadata.Properties.Add(new ImageProperty(TiffMetadataNames.Copyright, this.ReadString(ref copyrightEntry))); - } + int width = (int)metadata.Width; + int height = (int)metadata.Height; + var frame = new ImageFrame(this.configuration, width, height, coreMetadata); - if (ifd.TryGetIfdEntry(TiffTags.DateTime, out TiffIfdEntry dateTimeEntry)) - { - image.Metadata.Properties.Add(new ImageProperty(TiffMetadataNames.DateTime, this.ReadString(ref dateTimeEntry))); - } + int rowsPerStrip = (int)metadata.RowsPerStrip; + uint[] stripOffsets = metadata.StripOffsets; + uint[] stripByteCounts = metadata.StripByteCounts; - if (ifd.TryGetIfdEntry(TiffTags.HostComputer, out TiffIfdEntry hostComputerEntry)) - { - image.Metadata.Properties.Add(new ImageProperty(TiffMetadataNames.HostComputer, this.ReadString(ref hostComputerEntry))); - } - - if (ifd.TryGetIfdEntry(TiffTags.ImageDescription, out TiffIfdEntry imageDescriptionEntry)) - { - image.Metadata.Properties.Add(new ImageProperty(TiffMetadataNames.ImageDescription, this.ReadString(ref imageDescriptionEntry))); - } - - if (ifd.TryGetIfdEntry(TiffTags.Make, out TiffIfdEntry makeEntry)) - { - image.Metadata.Properties.Add(new ImageProperty(TiffMetadataNames.Make, this.ReadString(ref makeEntry))); - } + this.DecodeImageStrips(frame, rowsPerStrip, stripOffsets, stripByteCounts); - if (ifd.TryGetIfdEntry(TiffTags.Model, out TiffIfdEntry modelEntry)) - { - image.Metadata.Properties.Add(new ImageProperty(TiffMetadataNames.Model, this.ReadString(ref modelEntry))); - } - - if (ifd.TryGetIfdEntry(TiffTags.Software, out TiffIfdEntry softwareEntry)) - { - image.Metadata.Properties.Add(new ImageProperty(TiffMetadataNames.Software, this.ReadString(ref softwareEntry))); - } - */ - } - } - - /// - /// Determines the TIFF compression and color types, and reads any associated parameters. - /// - /// The IFD to read the image format information for. - public void ReadImageFormat(TiffIfd ifd) - { - TiffCompression compression = (TiffCompression)this.ReadUnsignedInteger(ifd, TiffTags.Compression, (uint)TiffCompression.None); - - switch (compression) - { - case TiffCompression.None: - { - this.CompressionType = TiffCompressionType.None; - break; - } - - case TiffCompression.PackBits: - { - this.CompressionType = TiffCompressionType.PackBits; - break; - } - - case TiffCompression.Deflate: - case TiffCompression.OldDeflate: - { - this.CompressionType = TiffCompressionType.Deflate; - break; - } - - case TiffCompression.Lzw: - { - this.CompressionType = TiffCompressionType.Lzw; - break; - } - - default: - { - throw new NotSupportedException("The specified TIFF compression format is not supported."); - } - } - - this.PlanarConfiguration = (TiffPlanarConfiguration)this.ReadUnsignedInteger(ifd, TiffTags.PlanarConfiguration, (uint)TiffPlanarConfiguration.Chunky); - - TiffPhotometricInterpretation photometricInterpretation; - - if (ifd.TryGetIfdEntry(TiffTags.PhotometricInterpretation, out TiffIfdEntry photometricInterpretationEntry)) - { - photometricInterpretation = (TiffPhotometricInterpretation)this.ReadUnsignedInteger(ref photometricInterpretationEntry); - } - else - { - if (compression == TiffCompression.Ccitt1D) - { - photometricInterpretation = TiffPhotometricInterpretation.WhiteIsZero; - } - else - { - throw new ImageFormatException("The TIFF photometric interpretation entry is missing."); - } - } - - if (ifd.TryGetIfdEntry(TiffTags.BitsPerSample, out TiffIfdEntry bitsPerSampleEntry)) - { - this.BitsPerSample = this.ReadUnsignedIntegerArray(ref bitsPerSampleEntry); - } - else - { - if (photometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero || - photometricInterpretation == TiffPhotometricInterpretation.BlackIsZero) - { - this.BitsPerSample = new[] { 1u }; - } - else - { - throw new ImageFormatException("The TIFF BitsPerSample entry is missing."); - } - } - - switch (photometricInterpretation) - { - case TiffPhotometricInterpretation.WhiteIsZero: - { - if (this.BitsPerSample.Length == 1) - { - switch (this.BitsPerSample[0]) - { - case 8: - { - this.ColorType = TiffColorType.WhiteIsZero8; - break; - } - - case 4: - { - this.ColorType = TiffColorType.WhiteIsZero4; - break; - } - - case 1: - { - this.ColorType = TiffColorType.WhiteIsZero1; - break; - } - - default: - { - this.ColorType = TiffColorType.WhiteIsZero; - break; - } - } - } - else - { - throw new NotSupportedException("The number of samples in the TIFF BitsPerSample entry is not supported."); - } - - break; - } - - case TiffPhotometricInterpretation.BlackIsZero: - { - if (this.BitsPerSample.Length == 1) - { - switch (this.BitsPerSample[0]) - { - case 8: - { - this.ColorType = TiffColorType.BlackIsZero8; - break; - } - - case 4: - { - this.ColorType = TiffColorType.BlackIsZero4; - break; - } - - case 1: - { - this.ColorType = TiffColorType.BlackIsZero1; - break; - } - - default: - { - this.ColorType = TiffColorType.BlackIsZero; - break; - } - } - } - else - { - throw new NotSupportedException("The number of samples in the TIFF BitsPerSample entry is not supported."); - } - - break; - } - - case TiffPhotometricInterpretation.Rgb: - { - if (this.BitsPerSample.Length == 3) - { - if (this.PlanarConfiguration == TiffPlanarConfiguration.Chunky) - { - if (this.BitsPerSample[0] == 8 && this.BitsPerSample[1] == 8 && this.BitsPerSample[2] == 8) - { - this.ColorType = TiffColorType.Rgb888; - } - else - { - this.ColorType = TiffColorType.Rgb; - } - } - else - { - this.ColorType = TiffColorType.RgbPlanar; - } - } - else - { - throw new NotSupportedException("The number of samples in the TIFF BitsPerSample entry is not supported."); - } - - break; - } - - case TiffPhotometricInterpretation.PaletteColor: - { - if (ifd.TryGetIfdEntry(TiffTags.ColorMap, out TiffIfdEntry colorMapEntry)) - { - this.ColorMap = this.ReadUnsignedIntegerArray(ref colorMapEntry); - - if (this.BitsPerSample.Length == 1) - { - switch (this.BitsPerSample[0]) - { - default: - { - this.ColorType = TiffColorType.PaletteColor; - break; - } - } - } - else - { - throw new NotSupportedException("The number of samples in the TIFF BitsPerSample entry is not supported."); - } - } - else - { - throw new ImageFormatException("The TIFF ColorMap entry is missing for a pallete color image."); - } - - break; - } - - default: - throw new NotSupportedException("The specified TIFF photometric interpretation is not supported."); - } + return frame; } /// @@ -526,7 +190,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The height for the desired pixel buffer. /// The index of the plane for planar image configuration (or zero for chunky). /// The size (in bytes) of the required pixel buffer. - public int CalculateImageBufferSize(int width, int height, int plane) + private int CalculateImageBufferSize(int width, int height, int plane) { uint bitsPerPixel = 0; @@ -546,724 +210,60 @@ namespace SixLabors.ImageSharp.Formats.Tiff return bytesPerRow * height; } - /// - /// Decompresses an image block from the input stream into the specified buffer. - /// - /// The offset within the file of the image block. - /// The size (in bytes) of the compressed data. - /// The buffer to write the uncompressed data. - public void DecompressImageBlock(uint offset, uint byteCount, byte[] buffer) - { - this.InputStream.Seek(offset, SeekOrigin.Begin); - - switch (this.CompressionType) - { - case TiffCompressionType.None: - NoneTiffCompression.Decompress(this.InputStream, (int)byteCount, buffer); - break; - case TiffCompressionType.PackBits: - PackBitsTiffCompression.Decompress(this.InputStream, (int)byteCount, buffer); - break; - case TiffCompressionType.Deflate: - DeflateTiffCompression.Decompress(this.InputStream, (int)byteCount, buffer); - break; - case TiffCompressionType.Lzw: - LzwTiffCompression.Decompress(this.InputStream, (int)byteCount, buffer); - break; - default: - throw new InvalidOperationException(); - } - } - - /// - /// Decodes pixel data using the current photometric interpretation (chunky configuration). - /// - /// The pixel format. - /// The buffer to read image data from. - /// The image buffer to write pixels to. - /// The x-coordinate of the left-hand side of the image block. - /// The y-coordinate of the top of the image block. - /// The width of the image block. - /// The height of the image block. - public void ProcessImageBlockChunky(byte[] data, Buffer2D pixels, int left, int top, int width, int height) - where TPixel : unmanaged, IPixel - { - switch (this.ColorType) - { - case TiffColorType.WhiteIsZero: - WhiteIsZeroTiffColor.Decode(data, this.BitsPerSample, pixels, left, top, width, height); - break; - case TiffColorType.WhiteIsZero1: - WhiteIsZero1TiffColor.Decode(data, pixels, left, top, width, height); - break; - case TiffColorType.WhiteIsZero4: - WhiteIsZero4TiffColor.Decode(data, pixels, left, top, width, height); - break; - case TiffColorType.WhiteIsZero8: - WhiteIsZero8TiffColor.Decode(data, pixels, left, top, width, height); - break; - case TiffColorType.BlackIsZero: - BlackIsZeroTiffColor.Decode(data, this.BitsPerSample, pixels, left, top, width, height); - break; - case TiffColorType.BlackIsZero1: - BlackIsZero1TiffColor.Decode(data, pixels, left, top, width, height); - break; - case TiffColorType.BlackIsZero4: - BlackIsZero4TiffColor.Decode(data, pixels, left, top, width, height); - break; - case TiffColorType.BlackIsZero8: - BlackIsZero8TiffColor.Decode(data, pixels, left, top, width, height); - break; - case TiffColorType.Rgb: - RgbTiffColor.Decode(data, this.BitsPerSample, pixels, left, top, width, height); - break; - case TiffColorType.Rgb888: - Rgb888TiffColor.Decode(data, pixels, left, top, width, height); - break; - case TiffColorType.PaletteColor: - PaletteTiffColor.Decode(data, this.BitsPerSample, this.ColorMap, pixels, left, top, width, height); - break; - default: - throw new InvalidOperationException(); - } - } - - /// - /// Decodes pixel data using the current photometric interpretation (planar configuration). - /// - /// The pixel format. - /// The buffer to read image data from. - /// The image buffer to write pixels to. - /// The x-coordinate of the left-hand side of the image block. - /// The y-coordinate of the top of the image block. - /// The width of the image block. - /// The height of the image block. - public void ProcessImageBlockPlanar(byte[][] data, Buffer2D pixels, int left, int top, int width, int height) - where TPixel : unmanaged, IPixel - { - switch (this.ColorType) - { - case TiffColorType.RgbPlanar: - RgbPlanarTiffColor.Decode(data, this.BitsPerSample, pixels, left, top, width, height); - break; - default: - throw new InvalidOperationException(); - } - } - - /// - /// Reads the data from a as an array of bytes. - /// - /// The to read. - /// The data. - public byte[] ReadBytes(ref TiffIfdEntry entry) - { - uint byteLength = GetSizeOfData(entry); - - if (entry.Value.Length < byteLength) - { - uint offset = this.ToUInt32(entry.Value, 0); - this.InputStream.Seek(offset, SeekOrigin.Begin); - - byte[] data = new byte[byteLength]; - this.InputStream.ReadFull(data, (int)byteLength); - entry.Value = data; - } - - return entry.Value; - } - - /// - /// Reads the data from a as an unsigned integer value. - /// - /// The to read. - /// The data. - /// - /// Thrown if the data-type specified by the file cannot be converted to a , or if - /// there is an array of items. - /// - public uint ReadUnsignedInteger(ref TiffIfdEntry entry) - { - if (entry.Count != 1) - { - throw new ImageFormatException($"Cannot read a single value from an array of multiple items."); - } - - switch (entry.Type) - { - case TiffType.Byte: - return (uint)this.ToByte(entry.Value, 0); - case TiffType.Short: - return (uint)this.ToUInt16(entry.Value, 0); - case TiffType.Long: - return this.ToUInt32(entry.Value, 0); - default: - throw new ImageFormatException($"A value of type '{entry.Type}' cannot be converted to an unsigned integer."); - } - } - - /// - /// Reads the data for a specified tag of a as an unsigned integer value. - /// - /// The to read from. - /// The tag ID to search for. - /// The default value if the entry is missing - /// The data. - /// - /// Thrown if the data-type specified by the file cannot be converted to a , or if - /// there is an array of items. - /// - public uint ReadUnsignedInteger(TiffIfd ifd, ushort tag, uint defaultValue) - { - if (ifd.TryGetIfdEntry(tag, out TiffIfdEntry entry)) - { - return this.ReadUnsignedInteger(ref entry); - } - else - { - return defaultValue; - } - } - - /// - /// Reads the data from a as a signed integer value. - /// - /// The to read. - /// The data. - /// - /// Thrown if the data-type specified by the file cannot be converted to an , or if - /// there is an array of items. - /// - public int ReadSignedInteger(ref TiffIfdEntry entry) - { - if (entry.Count != 1) - { - throw new ImageFormatException($"Cannot read a single value from an array of multiple items."); - } - - switch (entry.Type) - { - case TiffType.SByte: - return (int)this.ToSByte(entry.Value, 0); - case TiffType.SShort: - return (int)this.ToInt16(entry.Value, 0); - case TiffType.SLong: - return this.ToInt32(entry.Value, 0); - default: - throw new ImageFormatException($"A value of type '{entry.Type}' cannot be converted to a signed integer."); - } - } - - /// - /// Reads the data from a as an array of unsigned integer values. - /// - /// The to read. - /// The data. - /// - /// Thrown if the data-type specified by the file cannot be converted to a . - /// - public uint[] ReadUnsignedIntegerArray(ref TiffIfdEntry entry) - { - byte[] bytes = this.ReadBytes(ref entry); - uint[] result = new uint[entry.Count]; - - switch (entry.Type) - { - case TiffType.Byte: - { - for (int i = 0; i < result.Length; i++) - { - result[i] = (uint)this.ToByte(bytes, i); - } - - break; - } - - case TiffType.Short: - { - for (int i = 0; i < result.Length; i++) - { - result[i] = (uint)this.ToUInt16(bytes, i * TiffConstants.SizeOfShort); - } - - break; - } - - case TiffType.Long: - { - for (int i = 0; i < result.Length; i++) - { - result[i] = this.ToUInt32(bytes, i * TiffConstants.SizeOfLong); - } - - break; - } - - default: - throw new ImageFormatException($"A value of type '{entry.Type}' cannot be converted to an unsigned integer."); - } - - return result; - } - - /// - /// Reads the data from a as an array of signed integer values. - /// - /// The to read. - /// The data. - /// - /// Thrown if the data-type specified by the file cannot be converted to an . - /// - public int[] ReadSignedIntegerArray(ref TiffIfdEntry entry) - { - byte[] bytes = this.ReadBytes(ref entry); - int[] result = new int[entry.Count]; - - switch (entry.Type) - { - case TiffType.SByte: - { - for (int i = 0; i < result.Length; i++) - { - result[i] = (int)this.ToSByte(bytes, i); - } - - break; - } - - case TiffType.SShort: - { - for (int i = 0; i < result.Length; i++) - { - result[i] = (int)this.ToInt16(bytes, i * TiffConstants.SizeOfShort); - } - - break; - } - - case TiffType.SLong: - { - for (int i = 0; i < result.Length; i++) - { - result[i] = this.ToInt32(bytes, i * TiffConstants.SizeOfLong); - } - - break; - } - - default: - throw new ImageFormatException($"A value of type '{entry.Type}' cannot be converted to a signed integer."); - } - - return result; - } - - /// - /// Reads the data from a as a value. - /// - /// The to read. - /// The data. - /// - /// Thrown if the data-type specified by the file cannot be converted to a . - /// - public string ReadString(ref TiffIfdEntry entry) - { - if (entry.Type != TiffType.Ascii) - { - throw new ImageFormatException($"A value of type '{entry.Type}' cannot be converted to a string."); - } - - byte[] bytes = this.ReadBytes(ref entry); - - if (bytes[entry.Count - 1] != 0) - { - throw new ImageFormatException("The retrieved string is not null terminated."); - } - - return Encoding.UTF8.GetString(bytes, 0, (int)entry.Count - 1); - } - - /// - /// Reads the data from a as a value. - /// - /// The to read. - /// The data. - /// - /// Thrown if the data-type specified by the file cannot be converted to a , or if - /// there is an array of items. - /// - public Rational ReadUnsignedRational(ref TiffIfdEntry entry) - { - if (entry.Count != 1) - { - throw new ImageFormatException($"Cannot read a single value from an array of multiple items."); - } - - return this.ReadUnsignedRationalArray(ref entry)[0]; - } - - /// - /// Reads the data from a as a value. - /// - /// The to read. - /// The data. - /// - /// Thrown if the data-type specified by the file cannot be converted to a , or if - /// there is an array of items. - /// - public SignedRational ReadSignedRational(ref TiffIfdEntry entry) - { - if (entry.Count != 1) - { - throw new ImageFormatException($"Cannot read a single value from an array of multiple items."); - } - - return this.ReadSignedRationalArray(ref entry)[0]; - } - - /// - /// Reads the data from a as an array of values. - /// - /// The to read. - /// The data. - /// - /// Thrown if the data-type specified by the file cannot be converted to a . - /// - public Rational[] ReadUnsignedRationalArray(ref TiffIfdEntry entry) - { - if (entry.Type != TiffType.Rational) - { - throw new ImageFormatException($"A value of type '{entry.Type}' cannot be converted to a Rational."); - } - - byte[] bytes = this.ReadBytes(ref entry); - Rational[] result = new Rational[entry.Count]; - - for (int i = 0; i < result.Length; i++) - { - uint numerator = this.ToUInt32(bytes, i * TiffConstants.SizeOfRational); - uint denominator = this.ToUInt32(bytes, (i * TiffConstants.SizeOfRational) + TiffConstants.SizeOfLong); - result[i] = new Rational(numerator, denominator); - } - - return result; - } - - /// - /// Reads the data from a as an array of values. - /// - /// The to read. - /// The data. - /// - /// Thrown if the data-type specified by the file cannot be converted to a . - /// - public SignedRational[] ReadSignedRationalArray(ref TiffIfdEntry entry) - { - if (entry.Type != TiffType.SRational) - { - throw new ImageFormatException($"A value of type '{entry.Type}' cannot be converted to a SignedRational."); - } - - byte[] bytes = this.ReadBytes(ref entry); - SignedRational[] result = new SignedRational[entry.Count]; - - for (int i = 0; i < result.Length; i++) - { - int numerator = this.ToInt32(bytes, i * TiffConstants.SizeOfRational); - int denominator = this.ToInt32(bytes, (i * TiffConstants.SizeOfRational) + TiffConstants.SizeOfLong); - result[i] = new SignedRational(numerator, denominator); - } - - return result; - } - - /// - /// Reads the data from a as a value. - /// - /// The to read. - /// The data. - /// - /// Thrown if the data-type specified by the file cannot be converted to a , or if - /// there is an array of items. - /// - public float ReadFloat(ref TiffIfdEntry entry) - { - if (entry.Count != 1) - { - throw new ImageFormatException($"Cannot read a single value from an array of multiple items."); - } - - if (entry.Type != TiffType.Float) - { - throw new ImageFormatException($"A value of type '{entry.Type}' cannot be converted to a float."); - } - - return this.ToSingle(entry.Value, 0); - } - - /// - /// Reads the data from a as a value. - /// - /// The to read. - /// The data. - /// - /// Thrown if the data-type specified by the file cannot be converted to a , or if - /// there is an array of items. - /// - public double ReadDouble(ref TiffIfdEntry entry) - { - if (entry.Count != 1) - { - throw new ImageFormatException($"Cannot read a single value from an array of multiple items."); - } - - return this.ReadDoubleArray(ref entry)[0]; - } - - /// - /// Reads the data from a as an array of values. - /// - /// The to read. - /// The data. - /// - /// Thrown if the data-type specified by the file cannot be converted to a . - /// - public float[] ReadFloatArray(ref TiffIfdEntry entry) - { - if (entry.Type != TiffType.Float) - { - throw new ImageFormatException($"A value of type '{entry.Type}' cannot be converted to a float."); - } - - byte[] bytes = this.ReadBytes(ref entry); - float[] result = new float[entry.Count]; - - for (int i = 0; i < result.Length; i++) - { - result[i] = this.ToSingle(bytes, i * TiffConstants.SizeOfFloat); - } - - return result; - } - - /// - /// Reads the data from a as an array of values. - /// - /// The to read. - /// The data. - /// - /// Thrown if the data-type specified by the file cannot be converted to a . - /// - public double[] ReadDoubleArray(ref TiffIfdEntry entry) - { - if (entry.Type != TiffType.Double) - { - throw new ImageFormatException($"A value of type '{entry.Type}' cannot be converted to a double."); - } - - byte[] bytes = this.ReadBytes(ref entry); - double[] result = new double[entry.Count]; - - for (int i = 0; i < result.Length; i++) - { - result[i] = this.ToDouble(bytes, i * TiffConstants.SizeOfDouble); - } - - return result; - } - - /// - /// Calculates the size (in bytes) for the specified TIFF data-type. - /// - /// The data-type to calculate the size for. - /// The size of the data-type (in bytes). - private static uint SizeOfDataType(TiffType type) - { - switch (type) - { - case TiffType.Byte: - case TiffType.Ascii: - case TiffType.SByte: - case TiffType.Undefined: - return 1u; - case TiffType.Short: - case TiffType.SShort: - return 2u; - case TiffType.Long: - case TiffType.SLong: - case TiffType.Float: - case TiffType.Ifd: - return 4u; - case TiffType.Rational: - case TiffType.SRational: - case TiffType.Double: - return 8u; - default: - return 0u; - } - } - - /// - /// Converts buffer data into an using the correct endianness. - /// - /// The buffer. - /// The byte offset within the buffer. - /// The converted value. - private sbyte ToSByte(byte[] bytes, int offset) - { - return (sbyte)bytes[offset]; - } - - /// - /// Converts buffer data into an using the correct endianness. - /// - /// The buffer. - /// The byte offset within the buffer. - /// The converted value. - private short ToInt16(byte[] bytes, int offset) - { - if (this.IsLittleEndian) - { - return (short)(bytes[offset + 0] | (bytes[offset + 1] << 8)); - } - else - { - return (short)((bytes[offset + 0] << 8) | bytes[offset + 1]); - } - } - - /// - /// Converts buffer data into an using the correct endianness. - /// - /// The buffer. - /// The byte offset within the buffer. - /// The converted value. - private int ToInt32(byte[] bytes, int offset) - { - if (this.IsLittleEndian) - { - return bytes[offset + 0] | (bytes[offset + 1] << 8) | (bytes[offset + 2] << 16) | (bytes[offset + 3] << 24); - } - else - { - return (bytes[offset + 0] << 24) | (bytes[offset + 1] << 16) | (bytes[offset + 2] << 8) | bytes[offset + 3]; - } - } - - /// - /// Converts buffer data into a using the correct endianness. - /// - /// The buffer. - /// The byte offset within the buffer. - /// The converted value. - private byte ToByte(byte[] bytes, int offset) - { - return bytes[offset]; - } - - /// - /// Converts buffer data into a using the correct endianness. - /// - /// The buffer. - /// The byte offset within the buffer. - /// The converted value. - private uint ToUInt32(byte[] bytes, int offset) - { - return (uint)this.ToInt32(bytes, offset); - } - - /// - /// Converts buffer data into a using the correct endianness. - /// - /// The buffer. - /// The byte offset within the buffer. - /// The converted value. - private ushort ToUInt16(byte[] bytes, int offset) - { - return (ushort)this.ToInt16(bytes, offset); - } - - /// - /// Converts buffer data into a using the correct endianness. - /// - /// The buffer. - /// The byte offset within the buffer. - /// The converted value. - private float ToSingle(byte[] bytes, int offset) - { - byte[] buffer = new byte[4]; - Array.Copy(bytes, offset, buffer, 0, 4); - - if (this.IsLittleEndian != BitConverter.IsLittleEndian) - { - Array.Reverse(buffer); - } - - return BitConverter.ToSingle(buffer, 0); - } - - /// - /// Converts buffer data into a using the correct endianness. - /// - /// The buffer. - /// The byte offset within the buffer. - /// The converted value. - private double ToDouble(byte[] bytes, int offset) - { - byte[] buffer = new byte[8]; - Array.Copy(bytes, offset, buffer, 0, 8); - - if (this.IsLittleEndian != BitConverter.IsLittleEndian) - { - Array.Reverse(buffer); - } - - return BitConverter.ToDouble(buffer, 0); - } - /// /// Decodes the image data for strip encoded data. /// /// The pixel format. - /// The image to decode data into. + /// The image frame to decode data into. /// The number of rows per strip of data. /// An array of byte offsets to each strip in the image. /// An array of the size of each strip (in bytes). - private void DecodeImageStrips(Image image, int rowsPerStrip, uint[] stripOffsets, uint[] stripByteCounts) + private void DecodeImageStrips(ImageFrame frame, int rowsPerStrip, uint[] stripOffsets, uint[] stripByteCounts) where TPixel : unmanaged, IPixel { int stripsPerPixel = this.PlanarConfiguration == TiffPlanarConfiguration.Chunky ? 1 : this.BitsPerSample.Length; int stripsPerPlane = stripOffsets.Length / stripsPerPixel; - Buffer2D pixels = image.GetRootFramePixelBuffer(); + Buffer2D pixels = frame.PixelBuffer; byte[][] stripBytes = new byte[stripsPerPixel][]; for (int stripIndex = 0; stripIndex < stripBytes.Length; stripIndex++) { - int uncompressedStripSize = this.CalculateImageBufferSize(image.Width, rowsPerStrip, stripIndex); + int uncompressedStripSize = this.CalculateImageBufferSize(frame.Width, rowsPerStrip, stripIndex); stripBytes[stripIndex] = ArrayPool.Shared.Rent(uncompressedStripSize); } try { + TiffColorDecoder chunkyDecoder = null; + RgbPlanarTiffColor planarDecoder = null; + if (this.PlanarConfiguration == TiffPlanarConfiguration.Chunky) + { + chunkyDecoder = TiffColorDecoderFactory.Create(this.ColorType, this.BitsPerSample, this.ColorMap); + } + else + { + planarDecoder = TiffColorDecoderFactory.CreatePlanar(this.ColorType, this.BitsPerSample, this.ColorMap); + } + for (int i = 0; i < stripsPerPlane; i++) { - int stripHeight = i < stripsPerPlane - 1 || image.Height % rowsPerStrip == 0 ? rowsPerStrip : image.Height % rowsPerStrip; + int stripHeight = i < stripsPerPlane - 1 || frame.Height % rowsPerStrip == 0 ? rowsPerStrip : frame.Height % rowsPerStrip; for (int planeIndex = 0; planeIndex < stripsPerPixel; planeIndex++) { int stripIndex = (i * stripsPerPixel) + planeIndex; - this.DecompressImageBlock(stripOffsets[stripIndex], stripByteCounts[stripIndex], stripBytes[planeIndex]); + CompressionFactory.DecompressImageBlock(this.Stream.InputStream, this.CompressionType, stripOffsets[stripIndex], stripByteCounts[stripIndex], stripBytes[planeIndex]); } if (this.PlanarConfiguration == TiffPlanarConfiguration.Chunky) { - this.ProcessImageBlockChunky(stripBytes[0], pixels, 0, rowsPerStrip * i, image.Width, stripHeight); + chunkyDecoder.Decode(stripBytes[0], pixels, 0, rowsPerStrip * i, frame.Width, stripHeight); } else { - this.ProcessImageBlockPlanar(stripBytes, pixels, 0, rowsPerStrip * i, image.Width, stripHeight); + planarDecoder.Decode(stripBytes, pixels, 0, rowsPerStrip * i, frame.Width, stripHeight); } } } @@ -1275,5 +275,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff } } } + + public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) + where TPixel : unmanaged, IPixel + { + throw new NotImplementedException(); + } + + public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs new file mode 100644 index 000000000..4aa24f24d --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs @@ -0,0 +1,344 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http.Headers; +using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Metadata; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; +using SixLabors.ImageSharp.Metadata.Profiles.Iptc; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Tiff +{ + /// + /// The decoder helper methods. + /// + internal static class TiffDecoderHelpers + { + public static ImageMetadata CreateMetadata(this IList> frames, bool ignoreMetadata, TiffByteOrder byteOrder) + where TPixel : unmanaged, IPixel + { + var coreMetadata = new ImageMetadata(); + TiffMetadata tiffMetadata = coreMetadata.GetTiffMetadata(); + tiffMetadata.ByteOrder = byteOrder; + + TiffFrameMetadata rootFrameMetadata = frames.First().Metadata.GetTiffMetadata(); + switch (rootFrameMetadata.ResolutionUnit) + { + case TiffResolutionUnit.None: + coreMetadata.ResolutionUnits = PixelResolutionUnit.AspectRatio; + break; + case TiffResolutionUnit.Inch: + coreMetadata.ResolutionUnits = PixelResolutionUnit.PixelsPerInch; + break; + case TiffResolutionUnit.Centimeter: + coreMetadata.ResolutionUnits = PixelResolutionUnit.PixelsPerCentimeter; + break; + } + + if (rootFrameMetadata.HorizontalResolution != null) + { + coreMetadata.HorizontalResolution = rootFrameMetadata.HorizontalResolution.Value; + } + + if (rootFrameMetadata.VerticalResolution != null) + { + coreMetadata.VerticalResolution = rootFrameMetadata.VerticalResolution.Value; + } + + if (!ignoreMetadata) + { + foreach (ImageFrame frame in frames) + { + TiffFrameMetadata frameMetadata = frame.Metadata.GetTiffMetadata(); + + if (tiffMetadata.XmpProfile == null) + { + byte[] buf = frameMetadata.GetArrayValue(ExifTag.XMP, true); + if (buf != null) + { + tiffMetadata.XmpProfile = buf; + } + } + + if (coreMetadata.IptcProfile == null) + { + byte[] buf = frameMetadata.GetArrayValue(ExifTag.IPTC, true); + if (buf != null) + { + coreMetadata.IptcProfile = new IptcProfile(buf); + } + } + + if (coreMetadata.IccProfile == null) + { + byte[] buf = frameMetadata.GetArrayValue(ExifTag.IccProfile, true); + if (buf != null) + { + coreMetadata.IccProfile = new IccProfile(buf); + } + } + } + } + + return coreMetadata; + } + + /// + /// Determines the TIFF compression and color types, and reads any associated parameters. + /// + /// The options. + /// The IFD entries container to read the image format information for. + public static void VerifyAndParseOptions(this TiffDecoderCore options, TiffFrameMetadata entries) + { + if (entries.ExtraSamples != null) + { + throw new NotSupportedException("ExtraSamples is not supported."); + } + + if (entries.FillOrder != TiffFillOrder.MostSignificantBitFirst) + { + throw new NotSupportedException("The lower-order bits of the byte FillOrder is not supported."); + } + + if (entries.GetArrayValue(ExifTag.TileOffsets, true) != null) + { + throw new NotSupportedException("The Tile images is not supported."); + } + + ParseCompression(options, entries.Compression); + + options.PlanarConfiguration = entries.PlanarConfiguration; + + ParsePhotometric(options, entries); + + ParseBitsPerSample(options, entries); + + ParseColorType(options, entries); + } + + private static void ParseColorType(this TiffDecoderCore options, TiffFrameMetadata entries) + { + switch (options.PhotometricInterpretation) + { + case TiffPhotometricInterpretation.WhiteIsZero: + { + if (options.BitsPerSample.Length == 1) + { + switch (options.BitsPerSample[0]) + { + 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; + } + } + } + else + { + throw new NotSupportedException("The number of samples in the TIFF BitsPerSample entry is not supported."); + } + + break; + } + + case TiffPhotometricInterpretation.BlackIsZero: + { + if (options.BitsPerSample.Length == 1) + { + switch (options.BitsPerSample[0]) + { + 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; + } + } + } + else + { + throw new NotSupportedException("The number of samples in the TIFF BitsPerSample entry is not supported."); + } + + break; + } + + case TiffPhotometricInterpretation.Rgb: + { + if (options.BitsPerSample.Length == 3) + { + if (options.PlanarConfiguration == TiffPlanarConfiguration.Chunky) + { + if (options.BitsPerSample[0] == 8 && options.BitsPerSample[1] == 8 && options.BitsPerSample[2] == 8) + { + options.ColorType = TiffColorType.Rgb888; + } + else + { + options.ColorType = TiffColorType.Rgb; + } + } + else + { + options.ColorType = TiffColorType.RgbPlanar; + } + } + else + { + throw new NotSupportedException("The number of samples in the TIFF BitsPerSample entry is not supported."); + } + + break; + } + + case TiffPhotometricInterpretation.PaletteColor: + { + options.ColorMap = entries.ColorMap; + if (options.ColorMap != null) + { + if (options.BitsPerSample.Length == 1) + { + switch (options.BitsPerSample[0]) + { + default: + { + options.ColorType = TiffColorType.PaletteColor; + break; + } + } + } + else + { + throw new NotSupportedException("The number of samples in the TIFF BitsPerSample entry is not supported."); + } + } + else + { + throw new ImageFormatException("The TIFF ColorMap entry is missing for a palette color image."); + } + + break; + } + + default: + throw new NotSupportedException("The specified TIFF photometric interpretation is not supported: " + options.PhotometricInterpretation); + } + } + + private static void ParseBitsPerSample(this TiffDecoderCore options, TiffFrameMetadata entries) + { + options.BitsPerSample = entries.BitsPerSample; + if (options.BitsPerSample == null) + { + if (options.PhotometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero + || options.PhotometricInterpretation == TiffPhotometricInterpretation.BlackIsZero) + { + options.BitsPerSample = new[] { (ushort)1 }; + } + else + { + throw new ImageFormatException("The TIFF BitsPerSample entry is missing."); + } + } + } + + private static void ParsePhotometric(this TiffDecoderCore options, TiffFrameMetadata entries) + { + /* + if (!entries.TryGetSingleNumber(ExifTag.PhotometricInterpretation, out uint photometricInterpretation)) + { + if (entries.Compression == TiffCompression.Ccitt1D) + { + photometricInterpretation = (uint)TiffPhotometricInterpretation.WhiteIsZero; + } + else + { + throw new ImageFormatException("The TIFF photometric interpretation entry is missing."); + } + } + + options.PhotometricInterpretation = (TiffPhotometricInterpretation)photometricInterpretation; + /* */ + + // There is no default for PhotometricInterpretation, and it is required. + options.PhotometricInterpretation = entries.PhotometricInterpretation; + } + + private static void ParseCompression(this TiffDecoderCore options, TiffCompression compression) + { + switch (compression) + { + case TiffCompression.None: + { + options.CompressionType = TiffCompressionType.None; + break; + } + + case TiffCompression.PackBits: + { + options.CompressionType = TiffCompressionType.PackBits; + break; + } + + case TiffCompression.Deflate: + case TiffCompression.OldDeflate: + { + options.CompressionType = TiffCompressionType.Deflate; + break; + } + + case TiffCompression.Lzw: + { + options.CompressionType = TiffCompressionType.Lzw; + break; + } + + default: + { + throw new NotSupportedException("The specified TIFF compression format is not supported: " + compression); + } + } + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 2fa7e7925..8aa0edb97 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.IO; using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Tiff @@ -45,9 +46,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); - using (TiffWriter writer = new TiffWriter(stream)) + using (var writer = new TiffWriter(stream)) { long firstIfdMarker = this.WriteHeader(writer); + //// todo: multiframing is not support long nextIfdMarker = this.WriteImage(writer, image, firstIfdMarker); } } @@ -59,8 +61,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The marker to write the first IFD offset. public long WriteHeader(TiffWriter writer) { - ushort byteOrderMarker = BitConverter.IsLittleEndian ? TiffConstants.ByteOrderLittleEndianShort - : TiffConstants.ByteOrderBigEndianShort; + ushort byteOrderMarker = BitConverter.IsLittleEndian + ? TiffConstants.ByteOrderLittleEndianShort + : TiffConstants.ByteOrderBigEndianShort; writer.Write(byteOrderMarker); writer.Write((ushort)42); @@ -75,7 +78,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The to write data to. /// The IFD entries to write to the file. /// The marker to write the next IFD offset (if present). - public long WriteIfd(TiffWriter writer, List entries) + public long WriteIfd(TiffWriter writer, List entries) { if (entries.Count == 0) { @@ -83,27 +86,31 @@ namespace SixLabors.ImageSharp.Formats.Tiff } uint dataOffset = (uint)writer.Position + (uint)(6 + (entries.Count * 12)); - List largeDataBlocks = new List(); + var largeDataBlocks = new List(); - entries.Sort((a, b) => a.Tag - b.Tag); + entries.Sort((a, b) => (ushort)a.Tag - (ushort)b.Tag); writer.Write((ushort)entries.Count); - foreach (TiffIfdEntry entry in entries) + foreach (ExifValue entry in entries) { - writer.Write(entry.Tag); - writer.Write((ushort)entry.Type); - writer.Write(entry.Count); - - if (entry.Value.Length <= 4) + writer.Write((ushort)entry.Tag); + writer.Write((ushort)entry.DataType); + writer.Write(ExifWriter.GetNumberOfComponents(entry)); + + uint lenght = ExifWriter.GetLength(entry); + var raw = new byte[lenght]; + int sz = ExifWriter.WriteValue(entry, raw, 0); + DebugGuard.IsTrue(sz == raw.Length, "Incorrect number of bytes written"); + if (raw.Length <= 4) { - writer.WritePadded(entry.Value); + writer.WritePadded(raw); } else { - largeDataBlocks.Add(entry.Value); + largeDataBlocks.Add(raw); writer.Write(dataOffset); - dataOffset += (uint)(entry.Value.Length + (entry.Value.Length % 2)); + dataOffset += (uint)(raw.Length + (raw.Length % 2)); } } @@ -133,10 +140,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff public long WriteImage(TiffWriter writer, Image image, long ifdOffset) where TPixel : unmanaged, IPixel { - List ifdEntries = new List(); + var ifdEntries = new List(); this.AddImageFormat(image, ifdEntries); - this.AddMetadata(image, ifdEntries); writer.WriteMarker(ifdOffset, (uint)writer.Position); long nextIfdMarker = this.WriteIfd(writer, ifdEntries); @@ -144,83 +150,14 @@ namespace SixLabors.ImageSharp.Formats.Tiff return nextIfdMarker; } - /// - /// Adds image metadata to the specified IFD. - /// - /// The pixel format. - /// The to encode from. - /// The metadata entries to add to the IFD. - public void AddMetadata(Image image, List ifdEntries) - where TPixel : unmanaged, IPixel - { - ifdEntries.AddUnsignedRational(TiffTags.XResolution, new Rational(image.Metadata.HorizontalResolution)); - ifdEntries.AddUnsignedRational(TiffTags.YResolution, new Rational(image.Metadata.VerticalResolution)); - ifdEntries.AddUnsignedShort(TiffTags.ResolutionUnit, (uint)TiffResolutionUnit.Inch); - - /* - foreach (ImageProperty metadata in image.Metadata.Properties) - { - switch (metadata.Name) - { - case TiffMetadataNames.Artist: - { - ifdEntries.AddAscii(TiffTags.Artist, metadata.Value); - break; - } - - case TiffMetadataNames.Copyright: - { - ifdEntries.AddAscii(TiffTags.Copyright, metadata.Value); - break; - } - - case TiffMetadataNames.DateTime: - { - ifdEntries.AddAscii(TiffTags.DateTime, metadata.Value); - break; - } - - case TiffMetadataNames.HostComputer: - { - ifdEntries.AddAscii(TiffTags.HostComputer, metadata.Value); - break; - } - - case TiffMetadataNames.ImageDescription: - { - ifdEntries.AddAscii(TiffTags.ImageDescription, metadata.Value); - break; - } - - case TiffMetadataNames.Make: - { - ifdEntries.AddAscii(TiffTags.Make, metadata.Value); - break; - } - - case TiffMetadataNames.Model: - { - ifdEntries.AddAscii(TiffTags.Model, metadata.Value); - break; - } - - case TiffMetadataNames.Software: - { - ifdEntries.AddAscii(TiffTags.Software, metadata.Value); - break; - } - } - } */ - } - /// /// Adds image format information to the specified IFD. /// /// The pixel format. /// The to encode from. /// The image format entries to add to the IFD. - public void AddImageFormat(Image image, List ifdEntries) - where TPixel : unmanaged, IPixel + public void AddImageFormat(Image image, List ifdEntries) + where TPixel : unmanaged, IPixel { throw new NotImplementedException(); } diff --git a/src/ImageSharp/Formats/Tiff/TiffFormat.cs b/src/ImageSharp/Formats/Tiff/TiffFormat.cs index 1ee5c89cc..0628ef530 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFormat.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFormat.cs @@ -1,15 +1,14 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System.Collections.Generic; -using SixLabors.ImageSharp.Formats.Tiff; namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Encapsulates the means to encode and decode Tiff images. /// - public class TiffFormat : IImageFormat + public class TiffFormat : IImageFormat { private TiffFormat() { @@ -31,5 +30,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// public IEnumerable FileExtensions => TiffConstants.FileExtensions; + + /// + public TiffMetadata CreateDefaultFormatMetadata() => new TiffMetadata(); + + /// + public TiffFrameMetadata CreateDefaultFormatFrameMetadata() => new TiffFrameMetadata(); } } diff --git a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs new file mode 100644 index 000000000..bffbd1818 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs @@ -0,0 +1,324 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; + +namespace SixLabors.ImageSharp.Formats.Tiff +{ + /// + /// Provides Tiff specific metadata information for the frame. + /// + public class TiffFrameMetadata : IDeepCloneable + { + private const TiffResolutionUnit DefaultResolutionUnit = TiffResolutionUnit.Inch; + + private const TiffPlanarConfiguration DefaultPlanarConfiguration = TiffPlanarConfiguration.Chunky; + + /// + /// Initializes a new instance of the class. + /// + public TiffFrameMetadata() + { + } + + /// + /// Gets or sets the Tiff directory tags list. + /// + public IList Tags { get; set; } + + /// Gets a general indication of the kind of data contained in this subfile. + /// A general indication of the kind of data contained in this subfile. + public TiffNewSubfileType NewSubfileType => this.GetSingleEnum(ExifTag.SubfileType, TiffNewSubfileType.FullImage); + + /// Gets a general indication of the kind of data contained in this subfile. + /// A general indication of the kind of data contained in this subfile. + public TiffSubfileType? SubfileType => this.GetSingleEnumNullable(ExifTag.OldSubfileType); + + /// + /// Gets the number of columns in the image, i.e., the number of pixels per row. + /// + public uint Width => this.GetSingleUInt(ExifTag.ImageWidth); + + /// + /// Gets the number of rows of pixels in the image. + /// + public uint Height => this.GetSingleUInt(ExifTag.ImageLength); + + /// + /// Gets the number of bits per component. + /// + public ushort[] BitsPerSample => this.GetArrayValue(ExifTag.BitsPerSample, true); + + /// Gets the compression scheme used on the image data. + /// The compression scheme used on the image data. + public TiffCompression Compression => this.GetSingleEnum(ExifTag.Compression); + + /// + /// Gets the color space of the image data. + /// + public TiffPhotometricInterpretation PhotometricInterpretation => this.GetSingleEnum(ExifTag.PhotometricInterpretation); + + /// + /// Gets the logical order of bits within a byte. + /// + internal TiffFillOrder FillOrder => this.GetSingleEnum(ExifTag.FillOrder, TiffFillOrder.MostSignificantBitFirst); + + /// + /// Gets the a string that describes the subject of the image. + /// + public string ImageDescription => this.GetString(ExifTag.ImageDescription); + + /// + /// Gets the scanner manufacturer. + /// + public string Make => this.GetString(ExifTag.Make); + + /// + /// Gets the scanner model name or number. + /// + public string Model => this.GetString(ExifTag.Model); + + /// Gets for each strip, the byte offset of that strip.. + public uint[] StripOffsets => this.GetArrayValue(ExifTag.StripOffsets); + + /// + /// Gets the number of rows per strip. + /// + public uint RowsPerStrip => this.GetSingleUInt(ExifTag.RowsPerStrip); + + /// + /// Gets for each strip, the number of bytes in the strip after compression. + /// + public uint[] StripByteCounts => this.GetArrayValue(ExifTag.StripByteCounts); + + /// Gets the resolution of the image in x- direction. + /// The density of the image in x- direction. + public double? HorizontalResolution + { + get + { + if (this.ResolutionUnit != TiffResolutionUnit.None) + { + double resolutionUnitFactor = this.ResolutionUnit == TiffResolutionUnit.Centimeter ? 2.54 : 1.0; + + if (this.TryGetSingle(ExifTag.XResolution, out Rational xResolution)) + { + return xResolution.ToDouble() * resolutionUnitFactor; + } + } + + return null; + } + } + + /// + /// Gets the resolution of the image in y- direction. + /// + /// The density of the image in y- direction. + public double? VerticalResolution + { + get + { + if (this.ResolutionUnit != TiffResolutionUnit.None) + { + double resolutionUnitFactor = this.ResolutionUnit == TiffResolutionUnit.Centimeter ? 2.54 : 1.0; + + if (this.TryGetSingle(ExifTag.YResolution, out Rational yResolution)) + { + return yResolution.ToDouble() * resolutionUnitFactor; + } + } + + return null; + } + } + + /// + /// Gets how the components of each pixel are stored. + /// + public TiffPlanarConfiguration PlanarConfiguration => this.GetSingleEnum(ExifTag.PlanarConfiguration, DefaultPlanarConfiguration); + + /// + /// Gets the unit of measurement for XResolution and YResolution. + /// + public TiffResolutionUnit ResolutionUnit => this.GetSingleEnum(ExifTag.ResolutionUnit, DefaultResolutionUnit); + + /// + /// Gets the name and version number of the software package(s) used to create the image. + /// + public string Software => this.GetString(ExifTag.Software); + + /// + /// Gets the date and time of image creation. + /// + public string DateTime => this.GetString(ExifTag.DateTime); + + /// + /// Gets the person who created the image. + /// + public string Artist => this.GetString(ExifTag.Artist); + + /// + /// Gets the computer and/or operating system in use at the time of image creation. + /// + public string HostComputer => this.GetString(ExifTag.HostComputer); + + /// + /// Gets a color map for palette color images. + /// + public ushort[] ColorMap => this.GetArrayValue(ExifTag.ColorMap, true); + + /// + /// Gets the description of extra components. + /// + public ushort[] ExtraSamples => this.GetArrayValue(ExifTag.ExtraSamples, true); + + /// + /// Gets the copyright notice. + /// + public string Copyright => this.GetString(ExifTag.Copyright); + + internal T[] GetArrayValue(ExifTag tag, bool optional = false) + where T : struct + { + if (this.TryGetArrayValue(tag, out T[] result)) + { + return result; + } + + if (!optional) + { + throw new ArgumentException("Required tag is not founded: " + tag, nameof(tag)); + } + + return null; + } + + private bool TryGetArrayValue(ExifTag tag, out T[] result) + where T : struct + { + foreach (IExifValue entry in this.Tags) + { + if (entry.Tag == tag) + { + DebugGuard.IsTrue(entry.IsArray, "Expected array entry"); + + result = (T[])entry.GetValue(); + return true; + } + } + + result = null; + return false; + } + + private string GetString(ExifTag tag) + { + foreach (IExifValue entry in this.Tags) + { + if (entry.Tag == tag) + { + DebugGuard.IsTrue(entry.DataType == ExifDataType.Ascii, "Expected string entry"); + object value = entry.GetValue(); + DebugGuard.IsTrue(value is string, "Expected string entry"); + + return (string)value; + } + } + + return null; + } + + private TEnum? GetSingleEnumNullable(ExifTag tag) + where TEnum : struct + where TTagValue : struct + { + if (!this.TryGetSingle(tag, out TTagValue value)) + { + return null; + } + + return (TEnum)(object)value; + } + + private TEnum GetSingleEnum(ExifTag tag, TEnum? defaultValue = null) + where TEnum : struct + where TTagValue : struct + { + if (!this.TryGetSingle(tag, out TTagValue value)) + { + if (defaultValue != null) + { + return defaultValue.Value; + } + + throw new ArgumentException("Required tag is not founded: " + tag, nameof(tag)); + } + + return (TEnum)(object)value; + } + + /* + private TEnum GetSingleEnum(ExifTag tag, TEnum? defaultValue = null) + where TEnum : struct + where TEnumParent : struct + where TTagValue : struct + { + if (!this.TryGetSingle(tag, out TTagValue value)) + { + if (defaultValue != null) + { + return defaultValue.Value; + } + + throw new ArgumentException("Required tag is not founded: " + tag, nameof(tag)); + } + + return (TEnum)(object)(TEnumParent)Convert.ChangeType(value, typeof(TEnumParent)); + } */ + + private uint GetSingleUInt(ExifTag tag) + { + if (this.TryGetSingle(tag, out uint result)) + { + return result; + } + + throw new ArgumentException("Required tag is not founded: " + tag, nameof(tag)); + } + + private bool TryGetSingle(ExifTag tag, out T result) + where T : struct + { + foreach (IExifValue entry in this.Tags) + { + if (entry.Tag == tag) + { + DebugGuard.IsTrue(!entry.IsArray, "Expected non array entry"); + + object value = entry.GetValue(); + + result = (T)value; + return true; + } + } + + result = default; + return false; + } + + /// + public IDeepCloneable DeepClone() + { + var tags = new List(); + foreach (IExifValue entry in this.Tags) + { + tags.Add(entry.DeepClone()); + } + + return new TiffFrameMetadata() { Tags = tags }; + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfd.cs b/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfd.cs index 65d53e153..693b3abfc 100644 --- a/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfd.cs +++ b/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfd.cs @@ -1,63 +1,104 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; +using System.Collections.Generic; +using System.IO; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; + namespace SixLabors.ImageSharp.Formats.Tiff { /// - /// Data structure for holding details of each TIFF IFD. + /// The TIFF IFD reader class. /// - internal struct TiffIfd + internal class DirectoryReader { - /// - /// An array of the entries within this IFD. - /// - public TiffIfdEntry[] Entries; - - /// - /// Offset (in bytes) to the next IFD, or zero if this is the last IFD. - /// - public uint NextIfdOffset; - - /// - /// Initializes a new instance of the struct. - /// - /// An array of the entries within the IFD. - /// Offset (in bytes) to the next IFD, or zero if this is the last IFD. - public TiffIfd(TiffIfdEntry[] entries, uint nextIfdOffset) + private readonly TiffStream stream; + + private readonly EntryReader tagReader; + + private uint nextIfdOffset; + + public DirectoryReader(TiffStream stream) { - this.Entries = entries; - this.NextIfdOffset = nextIfdOffset; + this.stream = stream; + this.tagReader = new EntryReader(stream); } - /// - /// Gets the child with the specified tag ID. - /// - /// The tag ID to search for. - /// The resulting , or null if it does not exists. - public TiffIfdEntry? GetIfdEntry(ushort tag) + public IEnumerable Read() { - for (int i = 0; i < this.Entries.Length; i++) + if (this.ReadHeader()) { - if (this.Entries[i].Tag == tag) - { - return this.Entries[i]; - } + return this.ReadIfds(); } return null; } - /// - /// Gets the child with the specified tag ID. - /// - /// The tag ID to search for. - /// The resulting , if it exists. - /// A flag indicating whether the requested entry exists. - public bool TryGetIfdEntry(ushort tag, out TiffIfdEntry entry) + private bool ReadHeader() + { + ushort magic = this.stream.ReadUInt16(); + if (magic != TiffConstants.HeaderMagicNumber) + { + throw new ImageFormatException("Invalid TIFF header magic number: " + magic); + } + + uint firstIfdOffset = this.stream.ReadUInt32(); + if (firstIfdOffset == 0) + { + throw new ImageFormatException("Invalid TIFF file header."); + } + + this.nextIfdOffset = firstIfdOffset; + + return true; + } + + private IEnumerable ReadIfds() { - TiffIfdEntry? nullableEntry = this.GetIfdEntry(tag); - entry = nullableEntry ?? default(TiffIfdEntry); - return nullableEntry.HasValue; + var list = new List(); + while (this.nextIfdOffset != 0) + { + this.stream.Seek(this.nextIfdOffset); + IExifValue[] ifd = this.ReadIfd(); + list.Add(ifd); + } + + this.tagReader.LoadExtendedData(); + + return list; + } + + private IExifValue[] ReadIfd() + { + long pos = this.stream.Position; + + ushort entryCount = this.stream.ReadUInt16(); + var entries = new List(entryCount); + for (int i = 0; i < entryCount; i++) + { + IExifValue tag = this.tagReader.ReadNext(); + if (tag != null) + { + entries.Add(tag); + } + } + + this.nextIfdOffset = this.stream.ReadUInt32(); + + int ifdSize = 2 + (entryCount * TiffConstants.SizeOfIfdEntry) + 4; + int readedBytes = (int)(this.stream.Position - pos); + int leftBytes = ifdSize - readedBytes; + if (leftBytes > 0) + { + this.stream.Skip(leftBytes); + } + else if (leftBytes < 0) + { + throw new InvalidDataException("Out of range of IFD structure."); + } + + return entries.ToArray(); } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntry.cs b/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntry.cs index b98c3b862..fe0ab4ccb 100644 --- a/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntry.cs +++ b/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntry.cs @@ -1,46 +1,310 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; + namespace SixLabors.ImageSharp.Formats.Tiff { - /// - /// Data structure for holding details of each TIFF IFD entry. - /// - internal struct TiffIfdEntry + internal class EntryReader { - /// - /// The Tag ID for this entry. See for typical values. - /// - public ushort Tag; + private readonly TiffStream stream; - /// - /// The data-type of this entry. - /// - public TiffType Type; + private readonly SortedDictionary extValueLoaders = new SortedDictionary(); /// - /// The number of array items in this entry, or one if only a single value. + /// Initializes a new instance of the class. /// - public uint Count; + /// The stream. + public EntryReader(TiffStream stream) + { + this.stream = stream; + } - /// - /// The raw byte data for this entry. - /// - public byte[] Value; + public IExifValue ReadNext() + { + var tagId = (ExifTagValue)this.stream.ReadUInt16(); + var dataType = (ExifDataType)EnumUtils.Parse(this.stream.ReadUInt16(), ExifDataType.Unknown); + uint count = this.stream.ReadUInt32(); - /// - /// Initializes a new instance of the struct. - /// - /// The Tag ID for this entry. - /// The data-type of this entry. - /// The number of array items in this entry. - /// The raw byte data for this entry. - public TiffIfdEntry(ushort tag, TiffType type, uint count, byte[] value) - { - this.Tag = tag; - this.Type = type; - this.Count = count; - this.Value = value; + ExifDataType rawDataType = dataType; + dataType = LongOrShortFiltering(tagId, dataType); + bool isArray = GetIsArray(tagId, count); + + ExifValue entry = ExifValues.Create(tagId, dataType, isArray); + if (rawDataType == ExifDataType.Undefined && count == 0) + { + // todo: investgate + count = 4; + } + + if (this.ReadValueOrOffset(entry, rawDataType, count)) + { + return entry; + } + + return null; // new UnkownExifTag(tagId); + } + + public void LoadExtendedData() + { + foreach (Action action in this.extValueLoaders.Values) + { + action(); + } + } + + private static bool HasExtData(ExifValue tag, uint count) => ExifDataTypes.GetSize(tag.DataType) * count > 4; + + private static bool SetValue(ExifValue entry, object value) + { + if (!entry.IsArray && entry.DataType != ExifDataType.Ascii) + { + DebugGuard.IsTrue(((Array)value).Length == 1, "Expected a length is 1"); + + var single = ((Array)value).GetValue(0); + return entry.TrySetValue(single); + } + + return entry.TrySetValue(value); + } + + private static ExifDataType LongOrShortFiltering(ExifTagValue tagId, ExifDataType dataType) + { + switch (tagId) + { + case ExifTagValue.ImageWidth: + case ExifTagValue.ImageLength: + case ExifTagValue.StripOffsets: + case ExifTagValue.RowsPerStrip: + case ExifTagValue.StripByteCounts: + case ExifTagValue.TileWidth: + case ExifTagValue.TileLength: + case ExifTagValue.TileOffsets: + case ExifTagValue.TileByteCounts: + case ExifTagValue.OldSubfileType: // by spec SHORT, but can be LONG + return ExifDataType.Long; + + default: + return dataType; + } + } + + private static bool GetIsArray(ExifTagValue tagId, uint count) + { + switch (tagId) + { + case ExifTagValue.BitsPerSample: + case ExifTagValue.StripOffsets: + case ExifTagValue.StripByteCounts: + case ExifTagValue.TileOffsets: + case ExifTagValue.TileByteCounts: + case ExifTagValue.ColorMap: + case ExifTagValue.ExtraSamples: + return true; + + default: + return count > 1; + } + } + + private bool ReadValueOrOffset(ExifValue entry, ExifDataType rawDataType, uint count) + { + if (HasExtData(entry, count)) + { + uint offset = this.stream.ReadUInt32(); + this.extValueLoaders.Add(offset, () => + { + this.ReadExtValue(entry, rawDataType, offset, count); + }); + + return true; + } + + long pos = this.stream.Position; + object value = this.ReadData(entry.DataType, rawDataType, count); + if (value == null) + { + // read unknown type value + value = this.stream.ReadBytes(4); + } + else + { + int leftBytes = 4 - (int)(this.stream.Position - pos); + if (leftBytes > 0) + { + this.stream.Skip(leftBytes); + } + else if (leftBytes < 0) + { + throw new InvalidDataException("Out of range of IFD entry structure."); + } + } + + return SetValue(entry, value); + } + + private void ReadExtValue(ExifValue entry, ExifDataType rawDataType, uint offset, uint count) + { + DebugGuard.IsTrue(HasExtData(entry, count), "Excepted extended data"); + DebugGuard.MustBeGreaterThanOrEqualTo(offset, (uint)TiffConstants.SizeOfTiffHeader, nameof(offset)); + + this.stream.Seek(offset); + var value = this.ReadData(entry.DataType, rawDataType, count); + + SetValue(entry, value); + + DebugGuard.IsTrue(entry.DataType == ExifDataType.Ascii || count > 1 ^ !entry.IsArray, "Invalid tag"); + DebugGuard.IsTrue(entry.GetValue() != null, "Invalid tag"); + } + + private object ReadData(ExifDataType entryDataType, ExifDataType rawDataType, uint count) + { + switch (rawDataType) + { + case ExifDataType.Byte: + case ExifDataType.Undefined: + { + return this.stream.ReadBytes(count); + } + + case ExifDataType.SignedByte: + { + sbyte[] res = new sbyte[count]; + byte[] buf = this.stream.ReadBytes(count); + Array.Copy(buf, res, buf.Length); + return res; + } + + case ExifDataType.Short: + { + if (entryDataType == ExifDataType.Long) + { + uint[] buf = new uint[count]; + for (int i = 0; i < buf.Length; i++) + { + buf[i] = this.stream.ReadUInt16(); + } + + return buf; + } + else + { + ushort[] buf = new ushort[count]; + for (int i = 0; i < buf.Length; i++) + { + buf[i] = this.stream.ReadUInt16(); + } + + return buf; + } + } + + case ExifDataType.SignedShort: + { + short[] buf = new short[count]; + for (int i = 0; i < buf.Length; i++) + { + buf[i] = this.stream.ReadInt16(); + } + + return buf; + } + + case ExifDataType.Long: + { + uint[] buf = new uint[count]; + for (int i = 0; i < buf.Length; i++) + { + buf[i] = this.stream.ReadUInt32(); + } + + return buf; + } + + case ExifDataType.SignedLong: + { + int[] buf = new int[count]; + for (int i = 0; i < buf.Length; i++) + { + buf[i] = this.stream.ReadInt32(); + } + + return buf; + } + + case ExifDataType.Ascii: + { + byte[] buf = this.stream.ReadBytes(count); + + if (buf[buf.Length - 1] != 0) + { + throw new ImageFormatException("The retrieved string is not null terminated."); + } + + return Encoding.UTF8.GetString(buf, 0, buf.Length - 1); + } + + case ExifDataType.SingleFloat: + { + float[] buf = new float[count]; + for (int i = 0; i < buf.Length; i++) + { + buf[i] = this.stream.ReadSingle(); + } + + return buf; + } + + case ExifDataType.DoubleFloat: + { + double[] buf = new double[count]; + for (int i = 0; i < buf.Length; i++) + { + buf[i] = this.stream.ReadDouble(); + } + + return buf; + } + + case ExifDataType.Rational: + { + var buf = new Rational[count]; + for (int i = 0; i < buf.Length; i++) + { + uint numerator = this.stream.ReadUInt32(); + uint denominator = this.stream.ReadUInt32(); + buf[i] = new Rational(numerator, denominator); + } + + return buf; + } + + case ExifDataType.SignedRational: + { + var buf = new SignedRational[count]; + for (int i = 0; i < buf.Length; i++) + { + int numerator = this.stream.ReadInt32(); + int denominator = this.stream.ReadInt32(); + buf[i] = new SignedRational(numerator, denominator); + } + + return buf; + } + + case ExifDataType.Ifd: + { + return this.stream.ReadUInt32(); + } + + default: + return null; + } } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs new file mode 100644 index 000000000..ae25480ed --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs @@ -0,0 +1,44 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System.Collections; +using System.Collections.Generic; + +namespace SixLabors.ImageSharp.Formats.Tiff +{ + /// + /// Provides Tiff specific metadata information for the image. + /// + public class TiffMetadata : IDeepCloneable + { + /// + /// Initializes a new instance of the class. + /// + public TiffMetadata() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The metadata to create an instance from. + private TiffMetadata(TiffMetadata other) + { + this.ByteOrder = other.ByteOrder; + this.XmpProfile = other.XmpProfile; + } + + /// + /// Gets or sets the byte order. + /// + public TiffByteOrder ByteOrder { get; set; } + + /// + /// Gets or sets the XMP profile. + /// + public byte[] XmpProfile { get; set; } + + /// + public IDeepCloneable DeepClone() => new TiffMetadata(this); + } +} diff --git a/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntryCreator.cs b/src/ImageSharp/Formats/Tiff/__obsolete/TiffIfdEntryCreator.cs similarity index 100% rename from src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntryCreator.cs rename to src/ImageSharp/Formats/Tiff/__obsolete/TiffIfdEntryCreator.cs diff --git a/src/ImageSharp/Formats/Tiff/TiffMetadataNames.cs b/src/ImageSharp/Formats/Tiff/__obsolete/TiffMetadataNames.cs similarity index 100% rename from src/ImageSharp/Formats/Tiff/TiffMetadataNames.cs rename to src/ImageSharp/Formats/Tiff/__obsolete/TiffMetadataNames.cs diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffTags.cs b/src/ImageSharp/Formats/Tiff/__obsolete/TiffTagId.cs similarity index 100% rename from src/ImageSharp/Formats/Tiff/Constants/TiffTags.cs rename to src/ImageSharp/Formats/Tiff/__obsolete/TiffTagId.cs diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffType.cs b/src/ImageSharp/Formats/Tiff/__obsolete/TiffTagType.cs similarity index 100% rename from src/ImageSharp/Formats/Tiff/Constants/TiffType.cs rename to src/ImageSharp/Formats/Tiff/__obsolete/TiffTagType.cs diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index c3d9618c8..720d6c1b7 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -19,6 +19,12 @@ SixLabors.ImageSharp + + + + + + diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifDataType.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifDataType.cs index 13e67554c..733eb4a79 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifDataType.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifDataType.cs @@ -75,6 +75,11 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// /// A 64-bit double precision floating point value. /// - DoubleFloat = 12 + DoubleFloat = 12, + + /// + /// Reference to an IFD (32-bit (4-byte) unsigned integer). + /// + Ifd = 13 } } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs index a240c1392..e7a01b070 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs @@ -260,9 +260,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return length; } - private static uint GetLength(IExifValue value) => GetNumberOfComponents(value) * ExifDataTypes.GetSize(value.DataType); + internal static uint GetLength(IExifValue value) => GetNumberOfComponents(value) * ExifDataTypes.GetSize(value.DataType); - private static uint GetNumberOfComponents(IExifValue exifValue) + internal static uint GetNumberOfComponents(IExifValue exifValue) { object value = exifValue.GetValue(); @@ -279,17 +279,17 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return 1; } - private int WriteArray(IExifValue value, Span destination, int offset) + private static int WriteArray(IExifValue value, Span destination, int offset) { if (value.DataType == ExifDataType.Ascii) { - return this.WriteValue(ExifDataType.Ascii, value.GetValue(), destination, offset); + return WriteValue(ExifDataType.Ascii, value.GetValue(), destination, offset); } int newOffset = offset; foreach (object obj in (Array)value.GetValue()) { - newOffset = this.WriteValue(value.DataType, obj, destination, newOffset); + newOffset = WriteValue(value.DataType, obj, destination, newOffset); } return newOffset; @@ -310,7 +310,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif if (GetLength(value) > 4) { WriteUInt32((uint)(newOffset - startIndex), destination, this.dataOffsets[i++]); - newOffset = this.WriteValue(value, destination, newOffset); + newOffset = WriteValue(value, destination, newOffset); } } @@ -341,7 +341,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif } else { - this.WriteValue(value, destination, newOffset); + WriteValue(value, destination, newOffset); } newOffset += 4; @@ -362,7 +362,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif BinaryPrimitives.WriteInt32LittleEndian(destination.Slice(4, 4), value.Denominator); } - private int WriteValue(ExifDataType dataType, object value, Span destination, int offset) + private static int WriteValue(ExifDataType dataType, object value, Span destination, int offset) { switch (dataType) { @@ -410,14 +410,14 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif } } - private int WriteValue(IExifValue value, Span destination, int offset) + internal static int WriteValue(IExifValue value, Span destination, int offset) { if (value.IsArray && value.DataType != ExifDataType.Ascii) { - return this.WriteArray(value, destination, offset); + return WriteArray(value, destination, offset); } - return this.WriteValue(value.DataType, value.GetValue(), destination, offset); + return WriteValue(value.DataType, value.GetValue(), destination, offset); } } } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.ByteArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.ByteArray.cs index e20867b43..fdde66c51 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.ByteArray.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.ByteArray.cs @@ -21,6 +21,16 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// public static ExifTag XMP => new ExifTag(ExifTagValue.XMP); + /// + /// Gets the IPTC exif tag. + /// + public static ExifTag IPTC => new ExifTag(ExifTagValue.IPTC); + + /// + /// Gets the IccProfile exif tag. + /// + public static ExifTag IccProfile => new ExifTag(ExifTagValue.IccProfile); + /// /// Gets the CFAPattern2 exif tag. /// diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Long.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Long.cs index 8aae08160..68156fbb3 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Long.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Long.cs @@ -11,6 +11,11 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// public static ExifTag SubfileType { get; } = new ExifTag(ExifTagValue.SubfileType); + /// + /// Gets the RowsPerStrip exif tag. + /// + public static ExifTag RowsPerStrip { get; } = new ExifTag(ExifTagValue.RowsPerStrip); + /// /// Gets the SubIFDOffset exif tag. /// diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.LongArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.LongArray.cs index ac4b0a1bf..5d0a106ab 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.LongArray.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.LongArray.cs @@ -56,6 +56,12 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif ///
public static ExifTag StripRowCounts { get; } = new ExifTag(ExifTagValue.StripRowCounts); + /// + /// Gets the StripByteCounts exif tag. + /// + /// + public static ExifTag StripByteCounts { get; } = new ExifTag(ExifTagValue.StripByteCounts); + /// /// Gets the IntergraphRegisters exif tag. /// diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTagValue.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTagValue.cs index e07a32598..3d13a82dc 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTagValue.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTagValue.cs @@ -24,7 +24,16 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif GPSIFDOffset = 0x8825, /// - /// SubfileType + /// Indicates the identification of the Interoperability rule. + /// See https://www.awaresystems.be/imaging/tiff/tifftags/privateifd/interoperability/interoperabilityindex.html + /// + [ExifTagDescription("R98", "Indicates a file conforming to R98 file specification of Recommended Exif Interoperability Rules (ExifR98) or to DCF basic file stipulated by Design Rule for Camera File System.")] + [ExifTagDescription("THM", "Indicates a file conforming to DCF thumbnail file stipulated by Design rule for Camera File System.")] + InteroperabilityIndex = 0x0001, + + /// + /// A general indication of the kind of data contained in this subfile. + /// See Section 8: Baseline Fields. /// [ExifTagDescription(0U, "Full-resolution Image")] [ExifTagDescription(1U, "Reduced-resolution image")] @@ -38,7 +47,8 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif SubfileType = 0x00FE, /// - /// OldSubfileType + /// A general indication of the kind of data contained in this subfile. + /// See Section 8: Baseline Fields. /// [ExifTagDescription((ushort)1, "Full-resolution Image")] [ExifTagDescription((ushort)2, "Reduced-resolution image")] @@ -46,22 +56,26 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif OldSubfileType = 0x00FF, /// - /// ImageWidth + /// The number of columns in the image, i.e., the number of pixels per row. + /// See Section 8: Baseline Fields. /// ImageWidth = 0x0100, /// - /// ImageLength + /// The number of rows of pixels in the image. + /// See Section 8: Baseline Fields. /// ImageLength = 0x0101, /// - /// BitsPerSample + /// Number of bits per component. + /// See Section 8: Baseline Fields. /// BitsPerSample = 0x0102, /// - /// Compression + /// Compression scheme used on the image data. + /// See Section 8: Baseline Fields. /// [ExifTagDescription((ushort)1, "Uncompressed")] [ExifTagDescription((ushort)2, "CCITT 1D")] @@ -107,7 +121,8 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif Compression = 0x0103, /// - /// PhotometricInterpretation + /// The color space of the image data. + /// See Section 8: Baseline Fields. /// [ExifTagDescription((ushort)0, "WhiteIsZero")] [ExifTagDescription((ushort)1, "BlackIsZero")] @@ -126,7 +141,8 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif PhotometricInterpretation = 0x0106, /// - /// Thresholding + /// For black and white TIFF files that represent shades of gray, the technique used to convert from gray to black and white pixels. + /// See Section 8: Baseline Fields. /// [ExifTagDescription((ushort)1, "No dithering or halftoning")] [ExifTagDescription((ushort)2, "Ordered dither or halftone")] @@ -134,49 +150,58 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif Thresholding = 0x0107, /// - /// CellWidth + /// The width of the dithering or halftoning matrix used to create a dithered or halftoned bilevel file. + /// See Section 8: Baseline Fields. /// CellWidth = 0x0108, /// - /// CellLength + /// The length of the dithering or halftoning matrix used to create a dithered or halftoned bilevel file. + /// See Section 8: Baseline Fields. /// CellLength = 0x0109, /// - /// FillOrder + /// The logical order of bits within a byte. + /// See Section 8: Baseline Fields. /// [ExifTagDescription((ushort)1, "Normal")] [ExifTagDescription((ushort)2, "Reversed")] FillOrder = 0x010A, /// - /// DocumentName + /// The name of the document from which this image was scanned. + /// See Section 12: Document Storage and Retrieval. /// DocumentName = 0x010D, /// - /// ImageDescription + /// A string that describes the subject of the image. + /// See Section 8: Baseline Fields. /// ImageDescription = 0x010E, /// - /// Make + /// The scanner manufacturer. + /// See Section 8: Baseline Fields. /// Make = 0x010F, /// - /// Model + /// The scanner model name or number. + /// See Section 8: Baseline Fields. /// Model = 0x0110, /// - /// StripOffsets + /// For each strip, the byte offset of that strip. + /// See Section 8: Baseline Fields. /// StripOffsets = 0x0111, /// - /// Orientation + /// The orientation of the image with respect to the rows and columns. + /// See Section 8: Baseline Fields. /// [ExifTagDescription((ushort)1, "Horizontal (normal)")] [ExifTagDescription((ushort)2, "Mirror horizontal")] @@ -189,74 +214,88 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif Orientation = 0x0112, /// - /// SamplesPerPixel + /// The number of components per pixel. + /// See Section 8: Baseline Fields. /// SamplesPerPixel = 0x0115, /// - /// RowsPerStrip + /// The number of rows per strip. + /// See Section 8: Baseline Fields. /// RowsPerStrip = 0x0116, /// - /// StripByteCounts + /// For each strip, the number of bytes in the strip after compression. + /// See Section 8: Baseline Fields. /// StripByteCounts = 0x0117, /// - /// MinSampleValue + /// The minimum component value used. + /// See Section 8: Baseline Fields. /// MinSampleValue = 0x0118, /// - /// MaxSampleValue + /// The maximum component value used. + /// See Section 8: Baseline Fields. /// MaxSampleValue = 0x0119, /// - /// XResolution + /// The number of pixels per ResolutionUnit in the ImageWidth direction. + /// See Section 8: Baseline Fields. /// XResolution = 0x011A, /// - /// YResolution + /// The number of pixels per ResolutionUnit in the direction. + /// See Section 8: Baseline Fields. /// YResolution = 0x011B, /// - /// PlanarConfiguration + /// How the components of each pixel are stored. + /// See Section 8: Baseline Fields. /// [ExifTagDescription((ushort)1, "Chunky")] [ExifTagDescription((ushort)2, "Planar")] PlanarConfiguration = 0x011C, /// - /// PageName + /// The name of the page from which this image was scanned. + /// See Section 12: Document Storage and Retrieval. /// PageName = 0x011D, /// - /// XPosition + /// X position of the image. + /// See Section 12: Document Storage and Retrieval. /// XPosition = 0x011E, /// - /// YPosition + /// Y position of the image. + /// See Section 12: Document Storage and Retrieval. /// YPosition = 0x011F, /// - /// FreeOffsets + /// For each string of contiguous unused bytes in a TIFF file, the byte offset of the string. + /// See Section 8: Baseline Fields. /// FreeOffsets = 0x0120, /// - /// FreeByteCounts + /// For each string of contiguous unused bytes in a TIFF file, the number of bytes in the string. + /// See Section 8: Baseline Fields. /// FreeByteCounts = 0x0121, /// - /// GrayResponseUnit + /// The precision of the information contained in the GrayResponseCurve. + /// See Section 8: Baseline Fields. /// [ExifTagDescription((ushort)1, "0.1")] [ExifTagDescription((ushort)2, "0.001")] @@ -266,12 +305,13 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif GrayResponseUnit = 0x0122, /// - /// GrayResponseCurve + /// For grayscale data, the optical density of each possible pixel value. + /// See Section 8: Baseline Fields. /// GrayResponseCurve = 0x0123, /// - /// T4Options + /// Options for Group 3 Fax compression. /// [ExifTagDescription(0U, "2-Dimensional encoding")] [ExifTagDescription(1U, "Uncompressed")] @@ -279,13 +319,14 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif T4Options = 0x0124, /// - /// T6Options + /// Options for Group 4 Fax compression. /// [ExifTagDescription(1U, "Uncompressed")] T6Options = 0x0125, /// - /// ResolutionUnit + /// The unit of measurement for XResolution and YResolution. + /// See Section 8: Baseline Fields. /// [ExifTagDescription((ushort)1, "None")] [ExifTagDescription((ushort)2, "Inches")] @@ -293,7 +334,8 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif ResolutionUnit = 0x0128, /// - /// PageNumber + /// The page number of the page from which this image was scanned. + /// See Section 12: Document Storage and Retrieval. /// PageNumber = 0x0129, @@ -308,22 +350,26 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif TransferFunction = 0x012D, /// - /// Software + /// Name and version number of the software package(s) used to create the image. + /// See Section 8: Baseline Fields. /// Software = 0x0131, /// - /// DateTime + /// Date and time of image creation. + /// See Section 8: Baseline Fields. /// DateTime = 0x0132, /// - /// Artist + /// Person who created the image. + /// See Section 8: Baseline Fields. /// Artist = 0x013B, /// - /// HostComputer + /// The computer and/or operating system in use at the time of image creation. + /// See Section 8: Baseline Fields. /// HostComputer = 0x013C, @@ -343,7 +389,8 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif PrimaryChromaticities = 0x013F, /// - /// ColorMap + /// A color map for palette color images. + /// See Section 8: Baseline Fields. /// ColorMap = 0x0140, @@ -390,6 +437,14 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif ///
ConsecutiveBadFaxLines = 0x0148, + /// + /// Offset to child IFDs. + /// See TIFF Supplement 1: Adobe Pagemaker 6.0. + /// Each value is an offset (from the beginning of the TIFF file, as always) to a child IFD. Child images provide extra information for the parent image - such as a subsampled version of the parent image. + /// TIFF data type is Long or 13, IFD. The IFD type is identical to LONG, except that it is only used to point to other valid IFDs. + /// + SubIFDs = 0x014A, + /// /// InkSet /// @@ -418,7 +473,8 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif TargetPrinter = 0x0151, /// - /// ExtraSamples + /// Description of extra components. + /// See Section 8: Baseline Fields. /// [ExifTagDescription((ushort)0, "Unspecified")] [ExifTagDescription((ushort)1, "Associated Alpha")] @@ -485,6 +541,14 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif [ExifTagDescription((ushort)1, "Higher resolution image exists")] OPIProxy = 0x015F, + /// + /// Used in the TIFF-FX standard to point to an IFD containing tags that are globally applicable to the complete TIFF file. + /// See RFC2301: TIFF-F/FX Specification. + /// It is recommended that a TIFF writer place this field in the first IFD, where a TIFF reader would find it quickly. + /// Each field in the GlobalParametersIFD is a TIFF field that is legal in any IFD. Required baseline fields should not be located in the GlobalParametersIFD, but should be in each image IFD. If a conflict exists between fields in the GlobalParametersIFD and in the image IFDs, then the data in the image IFD shall prevail. + /// + GlobalParametersIFD = 0x0190, + /// /// ProfileType /// @@ -637,6 +701,12 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif ///
ImageID = 0x800D, + /// + /// Annotation data, as used in 'Imaging for Windows'. + /// See Other Private TIFF tags: http://www.awaresystems.be/imaging/tiff/tifftags/private.html + /// + WangAnnotation = 0x80A4, + /// /// CFARepeatPatternDim /// @@ -653,7 +723,8 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif BatteryLevel = 0x828F, /// - /// Copyright + /// Copyright notice. + /// See Section 8: Baseline Fields. /// Copyright = 0x8298, @@ -668,38 +739,70 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif FNumber = 0x829D, /// - /// MDFileTag + /// Specifies the pixel data format encoding in the Molecular Dynamics GEL file format. + /// See Molecular Dynamics GEL File Format and Private Tags: https://www.awaresystems.be/imaging/tiff/tifftags/docs/gel.html /// + [ExifTagDescription((ushort)2, "Squary root data format")] + [ExifTagDescription((ushort)128, "Linear data format")] MDFileTag = 0x82A5, /// - /// MDScalePixel + /// Specifies a scale factor in the Molecular Dynamics GEL file format. + /// See Molecular Dynamics GEL File Format and Private Tags: https://www.awaresystems.be/imaging/tiff/tifftags/docs/gel.html + /// The scale factor is to be applies to each pixel before presenting it to the user. /// MDScalePixel = 0x82A6, /// - /// MDLabName + /// Used to specify the conversion from 16bit to 8bit in the Molecular Dynamics GEL file format. + /// See Molecular Dynamics GEL File Format and Private Tags: https://www.awaresystems.be/imaging/tiff/tifftags/docs/gel.html + /// Since the display is only 9bit, the 16bit data must be converted before display. + /// 8bit value = (16bit value - low range ) * 255 / (high range - low range) + /// Count: n. + /// + [ExifTagDescription((ushort)0, "lowest possible")] + [ExifTagDescription((ushort)1, "low range")] + [ExifTagDescription("n-2", "high range")] + [ExifTagDescription("n-1", "highest possible")] + MDColorTable = 0x82A7, + + /// + /// Name of the lab that scanned this file, as used in the Molecular Dynamics GEL file format. + /// See Molecular Dynamics GEL File Format and Private Tags: https://www.awaresystems.be/imaging/tiff/tifftags/docs/gel.html /// MDLabName = 0x82A8, /// - /// MDSampleInfo + /// Information about the sample, as used in the Molecular Dynamics GEL file format. + /// See Molecular Dynamics GEL File Format and Private Tags: https://www.awaresystems.be/imaging/tiff/tifftags/docs/gel.html + /// This information is entered by the person that scanned the file. + /// Note that the word 'sample' as used here, refers to the scanned sample, not an image channel. /// MDSampleInfo = 0x82A9, /// - /// MDPrepDate + /// Date the sample was prepared, as used in the Molecular Dynamics GEL file format. + /// See Molecular Dynamics GEL File Format and Private Tags: https://www.awaresystems.be/imaging/tiff/tifftags/docs/gel.html + /// The format of this data is YY/MM/DD. + /// Note that the word 'sample' as used here, refers to the scanned sample, not an image channel. /// MDPrepDate = 0x82AA, /// - /// MDPrepTime + /// Time the sample was prepared, as used in the Molecular Dynamics GEL file format. + /// See Molecular Dynamics GEL File Format and Private Tags: https://www.awaresystems.be/imaging/tiff/tifftags/docs/gel.html + /// Format of this data is HH:MM using the 24-hour clock. + /// Note that the word 'sample' as used here, refers to the scanned sample, not an image channel. /// MDPrepTime = 0x82AB, /// - /// MDFileUnits + /// Units for data in this file, as used in the Molecular Dynamics GEL file format. + /// See Molecular Dynamics GEL File Format and Private Tags: https://www.awaresystems.be/imaging/tiff/tifftags/docs/gel.html /// + [ExifTagDescription("O.D.", "Densitometer")] + [ExifTagDescription("Counts", "PhosphorImager")] + [ExifTagDescription("RFU", "FluorImager")] MDFileUnits = 0x82AC, /// @@ -707,6 +810,12 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// PixelScale = 0x830E, + /// + /// IPTC (International Press Telecommunications Council) metadata. + /// See IPTC 4.1 specification. + /// + IPTC = 0x83BB, + /// /// IntergraphPacketData /// @@ -737,6 +846,40 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif ///
ModelTransform = 0x85D8, + /// + /// Collection of Photoshop 'Image Resource Blocks' (Embedded Metadata). + /// See Extracting the Thumbnail from the PhotoShop private TIFF Tag: https://www.awaresystems.be/imaging/tiff/tifftags/docs/photoshopthumbnail.html + /// + Photoshop = 0x8649, + + /// + /// ICC profile data. + /// See https://www.awaresystems.be/imaging/tiff/tifftags/iccprofile.html + /// + IccProfile = 0x8773, + + /// + /// Used in interchangeable GeoTIFF files. + /// See https://www.awaresystems.be/imaging/tiff/tifftags/geokeydirectorytag.html + /// This tag is also know as 'ProjectionInfoTag' and 'CoordSystemInfoTag' + /// This tag may be used to store the GeoKey Directory, which defines and references the "GeoKeys". + /// + GeoKeyDirectoryTag = 0x87AF, + + /// + /// Used in interchangeable GeoTIFF files. + /// See https://www.awaresystems.be/imaging/tiff/tifftags/geodoubleparamstag.html + /// This tag is used to store all of the DOUBLE valued GeoKeys, referenced by the GeoKeyDirectoryTag. The meaning of any value of this double array is determined from the GeoKeyDirectoryTag reference pointing to it. FLOAT values should first be converted to DOUBLE and stored here. + /// + GeoDoubleParamsTag = 0x87B0, + + /// + /// Used in interchangeable GeoTIFF files. + /// See https://www.awaresystems.be/imaging/tiff/tifftags/geoasciiparamstag.html + /// This tag is used to store all of the ASCII valued GeoKeys, referenced by the GeoKeyDirectoryTag. Since keys use offsets into tags, any special comments may be placed at the beginning of this tag. For the most part, the only keys that are ASCII valued are "Citation" keys, giving documentation and references for obscure projections, datums, etc. + /// + GeoAsciiParamsTag = 0x87B1, + /// /// ImageLayer /// @@ -1184,6 +1327,14 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif ///
RelatedSoundFile = 0xA004, + /// + /// A pointer to the Exif-related Interoperability IFD. + /// See https://www.awaresystems.be/imaging/tiff/tifftags/privateifd/interoperability.html + /// Interoperability IFD is composed of tags which stores the information to ensure the Interoperability. + /// The Interoperability structure of Interoperability IFD is same as TIFF defined IFD structure but does not contain the image data characteristically compared with normal TIFF IFD. + /// + InteroperabilityIFD = 0xA005, + /// /// FlashEnergy /// @@ -1539,5 +1690,41 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// GPSDifferential ///
GPSDifferential = 0x001E, + + /// + /// Used in the Oce scanning process. + /// Identifies the scanticket used in the scanning process. + /// Includes a trailing zero. + /// See https://www.awaresystems.be/imaging/tiff/tifftags/docs/oce.html + /// + OceScanjobDescription = 0xC427, + + /// + /// Used in the Oce scanning process. + /// Identifies the application to process the TIFF file that results from scanning. + /// Includes a trailing zero. + /// See https://www.awaresystems.be/imaging/tiff/tifftags/docs/oce.html + /// + OceApplicationSelector = 0xC428, + + /// + /// Used in the Oce scanning process. + /// This is the user's answer to an optional question embedded in the Oce scanticket, and presented to that user before scanning. It can serve in further determination of the workflow. + /// See https://www.awaresystems.be/imaging/tiff/tifftags/docs/oce.html + /// + OceIdentificationNumber = 0xC429, + + /// + /// Used in the Oce scanning process. + /// This tag encodes the imageprocessing done by the Oce ImageLogic module in the scanner to ensure optimal quality for certain workflows. + /// See https://www.awaresystems.be/imaging/tiff/tifftags/docs/oce.html + /// + OceImageLogicCharacteristics = 0xC42A, + + /// + /// Alias Sketchbook Pro layer usage description. + /// See https://www.awaresystems.be/imaging/tiff/tifftags/docs/alias.html + /// + AliasLayerMetadata = 0xC660, } } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs index 2d8aa9260..e47d5da25 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs @@ -9,10 +9,10 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif public static ExifValue Create(ExifTag tag) => (ExifValue)CreateValue((ExifTagValue)(ushort)tag); - public static ExifValue Create(ExifTagValue tag, ExifDataType dataType, uint numberOfComponents) - { - bool isArray = numberOfComponents != 1; + public static ExifValue Create(ExifTagValue tag, ExifDataType dataType, uint numberOfComponents) => Create(tag, dataType, numberOfComponents != 1); + public static ExifValue Create(ExifTagValue tag, ExifDataType dataType, bool isArray) + { switch (dataType) { case ExifDataType.Byte: return isArray ? (ExifValue)new ExifByteArray(tag, dataType) : new ExifByte(tag, dataType); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/ImageExtensionsTest.cs b/tests/ImageSharp.Tests/Formats/Tiff/ImageExtensionsTest.cs new file mode 100644 index 000000000..af82b4026 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Tiff/ImageExtensionsTest.cs @@ -0,0 +1,172 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Formats.Tiff +{ + [Trait("Category", "Tiff.BlackBox.Encoder")] + [Trait("Category", "Tiff")] + public class ImageExtensionsTest + { + [Theory] + [WithFile(TestImages.Tiff.RgbUncompressed, PixelTypes.Rgba32)] + public void ThrowsSavingNotImplemented(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + Assert.Throws(() => + { + string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest)); + string file = Path.Combine(dir, "SaveAsTiff_Path.tiff"); + using var image = provider.GetImage(new TiffDecoder()); + image.SaveAsTiff(file); + }); + } + + [Fact(Skip = "Saving not implemented")] + public void SaveAsTiff_Path() + { + string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest)); + string file = Path.Combine(dir, "SaveAsTiff_Path.tiff"); + + using (var image = new Image(10, 10)) + { + image.SaveAsTiff(file); + } + + using (Image.Load(file, out IImageFormat mime)) + { + Assert.Equal("image/tiff", mime.DefaultMimeType); + } + } + + [Fact(Skip = "Saving not implemented")] + public async Task SaveAsTiffAsync_Path() + { + string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest)); + string file = Path.Combine(dir, "SaveAsTiffAsync_Path.tiff"); + + using (var image = new Image(10, 10)) + { + await image.SaveAsTiffAsync(file); + } + + using (Image.Load(file, out IImageFormat mime)) + { + Assert.Equal("image/tiff", mime.DefaultMimeType); + } + } + + [Fact(Skip = "Saving not implemented")] + public void SaveAsTiff_Path_Encoder() + { + string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions)); + string file = Path.Combine(dir, "SaveAsTiff_Path_Encoder.tiff"); + + using (var image = new Image(10, 10)) + { + image.SaveAsTiff(file, new TiffEncoder()); + } + + using (Image.Load(file, out IImageFormat mime)) + { + Assert.Equal("image/tiff", mime.DefaultMimeType); + } + } + + [Fact(Skip = "Saving not implemented")] + public async Task SaveAsTiffAsync_Path_Encoder() + { + string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions)); + string file = Path.Combine(dir, "SaveAsTiffAsync_Path_Encoder.tiff"); + + using (var image = new Image(10, 10)) + { + await image.SaveAsTiffAsync(file, new TiffEncoder()); + } + + using (Image.Load(file, out IImageFormat mime)) + { + Assert.Equal("image/tiff", mime.DefaultMimeType); + } + } + + [Fact(Skip = "Saving not implemented")] + public void SaveAsTiff_Stream() + { + using var memoryStream = new MemoryStream(); + + using (var image = new Image(10, 10)) + { + image.SaveAsTiff(memoryStream); + } + + memoryStream.Position = 0; + + using (Image.Load(memoryStream, out IImageFormat mime)) + { + Assert.Equal("image/tiff", mime.DefaultMimeType); + } + } + + [Fact(Skip = "Saving not implemented")] + public async Task SaveAsTiffAsync_StreamAsync() + { + using var memoryStream = new MemoryStream(); + + using (var image = new Image(10, 10)) + { + await image.SaveAsTiffAsync(memoryStream); + } + + memoryStream.Position = 0; + + using (Image.Load(memoryStream, out IImageFormat mime)) + { + Assert.Equal("image/tiff", mime.DefaultMimeType); + } + } + + [Fact(Skip = "Saving not implemented")] + public void SaveAsTiff_Stream_Encoder() + { + using var memoryStream = new MemoryStream(); + + using (var image = new Image(10, 10)) + { + image.SaveAsTiff(memoryStream, new TiffEncoder()); + } + + memoryStream.Position = 0; + + using (Image.Load(memoryStream, out IImageFormat mime)) + { + Assert.Equal("image/tiff", mime.DefaultMimeType); + } + } + + [Fact(Skip = "Saving not implemented")] + public async Task SaveAsTiffAsync_Stream_Encoder() + { + using var memoryStream = new MemoryStream(); + + using (var image = new Image(10, 10)) + { + await image.SaveAsTiffAsync(memoryStream, new TiffEncoder()); + } + + memoryStream.Position = 0; + + using (Image.Load(memoryStream, out IImageFormat mime)) + { + Assert.Equal("image/tiff", mime.DefaultMimeType); + } + } + } +} diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs index 20fd53f41..ba7728b0f 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs @@ -19,52 +19,52 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff private static Rgba32 Bit0 = new Rgba32(0, 0, 0, 255); private static Rgba32 Bit1 = new Rgba32(255, 255, 255, 255); - private static byte[] Bilevel_Bytes4x4 = new byte[] { 0b01010000, + private static readonly byte[] Bilevel_Bytes4x4 = new byte[] { 0b01010000, 0b11110000, 0b01110000, 0b10010000 }; - private static Rgba32[][] Bilevel_Result4x4 = new[] { new[] { Bit0, Bit1, Bit0, Bit1 }, + private static readonly Rgba32[][] Bilevel_Result4x4 = new[] { new[] { Bit0, Bit1, Bit0, Bit1 }, new[] { Bit1, Bit1, Bit1, Bit1 }, new[] { Bit0, Bit1, Bit1, Bit1 }, new[] { Bit1, Bit0, Bit0, Bit1 }}; - private static byte[] Bilevel_Bytes12x4 = new byte[] { 0b01010101, 0b01010000, + private static readonly byte[] Bilevel_Bytes12x4 = new byte[] { 0b01010101, 0b01010000, 0b11111111, 0b11111111, 0b01101001, 0b10100000, 0b10010000, 0b01100000}; - private static Rgba32[][] Bilevel_Result12x4 = new[] { new[] { Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1 }, + private static readonly Rgba32[][] Bilevel_Result12x4 = new[] { new[] { Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1 }, new[] { Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1 }, new[] { Bit0, Bit1, Bit1, Bit0, Bit1, Bit0, Bit0, Bit1, Bit1, Bit0, Bit1, Bit0 }, new[] { Bit1, Bit0, Bit0, Bit1, Bit0, Bit0, Bit0, Bit0, Bit0, Bit1, Bit1, Bit0 }}; - private static byte[] Grayscale4_Bytes4x4 = new byte[] { 0x8F, 0x0F, + private static readonly byte[] Grayscale4_Bytes4x4 = new byte[] { 0x8F, 0x0F, 0xFF, 0xFF, 0x08, 0x8F, 0xF0, 0xF8 }; - private static Rgba32[][] Grayscale4_Result4x4 = new[] { new[] { Gray8, GrayF, Gray0, GrayF }, + private static readonly Rgba32[][] Grayscale4_Result4x4 = new[] { new[] { Gray8, GrayF, Gray0, GrayF }, new[] { GrayF, GrayF, GrayF, GrayF }, new[] { Gray0, Gray8, Gray8, GrayF }, new[] { GrayF, Gray0, GrayF, Gray8 }}; - private static byte[] Grayscale4_Bytes3x4 = new byte[] { 0x8F, 0x00, + private static readonly byte[] Grayscale4_Bytes3x4 = new byte[] { 0x8F, 0x00, 0xFF, 0xF0, 0x08, 0x80, 0xF0, 0xF0 }; - private static Rgba32[][] Grayscale4_Result3x4 = new[] { new[] { Gray8, GrayF, Gray0 }, + private static readonly Rgba32[][] Grayscale4_Result3x4 = new[] { new[] { Gray8, GrayF, Gray0 }, new[] { GrayF, GrayF, GrayF }, new[] { Gray0, Gray8, Gray8 }, new[] { GrayF, Gray0, GrayF }}; - private static byte[] Grayscale8_Bytes4x4 = new byte[] { 128, 255, 000, 255, + private static readonly byte[] Grayscale8_Bytes4x4 = new byte[] { 128, 255, 000, 255, 255, 255, 255, 255, 000, 128, 128, 255, 255, 000, 255, 128 }; - private static Rgba32[][] Grayscale8_Result4x4 = new[] { new[] { Gray128, Gray255, Gray000, Gray255 }, + private static readonly Rgba32[][] Grayscale8_Result4x4 = new[] { new[] { Gray128, Gray255, Gray000, Gray255 }, new[] { Gray255, Gray255, Gray255, Gray255 }, new[] { Gray000, Gray128, Gray128, Gray255 }, new[] { Gray255, Gray000, Gray255, Gray128 }}; @@ -125,7 +125,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { AssertDecode(expectedResult, pixels => { - BlackIsZeroTiffColor.Decode(inputData, new[] { (uint)bitsPerSample }, pixels, left, top, width, height); + new BlackIsZeroTiffColor(new[] { (ushort)bitsPerSample }).Decode(inputData, pixels, left, top, width, height); }); } @@ -135,7 +135,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { AssertDecode(expectedResult, pixels => { - BlackIsZero1TiffColor.Decode(inputData, pixels, left, top, width, height); + new BlackIsZero1TiffColor().Decode(inputData, pixels, left, top, width, height); }); } @@ -145,7 +145,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { AssertDecode(expectedResult, pixels => { - BlackIsZero4TiffColor.Decode(inputData, pixels, left, top, width, height); + new BlackIsZero4TiffColor().Decode(inputData, pixels, left, top, width, height); }); } @@ -155,7 +155,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { AssertDecode(expectedResult, pixels => { - BlackIsZero8TiffColor.Decode(inputData, pixels, left, top, width, height); + new BlackIsZero8TiffColor().Decode(inputData, pixels, left, top, width, height); }); } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs index 4f6bef0cf..98c7e6498 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs @@ -12,25 +12,25 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { public static uint[][] Palette4_ColorPalette { get => GeneratePalette(16); } - public static uint[] Palette4_ColorMap { get => GenerateColorMap(Palette4_ColorPalette); } + public static ushort[] Palette4_ColorMap { get => GenerateColorMap(Palette4_ColorPalette); } - private static byte[] Palette4_Bytes4x4 = new byte[] { 0x01, 0x23, + private static readonly byte[] Palette4_Bytes4x4 = new byte[] { 0x01, 0x23, 0x4A, 0xD2, 0x12, 0x34, 0xAB, 0xEF }; - private static Rgba32[][] Palette4_Result4x4 = GenerateResult(Palette4_ColorPalette, + private static readonly Rgba32[][] Palette4_Result4x4 = GenerateResult(Palette4_ColorPalette, new[] { new[] { 0x00, 0x01, 0x02, 0x03 }, new[] { 0x04, 0x0A, 0x0D, 0x02 }, new[] { 0x01, 0x02, 0x03, 0x04 }, new[] { 0x0A, 0x0B, 0x0E, 0x0F }}); - private static byte[] Palette4_Bytes3x4 = new byte[] { 0x01, 0x20, + private static readonly byte[] Palette4_Bytes3x4 = new byte[] { 0x01, 0x20, 0x4A, 0xD0, 0x12, 0x30, 0xAB, 0xE0 }; - private static Rgba32[][] Palette4_Result3x4 = GenerateResult(Palette4_ColorPalette, + private static readonly Rgba32[][] Palette4_Result3x4 = GenerateResult(Palette4_ColorPalette, new[] { new[] { 0x00, 0x01, 0x02 }, new[] { 0x04, 0x0A, 0x0D }, new[] { 0x01, 0x02, 0x03 }, @@ -57,14 +57,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public static uint[][] Palette8_ColorPalette { get => GeneratePalette(256); } - public static uint[] Palette8_ColorMap { get => GenerateColorMap(Palette8_ColorPalette); } + public static ushort[] Palette8_ColorMap { get => GenerateColorMap(Palette8_ColorPalette); } - private static byte[] Palette8_Bytes4x4 = new byte[] { 000, 001, 002, 003, + private static readonly byte[] Palette8_Bytes4x4 = new byte[] { 000, 001, 002, 003, 100, 110, 120, 130, 000, 255, 128, 255, 050, 100, 150, 200 }; - private static Rgba32[][] Palette8_Result4x4 = GenerateResult(Palette8_ColorPalette, + private static readonly Rgba32[][] Palette8_Result4x4 = GenerateResult(Palette8_ColorPalette, new[] { new[] { 000, 001, 002, 003 }, new[] { 100, 110, 120, 130 }, new[] { 000, 255, 128, 255 }, @@ -85,11 +85,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Theory] [MemberData(nameof(Palette4_Data))] [MemberData(nameof(Palette8_Data))] - public void Decode_WritesPixelData(byte[] inputData, int bitsPerSample, uint[] colorMap, int left, int top, int width, int height, Rgba32[][] expectedResult) + public void Decode_WritesPixelData(byte[] inputData, ushort bitsPerSample, ushort[] colorMap, int left, int top, int width, int height, Rgba32[][] expectedResult) { AssertDecode(expectedResult, pixels => { - PaletteTiffColor.Decode(inputData, new[] { (uint)bitsPerSample }, colorMap, pixels, left, top, width, height); + new PaletteTiffColor(new[] { (ushort)bitsPerSample }, colorMap).Decode(inputData, pixels, left, top, width, height); }); } @@ -105,16 +105,16 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff return palette; } - private static uint[] GenerateColorMap(uint[][] colorPalette) + private static ushort[] GenerateColorMap(uint[][] colorPalette) { int colorCount = colorPalette.Length; - uint[] colorMap = new uint[colorCount * 3]; + ushort[] colorMap = new ushort[colorCount * 3]; for (int i = 0; i < colorCount; i++) { - colorMap[colorCount * 0 + i] = colorPalette[i][0]; - colorMap[colorCount * 1 + i] = colorPalette[i][1]; - colorMap[colorCount * 2 + i] = colorPalette[i][2]; + colorMap[colorCount * 0 + i] = (ushort)colorPalette[i][0]; + colorMap[colorCount * 1 + i] = (ushort)colorPalette[i][1]; + colorMap[colorCount * 2 + i] = (ushort)colorPalette[i][2]; } return colorMap; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs index da48086bb..3faedfa10 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs @@ -2,8 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Collections.Generic; -using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; @@ -48,18 +46,22 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { int resultWidth = expectedResult[0].Length; int resultHeight = expectedResult.Length; - Image image = new Image(resultWidth, resultHeight); - image.Mutate(x => x.BackgroundColor(DefaultColor)); - Buffer2D pixels = image.GetRootFramePixelBuffer(); - decodeAction(pixels); - - for (int y = 0; y < resultHeight; y++) + using (Image image = new Image(resultWidth, resultHeight)) { - for (int x = 0; x < resultWidth; x++) + image.Mutate(x => x.BackgroundColor(DefaultColor)); + Buffer2D pixels = image.GetRootFramePixelBuffer(); + + decodeAction(pixels); + + for (int y = 0; y < resultHeight; y++) { - Assert.True(expectedResult[y][x] == pixels[x, y], - $"Pixel ({x}, {y}) should be {expectedResult[y][x]} but was {pixels[x, y]}"); + for (int x = 0; x < resultWidth; x++) + { + Assert.True( + expectedResult[y][x] == pixels[x, y], + $"Pixel ({x}, {y}) should be {expectedResult[y][x]} but was {pixels[x, y]}"); + } } } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs index cc025a452..c0e328c62 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs @@ -72,17 +72,17 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { get { - yield return new object[] { Rgb4_Bytes4x4, new[] { 4u, 4u, 4u }, 0, 0, 4, 4, Rgb4_Result4x4 }; - yield return new object[] { Rgb4_Bytes4x4, new[] { 4u, 4u, 4u }, 0, 0, 4, 4, Offset(Rgb4_Result4x4, 0, 0, 6, 6) }; - yield return new object[] { Rgb4_Bytes4x4, new[] { 4u, 4u, 4u }, 1, 0, 4, 4, Offset(Rgb4_Result4x4, 1, 0, 6, 6) }; - yield return new object[] { Rgb4_Bytes4x4, new[] { 4u, 4u, 4u }, 0, 1, 4, 4, Offset(Rgb4_Result4x4, 0, 1, 6, 6) }; - yield return new object[] { Rgb4_Bytes4x4, new[] { 4u, 4u, 4u }, 1, 1, 4, 4, Offset(Rgb4_Result4x4, 1, 1, 6, 6) }; - - yield return new object[] { Rgb4_Bytes3x4, new[] { 4u, 4u, 4u }, 0, 0, 3, 4, Rgb4_Result3x4 }; - yield return new object[] { Rgb4_Bytes3x4, new[] { 4u, 4u, 4u }, 0, 0, 3, 4, Offset(Rgb4_Result3x4, 0, 0, 6, 6) }; - yield return new object[] { Rgb4_Bytes3x4, new[] { 4u, 4u, 4u }, 1, 0, 3, 4, Offset(Rgb4_Result3x4, 1, 0, 6, 6) }; - yield return new object[] { Rgb4_Bytes3x4, new[] { 4u, 4u, 4u }, 0, 1, 3, 4, Offset(Rgb4_Result3x4, 0, 1, 6, 6) }; - yield return new object[] { Rgb4_Bytes3x4, new[] { 4u, 4u, 4u }, 1, 1, 3, 4, Offset(Rgb4_Result3x4, 1, 1, 6, 6) }; + yield return new object[] { Rgb4_Bytes4x4, new ushort[] { 4, 4, 4 }, 0, 0, 4, 4, Rgb4_Result4x4 }; + yield return new object[] { Rgb4_Bytes4x4, new ushort[] { 4, 4, 4 }, 0, 0, 4, 4, Offset(Rgb4_Result4x4, 0, 0, 6, 6) }; + yield return new object[] { Rgb4_Bytes4x4, new ushort[] { 4, 4, 4 }, 1, 0, 4, 4, Offset(Rgb4_Result4x4, 1, 0, 6, 6) }; + yield return new object[] { Rgb4_Bytes4x4, new ushort[] { 4, 4, 4 }, 0, 1, 4, 4, Offset(Rgb4_Result4x4, 0, 1, 6, 6) }; + yield return new object[] { Rgb4_Bytes4x4, new ushort[] { 4, 4, 4 }, 1, 1, 4, 4, Offset(Rgb4_Result4x4, 1, 1, 6, 6) }; + + yield return new object[] { Rgb4_Bytes3x4, new ushort[] { 4, 4, 4 }, 0, 0, 3, 4, Rgb4_Result3x4 }; + yield return new object[] { Rgb4_Bytes3x4, new ushort[] { 4, 4, 4 }, 0, 0, 3, 4, Offset(Rgb4_Result3x4, 0, 0, 6, 6) }; + yield return new object[] { Rgb4_Bytes3x4, new ushort[] { 4, 4, 4 }, 1, 0, 3, 4, Offset(Rgb4_Result3x4, 1, 0, 6, 6) }; + yield return new object[] { Rgb4_Bytes3x4, new ushort[] { 4, 4, 4 }, 0, 1, 3, 4, Offset(Rgb4_Result3x4, 0, 1, 6, 6) }; + yield return new object[] { Rgb4_Bytes3x4, new ushort[] { 4, 4, 4 }, 1, 1, 3, 4, Offset(Rgb4_Result3x4, 1, 1, 6, 6) }; } } @@ -126,11 +126,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { get { - yield return new object[] { Rgb8_Bytes4x4, new[] { 8u, 8u, 8u }, 0, 0, 4, 4, Rgb8_Result4x4 }; - yield return new object[] { Rgb8_Bytes4x4, new[] { 8u, 8u, 8u }, 0, 0, 4, 4, Offset(Rgb8_Result4x4, 0, 0, 6, 6) }; - yield return new object[] { Rgb8_Bytes4x4, new[] { 8u, 8u, 8u }, 1, 0, 4, 4, Offset(Rgb8_Result4x4, 1, 0, 6, 6) }; - yield return new object[] { Rgb8_Bytes4x4, new[] { 8u, 8u, 8u }, 0, 1, 4, 4, Offset(Rgb8_Result4x4, 0, 1, 6, 6) }; - yield return new object[] { Rgb8_Bytes4x4, new[] { 8u, 8u, 8u }, 1, 1, 4, 4, Offset(Rgb8_Result4x4, 1, 1, 6, 6) }; + yield return new object[] { Rgb8_Bytes4x4, new ushort[] { 8, 8, 8 }, 0, 0, 4, 4, Rgb8_Result4x4 }; + yield return new object[] { Rgb8_Bytes4x4, new ushort[] { 8, 8, 8 }, 0, 0, 4, 4, Offset(Rgb8_Result4x4, 0, 0, 6, 6) }; + yield return new object[] { Rgb8_Bytes4x4, new ushort[] { 8, 8, 8 }, 1, 0, 4, 4, Offset(Rgb8_Result4x4, 1, 0, 6, 6) }; + yield return new object[] { Rgb8_Bytes4x4, new ushort[] { 8, 8, 8 }, 0, 1, 4, 4, Offset(Rgb8_Result4x4, 0, 1, 6, 6) }; + yield return new object[] { Rgb8_Bytes4x4, new ushort[] { 8, 8, 8 }, 1, 1, 4, 4, Offset(Rgb8_Result4x4, 1, 1, 6, 6) }; } } @@ -174,11 +174,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { get { - yield return new object[] { Rgb484_Bytes4x4, new[] { 4u, 8u, 4u }, 0, 0, 4, 4, Rgb484_Result4x4 }; - yield return new object[] { Rgb484_Bytes4x4, new[] { 4u, 8u, 4u }, 0, 0, 4, 4, Offset(Rgb484_Result4x4, 0, 0, 6, 6) }; - yield return new object[] { Rgb484_Bytes4x4, new[] { 4u, 8u, 4u }, 1, 0, 4, 4, Offset(Rgb484_Result4x4, 1, 0, 6, 6) }; - yield return new object[] { Rgb484_Bytes4x4, new[] { 4u, 8u, 4u }, 0, 1, 4, 4, Offset(Rgb484_Result4x4, 0, 1, 6, 6) }; - yield return new object[] { Rgb484_Bytes4x4, new[] { 4u, 8u, 4u }, 1, 1, 4, 4, Offset(Rgb484_Result4x4, 1, 1, 6, 6) }; + yield return new object[] { Rgb484_Bytes4x4, new ushort[] { 4, 8, 4 }, 0, 0, 4, 4, Rgb484_Result4x4 }; + yield return new object[] { Rgb484_Bytes4x4, new ushort[] { 4, 8, 4 }, 0, 0, 4, 4, Offset(Rgb484_Result4x4, 0, 0, 6, 6) }; + yield return new object[] { Rgb484_Bytes4x4, new ushort[] { 4, 8, 4 }, 1, 0, 4, 4, Offset(Rgb484_Result4x4, 1, 0, 6, 6) }; + yield return new object[] { Rgb484_Bytes4x4, new ushort[] { 4, 8, 4 }, 0, 1, 4, 4, Offset(Rgb484_Result4x4, 0, 1, 6, 6) }; + yield return new object[] { Rgb484_Bytes4x4, new ushort[] { 4, 8, 4 }, 1, 1, 4, 4, Offset(Rgb484_Result4x4, 1, 1, 6, 6) }; } } @@ -186,11 +186,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [MemberData(nameof(Rgb4_Data))] [MemberData(nameof(Rgb8_Data))] [MemberData(nameof(Rgb484_Data))] - public void Decode_WritesPixelData(byte[][] inputData, uint[] bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult) + public void Decode_WritesPixelData(byte[][] inputData, ushort[] bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult) { AssertDecode(expectedResult, pixels => { - RgbPlanarTiffColor.Decode(inputData, bitsPerSample, pixels, left, top, width, height); + new RgbPlanarTiffColor(bitsPerSample).Decode(inputData, pixels, left, top, width, height); }); } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs index 5683e4752..f1ba32c5d 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs @@ -48,17 +48,17 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { get { - yield return new object[] { Rgb4_Bytes4x4, new[] { 4u, 4u, 4u }, 0, 0, 4, 4, Rgb4_Result4x4 }; - yield return new object[] { Rgb4_Bytes4x4, new[] { 4u, 4u, 4u }, 0, 0, 4, 4, Offset(Rgb4_Result4x4, 0, 0, 6, 6) }; - yield return new object[] { Rgb4_Bytes4x4, new[] { 4u, 4u, 4u }, 1, 0, 4, 4, Offset(Rgb4_Result4x4, 1, 0, 6, 6) }; - yield return new object[] { Rgb4_Bytes4x4, new[] { 4u, 4u, 4u }, 0, 1, 4, 4, Offset(Rgb4_Result4x4, 0, 1, 6, 6) }; - yield return new object[] { Rgb4_Bytes4x4, new[] { 4u, 4u, 4u }, 1, 1, 4, 4, Offset(Rgb4_Result4x4, 1, 1, 6, 6) }; - - yield return new object[] { Rgb4_Bytes3x4, new[] { 4u, 4u, 4u }, 0, 0, 3, 4, Rgb4_Result3x4 }; - yield return new object[] { Rgb4_Bytes3x4, new[] { 4u, 4u, 4u }, 0, 0, 3, 4, Offset(Rgb4_Result3x4, 0, 0, 6, 6) }; - yield return new object[] { Rgb4_Bytes3x4, new[] { 4u, 4u, 4u }, 1, 0, 3, 4, Offset(Rgb4_Result3x4, 1, 0, 6, 6) }; - yield return new object[] { Rgb4_Bytes3x4, new[] { 4u, 4u, 4u }, 0, 1, 3, 4, Offset(Rgb4_Result3x4, 0, 1, 6, 6) }; - yield return new object[] { Rgb4_Bytes3x4, new[] { 4u, 4u, 4u }, 1, 1, 3, 4, Offset(Rgb4_Result3x4, 1, 1, 6, 6) }; + yield return new object[] { Rgb4_Bytes4x4, new ushort[] { 4, 4, 4 }, 0, 0, 4, 4, Rgb4_Result4x4 }; + yield return new object[] { Rgb4_Bytes4x4, new ushort[] { 4, 4, 4 }, 0, 0, 4, 4, Offset(Rgb4_Result4x4, 0, 0, 6, 6) }; + yield return new object[] { Rgb4_Bytes4x4, new ushort[] { 4, 4, 4 }, 1, 0, 4, 4, Offset(Rgb4_Result4x4, 1, 0, 6, 6) }; + yield return new object[] { Rgb4_Bytes4x4, new ushort[] { 4, 4, 4 }, 0, 1, 4, 4, Offset(Rgb4_Result4x4, 0, 1, 6, 6) }; + yield return new object[] { Rgb4_Bytes4x4, new ushort[] { 4, 4, 4 }, 1, 1, 4, 4, Offset(Rgb4_Result4x4, 1, 1, 6, 6) }; + + yield return new object[] { Rgb4_Bytes3x4, new ushort[] { 4, 4, 4 }, 0, 0, 3, 4, Rgb4_Result3x4 }; + yield return new object[] { Rgb4_Bytes3x4, new ushort[] { 4, 4, 4 }, 0, 0, 3, 4, Offset(Rgb4_Result3x4, 0, 0, 6, 6) }; + yield return new object[] { Rgb4_Bytes3x4, new ushort[] { 4, 4, 4 }, 1, 0, 3, 4, Offset(Rgb4_Result3x4, 1, 0, 6, 6) }; + yield return new object[] { Rgb4_Bytes3x4, new ushort[] { 4, 4, 4 }, 0, 1, 3, 4, Offset(Rgb4_Result3x4, 0, 1, 6, 6) }; + yield return new object[] { Rgb4_Bytes3x4, new ushort[] { 4, 4, 4 }, 1, 1, 3, 4, Offset(Rgb4_Result3x4, 1, 1, 6, 6) }; } } @@ -90,11 +90,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { get { - yield return new object[] { Rgb8_Bytes4x4, new[] { 8u, 8u, 8u }, 0, 0, 4, 4, Rgb8_Result4x4 }; - yield return new object[] { Rgb8_Bytes4x4, new[] { 8u, 8u, 8u }, 0, 0, 4, 4, Offset(Rgb8_Result4x4, 0, 0, 6, 6) }; - yield return new object[] { Rgb8_Bytes4x4, new[] { 8u, 8u, 8u }, 1, 0, 4, 4, Offset(Rgb8_Result4x4, 1, 0, 6, 6) }; - yield return new object[] { Rgb8_Bytes4x4, new[] { 8u, 8u, 8u }, 0, 1, 4, 4, Offset(Rgb8_Result4x4, 0, 1, 6, 6) }; - yield return new object[] { Rgb8_Bytes4x4, new[] { 8u, 8u, 8u }, 1, 1, 4, 4, Offset(Rgb8_Result4x4, 1, 1, 6, 6) }; + yield return new object[] { Rgb8_Bytes4x4, new ushort[] { 8, 8, 8 }, 0, 0, 4, 4, Rgb8_Result4x4 }; + yield return new object[] { Rgb8_Bytes4x4, new ushort[] { 8, 8, 8 }, 0, 0, 4, 4, Offset(Rgb8_Result4x4, 0, 0, 6, 6) }; + yield return new object[] { Rgb8_Bytes4x4, new ushort[] { 8, 8, 8 }, 1, 0, 4, 4, Offset(Rgb8_Result4x4, 1, 0, 6, 6) }; + yield return new object[] { Rgb8_Bytes4x4, new ushort[] { 8, 8, 8 }, 0, 1, 4, 4, Offset(Rgb8_Result4x4, 0, 1, 6, 6) }; + yield return new object[] { Rgb8_Bytes4x4, new ushort[] { 8, 8, 8 }, 1, 1, 4, 4, Offset(Rgb8_Result4x4, 1, 1, 6, 6) }; } } @@ -126,11 +126,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { get { - yield return new object[] { Rgb484_Bytes4x4, new[] { 4u, 8u, 4u }, 0, 0, 4, 4, Rgb484_Result4x4 }; - yield return new object[] { Rgb484_Bytes4x4, new[] { 4u, 8u, 4u }, 0, 0, 4, 4, Offset(Rgb484_Result4x4, 0, 0, 6, 6) }; - yield return new object[] { Rgb484_Bytes4x4, new[] { 4u, 8u, 4u }, 1, 0, 4, 4, Offset(Rgb484_Result4x4, 1, 0, 6, 6) }; - yield return new object[] { Rgb484_Bytes4x4, new[] { 4u, 8u, 4u }, 0, 1, 4, 4, Offset(Rgb484_Result4x4, 0, 1, 6, 6) }; - yield return new object[] { Rgb484_Bytes4x4, new[] { 4u, 8u, 4u }, 1, 1, 4, 4, Offset(Rgb484_Result4x4, 1, 1, 6, 6) }; + yield return new object[] { Rgb484_Bytes4x4, new ushort[] { 4, 8, 4 }, 0, 0, 4, 4, Rgb484_Result4x4 }; + yield return new object[] { Rgb484_Bytes4x4, new ushort[] { 4, 8, 4 }, 0, 0, 4, 4, Offset(Rgb484_Result4x4, 0, 0, 6, 6) }; + yield return new object[] { Rgb484_Bytes4x4, new ushort[] { 4, 8, 4 }, 1, 0, 4, 4, Offset(Rgb484_Result4x4, 1, 0, 6, 6) }; + yield return new object[] { Rgb484_Bytes4x4, new ushort[] { 4, 8, 4 }, 0, 1, 4, 4, Offset(Rgb484_Result4x4, 0, 1, 6, 6) }; + yield return new object[] { Rgb484_Bytes4x4, new ushort[] { 4, 8, 4 }, 1, 1, 4, 4, Offset(Rgb484_Result4x4, 1, 1, 6, 6) }; } } @@ -138,21 +138,21 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [MemberData(nameof(Rgb4_Data))] [MemberData(nameof(Rgb8_Data))] [MemberData(nameof(Rgb484_Data))] - public void Decode_WritesPixelData(byte[] inputData, uint[] bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult) + public void Decode_WritesPixelData(byte[] inputData, ushort[] bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult) { AssertDecode(expectedResult, pixels => { - RgbTiffColor.Decode(inputData, bitsPerSample, pixels, left, top, width, height); + new RgbTiffColor(bitsPerSample).Decode(inputData, pixels, left, top, width, height); }); } [Theory] [MemberData(nameof(Rgb8_Data))] - public void Decode_WritesPixelData_8Bit(byte[] inputData, uint[] bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult) + public void Decode_WritesPixelData_8Bit(byte[] inputData, ushort[] bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult) { AssertDecode(expectedResult, pixels => { - Rgb888TiffColor.Decode(inputData, pixels, left, top, width, height); + new Rgb888TiffColor().Decode(inputData, pixels, left, top, width, height); }); } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs index 5334e2984..faea296d0 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs @@ -19,52 +19,52 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff private static Rgba32 Bit0 = new Rgba32(255, 255, 255, 255); private static Rgba32 Bit1 = new Rgba32(0, 0, 0, 255); - private static byte[] Bilevel_Bytes4x4 = new byte[] { 0b01010000, + private static readonly byte[] Bilevel_Bytes4x4 = new byte[] { 0b01010000, 0b11110000, 0b01110000, 0b10010000 }; - private static Rgba32[][] Bilevel_Result4x4 = new[] { new[] { Bit0, Bit1, Bit0, Bit1 }, + private static readonly Rgba32[][] Bilevel_Result4x4 = new[] { new[] { Bit0, Bit1, Bit0, Bit1 }, new[] { Bit1, Bit1, Bit1, Bit1 }, new[] { Bit0, Bit1, Bit1, Bit1 }, new[] { Bit1, Bit0, Bit0, Bit1 }}; - private static byte[] Bilevel_Bytes12x4 = new byte[] { 0b01010101, 0b01010000, + private static readonly byte[] Bilevel_Bytes12x4 = new byte[] { 0b01010101, 0b01010000, 0b11111111, 0b11111111, 0b01101001, 0b10100000, 0b10010000, 0b01100000}; - private static Rgba32[][] Bilevel_Result12x4 = new[] { new[] { Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1 }, + private static readonly Rgba32[][] Bilevel_Result12x4 = new[] { new[] { Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1 }, new[] { Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1 }, new[] { Bit0, Bit1, Bit1, Bit0, Bit1, Bit0, Bit0, Bit1, Bit1, Bit0, Bit1, Bit0 }, new[] { Bit1, Bit0, Bit0, Bit1, Bit0, Bit0, Bit0, Bit0, Bit0, Bit1, Bit1, Bit0 }}; - private static byte[] Grayscale4_Bytes4x4 = new byte[] { 0x8F, 0x0F, + private static readonly byte[] Grayscale4_Bytes4x4 = new byte[] { 0x8F, 0x0F, 0xFF, 0xFF, 0x08, 0x8F, 0xF0, 0xF8 }; - private static Rgba32[][] Grayscale4_Result4x4 = new[] { new[] { Gray8, GrayF, Gray0, GrayF }, + private static readonly Rgba32[][] Grayscale4_Result4x4 = new[] { new[] { Gray8, GrayF, Gray0, GrayF }, new[] { GrayF, GrayF, GrayF, GrayF }, new[] { Gray0, Gray8, Gray8, GrayF }, new[] { GrayF, Gray0, GrayF, Gray8 }}; - private static byte[] Grayscale4_Bytes3x4 = new byte[] { 0x8F, 0x00, + private static readonly byte[] Grayscale4_Bytes3x4 = new byte[] { 0x8F, 0x00, 0xFF, 0xF0, 0x08, 0x80, 0xF0, 0xF0 }; - private static Rgba32[][] Grayscale4_Result3x4 = new[] { new[] { Gray8, GrayF, Gray0 }, + private static readonly Rgba32[][] Grayscale4_Result3x4 = new[] { new[] { Gray8, GrayF, Gray0 }, new[] { GrayF, GrayF, GrayF }, new[] { Gray0, Gray8, Gray8 }, new[] { GrayF, Gray0, GrayF }}; - private static byte[] Grayscale8_Bytes4x4 = new byte[] { 128, 255, 000, 255, + private static readonly byte[] Grayscale8_Bytes4x4 = new byte[] { 128, 255, 000, 255, 255, 255, 255, 255, 000, 128, 128, 255, 255, 000, 255, 128 }; - private static Rgba32[][] Grayscale8_Result4x4 = new[] { new[] { Gray128, Gray255, Gray000, Gray255 }, + private static readonly Rgba32[][] Grayscale8_Result4x4 = new[] { new[] { Gray128, Gray255, Gray000, Gray255 }, new[] { Gray255, Gray255, Gray255, Gray255 }, new[] { Gray000, Gray128, Gray128, Gray255 }, new[] { Gray255, Gray000, Gray255, Gray128 }}; @@ -125,7 +125,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { AssertDecode(expectedResult, pixels => { - WhiteIsZeroTiffColor.Decode(inputData, new[] { (uint)bitsPerSample }, pixels, left, top, width, height); + new WhiteIsZeroTiffColor(new[] { (ushort)bitsPerSample }).Decode(inputData, pixels, left, top, width, height); }); } @@ -135,7 +135,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { AssertDecode(expectedResult, pixels => { - WhiteIsZero1TiffColor.Decode(inputData, pixels, left, top, width, height); + new WhiteIsZero1TiffColor().Decode(inputData, pixels, left, top, width, height); }); } @@ -145,7 +145,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { AssertDecode(expectedResult, pixels => { - WhiteIsZero4TiffColor.Decode(inputData, pixels, left, top, width, height); + new WhiteIsZero4TiffColor().Decode(inputData, pixels, left, top, width, height); }); } @@ -155,7 +155,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { AssertDecode(expectedResult, pixels => { - WhiteIsZero8TiffColor.Decode(inputData, pixels, left, top, width, height); + new WhiteIsZero8TiffColor().Decode(inputData, pixels, left, top, width, height); }); } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index b4d2a4894..58b917937 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -1,12 +1,9 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. // ReSharper disable InconsistentNaming - -using System.IO; -using SixLabors.ImageSharp.Formats; -using SixLabors.ImageSharp.Formats.Png; +using System; using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; @@ -15,13 +12,26 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff { - [Trait("Category", "Tiff.BlackBox")] + [Trait("Category", "Tiff.BlackBox.Decoder")] + [Trait("Category", "Tiff")] public class TiffDecoderTests { - public static readonly string[] CommonTestImages = TestImages.Tiff.All; + public static readonly string[] SingleTestImages = TestImages.Tiff.All; + + public static readonly string[] MultiframeTestImages = TestImages.Tiff.Multiframes; + + public static readonly string[] NotSupportedImages = TestImages.Tiff.NotSupported; [Theory] - [WithFileCollection(nameof(CommonTestImages), PixelTypes.Rgba32)] + [WithFileCollection(nameof(NotSupportedImages), PixelTypes.Rgba32)] + public void ThrowsNotSupported(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + Assert.Throws(() => provider.GetImage(new TiffDecoder())); + } + + [Theory] + [WithFileCollection(nameof(SingleTestImages), PixelTypes.Rgba32)] public void Decode(TestImageProvider provider) where TPixel : unmanaged, IPixel { @@ -31,5 +41,22 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff image.CompareToOriginal(provider, ImageComparer.Exact, new MagickReferenceDecoder()); } } + + [Theory] + [WithFileCollection(nameof(MultiframeTestImages), PixelTypes.Rgba32)] + public void DecodeMultiframe(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(new TiffDecoder())) + { + Assert.True(image.Frames.Count > 1); + + image.DebugSave(provider); + image.CompareToOriginal(provider, ImageComparer.Exact, new MagickReferenceDecoder()); + + image.DebugSaveMultiFrame(provider); + image.CompareToOriginalMultiFrame(provider, ImageComparer.Exact, new MagickReferenceDecoder()); + } + } } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs new file mode 100644 index 000000000..6c210eb1e --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -0,0 +1,105 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Metadata; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Formats.Tiff +{ + [Trait("Category", "Tiff.BlackBox")] + [Trait("Category", "Tiff")] + public class TiffMetadataTests + { + public static readonly string[] MetadataImages = TestImages.Tiff.Metadata; + + [Theory] + [WithFile(TestImages.Tiff.SampleMetadata, PixelTypes.Rgba32, false)] + [WithFile(TestImages.Tiff.SampleMetadata, PixelTypes.Rgba32, true)] + public void MetadataProfiles(TestImageProvider provider, bool ignoreMetadata) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(new TiffDecoder() { IgnoreMetadata = ignoreMetadata })) + { + TiffMetadata meta = image.Metadata.GetTiffMetadata(); + Assert.NotNull(meta); + if (ignoreMetadata) + { + Assert.Null(meta.XmpProfile); + } + else + { + Assert.NotNull(meta.XmpProfile); + } + } + } + + [Theory] + [WithFile(TestImages.Tiff.SampleMetadata, PixelTypes.Rgba32)] + public void BaselineTags(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(new TiffDecoder())) + { + TiffMetadata meta = image.Metadata.GetTiffMetadata(); + + Assert.NotNull(meta); + Assert.Equal(TiffByteOrder.LittleEndian, meta.ByteOrder); + Assert.Equal(PixelResolutionUnit.PixelsPerInch, image.Metadata.ResolutionUnits); + Assert.Equal(10, image.Metadata.HorizontalResolution); + Assert.Equal(10, image.Metadata.VerticalResolution); + + TiffFrameMetadata frame = image.Frames.RootFrame.Metadata.GetTiffMetadata(); + Assert.Equal(32u, frame.Width); + Assert.Equal(32u, frame.Height); + Assert.Equal(new ushort[] { 8, 8, 8 }, frame.BitsPerSample); + Assert.Equal(TiffCompression.Lzw, frame.Compression); + Assert.Equal(TiffPhotometricInterpretation.Rgb, frame.PhotometricInterpretation); + Assert.Equal("This is Название", frame.ImageDescription); + Assert.Equal("This is Изготовитель камеры", frame.Make); + Assert.Equal("This is Модель камеры", frame.Model); + Assert.Equal(new uint[] { 8 }, frame.StripOffsets); + Assert.Equal(32u, frame.RowsPerStrip); + Assert.Equal(new uint[] { 750 }, frame.StripByteCounts); + Assert.Equal(10, frame.HorizontalResolution); + Assert.Equal(10, frame.VerticalResolution); + Assert.Equal(TiffPlanarConfiguration.Chunky, frame.PlanarConfiguration); + Assert.Equal(TiffResolutionUnit.Inch, frame.ResolutionUnit); + Assert.Equal("IrfanView", frame.Software); + Assert.Equal(null, frame.DateTime); + Assert.Equal("This is;Автор", frame.Artist); + Assert.Equal(null, frame.HostComputer); + Assert.Equal(null, frame.ColorMap); + Assert.Equal(null, frame.ExtraSamples); + Assert.Equal("This is Авторские права", frame.Copyright); + } + } + + [Theory] + [WithFile(TestImages.Tiff.MultiframeDeflateWithPreview, PixelTypes.Rgba32)] + public void SubfileType(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(new TiffDecoder())) + { + TiffMetadata meta = image.Metadata.GetTiffMetadata(); + Assert.NotNull(meta); + + Assert.Equal(2, image.Frames.Count); + + TiffFrameMetadata frame0 = image.Frames[0].Metadata.GetTiffMetadata(); + Assert.Equal(TiffNewSubfileType.FullImage, frame0.NewSubfileType); + Assert.Equal(null, frame0.SubfileType); + Assert.Equal(255u, frame0.Width); + Assert.Equal(255u, frame0.Height); + + TiffFrameMetadata frame1 = image.Frames[1].Metadata.GetTiffMetadata(); + Assert.Equal(TiffNewSubfileType.Preview, frame1.NewSubfileType); + Assert.Equal(TiffSubfileType.Preview, frame1.SubfileType); + Assert.Equal(255u, frame1.Width); + Assert.Equal(255u, frame1.Height); + } + } + } +} diff --git a/tests/ImageSharp.Tests/TestUtilities/Tiff/ITiffGenDataSource.cs b/tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TestUtilities/Tiff/ITiffGenDataSource.cs similarity index 100% rename from tests/ImageSharp.Tests/TestUtilities/Tiff/ITiffGenDataSource.cs rename to tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TestUtilities/Tiff/ITiffGenDataSource.cs diff --git a/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenDataBlock.cs b/tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TestUtilities/Tiff/TiffGenDataBlock.cs similarity index 100% rename from tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenDataBlock.cs rename to tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TestUtilities/Tiff/TiffGenDataBlock.cs diff --git a/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenDataReference.cs b/tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TestUtilities/Tiff/TiffGenDataReference.cs similarity index 100% rename from tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenDataReference.cs rename to tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TestUtilities/Tiff/TiffGenDataReference.cs diff --git a/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenEntry.cs b/tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TestUtilities/Tiff/TiffGenEntry.cs similarity index 100% rename from tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenEntry.cs rename to tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TestUtilities/Tiff/TiffGenEntry.cs diff --git a/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenExtensions.cs b/tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TestUtilities/Tiff/TiffGenExtensions.cs similarity index 100% rename from tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenExtensions.cs rename to tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TestUtilities/Tiff/TiffGenExtensions.cs diff --git a/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenHeader.cs b/tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TestUtilities/Tiff/TiffGenHeader.cs similarity index 100% rename from tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenHeader.cs rename to tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TestUtilities/Tiff/TiffGenHeader.cs diff --git a/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenIfd.cs b/tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TestUtilities/Tiff/TiffGenIfd.cs similarity index 100% rename from tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenIfd.cs rename to tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TestUtilities/Tiff/TiffGenIfd.cs diff --git a/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenIfdExtensions.cs b/tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TestUtilities/Tiff/TiffGenIfdExtensions.cs similarity index 100% rename from tests/ImageSharp.Tests/TestUtilities/Tiff/TiffGenIfdExtensions.cs rename to tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TestUtilities/Tiff/TiffGenIfdExtensions.cs diff --git a/tests/ImageSharp.Tests/TestUtilities/Tiff/TiffIfdParser.cs b/tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TestUtilities/Tiff/TiffIfdParser.cs similarity index 100% rename from tests/ImageSharp.Tests/TestUtilities/Tiff/TiffIfdParser.cs rename to tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TestUtilities/Tiff/TiffIfdParser.cs diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TiffDecoderHeaderTests.cs similarity index 100% rename from tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs rename to tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TiffDecoderHeaderTests.cs diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TiffDecoderIfdEntryTests.cs similarity index 100% rename from tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs rename to tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TiffDecoderIfdEntryTests.cs diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderIfdTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TiffDecoderIfdTests.cs similarity index 100% rename from tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderIfdTests.cs rename to tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TiffDecoderIfdTests.cs diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderImageTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TiffDecoderImageTests.cs similarity index 100% rename from tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderImageTests.cs rename to tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TiffDecoderImageTests.cs diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TiffDecoderMetadataTests.cs similarity index 100% rename from tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs rename to tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TiffDecoderMetadataTests.cs diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderIfdTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TiffEncoderIfdTests.cs similarity index 100% rename from tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderIfdTests.cs rename to tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TiffEncoderIfdTests.cs diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TiffEncoderMetadataTests.cs similarity index 100% rename from tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderMetadataTests.cs rename to tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TiffEncoderMetadataTests.cs diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryCreatorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TiffIfd/TiffIfdEntryCreatorTests.cs similarity index 100% rename from tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryCreatorTests.cs rename to tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TiffIfd/TiffIfdEntryCreatorTests.cs diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TiffIfd/TiffIfdEntryTests.cs similarity index 100% rename from tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryTests.cs rename to tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TiffIfd/TiffIfdEntryTests.cs diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TiffIfd/TiffIfdTests.cs similarity index 100% rename from tests/ImageSharp.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs rename to tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TiffIfd/TiffIfdTests.cs diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffImageFormatDetectorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TiffImageFormatDetectorTests.cs similarity index 100% rename from tests/ImageSharp.Tests/Formats/Tiff/TiffImageFormatDetectorTests.cs rename to tests/ImageSharp.Tests/Formats/Tiff/__obsolete/TiffImageFormatDetectorTests.cs diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index 0761b0978..a297d4143 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -12,6 +12,12 @@ true + + + + + + diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs index de8278a33..f3af4dabd 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; using System.Threading; @@ -9,6 +10,7 @@ using System.Threading.Tasks; using ImageMagick; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs @@ -54,30 +56,37 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs public Image Decode(Configuration configuration, Stream stream) where TPixel : unmanaged, IPixel { - using var magickImage = new MagickImage(stream); - var result = new Image(configuration, magickImage.Width, magickImage.Height); - MemoryGroup resultPixels = result.GetRootFramePixelBuffer().FastMemoryGroup; - - using (IPixelCollection pixels = magickImage.GetPixelsUnsafe()) + using var magickImageCollection = new MagickImageCollection(stream); + var framesList = new List>(); + foreach (IMagickImage magicFrame in magickImageCollection) { - if (magickImage.Depth == 8) - { - byte[] data = pixels.ToByteArray(PixelMapping.RGBA); + var frame = new ImageFrame(configuration, magicFrame.Width, magicFrame.Height); + framesList.Add(frame); - FromRgba32Bytes(configuration, data, resultPixels); - } - else if (magickImage.Depth == 16) + MemoryGroup framePixels = frame.PixelBuffer.FastMemoryGroup; + using (IPixelCollection pixels = magicFrame.GetPixelsUnsafe()) { - ushort[] data = pixels.ToShortArray(PixelMapping.RGBA); - Span bytes = MemoryMarshal.Cast(data.AsSpan()); - FromRgba64Bytes(configuration, bytes, resultPixels); - } - else - { - throw new InvalidOperationException(); + if (magicFrame.Depth == 8) + { + byte[] data = pixels.ToByteArray(PixelMapping.RGBA); + + FromRgba32Bytes(configuration, data, framePixels); + } + else if (magicFrame.Depth == 16) + { + ushort[] data = pixels.ToShortArray(PixelMapping.RGBA); + Span bytes = MemoryMarshal.Cast(data.AsSpan()); + FromRgba64Bytes(configuration, bytes, framePixels); + } + else + { + throw new InvalidOperationException(); + } } } + var result = new Image(configuration, new ImageMetadata(), framesList); + return result; } diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index 073db1efe..de9b8a02e 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -537,6 +537,31 @@ namespace SixLabors.ImageSharp.Tests return image; } + public static Image CompareToOriginalMultiFrame( + this Image image, + ITestImageProvider provider, + ImageComparer comparer, + IImageDecoder referenceDecoder = null) + where TPixel : unmanaged, IPixel + { + string path = TestImageProvider.GetFilePathOrNull(provider); + if (path == null) + { + throw new InvalidOperationException("CompareToOriginal() works only with file providers!"); + } + + var testFile = TestFile.Create(path); + + referenceDecoder = referenceDecoder ?? TestEnvironment.GetReferenceDecoder(path); + + using (var original = Image.Load(testFile.Bytes, referenceDecoder)) + { + comparer.VerifySimilarity(original, image); + } + + return image; + } + /// /// Utility method for doing the following in one step: /// 1. Executing an operation (taken as a delegate) From a341782d6235abb7a3fb5444a43ca3a4ab750d7e Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Tue, 25 Aug 2020 21:41:51 +0300 Subject: [PATCH 069/275] Rename --- .../Formats/Tiff/{TiffIfd/TiffIfd.cs => Ifd/DirectoryReader.cs} | 0 .../Formats/Tiff/{TiffIfd/TiffIfdEntry.cs => Ifd/EntryReader.cs} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/ImageSharp/Formats/Tiff/{TiffIfd/TiffIfd.cs => Ifd/DirectoryReader.cs} (100%) rename src/ImageSharp/Formats/Tiff/{TiffIfd/TiffIfdEntry.cs => Ifd/EntryReader.cs} (100%) diff --git a/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfd.cs b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs similarity index 100% rename from src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfd.cs rename to src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs diff --git a/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntry.cs b/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs similarity index 100% rename from src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntry.cs rename to src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs From 1d77c2e5a2234e82c2fb6a888c0382c8319a2108 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Thu, 27 Aug 2020 20:47:07 +0300 Subject: [PATCH 070/275] Improve decoders - performance and memory usage. Implement Identify methods and add tests. Report not supported Predictor and SampleFormat features. --- .../Tiff/Compression/CompressionFactory.cs | 42 ----- .../Compression/DeflateTiffCompression.cs | 23 ++- .../Tiff/Compression/LzwTiffCompression.cs | 22 +-- .../Tiff/Compression/NoneTiffCompression.cs | 20 +-- .../Compression/PackBitsTiffCompression.cs | 80 ++++----- .../Tiff/Compression/TiffBaseCompression.cs | 29 +++ .../Compression/TiffCompressionFactory.cs | 28 +++ .../Formats/Tiff/Constants/TiffPredictor.cs | 26 +++ .../Tiff/Constants/TiffSampleFormat.cs | 41 +++++ .../Formats/Tiff/Ifd/EntryReader.cs | 1 + .../BlackIsZero1TiffColor.cs | 20 +-- .../BlackIsZero4TiffColor.cs | 19 +- .../BlackIsZero8TiffColor.cs | 20 +-- .../BlackIsZeroTiffColor.cs | 22 +-- .../PaletteTiffColor.cs | 23 +-- .../Rgb888TiffColor.cs | 24 +-- .../RgbPlanarTiffColor.cs | 23 ++- .../PhotometricInterpretation/RgbTiffColor.cs | 31 ++-- ...olorDecoder.cs => TiffBaseColorDecoder.cs} | 20 +-- .../TiffColorDecoderFactory.cs | 2 +- .../WhiteIsZero1TiffColor.cs | 20 +-- .../WhiteIsZero4TiffColor.cs | 19 +- .../WhiteIsZero8TiffColor.cs | 20 +-- .../WhiteIsZeroTiffColor.cs | 19 +- .../Formats/Tiff/Streams/TiffStreamFactory.cs | 10 +- src/ImageSharp/Formats/Tiff/TiffDecoder.cs | 29 +-- .../Formats/Tiff/TiffDecoderCore.cs | 168 +++++++++++------- .../Formats/Tiff/TiffDecoderHelpers.cs | 36 ++-- .../Formats/Tiff/TiffFrameMetadata.cs | 99 ++++++----- .../Formats/Tiff/Utils/BitReader.cs | 14 +- .../Formats/Tiff/Utils/TiffLzwDecoder.cs | 6 +- .../Formats/Tiff/Utils/TiffUtils.cs | 7 +- .../Profiles/Exif/Tags/ExifTag.LongArray.cs | 1 - .../Codecs/DecodeTiff.cs | 2 +- tests/ImageSharp.Tests/FileTestBase.cs | 4 +- .../DeflateTiffCompressionTests.cs | 3 +- .../Compression/LzwTiffCompressionTests.cs | 2 +- .../Compression/NoneTiffCompressionTests.cs | 2 +- .../PackBitsTiffCompressionTests.cs | 3 +- .../RgbPlanarTiffColorTests.cs | 11 +- .../Formats/Tiff/TiffDecoderTests.cs | 23 +++ .../Formats/Tiff/TiffMetadataTests.cs | 20 ++- tests/ImageSharp.Tests/TestImages.cs | 21 ++- .../Decode_Rgba32_Calliphora_rgb_deflate.png | 3 - .../Decode_Rgba32_Calliphora_rgb_lzw.png | 3 - .../net472/Decode_Rgba32_metadata_sample.png | 3 - .../net472/Decode_Rgba32_multipage_lzw.png | 3 - .../net472/Decode_Rgba32_rgb_deflate.png | 3 - .../Decode_Rgba32_rgb_lzw_multistrip.png | 3 - .../net472/Decode_Rgba32_rgb_small_lzw.png | 3 + .../Decode_Rgba32_Calliphora_rgb_deflate.png | 3 - .../Decode_Rgba32_Calliphora_rgb_lzw.png | 3 - .../Decode_Rgba32_metadata_sample.png | 3 - .../Decode_Rgba32_multipage_lzw.png | 3 - .../Decode_Rgba32_rgb_deflate.png | 3 - .../Decode_Rgba32_rgb_lzw_multistrip.png | 3 - .../Decode_Rgba32_rgb_small_deflate.png | 3 + .../Decode_Rgba32_Calliphora_rgb_deflate.png | 3 - .../Decode_Rgba32_Calliphora_rgb_lzw.png | 3 - .../Decode_Rgba32_metadata_sample.png | 3 - .../Decode_Rgba32_multipage_lzw.png | 3 - .../Decode_Rgba32_rgb_deflate.png | 3 - .../netcoreapp3.1/Decode_Rgba32_rgb_lzw.png | 3 - .../Decode_Rgba32_rgb_lzw_multistrip.png | 3 - .../Decode_Rgba32_rgb_small_lzw.png | 3 + tests/Images/Input/Tiff/metadata_sample.tiff | 4 +- .../Images/Input/Tiff/rgb_small_deflate.tiff | 3 + tests/Images/Input/Tiff/rgb_small_lzw.tiff | 3 + 68 files changed, 583 insertions(+), 548 deletions(-) delete mode 100644 src/ImageSharp/Formats/Tiff/Compression/CompressionFactory.cs create mode 100644 src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs create mode 100644 src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs create mode 100644 src/ImageSharp/Formats/Tiff/Constants/TiffPredictor.cs create mode 100644 src/ImageSharp/Formats/Tiff/Constants/TiffSampleFormat.cs rename src/ImageSharp/Formats/Tiff/PhotometricInterpretation/{TiffColorDecoder.cs => TiffBaseColorDecoder.cs} (61%) delete mode 100644 tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_Calliphora_rgb_deflate.png delete mode 100644 tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_Calliphora_rgb_lzw.png delete mode 100644 tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_metadata_sample.png delete mode 100644 tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_multipage_lzw.png delete mode 100644 tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_rgb_deflate.png delete mode 100644 tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_rgb_lzw_multistrip.png create mode 100644 tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_rgb_small_lzw.png delete mode 100644 tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_Calliphora_rgb_deflate.png delete mode 100644 tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_Calliphora_rgb_lzw.png delete mode 100644 tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_metadata_sample.png delete mode 100644 tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_multipage_lzw.png delete mode 100644 tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_rgb_deflate.png delete mode 100644 tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_rgb_lzw_multistrip.png create mode 100644 tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_rgb_small_deflate.png delete mode 100644 tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_Calliphora_rgb_deflate.png delete mode 100644 tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_Calliphora_rgb_lzw.png delete mode 100644 tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_metadata_sample.png delete mode 100644 tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_multipage_lzw.png delete mode 100644 tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_deflate.png delete mode 100644 tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_lzw.png delete mode 100644 tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_lzw_multistrip.png create mode 100644 tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_small_lzw.png create mode 100644 tests/Images/Input/Tiff/rgb_small_deflate.tiff create mode 100644 tests/Images/Input/Tiff/rgb_small_lzw.tiff diff --git a/src/ImageSharp/Formats/Tiff/Compression/CompressionFactory.cs b/src/ImageSharp/Formats/Tiff/Compression/CompressionFactory.cs deleted file mode 100644 index f65b3caf3..000000000 --- a/src/ImageSharp/Formats/Tiff/Compression/CompressionFactory.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.IO; - -namespace SixLabors.ImageSharp.Formats.Tiff -{ - internal static class CompressionFactory - { - /// - /// Decompresses an image block from the input stream into the specified buffer. - /// - /// The input stream. - /// Type of the compression. - /// The offset within the file of the image block. - /// The size (in bytes) of the compressed data. - /// The buffer to write the uncompressed data. - public static void DecompressImageBlock(Stream stream, TiffCompressionType compressionType, uint offset, uint byteCount, byte[] buffer) - { - stream.Seek(offset, SeekOrigin.Begin); - - switch (compressionType) - { - case TiffCompressionType.None: - NoneTiffCompression.Decompress(stream, (int)byteCount, buffer); - break; - case TiffCompressionType.PackBits: - PackBitsTiffCompression.Decompress(stream, (int)byteCount, buffer); - break; - case TiffCompressionType.Deflate: - DeflateTiffCompression.Decompress(stream, (int)byteCount, buffer); - break; - case TiffCompressionType.Lzw: - LzwTiffCompression.Decompress(stream, (int)byteCount, buffer); - break; - default: - throw new InvalidOperationException(); - } - } - } -} diff --git a/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs index f5295de4a..e10d8195b 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs @@ -4,7 +4,7 @@ using System; using System.IO; using System.IO.Compression; -using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Tiff { @@ -14,16 +14,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Note that the 'OldDeflate' compression type is identical to the 'Deflate' compression type. /// - internal static class DeflateTiffCompression + internal class DeflateTiffCompression : TiffBaseCompression { - /// - /// Decompresses image data into the supplied buffer. - /// - /// The to read image data from. - /// The number of bytes to read from the input stream. - /// The output buffer for uncompressed data. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decompress(Stream stream, int byteCount, byte[] buffer) + public DeflateTiffCompression(MemoryAllocator allocator) + : base(allocator) + { + } + + /// + public override void Decompress(Stream stream, int byteCount, Span buffer) { // Read the 'zlib' header information int cmf = stream.ReadByte(); @@ -47,8 +46,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff // The subsequent data is the Deflate compressed data (except for the last four bytes of checksum) int headerLength = fdict ? 10 : 6; - SubStream subStream = new SubStream(stream, byteCount - headerLength); - using (DeflateStream deflateStream = new DeflateStream(subStream, CompressionMode.Decompress, true)) + var subStream = new SubStream(stream, byteCount - headerLength); + using (var deflateStream = new DeflateStream(subStream, CompressionMode.Decompress, true)) { deflateStream.ReadFull(buffer); } diff --git a/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs index 2c244b606..bba9739e2 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs @@ -1,26 +1,26 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; using System.IO; -using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Class to handle cases where TIFF image data is compressed using LZW compression. /// - internal static class LzwTiffCompression + internal class LzwTiffCompression : TiffBaseCompression { - /// - /// Decompresses image data into the supplied buffer. - /// - /// The to read image data from. - /// The number of bytes to read from the input stream. - /// The output buffer for uncompressed data. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decompress(Stream stream, int byteCount, byte[] buffer) + public LzwTiffCompression(MemoryAllocator allocator) + : base(allocator) { - SubStream subStream = new SubStream(stream, byteCount); + } + + /// + public override void Decompress(Stream stream, int byteCount, Span buffer) + { + var subStream = new SubStream(stream, byteCount); using (var decoder = new TiffLzwDecoder(subStream)) { decoder.DecodePixels(buffer.Length, 8, buffer); diff --git a/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs index c78e22b41..ad6801c6a 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs @@ -1,24 +1,24 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; using System.IO; -using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Class to handle cases where TIFF image data is not compressed. /// - internal static class NoneTiffCompression + internal class NoneTiffCompression : TiffBaseCompression { - /// - /// Decompresses image data into the supplied buffer. - /// - /// The to read image data from. - /// The number of bytes to read from the input stream. - /// The output buffer for uncompressed data. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decompress(Stream stream, int byteCount, byte[] buffer) + public NoneTiffCompression(MemoryAllocator allocator) + : base(allocator) + { + } + + /// + public override void Decompress(Stream stream, int byteCount, Span buffer) { stream.ReadFull(buffer, byteCount); } diff --git a/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs index 9d5f041bf..9862fea74 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs @@ -4,69 +4,63 @@ using System; using System.Buffers; using System.IO; -using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Class to handle cases where TIFF image data is compressed using PackBits compression. /// - internal static class PackBitsTiffCompression + internal class PackBitsTiffCompression : TiffBaseCompression { - /// - /// Decompresses image data into the supplied buffer. - /// - /// The to read image data from. - /// The number of bytes to read from the input stream. - /// The output buffer for uncompressed data. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decompress(Stream stream, int byteCount, byte[] buffer) + public PackBitsTiffCompression(MemoryAllocator allocator) + : base(allocator) { - byte[] compressedData = ArrayPool.Shared.Rent(byteCount); + } + + /// + public override void Decompress(Stream stream, int byteCount, Span buffer) + { + using IMemoryOwner compressedDataMemory = this.Allocator.Allocate(byteCount); - try + Span compressedData = compressedDataMemory.GetSpan(); + + stream.ReadFull(compressedData, byteCount); + int compressedOffset = 0; + int decompressedOffset = 0; + + while (compressedOffset < byteCount) { - stream.ReadFull(compressedData, byteCount); - int compressedOffset = 0; - int decompressedOffset = 0; + byte headerByte = compressedData[compressedOffset]; - while (compressedOffset < byteCount) + if (headerByte <= (byte)127) { - byte headerByte = compressedData[compressedOffset]; - - if (headerByte <= (byte)127) - { - int literalOffset = compressedOffset + 1; - int literalLength = compressedData[compressedOffset] + 1; + int literalOffset = compressedOffset + 1; + int literalLength = compressedData[compressedOffset] + 1; - Array.Copy(compressedData, literalOffset, buffer, decompressedOffset, literalLength); + compressedData.Slice(literalOffset, literalLength).CopyTo(buffer.Slice(decompressedOffset)); - compressedOffset += literalLength + 1; - decompressedOffset += literalLength; - } - else if (headerByte == (byte)0x80) - { - compressedOffset += 1; - } - else - { - byte repeatData = compressedData[compressedOffset + 1]; - int repeatLength = 257 - headerByte; + compressedOffset += literalLength + 1; + decompressedOffset += literalLength; + } + else if (headerByte == (byte)0x80) + { + compressedOffset += 1; + } + else + { + byte repeatData = compressedData[compressedOffset + 1]; + int repeatLength = 257 - headerByte; - ArrayCopyRepeat(repeatData, buffer, decompressedOffset, repeatLength); + ArrayCopyRepeat(repeatData, buffer, decompressedOffset, repeatLength); - compressedOffset += 2; - decompressedOffset += repeatLength; - } + compressedOffset += 2; + decompressedOffset += repeatLength; } } - finally - { - ArrayPool.Shared.Return(compressedData); - } } - private static void ArrayCopyRepeat(byte value, byte[] destinationArray, int destinationIndex, int length) + private static void ArrayCopyRepeat(byte value, Span destinationArray, int destinationIndex, int length) { for (int i = 0; i < length; i++) { diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs new file mode 100644 index 000000000..33330beba --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs @@ -0,0 +1,29 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Formats.Tiff +{ + /// + /// Base tiff decompressor class. + /// + internal abstract class TiffBaseCompression + { + private readonly MemoryAllocator allocator; + + public TiffBaseCompression(MemoryAllocator allocator) => this.allocator = allocator; + + protected MemoryAllocator Allocator => this.allocator; + + /// + /// Decompresses image data into the supplied buffer. + /// + /// The to read image data from. + /// The number of bytes to read from the input stream. + /// The output buffer for uncompressed data. + public abstract void Decompress(Stream stream, int byteCount, Span buffer); + } +} diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs new file mode 100644 index 000000000..7e077983d --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs @@ -0,0 +1,28 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Formats.Tiff +{ + internal static class TiffCompressionFactory + { + public static TiffBaseCompression Create(TiffCompressionType compressionType, MemoryAllocator allocator) + { + switch (compressionType) + { + case TiffCompressionType.None: + return new NoneTiffCompression(allocator); + case TiffCompressionType.PackBits: + return new PackBitsTiffCompression(allocator); + case TiffCompressionType.Deflate: + return new DeflateTiffCompression(allocator); + case TiffCompressionType.Lzw: + return new LzwTiffCompression(allocator); + default: + throw new InvalidOperationException(); + } + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffPredictor.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffPredictor.cs new file mode 100644 index 000000000..03965c06f --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffPredictor.cs @@ -0,0 +1,26 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Tiff +{ + /// + /// A mathematical operator that is applied to the image data before an encoding scheme is applied. + /// + public enum TiffPredictor : ushort + { + /// + /// No prediction scheme used before coding + /// + None = 1, + + /// + /// Horizontal differencing. + /// + Horizontal = 2, + + /// + /// Floating point horizontal differencing. + /// + FloatingPoint = 3 + } +} diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffSampleFormat.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffSampleFormat.cs new file mode 100644 index 000000000..cdd618224 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffSampleFormat.cs @@ -0,0 +1,41 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Tiff +{ + /// + /// Specifies how to interpret each data sample in a pixel. + /// + public enum TiffSampleFormat : ushort + { + /// + /// Unsigned integer data. Default value. + /// + UnsignedInteger = 1, + + /// + /// Signed integer data. + /// + SignedInteger = 2, + + /// + /// IEEE floating point data. + /// + Float = 3, + + /// + /// Undefined data format. + /// + Undefined = 4, + + /// + /// The complex int. + /// + ComplexInt = 5, + + /// + /// The complex float. + /// + ComplexFloat = 6 + } +} diff --git a/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs b/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs index fe0ab4ccb..b605a8737 100644 --- a/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs +++ b/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs @@ -105,6 +105,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff case ExifTagValue.TileByteCounts: case ExifTagValue.ColorMap: case ExifTagValue.ExtraSamples: + case ExifTagValue.SampleFormat: return true; default: diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs index a19cd6d44..902882c56 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs @@ -11,28 +11,19 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// Implements the 'BlackIsZero' photometric interpretation (optimised for bilevel images). /// /// The pixel format. - internal class BlackIsZero1TiffColor : TiffColorDecoder + internal class BlackIsZero1TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { public BlackIsZero1TiffColor() - : base(null, null) { } - /// - /// Decodes pixel data using the current photometric interpretation. - /// - /// The buffer to read image data from. - /// The image buffer to write pixels to. - /// The x-coordinate of the left-hand side of the image block. - /// The y-coordinate of the top of the image block. - /// The width of the image block. - /// The height of the image block. - public override void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) + /// + public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - TPixel color = default(TPixel); + var color = default(TPixel); - uint offset = 0; + int offset = 0; for (int y = top; y < top + height; y++) { @@ -45,6 +36,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff { int bit = (b >> (7 - shift)) & 1; byte intensity = (bit == 1) ? (byte)255 : (byte)0; + color.FromRgba32(new Rgba32(intensity, intensity, intensity, 255)); pixels[x + shift, y] = color; } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs index 059de1a3e..46e0e82bc 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs @@ -10,28 +10,19 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Implements the 'BlackIsZero' photometric interpretation (optimised for 4-bit grayscale images). /// - internal class BlackIsZero4TiffColor : TiffColorDecoder + internal class BlackIsZero4TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { public BlackIsZero4TiffColor() - : base(null, null) { } - /// - /// Decodes pixel data using the current photometric interpretation. - /// - /// The buffer to read image data from. - /// The image buffer to write pixels to. - /// The x-coordinate of the left-hand side of the image block. - /// The y-coordinate of the top of the image block. - /// The width of the image block. - /// The height of the image block. - public override void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) + /// + public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - TPixel color = default(TPixel); + var color = default(TPixel); - uint offset = 0; + int offset = 0; bool isOddWidth = (width & 1) == 1; for (int y = top; y < top + height; y++) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs index 5d50600d9..013dae688 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs @@ -10,34 +10,26 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Implements the 'BlackIsZero' photometric interpretation (optimised for 8-bit grayscale images). /// - internal class BlackIsZero8TiffColor : TiffColorDecoder + internal class BlackIsZero8TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { public BlackIsZero8TiffColor() - : base(null, null) { } - /// - /// Decodes pixel data using the current photometric interpretation. - /// - /// The buffer to read image data from. - /// The image buffer to write pixels to. - /// The x-coordinate of the left-hand side of the image block. - /// The y-coordinate of the top of the image block. - /// The width of the image block. - /// The height of the image block. - public override void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) + /// + public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - TPixel color = default(TPixel); + var color = default(TPixel); - uint offset = 0; + int offset = 0; for (int y = top; y < top + height; y++) { for (int x = left; x < left + width; x++) { byte intensity = data[offset++]; + color.FromRgba32(new Rgba32(intensity, intensity, intensity, 255)); pixels[x, y] = color; } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs index 7de303536..91518c662 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Implements the 'BlackIsZero' photometric interpretation (for all bit depths). /// - internal class BlackIsZeroTiffColor : TiffColorDecoder + internal class BlackIsZeroTiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { private readonly ushort bitsPerSample0; @@ -19,26 +19,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff private readonly float factor; public BlackIsZeroTiffColor(ushort[] bitsPerSample) - : base(bitsPerSample, null) { this.bitsPerSample0 = bitsPerSample[0]; - this.factor = (float)Math.Pow(2, this.bitsPerSample0) - 1.0f; + this.factor = (float)(1 << this.bitsPerSample0) - 1.0f; } - /// - /// Decodes pixel data using the current photometric interpretation. - /// - /// The buffer to read image data from. - /// The image buffer to write pixels to. - /// The x-coordinate of the left-hand side of the image block. - /// The y-coordinate of the top of the image block. - /// The width of the image block. - /// The height of the image block. - public override void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) + /// + public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - TPixel color = default(TPixel); + var color = default(TPixel); - BitReader bitReader = new BitReader(data); + var bitReader = new BitReader(data); for (int y = top; y < top + height; y++) { @@ -46,6 +37,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff { int value = bitReader.ReadBits(this.bitsPerSample0); float intensity = ((float)value) / this.factor; + color.FromVector4(new Vector4(intensity, intensity, intensity, 1.0f)); pixels[x, y] = color; } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs index 7b4f50e80..0af9d8698 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs @@ -3,7 +3,6 @@ using System; using System.Numerics; -using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -12,33 +11,26 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Implements the 'PaletteTiffColor' photometric interpretation (for all bit depths). /// - internal class PaletteTiffColor : TiffColorDecoder + internal class PaletteTiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { private readonly ushort bitsPerSample0; private readonly TPixel[] palette; + /// The number of bits per sample for each pixel. + /// The RGB color lookup table to use for decoding the image. public PaletteTiffColor(ushort[] bitsPerSample, ushort[] colorMap) - : base(bitsPerSample, colorMap) { this.bitsPerSample0 = bitsPerSample[0]; - int colorCount = (int)Math.Pow(2, this.bitsPerSample0); + int colorCount = 1 << this.bitsPerSample0; this.palette = GeneratePalette(colorMap, colorCount); } - /// - /// Decodes pixel data using the current photometric interpretation. - /// - /// The buffer to read image data from. - /// The image buffer to write pixels to. - /// The x-coordinate of the left-hand side of the image block. - /// The y-coordinate of the top of the image block. - /// The width of the image block. - /// The height of the image block. - public override void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) + /// + public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - BitReader bitReader = new BitReader(data); + var bitReader = new BitReader(data); for (int y = top; y < top + height; y++) { @@ -52,7 +44,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] private static TPixel[] GeneratePalette(ushort[] colorMap, int colorCount) { var palette = new TPixel[colorCount]; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs index 352c1e26c..b19028a97 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs @@ -10,38 +10,32 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Implements the 'RGB' photometric interpretation (optimised for 8-bit full color images). /// - internal class Rgb888TiffColor : TiffColorDecoder + internal class Rgb888TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { public Rgb888TiffColor() - : base(null, null) { } - /// - /// Decodes pixel data using the current photometric interpretation. - /// - /// The buffer to read image data from. - /// The image buffer to write pixels to. - /// The x-coordinate of the left-hand side of the image block. - /// The y-coordinate of the top of the image block. - /// The width of the image block. - /// The height of the image block. - public override void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) + /// + public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - TPixel color = default(TPixel); + var color = default(TPixel); - uint offset = 0; + int offset = 0; for (int y = top; y < top + height; y++) { + Span pixelRow = pixels.GetRowSpan(y); + for (int x = left; x < left + width; x++) { byte r = data[offset++]; byte g = data[offset++]; byte b = data[offset++]; + color.FromRgba32(new Rgba32(r, g, b, 255)); - pixels[x, y] = color; + pixelRow[x] = color; } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs index 23f05c35f..3bd263ef3 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using System; using System.Numerics; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -20,11 +19,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff private readonly float bFactor; - private readonly uint bitsPerSampleR; + private readonly ushort bitsPerSampleR; - private readonly uint bitsPerSampleG; + private readonly ushort bitsPerSampleG; - private readonly uint bitsPerSampleB; + private readonly ushort bitsPerSampleB; public RgbPlanarTiffColor(ushort[] bitsPerSample) /* : base(bitsPerSample, null) */ @@ -33,9 +32,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff this.bitsPerSampleG = bitsPerSample[1]; this.bitsPerSampleB = bitsPerSample[2]; - this.rFactor = (float)Math.Pow(2, this.bitsPerSampleR) - 1.0f; - this.gFactor = (float)Math.Pow(2, this.bitsPerSampleG) - 1.0f; - this.bFactor = (float)Math.Pow(2, this.bitsPerSampleB) - 1.0f; + this.rFactor = (float)(1 << this.bitsPerSampleR) - 1.0f; + this.gFactor = (float)(1 << this.bitsPerSampleG) - 1.0f; + this.bFactor = (float)(1 << this.bitsPerSampleB) - 1.0f; } /// @@ -47,13 +46,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The y-coordinate of the top of the image block. /// The width of the image block. /// The height of the image block. - public void Decode(byte[][] data, Buffer2D pixels, int left, int top, int width, int height) + public void Decode(IManagedByteBuffer[] data, Buffer2D pixels, int left, int top, int width, int height) { - TPixel color = default(TPixel); + var color = default(TPixel); - var rBitReader = new BitReader(data[0]); - var gBitReader = new BitReader(data[1]); - var bBitReader = new BitReader(data[2]); + var rBitReader = new BitReader(data[0].GetSpan()); + var gBitReader = new BitReader(data[1].GetSpan()); + var bBitReader = new BitReader(data[2].GetSpan()); for (int y = top; y < top + height; y++) { diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs index 35b51fd95..bcc303f17 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Implements the 'RGB' photometric interpretation (for all bit depths). /// - internal class RgbTiffColor : TiffColorDecoder + internal class RgbTiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { private readonly float rFactor; @@ -20,38 +20,29 @@ namespace SixLabors.ImageSharp.Formats.Tiff private readonly float bFactor; - private readonly uint bitsPerSampleR; + private readonly ushort bitsPerSampleR; - private readonly uint bitsPerSampleG; + private readonly ushort bitsPerSampleG; - private readonly uint bitsPerSampleB; + private readonly ushort bitsPerSampleB; public RgbTiffColor(ushort[] bitsPerSample) - : base(bitsPerSample, null) { this.bitsPerSampleR = bitsPerSample[0]; this.bitsPerSampleG = bitsPerSample[1]; this.bitsPerSampleB = bitsPerSample[2]; - this.rFactor = (float)Math.Pow(2, this.bitsPerSampleR) - 1.0f; - this.gFactor = (float)Math.Pow(2, this.bitsPerSampleG) - 1.0f; - this.bFactor = (float)Math.Pow(2, this.bitsPerSampleB) - 1.0f; + this.rFactor = (float)(1 << this.bitsPerSampleR) - 1.0f; + this.gFactor = (float)(1 << this.bitsPerSampleG) - 1.0f; + this.bFactor = (float)(1 << this.bitsPerSampleB) - 1.0f; } - /// - /// Decodes pixel data using the current photometric interpretation. - /// - /// The buffer to read image data from. - /// The image buffer to write pixels to. - /// The x-coordinate of the left-hand side of the image block. - /// The y-coordinate of the top of the image block. - /// The width of the image block. - /// The height of the image block. - public override void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) + /// + public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - TPixel color = default(TPixel); + var color = default(TPixel); - BitReader bitReader = new BitReader(data); + var bitReader = new BitReader(data); for (int y = top; y < top + height; y++) { diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoder.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffBaseColorDecoder.cs similarity index 61% rename from src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoder.cs rename to src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffBaseColorDecoder.cs index 8c4f7e9b5..ad67c463f 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffBaseColorDecoder.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -10,22 +11,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The base class for photometric interpretation decoders. /// /// The pixel format. - internal abstract class TiffColorDecoder + internal abstract class TiffBaseColorDecoder where TPixel : unmanaged, IPixel { - private readonly ushort[] bitsPerSample; - - private readonly ushort[] colorMap; - - /// - /// Initializes a new instance of the class. - /// - /// The number of bits per sample for each pixel. - /// The RGB color lookup table to use for decoding the image. - protected TiffColorDecoder(ushort[] bitsPerSample, ushort[] colorMap) + protected TiffBaseColorDecoder() { - this.bitsPerSample = bitsPerSample; - this.colorMap = colorMap; } /* @@ -46,7 +36,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The x-coordinate of the left-hand side of the image block. /// The y-coordinate of the top of the image block. /// The width of the image block. - /// The height of the image block. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public abstract void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height); + /// The height of the image block. + public abstract void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height); } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory.cs index 37bc96ef4..a01a25e8b 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff internal static class TiffColorDecoderFactory where TPixel : unmanaged, IPixel { - public static TiffColorDecoder Create(TiffColorType colorType, ushort[] bitsPerSample, ushort[] colorMap) + public static TiffBaseColorDecoder Create(TiffColorType colorType, ushort[] bitsPerSample, ushort[] colorMap) { switch (colorType) { diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs index 666d03f9c..95ff7c6a7 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs @@ -10,28 +10,19 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Implements the 'WhiteIsZero' photometric interpretation (optimised for bilevel images). /// - internal class WhiteIsZero1TiffColor : TiffColorDecoder + internal class WhiteIsZero1TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { public WhiteIsZero1TiffColor() - : base(null, null) { } - /// - /// Decodes pixel data using the current photometric interpretation. - /// - /// The buffer to read image data from. - /// The image buffer to write pixels to. - /// The x-coordinate of the left-hand side of the image block. - /// The y-coordinate of the top of the image block. - /// The width of the image block. - /// The height of the image block. - public override void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) + /// + public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - TPixel color = default(TPixel); + var color = default(TPixel); - uint offset = 0; + int offset = 0; for (int y = top; y < top + height; y++) { @@ -44,6 +35,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff { int bit = (b >> (7 - shift)) & 1; byte intensity = (bit == 1) ? (byte)0 : (byte)255; + color.FromRgba32(new Rgba32(intensity, intensity, intensity, 255)); pixels[x + shift, y] = color; } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs index 9e5ce1f59..2720a1aa5 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs @@ -10,28 +10,19 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Implements the 'WhiteIsZero' photometric interpretation (optimised for 4-bit grayscale images). /// - internal class WhiteIsZero4TiffColor : TiffColorDecoder + internal class WhiteIsZero4TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { public WhiteIsZero4TiffColor() - : base(null, null) { } - /// - /// Decodes pixel data using the current photometric interpretation. - /// - /// The buffer to read image data from. - /// The image buffer to write pixels to. - /// The x-coordinate of the left-hand side of the image block. - /// The y-coordinate of the top of the image block. - /// The width of the image block. - /// The height of the image block. - public override void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) + /// + public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - TPixel color = default(TPixel); + var color = default(TPixel); - uint offset = 0; + int offset = 0; bool isOddWidth = (width & 1) == 1; for (int y = top; y < top + height; y++) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs index 4e3a0e443..30d8ea1db 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs @@ -10,34 +10,26 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Implements the 'WhiteIsZero' photometric interpretation (optimised for 8-bit grayscale images). /// - internal class WhiteIsZero8TiffColor : TiffColorDecoder + internal class WhiteIsZero8TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { public WhiteIsZero8TiffColor() - : base(null, null) { } - /// - /// Decodes pixel data using the current photometric interpretation. - /// - /// The buffer to read image data from. - /// The image buffer to write pixels to. - /// The x-coordinate of the left-hand side of the image block. - /// The y-coordinate of the top of the image block. - /// The width of the image block. - /// The height of the image block. - public override void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) + /// + public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - TPixel color = default(TPixel); + var color = default(TPixel); - uint offset = 0; + int offset = 0; for (int y = top; y < top + height; y++) { for (int x = left; x < left + width; x++) { byte intensity = (byte)(255 - data[offset++]); + color.FromRgba32(new Rgba32(intensity, intensity, intensity, 255)); pixels[x, y] = color; } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs index 329454e9d..dda338d3b 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Implements the 'WhiteIsZero' photometric interpretation (for all bit depths). /// - internal class WhiteIsZeroTiffColor : TiffColorDecoder + internal class WhiteIsZeroTiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { private readonly ushort bitsPerSample0; @@ -19,26 +19,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff private readonly float factor; public WhiteIsZeroTiffColor(ushort[] bitsPerSample) - : base(bitsPerSample, null) { this.bitsPerSample0 = bitsPerSample[0]; this.factor = (float)Math.Pow(2, this.bitsPerSample0) - 1.0f; } - /// - /// Decodes pixel data using the current photometric interpretation. - /// - /// The buffer to read image data from. - /// The image buffer to write pixels to. - /// The x-coordinate of the left-hand side of the image block. - /// The y-coordinate of the top of the image block. - /// The width of the image block. - /// The height of the image block. - public override void Decode(byte[] data, Buffer2D pixels, int left, int top, int width, int height) + /// + public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - TPixel color = default(TPixel); + var color = default(TPixel); - BitReader bitReader = new BitReader(data); + var bitReader = new BitReader(data); for (int y = top; y < top + height; y++) { diff --git a/src/ImageSharp/Formats/Tiff/Streams/TiffStreamFactory.cs b/src/ImageSharp/Formats/Tiff/Streams/TiffStreamFactory.cs index 7c9715380..f65062d59 100644 --- a/src/ImageSharp/Formats/Tiff/Streams/TiffStreamFactory.cs +++ b/src/ImageSharp/Formats/Tiff/Streams/TiffStreamFactory.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System; @@ -11,12 +11,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff ///
internal static class TiffStreamFactory { - public static TiffStream CreateBySignature(Stream stream) - { - TiffByteOrder order = ReadByteOrder(stream); - return Create(order, stream); - } - /// /// Creates the specified byte order. /// @@ -40,7 +34,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// Reads the byte order of stream. ///
/// The stream. - private static TiffByteOrder ReadByteOrder(Stream stream) + public static TiffByteOrder ReadByteOrder(Stream stream) { byte[] headerBytes = new byte[2]; stream.Read(headerBytes, 0, 2); diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs index 74d11f1d4..aed21af56 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs @@ -4,6 +4,7 @@ using System.IO; using System.Threading; using System.Threading.Tasks; +using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Tiff @@ -24,10 +25,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff { Guard.NotNull(stream, "stream"); - using (var decoder = new TiffDecoderCore(stream, configuration, this)) - { - return decoder.Decode(); - } + using var decoder = new TiffDecoderCore(stream, configuration, this); + return decoder.Decode(configuration, stream); } /// @@ -37,25 +36,33 @@ namespace SixLabors.ImageSharp.Formats.Tiff public Task> DecodeAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { - throw new System.NotImplementedException(); + Guard.NotNull(stream, nameof(stream)); + + var decoder = new TiffDecoderCore(stream, configuration, this); + return decoder.DecodeAsync(configuration, stream, cancellationToken); } /// - public Task DecodeAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken) - { - throw new System.NotImplementedException(); - } + public async Task DecodeAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken) + => await this.DecodeAsync(configuration, stream, cancellationToken) + .ConfigureAwait(false); /// public IImageInfo Identify(Configuration configuration, Stream stream) { - throw new System.NotImplementedException(); + Guard.NotNull(stream, nameof(stream)); + + var decoder = new TiffDecoderCore(stream, configuration, this); + return decoder.Identify(configuration, stream); } /// public Task IdentifyAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken) { - throw new System.NotImplementedException(); + Guard.NotNull(stream, nameof(stream)); + + var decoder = new TiffDecoderCore(stream, configuration, this); + return decoder.IdentifyAsync(configuration, stream, cancellationToken); } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 0d089287f..947110137 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -25,6 +25,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff ///
private readonly Configuration configuration; + /// + /// Used for allocating memory during processing operations. + /// + private readonly MemoryAllocator memoryAllocator; + /// /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. /// @@ -41,6 +46,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff this.configuration = configuration ?? Configuration.Default; this.ignoreMetadata = options.IgnoreMetadata; + this.memoryAllocator = this.configuration.MemoryAllocator; } /// @@ -52,26 +58,18 @@ namespace SixLabors.ImageSharp.Formats.Tiff public TiffDecoderCore(Stream stream, Configuration configuration, ITiffDecoderOptions options) : this(configuration, options) { - this.Stream = TiffStreamFactory.CreateBySignature(stream); + this.ByteOrder = TiffStreamFactory.ReadByteOrder(stream); } /// - /// Initializes a new instance of the class. + /// Gets the byte order. /// - /// The byte order. - /// The input stream. - /// The configuration. - /// The decoder options. - public TiffDecoderCore(TiffByteOrder byteOrder, Stream stream, Configuration configuration, ITiffDecoderOptions options) - : this(configuration, options) - { - this.Stream = TiffStreamFactory.Create(byteOrder, stream); - } + public TiffByteOrder ByteOrder { get; } /// /// Gets the input stream. /// - public TiffStream Stream { get; } + public TiffStream Stream { get; private set; } /// /// Gets or sets the number of bits for each sample of the pixel format used to encode the image. @@ -109,32 +107,31 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// public Size Dimensions { get; private set; } - /// - /// Decodes the image from the specified and sets - /// the data to image. - /// - /// The pixel format. - /// The decoded image. - public Image Decode() + /// + public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { + this.Stream = TiffStreamFactory.Create(this.ByteOrder, stream); var reader = new DirectoryReader(this.Stream); + IEnumerable directories = reader.Read(); var frames = new List>(); + var framesMetadata = new List(); foreach (IExifValue[] ifd in directories) { - ImageFrame frame = this.DecodeFrame(ifd); + ImageFrame frame = this.DecodeFrame(ifd, out TiffFrameMetadata frameMetadata); frames.Add(frame); + framesMetadata.Add(frameMetadata); } - ImageMetadata metadata = frames.CreateMetadata(this.ignoreMetadata, this.Stream.ByteOrder); + ImageMetadata metadata = framesMetadata.CreateMetadata(this.ignoreMetadata, this.Stream.ByteOrder); // todo: tiff frames can have different sizes { - var root = frames.First(); + ImageFrame root = frames.First(); this.Dimensions = root.Size(); - foreach (var frame in frames) + foreach (ImageFrame frame in frames) { if (frame.Size() != root.Size()) { @@ -148,9 +145,33 @@ namespace SixLabors.ImageSharp.Formats.Tiff return image; } - /// - /// Dispose - /// + /// + public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) + { + this.Stream = TiffStreamFactory.Create(this.ByteOrder, stream); + var reader = new DirectoryReader(this.Stream); + + IEnumerable directories = reader.Read(); + + var framesMetadata = new List(); + foreach (IExifValue[] ifd in directories) + { + framesMetadata.Add(new TiffFrameMetadata() { Tags = ifd }); + } + + ImageMetadata metadata = framesMetadata.CreateMetadata(this.ignoreMetadata, this.Stream.ByteOrder); + + TiffFrameMetadata root = framesMetadata.First(); + int bitsPerPixel = 0; + foreach (var bits in root.BitsPerSample) + { + bitsPerPixel += bits; + } + + return new ImageInfo(new PixelTypeInfo(bitsPerPixel), (int)root.Width, (int)root.Height, metadata); + } + + /// public void Dispose() { // nothing @@ -161,11 +182,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// The pixel format. /// The IFD tags. - private ImageFrame DecodeFrame(IExifValue[] tags) + /// The frame metadata. + /// + /// The tiff frame. + /// + private ImageFrame DecodeFrame(IExifValue[] tags, out TiffFrameMetadata metadata) where TPixel : unmanaged, IPixel { var coreMetadata = new ImageFrameMetadata(); - TiffFrameMetadata metadata = coreMetadata.GetTiffMetadata(); + metadata = coreMetadata.GetTiffMetadata(); metadata.Tags = tags; this.VerifyAndParseOptions(metadata); @@ -178,7 +203,14 @@ namespace SixLabors.ImageSharp.Formats.Tiff uint[] stripOffsets = metadata.StripOffsets; uint[] stripByteCounts = metadata.StripByteCounts; - this.DecodeImageStrips(frame, rowsPerStrip, stripOffsets, stripByteCounts); + if (this.PlanarConfiguration == TiffPlanarConfiguration.Planar) + { + this.DecodeStripsPlanar(frame, rowsPerStrip, stripOffsets, stripByteCounts); + } + else + { + this.DecodeStripsChunky(frame, rowsPerStrip, stripOffsets, stripByteCounts); + } return frame; } @@ -190,12 +222,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The height for the desired pixel buffer. /// The index of the plane for planar image configuration (or zero for chunky). /// The size (in bytes) of the required pixel buffer. - private int CalculateImageBufferSize(int width, int height, int plane) + private int CalculateStripBufferSize(int width, int height, int plane = -1) { uint bitsPerPixel = 0; if (this.PlanarConfiguration == TiffPlanarConfiguration.Chunky) { + DebugGuard.IsTrue(plane == -1, "Excepted Chunky planar."); for (int i = 0; i < this.BitsPerSample.Length; i++) { bitsPerPixel += this.BitsPerSample[i]; @@ -207,7 +240,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff } int bytesPerRow = ((width * (int)bitsPerPixel) + 7) / 8; - return bytesPerRow * height; + int stripBytes = bytesPerRow * height; + + return stripBytes; } /// @@ -218,35 +253,28 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The number of rows per strip of data. /// An array of byte offsets to each strip in the image. /// An array of the size of each strip (in bytes). - private void DecodeImageStrips(ImageFrame frame, int rowsPerStrip, uint[] stripOffsets, uint[] stripByteCounts) + private void DecodeStripsPlanar(ImageFrame frame, int rowsPerStrip, uint[] stripOffsets, uint[] stripByteCounts) where TPixel : unmanaged, IPixel { - int stripsPerPixel = this.PlanarConfiguration == TiffPlanarConfiguration.Chunky ? 1 : this.BitsPerSample.Length; + int stripsPerPixel = this.BitsPerSample.Length; int stripsPerPlane = stripOffsets.Length / stripsPerPixel; Buffer2D pixels = frame.PixelBuffer; - byte[][] stripBytes = new byte[stripsPerPixel][]; - - for (int stripIndex = 0; stripIndex < stripBytes.Length; stripIndex++) - { - int uncompressedStripSize = this.CalculateImageBufferSize(frame.Width, rowsPerStrip, stripIndex); - stripBytes[stripIndex] = ArrayPool.Shared.Rent(uncompressedStripSize); - } + var stripBuffers = new IManagedByteBuffer[stripsPerPixel]; try { - TiffColorDecoder chunkyDecoder = null; - RgbPlanarTiffColor planarDecoder = null; - if (this.PlanarConfiguration == TiffPlanarConfiguration.Chunky) - { - chunkyDecoder = TiffColorDecoderFactory.Create(this.ColorType, this.BitsPerSample, this.ColorMap); - } - else + for (int stripIndex = 0; stripIndex < stripBuffers.Length; stripIndex++) { - planarDecoder = TiffColorDecoderFactory.CreatePlanar(this.ColorType, this.BitsPerSample, this.ColorMap); + int uncompressedStripSize = this.CalculateStripBufferSize(frame.Width, rowsPerStrip, stripIndex); + stripBuffers[stripIndex] = this.memoryAllocator.AllocateManagedByteBuffer(uncompressedStripSize); } + TiffBaseCompression decompressor = TiffCompressionFactory.Create(this.CompressionType, this.memoryAllocator); + + RgbPlanarTiffColor colorDecoder = TiffColorDecoderFactory.CreatePlanar(this.ColorType, this.BitsPerSample, this.ColorMap); + for (int i = 0; i < stripsPerPlane; i++) { int stripHeight = i < stripsPerPlane - 1 || frame.Height % rowsPerStrip == 0 ? rowsPerStrip : frame.Height % rowsPerStrip; @@ -254,37 +282,45 @@ namespace SixLabors.ImageSharp.Formats.Tiff for (int planeIndex = 0; planeIndex < stripsPerPixel; planeIndex++) { int stripIndex = (i * stripsPerPixel) + planeIndex; - CompressionFactory.DecompressImageBlock(this.Stream.InputStream, this.CompressionType, stripOffsets[stripIndex], stripByteCounts[stripIndex], stripBytes[planeIndex]); - } - if (this.PlanarConfiguration == TiffPlanarConfiguration.Chunky) - { - chunkyDecoder.Decode(stripBytes[0], pixels, 0, rowsPerStrip * i, frame.Width, stripHeight); - } - else - { - planarDecoder.Decode(stripBytes, pixels, 0, rowsPerStrip * i, frame.Width, stripHeight); + this.Stream.Seek(stripOffsets[stripIndex]); + decompressor.Decompress(this.Stream.InputStream, (int)stripByteCounts[stripIndex], stripBuffers[planeIndex].GetSpan()); } + + colorDecoder.Decode(stripBuffers, pixels, 0, rowsPerStrip * i, frame.Width, stripHeight); } } finally { - for (int stripIndex = 0; stripIndex < stripBytes.Length; stripIndex++) + foreach (IManagedByteBuffer buf in stripBuffers) { - ArrayPool.Shared.Return(stripBytes[stripIndex]); + buf?.Dispose(); } } } - public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + private void DecodeStripsChunky(ImageFrame frame, int rowsPerStrip, uint[] stripOffsets, uint[] stripByteCounts) + where TPixel : unmanaged, IPixel { - throw new NotImplementedException(); - } + int uncompressedStripSize = this.CalculateStripBufferSize(frame.Width, rowsPerStrip); - public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) - { - throw new NotImplementedException(); + using IManagedByteBuffer stripBuffer = this.memoryAllocator.AllocateManagedByteBuffer(uncompressedStripSize); + + Buffer2D pixels = frame.PixelBuffer; + + TiffBaseCompression decompressor = TiffCompressionFactory.Create(this.CompressionType, this.memoryAllocator); + + TiffBaseColorDecoder colorDecoder = TiffColorDecoderFactory.Create(this.ColorType, this.BitsPerSample, this.ColorMap); + + for (int stripIndex = 0; stripIndex < stripOffsets.Length; stripIndex++) + { + int stripHeight = stripIndex < stripOffsets.Length - 1 || frame.Height % rowsPerStrip == 0 ? rowsPerStrip : frame.Height % rowsPerStrip; + + this.Stream.Seek(stripOffsets[stripIndex]); + decompressor.Decompress(this.Stream.InputStream, (int)stripByteCounts[stripIndex], stripBuffer.GetSpan()); + + colorDecoder.Decode(stripBuffer.GetSpan(), pixels, 0, rowsPerStrip * stripIndex, frame.Width, stripHeight); + } } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs index 4aa24f24d..ebfdf5df0 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs @@ -2,11 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Collections; using System.Collections.Generic; using System.Linq; -using System.Net.Http.Headers; -using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Icc; @@ -20,14 +17,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// internal static class TiffDecoderHelpers { - public static ImageMetadata CreateMetadata(this IList> frames, bool ignoreMetadata, TiffByteOrder byteOrder) - where TPixel : unmanaged, IPixel + public static ImageMetadata CreateMetadata(this IList frames, bool ignoreMetadata, TiffByteOrder byteOrder) { var coreMetadata = new ImageMetadata(); TiffMetadata tiffMetadata = coreMetadata.GetTiffMetadata(); tiffMetadata.ByteOrder = byteOrder; - TiffFrameMetadata rootFrameMetadata = frames.First().Metadata.GetTiffMetadata(); + TiffFrameMetadata rootFrameMetadata = frames.First(); switch (rootFrameMetadata.ResolutionUnit) { case TiffResolutionUnit.None: @@ -53,13 +49,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff if (!ignoreMetadata) { - foreach (ImageFrame frame in frames) + foreach (TiffFrameMetadata frame in frames) { - TiffFrameMetadata frameMetadata = frame.Metadata.GetTiffMetadata(); - if (tiffMetadata.XmpProfile == null) { - byte[] buf = frameMetadata.GetArrayValue(ExifTag.XMP, true); + byte[] buf = frame.GetArray(ExifTag.XMP, true); if (buf != null) { tiffMetadata.XmpProfile = buf; @@ -68,7 +62,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff if (coreMetadata.IptcProfile == null) { - byte[] buf = frameMetadata.GetArrayValue(ExifTag.IPTC, true); + byte[] buf = frame.GetArray(ExifTag.IPTC, true); if (buf != null) { coreMetadata.IptcProfile = new IptcProfile(buf); @@ -77,7 +71,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff if (coreMetadata.IccProfile == null) { - byte[] buf = frameMetadata.GetArrayValue(ExifTag.IccProfile, true); + byte[] buf = frame.GetArray(ExifTag.IccProfile, true); if (buf != null) { coreMetadata.IccProfile = new IccProfile(buf); @@ -106,11 +100,27 @@ namespace SixLabors.ImageSharp.Formats.Tiff throw new NotSupportedException("The lower-order bits of the byte FillOrder is not supported."); } - if (entries.GetArrayValue(ExifTag.TileOffsets, true) != null) + if (entries.GetArray(ExifTag.TileOffsets, true) != null) { throw new NotSupportedException("The Tile images is not supported."); } + if (entries.Predictor != TiffPredictor.None) + { + throw new NotSupportedException("At the moment support only None Predictor."); + } + + if (entries.SampleFormat != null) + { + foreach (TiffSampleFormat format in entries.SampleFormat) + { + if (format != TiffSampleFormat.UnsignedInteger) + { + throw new NotSupportedException("At the moment support only UnsignedInteger SampleFormat."); + } + } + } + ParseCompression(options, entries.Compression); options.PlanarConfiguration = entries.PlanarConfiguration; diff --git a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs index bffbd1818..cfec448a4 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using SixLabors.ImageSharp.Metadata.Profiles.Exif; namespace SixLabors.ImageSharp.Formats.Tiff @@ -16,6 +17,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff private const TiffPlanarConfiguration DefaultPlanarConfiguration = TiffPlanarConfiguration.Chunky; + private const TiffPredictor DefaultPredictor = TiffPredictor.None; + /// /// Initializes a new instance of the class. /// @@ -39,17 +42,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Gets the number of columns in the image, i.e., the number of pixels per row. /// - public uint Width => this.GetSingleUInt(ExifTag.ImageWidth); + public uint Width => this.GetSingle(ExifTag.ImageWidth); /// /// Gets the number of rows of pixels in the image. /// - public uint Height => this.GetSingleUInt(ExifTag.ImageLength); + public uint Height => this.GetSingle(ExifTag.ImageLength); /// /// Gets the number of bits per component. /// - public ushort[] BitsPerSample => this.GetArrayValue(ExifTag.BitsPerSample, true); + public ushort[] BitsPerSample => this.GetArray(ExifTag.BitsPerSample, true); /// Gets the compression scheme used on the image data. /// The compression scheme used on the image data. @@ -81,17 +84,22 @@ namespace SixLabors.ImageSharp.Formats.Tiff public string Model => this.GetString(ExifTag.Model); /// Gets for each strip, the byte offset of that strip.. - public uint[] StripOffsets => this.GetArrayValue(ExifTag.StripOffsets); + public uint[] StripOffsets => this.GetArray(ExifTag.StripOffsets); + + /// + /// Gets the number of components per pixel. + /// + public ushort SamplesPerPixel => this.GetSingle(ExifTag.SamplesPerPixel); /// /// Gets the number of rows per strip. /// - public uint RowsPerStrip => this.GetSingleUInt(ExifTag.RowsPerStrip); + public uint RowsPerStrip => this.GetSingle(ExifTag.RowsPerStrip); /// /// Gets for each strip, the number of bytes in the strip after compression. /// - public uint[] StripByteCounts => this.GetArrayValue(ExifTag.StripByteCounts); + public uint[] StripByteCounts => this.GetArray(ExifTag.StripByteCounts); /// Gets the resolution of the image in x- direction. /// The density of the image in x- direction. @@ -168,22 +176,33 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Gets a color map for palette color images. /// - public ushort[] ColorMap => this.GetArrayValue(ExifTag.ColorMap, true); + public ushort[] ColorMap => this.GetArray(ExifTag.ColorMap, true); /// /// Gets the description of extra components. /// - public ushort[] ExtraSamples => this.GetArrayValue(ExifTag.ExtraSamples, true); + public ushort[] ExtraSamples => this.GetArray(ExifTag.ExtraSamples, true); /// /// Gets the copyright notice. /// public string Copyright => this.GetString(ExifTag.Copyright); - internal T[] GetArrayValue(ExifTag tag, bool optional = false) + /// + /// Gets a mathematical operator that is applied to the image data before an encoding scheme is applied. + /// + public TiffPredictor Predictor => this.GetSingleEnum(ExifTag.Predictor, DefaultPredictor); + + /// + /// Gets the specifies how to interpret each data sample in a pixel. + /// + /// + public TiffSampleFormat[] SampleFormat => this.GetEnumArray(ExifTag.SampleFormat, true); + + internal T[] GetArray(ExifTag tag, bool optional = false) where T : struct { - if (this.TryGetArrayValue(tag, out T[] result)) + if (this.TryGetArray(tag, out T[] result)) { return result; } @@ -196,7 +215,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff return null; } - private bool TryGetArrayValue(ExifTag tag, out T[] result) + private bool TryGetArray(ExifTag tag, out T[] result) where T : struct { foreach (IExifValue entry in this.Tags) @@ -214,6 +233,24 @@ namespace SixLabors.ImageSharp.Formats.Tiff return false; } + private TEnum[] GetEnumArray(ExifTag tag, bool optional = false) + where TEnum : struct + where TTagValue : struct + { + if (this.TryGetArray(tag, out TTagValue[] result)) + { + // todo: improve + return result.Select(a => (TEnum)(object)a).ToArray(); + } + + if (!optional) + { + throw new ArgumentException("Required tag is not founded: " + tag, nameof(tag)); + } + + return null; + } + private string GetString(ExifTag tag) { foreach (IExifValue entry in this.Tags) @@ -246,42 +283,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff private TEnum GetSingleEnum(ExifTag tag, TEnum? defaultValue = null) where TEnum : struct where TTagValue : struct - { - if (!this.TryGetSingle(tag, out TTagValue value)) - { - if (defaultValue != null) - { - return defaultValue.Value; - } - - throw new ArgumentException("Required tag is not founded: " + tag, nameof(tag)); - } - - return (TEnum)(object)value; - } + => this.GetSingleEnumNullable(tag) ?? (defaultValue != null ? defaultValue.Value : throw new ArgumentException("Required tag is not founded: " + tag, nameof(tag))); - /* - private TEnum GetSingleEnum(ExifTag tag, TEnum? defaultValue = null) - where TEnum : struct - where TEnumParent : struct - where TTagValue : struct - { - if (!this.TryGetSingle(tag, out TTagValue value)) - { - if (defaultValue != null) - { - return defaultValue.Value; - } - - throw new ArgumentException("Required tag is not founded: " + tag, nameof(tag)); - } - - return (TEnum)(object)(TEnumParent)Convert.ChangeType(value, typeof(TEnumParent)); - } */ - - private uint GetSingleUInt(ExifTag tag) + private T GetSingle(ExifTag tag) + where T : struct { - if (this.TryGetSingle(tag, out uint result)) + if (this.TryGetSingle(tag, out T result)) { return result; } @@ -290,7 +297,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff } private bool TryGetSingle(ExifTag tag, out T result) - where T : struct + where T : struct { foreach (IExifValue entry in this.Tags) { diff --git a/src/ImageSharp/Formats/Tiff/Utils/BitReader.cs b/src/ImageSharp/Formats/Tiff/Utils/BitReader.cs index 2c1b25000..0093f342a 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/BitReader.cs @@ -1,24 +1,28 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; + namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Utility class to read a sequence of bits from an array /// - internal class BitReader + internal ref struct BitReader { - private readonly byte[] array; + private readonly ReadOnlySpan array; private int offset; private int bitOffset; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the struct. /// /// The array to read data from. - public BitReader(byte[] array) + public BitReader(ReadOnlySpan array) { this.array = array; + this.offset = 0; + this.bitOffset = 0; } /// @@ -59,4 +63,4 @@ namespace SixLabors.ImageSharp.Formats.Tiff } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs index 7e33f186b..a490e7c0d 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System; @@ -91,7 +91,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The length of the compressed data. /// Size of the data. /// The pixel array to decode to. - public void DecodePixels(int length, int dataSize, byte[] pixels) + public void DecodePixels(int length, int dataSize, Span pixels) { Guard.MustBeLessThan(dataSize, int.MaxValue, nameof(dataSize)); @@ -268,4 +268,4 @@ namespace SixLabors.ImageSharp.Formats.Tiff this.isDisposed = true; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs index 823724409..5c68ca14d 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; using System.IO; namespace SixLabors.ImageSharp.Formats.Tiff @@ -16,7 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The stream to read from. /// A buffer to store the retrieved data. /// The number of bytes to read. - public static void ReadFull(this Stream stream, byte[] buffer, int count) + public static void ReadFull(this Stream stream, Span buffer, int count) { int offset = 0; @@ -39,9 +40,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// The stream to read from. /// A buffer to store the retrieved data. - public static void ReadFull(this Stream stream, byte[] buffer) + public static void ReadFull(this Stream stream, Span buffer) { ReadFull(stream, buffer, buffer.Length); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.LongArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.LongArray.cs index 5d0a106ab..e08a0b1e7 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.LongArray.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.LongArray.cs @@ -59,7 +59,6 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// /// Gets the StripByteCounts exif tag. /// - /// public static ExifTag StripByteCounts { get; } = new ExifTag(ExifTagValue.StripByteCounts); /// diff --git a/tests/ImageSharp.Benchmarks/Codecs/DecodeTiff.cs b/tests/ImageSharp.Benchmarks/Codecs/DecodeTiff.cs index 7e42f1bee..09c0daa48 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/DecodeTiff.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/DecodeTiff.cs @@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); - [Params(TestImages.Tiff.RgbLzw)] + [Params(TestImages.Tiff.RgbPackbits)] public string TestImage { get; set; } [GlobalSetup] diff --git a/tests/ImageSharp.Tests/FileTestBase.cs b/tests/ImageSharp.Tests/FileTestBase.cs index cf5f87e66..cead349d1 100644 --- a/tests/ImageSharp.Tests/FileTestBase.cs +++ b/tests/ImageSharp.Tests/FileTestBase.cs @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Tests TestImages.Png.Splash, TestImages.Gif.Trans, TestImages.Tga.Bit24PalRleTopRight, - TestImages.Tiff.RgbLzw, + TestImages.Tiff.RgbPackbits, }; /// @@ -116,7 +116,7 @@ namespace SixLabors.ImageSharp.Tests // TestFile.Create(TestImages.Gif.Cheers), // Perf: Enable for local testing only // TestFile.Create(TestImages.Gif.Giphy) // Perf: Enable for local testing only TestFile.Create(TestImages.Tga.Bit24PalRleTopRight), - TestFile.Create(TestImages.Tiff.RgbLzw), + TestFile.Create(TestImages.Tiff.RgbPackbits), }; #pragma warning restore SA1515 // Single-line comment should be preceded by blank line } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs index 66868d3dc..10f5819ac 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs @@ -4,6 +4,7 @@ using System.IO; using SixLabors.ImageSharp.Formats.Png.Zlib; using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Memory; using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff @@ -23,7 +24,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { byte[] buffer = new byte[data.Length]; - DeflateTiffCompression.Decompress(stream, (int)stream.Length, buffer); + new DeflateTiffCompression(null).Decompress(stream, (int)stream.Length, buffer); Assert.Equal(data, buffer); } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs index de4d5f46d..df9208434 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs @@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { byte[] buffer = new byte[data.Length]; - LzwTiffCompression.Decompress(stream, (int)stream.Length, buffer); + new LzwTiffCompression(null).Decompress(stream, (int)stream.Length, buffer); Assert.Equal(data, buffer); } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs index 8e0d81fa4..28fbce69f 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs @@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Stream stream = new MemoryStream(inputData); byte[] buffer = new byte[expectedResult.Length]; - NoneTiffCompression.Decompress(stream, byteCount, buffer); + new NoneTiffCompression(null).Decompress(stream, byteCount, buffer); Assert.Equal(expectedResult, buffer); } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs index 3888f6bf9..b08648465 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs @@ -3,6 +3,7 @@ using System.IO; using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Memory; using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Formats.Tiff @@ -25,7 +26,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Formats.Tiff Stream stream = new MemoryStream(inputData); byte[] buffer = new byte[expectedResult.Length]; - PackBitsTiffCompression.Decompress(stream, inputData.Length, buffer); + new PackBitsTiffCompression(new ArrayPoolMemoryAllocator()).Decompress(stream, inputData.Length, buffer); Assert.Equal(expectedResult, buffer); } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs index c0e328c62..1982a8ebe 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs @@ -1,8 +1,10 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; using System.Collections.Generic; using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using Xunit; @@ -190,7 +192,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { AssertDecode(expectedResult, pixels => { - new RgbPlanarTiffColor(bitsPerSample).Decode(inputData, pixels, left, top, width, height); + var buffers = new IManagedByteBuffer[inputData.Length]; + for (int i = 0; i < buffers.Length; i++) + { + buffers[i] = Configuration.Default.MemoryAllocator.AllocateManagedByteBuffer(inputData[i].Length); + ((Span)inputData[i]).CopyTo(buffers[i].GetSpan()); + } + + new RgbPlanarTiffColor(bitsPerSample).Decode(buffers, pixels, left, top, width, height); }); } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index 58b917937..d9abc163a 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -4,7 +4,9 @@ // ReSharper disable InconsistentNaming using System; +using System.IO; using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; @@ -30,6 +32,27 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Throws(() => provider.GetImage(new TiffDecoder())); } + [Theory] + [InlineData(TestImages.Tiff.RgbUncompressed, 24, 256, 256, 300, 300, PixelResolutionUnit.PixelsPerInch)] + [InlineData(TestImages.Tiff.SmallRgbDeflate, 24, 32, 32, 96, 96, PixelResolutionUnit.PixelsPerInch)] + [InlineData(TestImages.Tiff.Calliphora_GrayscaleUncompressed, 8, 804, 1198, 96, 96, PixelResolutionUnit.PixelsPerInch)] + public void Identify(string imagePath, int expectedPixelSize, int expectedWidth, int expectedHeight, double expectedHResolution, double expectedVResolution, PixelResolutionUnit expectedResolutionUnit) + { + var testFile = TestFile.Create(imagePath); + using (var stream = new MemoryStream(testFile.Bytes, false)) + { + IImageInfo info = Image.Identify(stream); + + Assert.Equal(expectedPixelSize, info.PixelType?.BitsPerPixel); + Assert.Equal(expectedWidth, info.Width); + Assert.Equal(expectedHeight, info.Height); + Assert.NotNull(info.Metadata); + Assert.Equal(expectedHResolution, info.Metadata.HorizontalResolution); + Assert.Equal(expectedVResolution, info.Metadata.VerticalResolution); + Assert.Equal(expectedResolutionUnit, info.Metadata.ResolutionUnits); + } + } + [Theory] [WithFileCollection(nameof(SingleTestImages), PixelTypes.Rgba32)] public void Decode(TestImageProvider provider) diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index 6c210eb1e..0090a8222 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -8,7 +8,6 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff { - [Trait("Category", "Tiff.BlackBox")] [Trait("Category", "Tiff")] public class TiffMetadataTests { @@ -31,6 +30,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff else { Assert.NotNull(meta.XmpProfile); + Assert.Equal(2599, meta.XmpProfile.Length); } } } @@ -53,25 +53,33 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff TiffFrameMetadata frame = image.Frames.RootFrame.Metadata.GetTiffMetadata(); Assert.Equal(32u, frame.Width); Assert.Equal(32u, frame.Height); - Assert.Equal(new ushort[] { 8, 8, 8 }, frame.BitsPerSample); + Assert.Equal(new ushort[] { 4 }, frame.BitsPerSample); Assert.Equal(TiffCompression.Lzw, frame.Compression); - Assert.Equal(TiffPhotometricInterpretation.Rgb, frame.PhotometricInterpretation); + Assert.Equal(TiffPhotometricInterpretation.PaletteColor, frame.PhotometricInterpretation); Assert.Equal("This is Название", frame.ImageDescription); Assert.Equal("This is Изготовитель камеры", frame.Make); Assert.Equal("This is Модель камеры", frame.Model); Assert.Equal(new uint[] { 8 }, frame.StripOffsets); + Assert.Equal(1, frame.SamplesPerPixel); Assert.Equal(32u, frame.RowsPerStrip); - Assert.Equal(new uint[] { 750 }, frame.StripByteCounts); + Assert.Equal(new uint[] { 297 }, frame.StripByteCounts); Assert.Equal(10, frame.HorizontalResolution); Assert.Equal(10, frame.VerticalResolution); Assert.Equal(TiffPlanarConfiguration.Chunky, frame.PlanarConfiguration); Assert.Equal(TiffResolutionUnit.Inch, frame.ResolutionUnit); Assert.Equal("IrfanView", frame.Software); Assert.Equal(null, frame.DateTime); - Assert.Equal("This is;Автор", frame.Artist); + Assert.Equal("This is author1;Author2", frame.Artist); Assert.Equal(null, frame.HostComputer); - Assert.Equal(null, frame.ColorMap); + Assert.Equal(48, frame.ColorMap.Length); + Assert.Equal(10537, frame.ColorMap[0]); + Assert.Equal(14392, frame.ColorMap[1]); + Assert.Equal(58596, frame.ColorMap[46]); + Assert.Equal(3855, frame.ColorMap[47]); + Assert.Equal(null, frame.ExtraSamples); + Assert.Equal(TiffPredictor.None, frame.Predictor); + Assert.Equal(null, frame.SampleFormat); Assert.Equal("This is Авторские права", frame.Copyright); } } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 74967f3ec..f90324d64 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -502,9 +502,9 @@ namespace SixLabors.ImageSharp.Tests public const string Calliphora_GrayscaleUncompressed = "Tiff/Calliphora_grayscale_uncompressed.tiff"; public const string Calliphora_PaletteUncompressed = "Tiff/Calliphora_palette_uncompressed.tiff"; - public const string Calliphora_RgbDeflate = "Tiff/Calliphora_rgb_deflate.tiff"; + public const string Calliphora_RgbDeflate_Predictor = "Tiff/Calliphora_rgb_deflate.tiff"; public const string Calliphora_RgbJpeg = "Tiff/Calliphora_rgb_jpeg.tiff"; - public const string Calliphora_RgbLzw = "Tiff/Calliphora_rgb_lzw.tiff"; + public const string Calliphora_RgbLzwe_Predictor = "Tiff/Calliphora_rgb_lzw.tiff"; public const string Calliphora_RgbPackbits = "Tiff/Calliphora_rgb_packbits.tiff"; public const string Calliphora_RgbUncompressed = "Tiff/Calliphora_rgb_uncompressed.tiff"; @@ -512,32 +512,35 @@ namespace SixLabors.ImageSharp.Tests public const string GrayscaleUncompressed = "Tiff/grayscale_uncompressed.tiff"; public const string PaletteDeflateMultistrip = "Tiff/palette_grayscale_deflate_multistrip.tiff"; public const string PaletteUncompressed = "Tiff/palette_uncompressed.tiff"; - public const string RgbDeflate = "Tiff/rgb_deflate.tiff"; + public const string RgbDeflate_Predictor = "Tiff/rgb_deflate.tiff"; public const string RgbDeflateMultistrip = "Tiff/rgb_deflate_multistrip.tiff"; public const string RgbJpeg = "Tiff/rgb_jpeg.tiff"; - public const string RgbLzw = "Tiff/rgb_lzw.tiff"; - public const string RgbLzwMultistrip = "Tiff/rgb_lzw_multistrip.tiff"; + public const string RgbLzw_Predictor = "Tiff/rgb_lzw.tiff"; + public const string RgbLzwMultistrip_Predictor = "Tiff/rgb_lzw_multistrip.tiff"; public const string RgbPackbits = "Tiff/rgb_packbits.tiff"; public const string RgbPackbitsMultistrip = "Tiff/rgb_packbits_multistrip.tiff"; public const string RgbUncompressed = "Tiff/rgb_uncompressed.tiff"; + public const string SmallRgbDeflate = "Tiff/rgb_small_deflate.tiff"; + public const string SmallRgbLzw = "Tiff/rgb_small_lzw.tiff"; + public const string RgbUncompressedTiled = "Tiff/rgb_uncompressed_tiled.tiff"; public const string MultiframeDifferentSizeTiled = "Tiff/multipage_ withPreview_differentSize_tiled.tiff"; - public const string MultiframeLzw = "Tiff/multipage_lzw.tiff"; + public const string MultiframeLzw_Predictor = "Tiff/multipage_lzw.tiff"; public const string MultiframeDeflateWithPreview = "Tiff/multipage_deflate_withPreview.tiff"; public const string MultiframeDifferentSize = "Tiff/multipage_differentSize.tiff"; public const string MultiframeDifferentVariants = "Tiff/multipage_differentVariants.tiff"; public const string SampleMetadata = "Tiff/metadata_sample.tiff"; - public static readonly string[] All = { Calliphora_GrayscaleUncompressed, Calliphora_PaletteUncompressed, Calliphora_RgbDeflate, Calliphora_RgbLzw, Calliphora_RgbPackbits, Calliphora_RgbUncompressed, GrayscaleDeflateMultistrip, GrayscaleUncompressed, PaletteDeflateMultistrip, PaletteUncompressed, RgbDeflate, RgbDeflateMultistrip, /*RgbJpeg,*/ RgbLzw, RgbLzwMultistrip, RgbPackbits, RgbPackbitsMultistrip, RgbUncompressed, MultiframeLzw, /*MultiFrameDifferentVariants,*/ SampleMetadata, }; + public static readonly string[] All = { Calliphora_GrayscaleUncompressed, Calliphora_PaletteUncompressed, /*Calliphora_RgbDeflate_Predictor, Calliphora_RgbLzwe_Predictor, */ Calliphora_RgbPackbits, Calliphora_RgbUncompressed, GrayscaleDeflateMultistrip, GrayscaleUncompressed, PaletteDeflateMultistrip, PaletteUncompressed, /*RgbDeflate_Predictor,*/ RgbDeflateMultistrip, /*RgbJpeg,*/ /*RgbLzw_Predictor, RgbLzwMultistrip_Predictor,*/ RgbPackbits, RgbPackbitsMultistrip, RgbUncompressed, /* MultiframeLzw_Predictor, MultiFrameDifferentVariants, SampleMetadata,*/ SmallRgbDeflate, SmallRgbLzw }; - public static readonly string[] Multiframes = { MultiframeLzw, MultiframeDeflateWithPreview /*MultiFrameDifferentSize, MultiframeDifferentSizeTiled, MultiFrameDifferentVariants,*/ }; + public static readonly string[] Multiframes = { MultiframeDeflateWithPreview /*MultiframeLzw_Predictor, MultiFrameDifferentSize, MultiframeDifferentSizeTiled, MultiFrameDifferentVariants,*/ }; public static readonly string[] Metadata = { SampleMetadata }; - public static readonly string[] NotSupported = { Calliphora_RgbJpeg, RgbJpeg, RgbUncompressedTiled, MultiframeDifferentSize, MultiframeDifferentVariants }; + public static readonly string[] NotSupported = { Calliphora_RgbJpeg, Calliphora_RgbDeflate_Predictor, Calliphora_RgbLzwe_Predictor, RgbDeflate_Predictor, RgbLzw_Predictor, RgbLzwMultistrip_Predictor, RgbJpeg, RgbUncompressedTiled, MultiframeLzw_Predictor, MultiframeDifferentSize, MultiframeDifferentVariants }; } } } diff --git a/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_Calliphora_rgb_deflate.png b/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_Calliphora_rgb_deflate.png deleted file mode 100644 index e49bf1073..000000000 --- a/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_Calliphora_rgb_deflate.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:087d7479ebe3bdd95281584cf4c9582603d90e157d136cf4233dcdefd909ba73 -size 1696927 diff --git a/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_Calliphora_rgb_lzw.png b/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_Calliphora_rgb_lzw.png deleted file mode 100644 index 891a2ace6..000000000 --- a/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_Calliphora_rgb_lzw.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:14f7b8e8275b4488418e4403c31e1a5c7565bf062fbd962f09f7a665468e2481 -size 7358 diff --git a/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_metadata_sample.png b/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_metadata_sample.png deleted file mode 100644 index 9eb1808f2..000000000 --- a/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_metadata_sample.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:91c87bc3d75b1386b30990513fab2da26bad065b977108904e866c850d66a7e5 -size 197 diff --git a/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_multipage_lzw.png b/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_multipage_lzw.png deleted file mode 100644 index a6642e716..000000000 --- a/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_multipage_lzw.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:727356bf611957750a0427bda4582f9ecc0f8935884f2158e8a2d5e65c3469b4 -size 18278 diff --git a/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_rgb_deflate.png b/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_rgb_deflate.png deleted file mode 100644 index 9e95173bc..000000000 --- a/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_rgb_deflate.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b077fb63012967c39c5a0b1b515ada33ad5470160ad0fa1aa89581aa77238c82 -size 18237 diff --git a/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_rgb_lzw_multistrip.png b/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_rgb_lzw_multistrip.png deleted file mode 100644 index 9dcd861ec..000000000 --- a/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_rgb_lzw_multistrip.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6d82fec289ce819c51fcb00d7eee662afc2d7357c940154e651e9e5ebf8a0287 -size 91898 diff --git a/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_rgb_small_lzw.png b/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_rgb_small_lzw.png new file mode 100644 index 000000000..a05e8248e --- /dev/null +++ b/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_rgb_small_lzw.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bd197010f3c0513e7782a33f187c79f2b5c5beaa5e0b2a472a8ab82b17ca2fed +size 208 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_Calliphora_rgb_deflate.png b/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_Calliphora_rgb_deflate.png deleted file mode 100644 index 415f73d87..000000000 --- a/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_Calliphora_rgb_deflate.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:beb1b3f0229c9a1ed78d4c1ab3cd786d96d70b904398ba008f1aa4157862554c -size 1696925 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_Calliphora_rgb_lzw.png b/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_Calliphora_rgb_lzw.png deleted file mode 100644 index 36dcfd9e8..000000000 --- a/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_Calliphora_rgb_lzw.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c74fae7a00dbf00bc259851b1e9c774a10fac2bd8581397d8680ebc47a7d0340 -size 31982 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_metadata_sample.png b/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_metadata_sample.png deleted file mode 100644 index 96cbbca22..000000000 --- a/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_metadata_sample.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5727c4007787bf0d6af78763c94125029255e41ebe570a6b8f3cbdb65e2a4d5f -size 1488 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_multipage_lzw.png b/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_multipage_lzw.png deleted file mode 100644 index 575e6cd56..000000000 --- a/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_multipage_lzw.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9d788e1facd4ee5cea5c57caa8b38a26a45fc8423b9967e9348f0514d734524f -size 18278 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_rgb_deflate.png b/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_rgb_deflate.png deleted file mode 100644 index 82d582e77..000000000 --- a/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_rgb_deflate.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9adbd0ce357c08c7b5ef543ff81bc32d227f9c2a016965f110f68c032f640ff1 -size 18237 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_rgb_lzw_multistrip.png b/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_rgb_lzw_multistrip.png deleted file mode 100644 index 9dcd861ec..000000000 --- a/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_rgb_lzw_multistrip.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6d82fec289ce819c51fcb00d7eee662afc2d7357c940154e651e9e5ebf8a0287 -size 91898 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_rgb_small_deflate.png b/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_rgb_small_deflate.png new file mode 100644 index 000000000..6020d448a --- /dev/null +++ b/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_rgb_small_deflate.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c7a31971806dd550f722bb519f90e194b1fbb7e1b42d54f78b5ad8ce9da094c4 +size 2660 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_Calliphora_rgb_deflate.png b/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_Calliphora_rgb_deflate.png deleted file mode 100644 index 415f73d87..000000000 --- a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_Calliphora_rgb_deflate.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:beb1b3f0229c9a1ed78d4c1ab3cd786d96d70b904398ba008f1aa4157862554c -size 1696925 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_Calliphora_rgb_lzw.png b/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_Calliphora_rgb_lzw.png deleted file mode 100644 index a79ae6096..000000000 --- a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_Calliphora_rgb_lzw.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4acb6da968aa5bfc7af57b41fe9aefe13e7b2d3ee4379867b83548209fbc94eb -size 8108 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_metadata_sample.png b/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_metadata_sample.png deleted file mode 100644 index 249f68831..000000000 --- a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_metadata_sample.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:977c552ee08788244fa4579c23ca59dfcb6e91df706774dc8167286a4ce5a536 -size 821 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_multipage_lzw.png b/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_multipage_lzw.png deleted file mode 100644 index 575e6cd56..000000000 --- a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_multipage_lzw.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9d788e1facd4ee5cea5c57caa8b38a26a45fc8423b9967e9348f0514d734524f -size 18278 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_deflate.png b/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_deflate.png deleted file mode 100644 index 82d582e77..000000000 --- a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_deflate.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9adbd0ce357c08c7b5ef543ff81bc32d227f9c2a016965f110f68c032f640ff1 -size 18237 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_lzw.png b/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_lzw.png deleted file mode 100644 index c0da2eb59..000000000 --- a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_lzw.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:24fe217e11cbc2944e7a8aba0411e37314822024e90fdd873cd9e3feb0e55898 -size 77527 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_lzw_multistrip.png b/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_lzw_multistrip.png deleted file mode 100644 index 61d88337b..000000000 --- a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_lzw_multistrip.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:82f30f2936880eacc3248fbc759f84adac7d625b974259d61aeed16bc00f01b9 -size 64056 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_small_lzw.png b/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_small_lzw.png new file mode 100644 index 000000000..a05e8248e --- /dev/null +++ b/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_small_lzw.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bd197010f3c0513e7782a33f187c79f2b5c5beaa5e0b2a472a8ab82b17ca2fed +size 208 diff --git a/tests/Images/Input/Tiff/metadata_sample.tiff b/tests/Images/Input/Tiff/metadata_sample.tiff index aac5fe2c4..d76735268 100644 --- a/tests/Images/Input/Tiff/metadata_sample.tiff +++ b/tests/Images/Input/Tiff/metadata_sample.tiff @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ea7bc7404614a90da555637f6fed88defe5c6be8b5d1da2ff5980c39d249a01b -size 8833 +oid sha256:72a1c8022d699e0e7248940f0734d01d6ab9bf4a71022e8b5626b64d66a5f39d +size 8107 diff --git a/tests/Images/Input/Tiff/rgb_small_deflate.tiff b/tests/Images/Input/Tiff/rgb_small_deflate.tiff new file mode 100644 index 000000000..cd78dfc88 --- /dev/null +++ b/tests/Images/Input/Tiff/rgb_small_deflate.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:09c964c2f11806c2ff1e9fc7b06ddbecbc9e94cb7f4bd2b9841f0a3939d98eef +size 2575 diff --git a/tests/Images/Input/Tiff/rgb_small_lzw.tiff b/tests/Images/Input/Tiff/rgb_small_lzw.tiff new file mode 100644 index 000000000..deaeda645 --- /dev/null +++ b/tests/Images/Input/Tiff/rgb_small_lzw.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2630a452dcc86f557594fe29ae4244fbb29a276cdee53835157af17f966e1405 +size 3221 From a9e3d895dbedbd7f02fed6ba0c2a80c2f403161a Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Fri, 28 Aug 2020 10:53:56 +0300 Subject: [PATCH 071/275] Update README.md --- src/ImageSharp/Formats/Tiff/README.md | 36 +++++++++++++-------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/README.md b/src/ImageSharp/Formats/Tiff/README.md index c2527b008..2eed880b6 100644 --- a/src/ImageSharp/Formats/Tiff/README.md +++ b/src/ImageSharp/Formats/Tiff/README.md @@ -1,4 +1,4 @@ -# ImageSharp TIFF codec +# ImageSharp TIFF codec ## References - TIFF @@ -19,7 +19,7 @@ - Metadata (XMP) - [Adobe XMP Pages](http://www.adobe.com/products/xmp.html) - - [Adobe XMP Developer Centre](http://www.adobe.com/devnet/xmp.html) + - [Adobe XMP Developer Center](http://www.adobe.com/devnet/xmp.html) ## Implementation Status @@ -78,13 +78,13 @@ |Threshholding | | | | |CellWidth | | | | |CellLength | | | | -|FillOrder | | | | +|FillOrder | | - | Ignore. In practice is very uncommon, and is not recommended. | |ImageDescription | | Y | | |Make | | Y | | |Model | | Y | | |StripOffsets | | Y | | -|Orientation | | | | -|SamplesPerPixel | | | Currently ignored, as can be inferred from count of BitsPerSample | +|Orientation | | - | Ignore. Many readers ignore this tag. | +|SamplesPerPixel | | - | Currently ignored, as can be inferred from count of BitsPerSample | |RowsPerStrip | | Y | | |StripByteCounts | | Y | | |MinSampleValue | | | | @@ -102,7 +102,7 @@ |Artist | | Y | | |HostComputer | | Y | | |ColorMap | | Y | | -|ExtraSamples | | | | +|ExtraSamples | | - | | |Copyright | | Y | | ### Extension TIFF Tags @@ -118,24 +118,24 @@ |T6Options | | | | |PageNumber | | | | |TransferFunction | | | | -|Predictor | | | | +|Predictor | | - | priority | |WhitePoint | | | | |PrimaryChromaticities | | | | |HalftoneHints | | | | -|TileWidth | | | | -|TileLength | | | | -|TileOffsets | | | | -|TileByteCounts | | | | +|TileWidth | | - | | +|TileLength | | - | | +|TileOffsets | | - | | +|TileByteCounts | | - | | |BadFaxLines | | | | |CleanFaxData | | | | |ConsecutiveBadFaxLines | | | | -|SubIFDs | | | | +|SubIFDs | | - | | |InkSet | | | | |InkNames | | | | |NumberOfInks | | | | |DotRange | | | | |TargetPrinter | | | | -|SampleFormat | | | | +|SampleFormat | | - | | |SMinSampleValue | | | | |SMaxSampleValue | | | | |TransferRange | | | | @@ -166,8 +166,8 @@ |YCbCrSubSampling | | | | |YCbCrPositioning | | | | |ReferenceBlackWhite | | | | -|StripRowCounts | | | | -|XMP | | | | +|StripRowCounts | | - | | +|XMP | | Y | | |ImageID | | | | |ImageLayer | | | | @@ -185,15 +185,15 @@ |MD PrepTime | | | | |MD FileUnits | | | | |ModelPixelScaleTag | | | | -|IPTC | | | | +|IPTC | | Y | | |INGR Packet Data Tag | | | | |INGR Flag Registers | | | | |IrasB Transformation Matrix| | | | |ModelTiepointTag | | | | |ModelTransformationTag | | | | |Photoshop | | | | -|Exif IFD | | | | -|ICC Profile | | | | +|Exif IFD | | - | 0x8769 SubExif | +|ICC Profile | | Y | | |GeoKeyDirectoryTag | | | | |GeoDoubleParamsTag | | | | |GeoAsciiParamsTag | | | | From 1e4f7067024e6f18a605021adfd69f364d3f3223 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Tue, 29 Sep 2020 11:24:10 +0300 Subject: [PATCH 072/275] removing accidentally added file --- .../issues/netcoreapp2.1/Decode_Rgba32_rgb_small_deflate.png | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_rgb_small_deflate.png diff --git a/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_rgb_small_deflate.png b/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_rgb_small_deflate.png deleted file mode 100644 index 6020d448a..000000000 --- a/tests/Images/Input/Tiff/issues/netcoreapp2.1/Decode_Rgba32_rgb_small_deflate.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c7a31971806dd550f722bb519f90e194b1fbb7e1b42d54f78b5ad8ce9da094c4 -size 2660 From 599f24feff27c1ad245e52669b4e037ae7a75569 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Mon, 28 Sep 2020 23:20:53 +0300 Subject: [PATCH 073/275] #12 LZW bug fix --- .../Tiff/Compression/LzwTiffCompression.cs | 6 +- .../Formats/Tiff/Utils/TiffLzwDecoder.cs | 155 +++++------------- .../Formats/Tiff/TiffDecoderTests.cs | 21 +++ tests/ImageSharp.Tests/TestImages.cs | 9 +- .../net472/Decode_Rgba32_rgb_small_lzw.png | 3 - .../Decode_Rgba32_rgb_small_lzw.png | 3 - tests/Images/Input/Tiff/issues/readme.md | 2 - .../Tiff/rgb_lzw_noPredictor_multistrip.tiff | 3 + ...b_lzw_noPredictor_multistrip_Motorola.tiff | 3 + ..._lzw_noPredictor_singlestrip_Motorola.tiff | 3 + 10 files changed, 78 insertions(+), 130 deletions(-) delete mode 100644 tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_rgb_small_lzw.png delete mode 100644 tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_small_lzw.png delete mode 100644 tests/Images/Input/Tiff/issues/readme.md create mode 100644 tests/Images/Input/Tiff/rgb_lzw_noPredictor_multistrip.tiff create mode 100644 tests/Images/Input/Tiff/rgb_lzw_noPredictor_multistrip_Motorola.tiff create mode 100644 tests/Images/Input/Tiff/rgb_lzw_noPredictor_singlestrip_Motorola.tiff diff --git a/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs index bba9739e2..f4caeb3c2 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs @@ -21,10 +21,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff public override void Decompress(Stream stream, int byteCount, Span buffer) { var subStream = new SubStream(stream, byteCount); - using (var decoder = new TiffLzwDecoder(subStream)) - { - decoder.DecodePixels(buffer.Length, 8, buffer); - } + var decoder = new TiffLzwDecoder(subStream, this.Allocator); + decoder.DecodePixels(buffer.Length, 8, buffer); } } } diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs index a490e7c0d..2e95d7e5a 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs @@ -5,6 +5,7 @@ using System; using System.Buffers; using System.IO; using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Tiff { @@ -19,7 +20,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// byte indicating the length of the sub-block. In TIFF the data is written as a single block /// with no length indicator (this can be determined from the 'StripByteCounts' entry). /// - internal sealed class TiffLzwDecoder : IDisposable + internal sealed class TiffLzwDecoder { /// /// The max decoder pixel stack size. @@ -37,52 +38,23 @@ namespace SixLabors.ImageSharp.Formats.Tiff private readonly Stream stream; /// - /// The prefix buffer. + /// The memory allocator. /// - private readonly int[] prefix; + private readonly MemoryAllocator allocator; /// - /// The suffix buffer. - /// - private readonly int[] suffix; - - /// - /// The pixel stack buffer. - /// - private readonly int[] pixelStack; - - /// - /// A value indicating whether this instance of the given entity has been disposed. - /// - /// if this instance has been disposed; otherwise, . - /// - /// If the entity is disposed, it must not be disposed a second - /// time. The isDisposed field is set the first time the entity - /// is disposed. If the isDisposed field is true, then the Dispose() - /// method will not dispose again. This help not to prolong the entity's - /// life in the Garbage Collector. - /// - private bool isDisposed; - - /// - /// Initializes a new instance of the class + /// Initializes a new instance of the class /// and sets the stream, where the compressed data should be read from. /// /// The stream to read from. - /// is null. - public TiffLzwDecoder(Stream stream) + /// The memory allocator. + /// is null. + public TiffLzwDecoder(Stream stream, MemoryAllocator allocator) { Guard.NotNull(stream, nameof(stream)); this.stream = stream; - - this.prefix = ArrayPool.Shared.Rent(MaxStackSize); - this.suffix = ArrayPool.Shared.Rent(MaxStackSize); - this.pixelStack = ArrayPool.Shared.Rent(MaxStackSize + 1); - - Array.Clear(this.prefix, 0, MaxStackSize); - Array.Clear(this.suffix, 0, MaxStackSize); - Array.Clear(this.pixelStack, 0, MaxStackSize + 1); + this.allocator = allocator; } /// @@ -95,6 +67,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff { Guard.MustBeLessThan(dataSize, int.MaxValue, nameof(dataSize)); + // Initialize buffers + using IMemoryOwner prefixMemory = this.allocator.Allocate(MaxStackSize, AllocationOptions.Clean); + using IMemoryOwner suffixMemory = this.allocator.Allocate(MaxStackSize, AllocationOptions.Clean); + using IMemoryOwner pixelStackMemory = this.allocator.Allocate(MaxStackSize + 1, AllocationOptions.Clean); + + Span prefix = prefixMemory.GetSpan(); + Span suffix = suffixMemory.GetSpan(); + Span pixelStack = pixelStackMemory.GetSpan(); + // Calculate the clear code. The value of the clear code is 2 ^ dataSize int clearCode = 1 << dataSize; @@ -111,54 +92,39 @@ namespace SixLabors.ImageSharp.Formats.Tiff int code; int oldCode = NullCode; int codeMask = (1 << codeSize) - 1; + + int inputByte = 0; int bits = 0; int top = 0; - int count = 0; - int bi = 0; int xyz = 0; - int data = 0; int first = 0; for (code = 0; code < clearCode; code++) { - this.prefix[code] = 0; - this.suffix[code] = (byte)code; + prefix[code] = 0; + suffix[code] = (byte)code; } - byte[] buffer = new byte[255]; + // Decoding process while (xyz < length) { if (top == 0) { - if (bits < codeSize) - { - // Load bytes until there are enough bits for a code. - if (count == 0) - { - // Read a new data block. - count = this.ReadBlock(buffer); - if (count == 0) - { - break; - } - - bi = 0; - } - - data += buffer[bi] << bits; + // Get the next code + int data = inputByte & ((1 << bits) - 1); + while (bits < codeSize) + { + inputByte = this.stream.ReadByte(); + data = (data << 8) | inputByte; bits += 8; - bi++; - count--; - continue; } - // Get the next code - code = data & codeMask; - data >>= codeSize; + data >>= bits - codeSize; bits -= codeSize; + code = data & codeMask; // Interpret the code if (code > availableCode || code == endCode) @@ -178,7 +144,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff if (oldCode == NullCode) { - this.pixelStack[top++] = this.suffix[code]; + pixelStack[top++] = suffix[code]; oldCode = code; first = code; continue; @@ -187,27 +153,27 @@ namespace SixLabors.ImageSharp.Formats.Tiff int inCode = code; if (code == availableCode) { - this.pixelStack[top++] = (byte)first; + pixelStack[top++] = (byte)first; code = oldCode; } while (code > clearCode) { - this.pixelStack[top++] = this.suffix[code]; - code = this.prefix[code]; + pixelStack[top++] = suffix[code]; + code = prefix[code]; } - first = this.suffix[code]; + first = suffix[code]; - this.pixelStack[top++] = this.suffix[code]; + pixelStack[top++] = suffix[code]; // Fix for Gifs that have "deferred clear code" as per here : // https://bugzilla.mozilla.org/show_bug.cgi?id=55918 if (availableCode < MaxStackSize) { - this.prefix[availableCode] = oldCode; - this.suffix[availableCode] = first; + prefix[availableCode] = oldCode; + suffix[availableCode] = first; availableCode++; if (availableCode == codeMask + 1 && availableCode < MaxStackSize) { @@ -223,49 +189,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff top--; // Clear missing pixels - pixels[xyz++] = (byte)this.pixelStack[top]; + pixels[xyz++] = (byte)pixelStack[top]; } } - - /// - public void Dispose() - { - // Do not change this code. Put cleanup code in Dispose(bool disposing) above. - this.Dispose(true); - } - - /// - /// Reads the next data block from the stream. For consistency with the GIF decoder, - /// the image is read in blocks - For TIFF this is always a maximum of 255 - /// - /// The buffer to store the block in. - /// - /// The . - /// - private int ReadBlock(byte[] buffer) - { - return this.stream.Read(buffer, 0, 255); - } - - /// - /// Disposes the object and frees resources for the Garbage Collector. - /// - /// If true, the object gets disposed. - private void Dispose(bool disposing) - { - if (this.isDisposed) - { - return; - } - - if (disposing) - { - ArrayPool.Shared.Return(this.prefix); - ArrayPool.Shared.Return(this.suffix); - ArrayPool.Shared.Return(this.pixelStack); - } - - this.isDisposed = true; - } } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index d9abc163a..3a40d5ce2 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -53,6 +53,27 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff } } + [Theory] + [InlineData(TestImages.Tiff.RgbLzw_NoPredictor_Multistrip, TiffByteOrder.LittleEndian)] + [InlineData(TestImages.Tiff.RgbLzw_NoPredictor_Multistrip_Motorola, TiffByteOrder.BigEndian)] + public void ByteOrder(string imagePath, TiffByteOrder expectedByteOrder) + { + var testFile = TestFile.Create(imagePath); + using (var stream = new MemoryStream(testFile.Bytes, false)) + { + IImageInfo info = Image.Identify(stream); + + Assert.NotNull(info.Metadata); + Assert.Equal(expectedByteOrder, info.Metadata.GetTiffMetadata().ByteOrder); + + // todo: it's not a mistake? + stream.Seek(0, SeekOrigin.Begin); + + using var img = Image.Load(stream); + Assert.Equal(expectedByteOrder, img.Metadata.GetTiffMetadata().ByteOrder); + } + } + [Theory] [WithFileCollection(nameof(SingleTestImages), PixelTypes.Rgba32)] public void Decode(TestImageProvider provider) diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index f90324d64..d66f1a5c7 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -504,7 +504,7 @@ namespace SixLabors.ImageSharp.Tests public const string Calliphora_PaletteUncompressed = "Tiff/Calliphora_palette_uncompressed.tiff"; public const string Calliphora_RgbDeflate_Predictor = "Tiff/Calliphora_rgb_deflate.tiff"; public const string Calliphora_RgbJpeg = "Tiff/Calliphora_rgb_jpeg.tiff"; - public const string Calliphora_RgbLzwe_Predictor = "Tiff/Calliphora_rgb_lzw.tiff"; + public const string Calliphora_RgbLzw_Predictor = "Tiff/Calliphora_rgb_lzw.tiff"; public const string Calliphora_RgbPackbits = "Tiff/Calliphora_rgb_packbits.tiff"; public const string Calliphora_RgbUncompressed = "Tiff/Calliphora_rgb_uncompressed.tiff"; @@ -516,6 +516,9 @@ namespace SixLabors.ImageSharp.Tests public const string RgbDeflateMultistrip = "Tiff/rgb_deflate_multistrip.tiff"; public const string RgbJpeg = "Tiff/rgb_jpeg.tiff"; public const string RgbLzw_Predictor = "Tiff/rgb_lzw.tiff"; + public const string RgbLzw_NoPredictor_Multistrip = "Tiff/rgb_lzw_noPredictor_multistrip.tiff"; + public const string RgbLzw_NoPredictor_Multistrip_Motorola = "Tiff/rgb_lzw_noPredictor_multistrip_Motorola.tiff"; + public const string RgbLzw_NoPredictor_Singlestrip_Motorola = "Tiff/rgb_lzw_noPredictor_singlestrip_Motorola.tiff"; public const string RgbLzwMultistrip_Predictor = "Tiff/rgb_lzw_multistrip.tiff"; public const string RgbPackbits = "Tiff/rgb_packbits.tiff"; public const string RgbPackbitsMultistrip = "Tiff/rgb_packbits_multistrip.tiff"; @@ -534,13 +537,13 @@ namespace SixLabors.ImageSharp.Tests public const string SampleMetadata = "Tiff/metadata_sample.tiff"; - public static readonly string[] All = { Calliphora_GrayscaleUncompressed, Calliphora_PaletteUncompressed, /*Calliphora_RgbDeflate_Predictor, Calliphora_RgbLzwe_Predictor, */ Calliphora_RgbPackbits, Calliphora_RgbUncompressed, GrayscaleDeflateMultistrip, GrayscaleUncompressed, PaletteDeflateMultistrip, PaletteUncompressed, /*RgbDeflate_Predictor,*/ RgbDeflateMultistrip, /*RgbJpeg,*/ /*RgbLzw_Predictor, RgbLzwMultistrip_Predictor,*/ RgbPackbits, RgbPackbitsMultistrip, RgbUncompressed, /* MultiframeLzw_Predictor, MultiFrameDifferentVariants, SampleMetadata,*/ SmallRgbDeflate, SmallRgbLzw }; + public static readonly string[] All = { Calliphora_GrayscaleUncompressed, Calliphora_PaletteUncompressed, /*Calliphora_RgbDeflate_Predictor, Calliphora_RgbLzwe_Predictor, */ Calliphora_RgbPackbits, Calliphora_RgbUncompressed, GrayscaleDeflateMultistrip, GrayscaleUncompressed, PaletteDeflateMultistrip, PaletteUncompressed, /*RgbDeflate_Predictor,*/ RgbDeflateMultistrip, /*RgbJpeg,*/ /*RgbLzw_Predictor, RgbLzwMultistrip_Predictor,*/ RgbLzw_NoPredictor_Multistrip, RgbLzw_NoPredictor_Multistrip_Motorola, RgbLzw_NoPredictor_Singlestrip_Motorola, RgbPackbits, RgbPackbitsMultistrip, RgbUncompressed, /* MultiframeLzw_Predictor, MultiFrameDifferentVariants, SampleMetadata,*/ SmallRgbDeflate, SmallRgbLzw, }; public static readonly string[] Multiframes = { MultiframeDeflateWithPreview /*MultiframeLzw_Predictor, MultiFrameDifferentSize, MultiframeDifferentSizeTiled, MultiFrameDifferentVariants,*/ }; public static readonly string[] Metadata = { SampleMetadata }; - public static readonly string[] NotSupported = { Calliphora_RgbJpeg, Calliphora_RgbDeflate_Predictor, Calliphora_RgbLzwe_Predictor, RgbDeflate_Predictor, RgbLzw_Predictor, RgbLzwMultistrip_Predictor, RgbJpeg, RgbUncompressedTiled, MultiframeLzw_Predictor, MultiframeDifferentSize, MultiframeDifferentVariants }; + public static readonly string[] NotSupported = { Calliphora_RgbJpeg, Calliphora_RgbDeflate_Predictor, Calliphora_RgbLzw_Predictor, RgbDeflate_Predictor, RgbLzw_Predictor, RgbLzwMultistrip_Predictor, RgbJpeg, RgbUncompressedTiled, MultiframeLzw_Predictor, MultiframeDifferentSize, MultiframeDifferentVariants }; } } } diff --git a/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_rgb_small_lzw.png b/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_rgb_small_lzw.png deleted file mode 100644 index a05e8248e..000000000 --- a/tests/Images/Input/Tiff/issues/net472/Decode_Rgba32_rgb_small_lzw.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:bd197010f3c0513e7782a33f187c79f2b5c5beaa5e0b2a472a8ab82b17ca2fed -size 208 diff --git a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_small_lzw.png b/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_small_lzw.png deleted file mode 100644 index a05e8248e..000000000 --- a/tests/Images/Input/Tiff/issues/netcoreapp3.1/Decode_Rgba32_rgb_small_lzw.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:bd197010f3c0513e7782a33f187c79f2b5c5beaa5e0b2a472a8ab82b17ca2fed -size 208 diff --git a/tests/Images/Input/Tiff/issues/readme.md b/tests/Images/Input/Tiff/issues/readme.md deleted file mode 100644 index 1616a432c..000000000 --- a/tests/Images/Input/Tiff/issues/readme.md +++ /dev/null @@ -1,2 +0,0 @@ -SixLabors.ImageSharp.Tests.Formats.Tiff.TiffDecoderTests.Decode -damaged output files \ No newline at end of file diff --git a/tests/Images/Input/Tiff/rgb_lzw_noPredictor_multistrip.tiff b/tests/Images/Input/Tiff/rgb_lzw_noPredictor_multistrip.tiff new file mode 100644 index 000000000..4d8c11afe --- /dev/null +++ b/tests/Images/Input/Tiff/rgb_lzw_noPredictor_multistrip.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b67399bf019d8a585a6433bcaf6299846b85cb7783cf9350340b900185e645c6 +size 155004 diff --git a/tests/Images/Input/Tiff/rgb_lzw_noPredictor_multistrip_Motorola.tiff b/tests/Images/Input/Tiff/rgb_lzw_noPredictor_multistrip_Motorola.tiff new file mode 100644 index 000000000..59290df1c --- /dev/null +++ b/tests/Images/Input/Tiff/rgb_lzw_noPredictor_multistrip_Motorola.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f3fa8877b14979d2b56c0f0acd18b1c797ffe5d4ab91fce5dad8e1177acf7f4d +size 155004 diff --git a/tests/Images/Input/Tiff/rgb_lzw_noPredictor_singlestrip_Motorola.tiff b/tests/Images/Input/Tiff/rgb_lzw_noPredictor_singlestrip_Motorola.tiff new file mode 100644 index 000000000..557fb4c51 --- /dev/null +++ b/tests/Images/Input/Tiff/rgb_lzw_noPredictor_singlestrip_Motorola.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:86cd325c7587d1712c91fed16d18a16dcbbbce97de6f9e3ae782e0e5da1ff541 +size 154735 From 2da3c6aed7f5028b5298d738534b72007de09fd0 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Thu, 22 Oct 2020 16:22:37 +0300 Subject: [PATCH 074/275] Small improve tiff decoder. Add TiffThrowHelper. --- .../Compression/DeflateTiffCompression.cs | 2 +- .../Compression/TiffCompressionFactory.cs | 2 +- .../Formats/Tiff/Ifd/DirectoryReader.cs | 8 +- .../Formats/Tiff/Ifd/EntryReader.cs | 4 +- .../TiffColorDecoderFactory.cs | 4 +- .../Formats/Tiff/Streams/TiffStreamFactory.cs | 53 ------------ src/ImageSharp/Formats/Tiff/TiffDecoder.cs | 8 +- .../Formats/Tiff/TiffDecoderCore.cs | 85 ++++++++++--------- .../Formats/Tiff/TiffDecoderHelpers.cs | 33 ++++--- .../Formats/Tiff/TiffFrameMetadata.cs | 8 +- .../Formats/Tiff/TiffThrowHelper.cs | 50 +++++++++++ 11 files changed, 132 insertions(+), 125 deletions(-) delete mode 100644 src/ImageSharp/Formats/Tiff/Streams/TiffStreamFactory.cs create mode 100644 src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs diff --git a/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs index e10d8195b..3e07851d4 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs @@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff if ((cmf & 0x0f) != 8) { - throw new Exception($"Bad compression method for ZLIB header: cmf={cmf}"); + TiffThrowHelper.ThrowBadZlibHeader(cmf); } // If the 'fdict' flag is set then we should skip the next four bytes diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs index 7e077983d..0f893448d 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs @@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff case TiffCompressionType.Lzw: return new LzwTiffCompression(allocator); default: - throw new InvalidOperationException(); + throw TiffThrowHelper.NotSupportedCompression(nameof(compressionType)); } } } diff --git a/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs index 693b3abfc..3aedf422b 100644 --- a/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs +++ b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs @@ -1,9 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using System; using System.Collections.Generic; -using System.IO; using SixLabors.ImageSharp.Metadata.Profiles.Exif; namespace SixLabors.ImageSharp.Formats.Tiff @@ -40,13 +38,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff ushort magic = this.stream.ReadUInt16(); if (magic != TiffConstants.HeaderMagicNumber) { - throw new ImageFormatException("Invalid TIFF header magic number: " + magic); + TiffThrowHelper.ThrowInvalidHeader(); } uint firstIfdOffset = this.stream.ReadUInt32(); if (firstIfdOffset == 0) { - throw new ImageFormatException("Invalid TIFF file header."); + TiffThrowHelper.ThrowInvalidHeader(); } this.nextIfdOffset = firstIfdOffset; @@ -95,7 +93,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff } else if (leftBytes < 0) { - throw new InvalidDataException("Out of range of IFD structure."); + TiffThrowHelper.ThrowOutOfRange("IFD"); } return entries.ToArray(); diff --git a/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs b/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs index b605a8737..57d69b4a8 100644 --- a/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs +++ b/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs @@ -142,7 +142,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff } else if (leftBytes < 0) { - throw new InvalidDataException("Out of range of IFD entry structure."); + TiffThrowHelper.ThrowOutOfRange("IFD entry"); } } @@ -244,7 +244,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff if (buf[buf.Length - 1] != 0) { - throw new ImageFormatException("The retrieved string is not null terminated."); + TiffThrowHelper.ThrowBadStringEntry(); } return Encoding.UTF8.GetString(buf, 0, buf.Length - 1); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory.cs index a01a25e8b..20129da99 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory.cs @@ -74,7 +74,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff return new PaletteTiffColor(bitsPerSample, colorMap); default: - throw new InvalidOperationException(); + throw TiffThrowHelper.InvalidColorType(colorType.ToString()); } } @@ -88,7 +88,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff return new RgbPlanarTiffColor(bitsPerSample); default: - throw new InvalidOperationException(); + throw TiffThrowHelper.InvalidColorType(colorType.ToString()); } } } diff --git a/src/ImageSharp/Formats/Tiff/Streams/TiffStreamFactory.cs b/src/ImageSharp/Formats/Tiff/Streams/TiffStreamFactory.cs deleted file mode 100644 index f65062d59..000000000 --- a/src/ImageSharp/Formats/Tiff/Streams/TiffStreamFactory.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.IO; - -namespace SixLabors.ImageSharp.Formats.Tiff -{ - /// - /// The tiff data stream factory class. - /// - internal static class TiffStreamFactory - { - /// - /// Creates the specified byte order. - /// - /// The byte order. - /// The stream. - public static TiffStream Create(TiffByteOrder byteOrder, Stream stream) - { - if (byteOrder == TiffByteOrder.BigEndian) - { - return new TiffBigEndianStream(stream); - } - else if (byteOrder == TiffByteOrder.LittleEndian) - { - return new TiffLittleEndianStream(stream); - } - - throw new ArgumentOutOfRangeException(nameof(byteOrder)); - } - - /// - /// Reads the byte order of stream. - /// - /// The stream. - public static TiffByteOrder ReadByteOrder(Stream stream) - { - byte[] headerBytes = new byte[2]; - stream.Read(headerBytes, 0, 2); - if (headerBytes[0] == TiffConstants.ByteOrderLittleEndian && headerBytes[1] == TiffConstants.ByteOrderLittleEndian) - { - return TiffByteOrder.LittleEndian; - } - else if (headerBytes[0] == TiffConstants.ByteOrderBigEndian && headerBytes[1] == TiffConstants.ByteOrderBigEndian) - { - return TiffByteOrder.BigEndian; - } - - throw new ImageFormatException("Invalid TIFF file header."); - } - } -} diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs index aed21af56..fadcb7550 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff { Guard.NotNull(stream, "stream"); - using var decoder = new TiffDecoderCore(stream, configuration, this); + var decoder = new TiffDecoderCore(configuration, this); return decoder.Decode(configuration, stream); } @@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff { Guard.NotNull(stream, nameof(stream)); - var decoder = new TiffDecoderCore(stream, configuration, this); + var decoder = new TiffDecoderCore(configuration, this); return decoder.DecodeAsync(configuration, stream, cancellationToken); } @@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff { Guard.NotNull(stream, nameof(stream)); - var decoder = new TiffDecoderCore(stream, configuration, this); + var decoder = new TiffDecoderCore(configuration, this); return decoder.Identify(configuration, stream); } @@ -61,7 +61,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff { Guard.NotNull(stream, nameof(stream)); - var decoder = new TiffDecoderCore(stream, configuration, this); + var decoder = new TiffDecoderCore(configuration, this); return decoder.IdentifyAsync(configuration, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 947110137..468989d19 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Performs the tiff decoding operation. /// - internal class TiffDecoderCore : IImageDecoderInternals, IDisposable + internal class TiffDecoderCore : IImageDecoderInternals { /// /// The global configuration @@ -35,42 +35,22 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// private readonly bool ignoreMetadata; + private BufferedReadStream inputStream; + /// /// Initializes a new instance of the class. /// /// The configuration. /// The decoder options. - private TiffDecoderCore(Configuration configuration, ITiffDecoderOptions options) + public TiffDecoderCore(Configuration configuration, ITiffDecoderOptions options) { - options = options ?? new TiffDecoder(); + options ??= new TiffDecoder(); this.configuration = configuration ?? Configuration.Default; this.ignoreMetadata = options.IgnoreMetadata; this.memoryAllocator = this.configuration.MemoryAllocator; } - /// - /// Initializes a new instance of the class. - /// - /// The stream. - /// The configuration. - /// The decoder options. - public TiffDecoderCore(Stream stream, Configuration configuration, ITiffDecoderOptions options) - : this(configuration, options) - { - this.ByteOrder = TiffStreamFactory.ReadByteOrder(stream); - } - - /// - /// Gets the byte order. - /// - public TiffByteOrder ByteOrder { get; } - - /// - /// Gets the input stream. - /// - public TiffStream Stream { get; private set; } - /// /// Gets or sets the number of bits for each sample of the pixel format used to encode the image. /// @@ -111,8 +91,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { - this.Stream = TiffStreamFactory.Create(this.ByteOrder, stream); - var reader = new DirectoryReader(this.Stream); + this.inputStream = stream; + TiffStream tiffStream = CreateStream(stream); + var reader = new DirectoryReader(tiffStream); IEnumerable directories = reader.Read(); @@ -125,7 +106,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff framesMetadata.Add(frameMetadata); } - ImageMetadata metadata = framesMetadata.CreateMetadata(this.ignoreMetadata, this.Stream.ByteOrder); + ImageMetadata metadata = framesMetadata.CreateMetadata(this.ignoreMetadata, tiffStream.ByteOrder); // todo: tiff frames can have different sizes { @@ -135,7 +116,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff { if (frame.Size() != root.Size()) { - throw new NotSupportedException("Images with different sizes are not supported"); + TiffThrowHelper.ThrowNotSupported("Images with different sizes are not supported"); } } } @@ -148,8 +129,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { - this.Stream = TiffStreamFactory.Create(this.ByteOrder, stream); - var reader = new DirectoryReader(this.Stream); + this.inputStream = stream; + TiffStream tiffStream = CreateStream(stream); + var reader = new DirectoryReader(tiffStream); IEnumerable directories = reader.Read(); @@ -159,7 +141,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff framesMetadata.Add(new TiffFrameMetadata() { Tags = ifd }); } - ImageMetadata metadata = framesMetadata.CreateMetadata(this.ignoreMetadata, this.Stream.ByteOrder); + ImageMetadata metadata = framesMetadata.CreateMetadata(this.ignoreMetadata, tiffStream.ByteOrder); TiffFrameMetadata root = framesMetadata.First(); int bitsPerPixel = 0; @@ -171,10 +153,35 @@ namespace SixLabors.ImageSharp.Formats.Tiff return new ImageInfo(new PixelTypeInfo(bitsPerPixel), (int)root.Width, (int)root.Height, metadata); } - /// - public void Dispose() + private static TiffStream CreateStream(Stream stream) + { + TiffByteOrder byteOrder = ReadByteOrder(stream); + if (byteOrder == TiffByteOrder.BigEndian) + { + return new TiffBigEndianStream(stream); + } + else if (byteOrder == TiffByteOrder.LittleEndian) + { + return new TiffLittleEndianStream(stream); + } + + throw TiffThrowHelper.InvalidHeader(); + } + + private static TiffByteOrder ReadByteOrder(Stream stream) { - // nothing + byte[] headerBytes = new byte[2]; + stream.Read(headerBytes, 0, 2); + if (headerBytes[0] == TiffConstants.ByteOrderLittleEndian && headerBytes[1] == TiffConstants.ByteOrderLittleEndian) + { + return TiffByteOrder.LittleEndian; + } + else if (headerBytes[0] == TiffConstants.ByteOrderBigEndian && headerBytes[1] == TiffConstants.ByteOrderBigEndian) + { + return TiffByteOrder.BigEndian; + } + + throw TiffThrowHelper.InvalidHeader(); } /// @@ -283,8 +290,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff { int stripIndex = (i * stripsPerPixel) + planeIndex; - this.Stream.Seek(stripOffsets[stripIndex]); - decompressor.Decompress(this.Stream.InputStream, (int)stripByteCounts[stripIndex], stripBuffers[planeIndex].GetSpan()); + this.inputStream.Seek(stripOffsets[stripIndex], SeekOrigin.Begin); + decompressor.Decompress(this.inputStream, (int)stripByteCounts[stripIndex], stripBuffers[planeIndex].GetSpan()); } colorDecoder.Decode(stripBuffers, pixels, 0, rowsPerStrip * i, frame.Width, stripHeight); @@ -316,8 +323,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff { int stripHeight = stripIndex < stripOffsets.Length - 1 || frame.Height % rowsPerStrip == 0 ? rowsPerStrip : frame.Height % rowsPerStrip; - this.Stream.Seek(stripOffsets[stripIndex]); - decompressor.Decompress(this.Stream.InputStream, (int)stripByteCounts[stripIndex], stripBuffer.GetSpan()); + this.inputStream.Seek(stripOffsets[stripIndex], SeekOrigin.Begin); + decompressor.Decompress(this.inputStream, (int)stripByteCounts[stripIndex], stripBuffer.GetSpan()); colorDecoder.Decode(stripBuffer.GetSpan(), pixels, 0, rowsPerStrip * stripIndex, frame.Width, stripHeight); } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs index ebfdf5df0..86a7560cf 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs @@ -92,22 +92,22 @@ namespace SixLabors.ImageSharp.Formats.Tiff { if (entries.ExtraSamples != null) { - throw new NotSupportedException("ExtraSamples is not supported."); + TiffThrowHelper.ThrowNotSupported("ExtraSamples is not supported."); } if (entries.FillOrder != TiffFillOrder.MostSignificantBitFirst) { - throw new NotSupportedException("The lower-order bits of the byte FillOrder is not supported."); + TiffThrowHelper.ThrowNotSupported("The lower-order bits of the byte FillOrder is not supported."); } if (entries.GetArray(ExifTag.TileOffsets, true) != null) { - throw new NotSupportedException("The Tile images is not supported."); + TiffThrowHelper.ThrowNotSupported("The Tile images is not supported."); } if (entries.Predictor != TiffPredictor.None) { - throw new NotSupportedException("At the moment support only None Predictor."); + TiffThrowHelper.ThrowNotSupported("At the moment support only None Predictor."); } if (entries.SampleFormat != null) @@ -116,7 +116,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff { if (format != TiffSampleFormat.UnsignedInteger) { - throw new NotSupportedException("At the moment support only UnsignedInteger SampleFormat."); + TiffThrowHelper.ThrowNotSupported("At the moment support only UnsignedInteger SampleFormat."); } } } @@ -169,7 +169,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff } else { - throw new NotSupportedException("The number of samples in the TIFF BitsPerSample entry is not supported."); + TiffThrowHelper.ThrowNotSupported("The number of samples in the TIFF BitsPerSample entry is not supported."); } break; @@ -208,7 +208,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff } else { - throw new NotSupportedException("The number of samples in the TIFF BitsPerSample entry is not supported."); + TiffThrowHelper.ThrowNotSupported("The number of samples in the TIFF BitsPerSample entry is not supported."); } break; @@ -236,7 +236,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff } else { - throw new NotSupportedException("The number of samples in the TIFF BitsPerSample entry is not supported."); + TiffThrowHelper.ThrowNotSupported("The number of samples in the TIFF BitsPerSample entry is not supported."); } break; @@ -260,19 +260,23 @@ namespace SixLabors.ImageSharp.Formats.Tiff } else { - throw new NotSupportedException("The number of samples in the TIFF BitsPerSample entry is not supported."); + TiffThrowHelper.ThrowNotSupported("The number of samples in the TIFF BitsPerSample entry is not supported."); } } else { - throw new ImageFormatException("The TIFF ColorMap entry is missing for a palette color image."); + TiffThrowHelper.ThrowNotSupported("The TIFF ColorMap entry is missing for a palette color image."); } break; } default: - throw new NotSupportedException("The specified TIFF photometric interpretation is not supported: " + options.PhotometricInterpretation); + { + TiffThrowHelper.ThrowNotSupported("The specified TIFF photometric interpretation is not supported: " + options.PhotometricInterpretation); + } + + break; } } @@ -288,7 +292,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff } else { - throw new ImageFormatException("The TIFF BitsPerSample entry is missing."); + TiffThrowHelper.ThrowNotSupported("The TIFF BitsPerSample entry is missing."); } } } @@ -304,7 +308,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff } else { - throw new ImageFormatException("The TIFF photometric interpretation entry is missing."); + TiffThrowHelper.ThrowNotSupported("The TIFF photometric interpretation entry is missing."); } } @@ -346,7 +350,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff default: { - throw new NotSupportedException("The specified TIFF compression format is not supported: " + compression); + TiffThrowHelper.ThrowNotSupported("The specified TIFF compression format is not supported: " + compression); + break; } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs index cfec448a4..466702693 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs @@ -209,7 +209,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff if (!optional) { - throw new ArgumentException("Required tag is not founded: " + tag, nameof(tag)); + TiffThrowHelper.ThrowTagNotFound(nameof(tag)); } return null; @@ -245,7 +245,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff if (!optional) { - throw new ArgumentException("Required tag is not founded: " + tag, nameof(tag)); + TiffThrowHelper.ThrowTagNotFound(nameof(tag)); } return null; @@ -283,7 +283,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff private TEnum GetSingleEnum(ExifTag tag, TEnum? defaultValue = null) where TEnum : struct where TTagValue : struct - => this.GetSingleEnumNullable(tag) ?? (defaultValue != null ? defaultValue.Value : throw new ArgumentException("Required tag is not founded: " + tag, nameof(tag))); + => this.GetSingleEnumNullable(tag) ?? (defaultValue != null ? defaultValue.Value : throw TiffThrowHelper.TagNotFound(nameof(tag))); private T GetSingle(ExifTag tag) where T : struct @@ -293,7 +293,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff return result; } - throw new ArgumentException("Required tag is not founded: " + tag, nameof(tag)); + throw TiffThrowHelper.TagNotFound(nameof(tag)); } private bool TryGetSingle(ExifTag tag, out T result) diff --git a/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs b/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs new file mode 100644 index 000000000..3254744bc --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs @@ -0,0 +1,50 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.Formats.Tiff +{ + /// + /// Cold path optimizations for throwing tiff format based exceptions. + /// + internal static class TiffThrowHelper + { + [MethodImpl(InliningOptions.ColdPath)] + public static Exception TagNotFound(string tagName) + => new ArgumentException("Required tag is not found.", tagName); + + [MethodImpl(InliningOptions.ColdPath)] + public static void ThrowTagNotFound(string tagName) + => throw TagNotFound(tagName); + + [MethodImpl(InliningOptions.ColdPath)] + public static void ThrowBadZlibHeader(int cmf) => throw new ImageFormatException($"Bad compression method for ZLIB header: cmf={cmf}"); + + [MethodImpl(InliningOptions.ColdPath)] + public static Exception NotSupportedCompression(string compressionType) => new NotSupportedException("Not supported compression: " + compressionType); + + [MethodImpl(InliningOptions.ColdPath)] + public static void ThrowNotSupportedCompression(string compressionType) => throw NotSupportedCompression(compressionType); + + [MethodImpl(InliningOptions.ColdPath)] + public static Exception InvalidColorType(string colorType) => new NotSupportedException("Invalid color type: " + colorType); + + [MethodImpl(InliningOptions.ColdPath)] + public static Exception InvalidHeader() => new ImageFormatException("Invalid TIFF file header."); + + [MethodImpl(InliningOptions.ColdPath)] + public static void ThrowInvalidHeader() => throw InvalidHeader(); + + [MethodImpl(InliningOptions.ColdPath)] + public static void ThrowOutOfRange(string structure) => throw new InvalidDataException($"Out of range of {structure} structure."); + + [MethodImpl(InliningOptions.ColdPath)] + public static void ThrowBadStringEntry() => throw new ImageFormatException("The retrieved string is not null terminated."); + + [MethodImpl(InliningOptions.ColdPath)] + public static void ThrowNotSupported(string message) => throw new NotSupportedException(message); + } +} From 5edec6dc312d666027a05fa9439fcfd8101ec6ac Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Thu, 22 Oct 2020 16:27:59 +0300 Subject: [PATCH 075/275] Lzw test fix. --- .../Formats/Tiff/Compression/LzwTiffCompressionTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs index df9208434..a26f0b117 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs @@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { byte[] buffer = new byte[data.Length]; - new LzwTiffCompression(null).Decompress(stream, (int)stream.Length, buffer); + new LzwTiffCompression(Configuration.Default.MemoryAllocator).Decompress(stream, (int)stream.Length, buffer); Assert.Equal(data, buffer); } From 9f085857e046a4b62f10a4f15361c8575b1ee19a Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 21 Nov 2020 18:03:53 +0100 Subject: [PATCH 076/275] Add support for de-compressing CCITT t4 tiffs --- .../Formats/Tiff/Compression/T4BitReader.cs | 1110 +++++++++++++++++ .../Tiff/Compression/T4TiffCompression.cs | 95 ++ .../Compression/TiffCompressionFactory.cs | 4 +- .../Tiff/Compression/TiffCompressionType.cs | 7 +- .../Formats/Tiff/TiffDecoderCore.cs | 4 +- .../Formats/Tiff/TiffDecoderHelpers.cs | 8 +- .../Formats/Tiff/TiffThrowHelper.cs | 10 + 7 files changed, 1231 insertions(+), 7 deletions(-) create mode 100644 src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs create mode 100644 src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs diff --git a/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs new file mode 100644 index 000000000..c37de8031 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs @@ -0,0 +1,1110 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; +using System.Linq; + +namespace SixLabors.ImageSharp.Formats.Tiff.Compression +{ + /// + /// Bitreader for reading compressed CCITT T4 1D data. + /// + internal class T4BitReader + { + /// + /// Number of bits read. + /// + private int bitsRead; + + /// + /// Current value. + /// + private uint value; + + /// + /// Number of bits read for the current run value. + /// + private int curValueBitsRead; + + /// + /// Byte position in the buffer. + /// + private ulong position; + + /// + /// Indicates, if the current run are white pixels. + /// + private bool isWhiteRun; + + /// + /// Indicates whether its the first line of data which is read from the image. + /// + private bool isFirstScanLine; + + private bool terminationCodeFound; + + /// + /// Number of pixels in the current run. + /// + private uint runLength; + + private const int MinCodeLength = 2; + + private const int MaxCodeLength = 13; + + public T4BitReader(Stream input, int bytesToRead) + { + // TODO: use memory allocator + this.Data = new byte[bytesToRead]; + this.ReadImageDataFromStream(input, bytesToRead); + + this.bitsRead = 0; + this.value = 0; + this.curValueBitsRead = 0; + this.position = 0; + this.isWhiteRun = true; + this.isFirstScanLine = true; + this.terminationCodeFound = false; + this.runLength = 0; + } + + /// + /// Gets the compressed image data. + /// + public byte[] Data { get; } + + /// + /// Gets a value indicating whether there is more data to read left. + /// + public bool HasMoreData + { + get + { + return this.position < (ulong)this.Data.Length - 1; + } + } + + /// + /// Gets a value indicating whether the current run is a white pixel run, otherwise its a black pixel run. + /// + public bool IsWhiteRun + { + get + { + return this.isWhiteRun; + } + } + + /// + /// Gets the number of pixels in the current run. + /// + public uint RunLength + { + get + { + return this.runLength; + } + } + + /// + /// Gets a value indicating whether the end of a pixel row has been reached. + /// + public bool IsEndOfScanLine + { + get + { + return this.curValueBitsRead == 12 && this.value == 1; + } + } + + /// + /// Read the next run of pixels. + /// + public void ReadNextRun() + { + if (this.terminationCodeFound) + { + this.isWhiteRun = !this.IsWhiteRun; + this.terminationCodeFound = false; + } + + this.Reset(); + + if (this.isFirstScanLine) + { + // We expect an EOL before the first data. + this.value = this.ReadValue(12); + if (!this.IsEndOfScanLine) + { + TiffThrowHelper.ThrowImageFormatException("t4 parsing error: expected start of data marker not found"); + } + + this.Reset(); + } + + // A code word must have at least 2 bits. + this.value = this.ReadValue(MinCodeLength); + + do + { + if (this.curValueBitsRead > MaxCodeLength) + { + TiffThrowHelper.ThrowImageFormatException("t4 parsing error: invalid code length read"); + } + + bool isTerminatingCode = this.IsTerminatingCode(); + if (isTerminatingCode) + { + // Each line starts with a white run. If the image starts with black, a white run with length zero is written. + if (this.IsWhiteRun && this.WhiteTerminatingCodeRunLength() == 0) + { + this.isWhiteRun = !this.IsWhiteRun; + this.Reset(); + continue; + } + + if (this.IsWhiteRun) + { + this.runLength += this.WhiteTerminatingCodeRunLength(); + } + else + { + this.runLength += this.BlackTerminatingCodeRunLength(); + } + + this.terminationCodeFound = true; + break; + } + + bool isMakeupCode = this.IsMakeupCode(); + if (isMakeupCode) + { + if (this.IsWhiteRun) + { + this.runLength += this.WhiteMakeupCodeRunLength(); + } + else + { + this.runLength += this.BlackMakeupCodeRunLength(); + } + + this.Reset(false); + continue; + } + + var currBit = this.ReadValue(1); + this.value = (this.value << 1) | currBit; + + if (this.IsEndOfScanLine) + { + // Each new row starts with a white run. + this.isWhiteRun = true; + } + } + while (!this.IsEndOfScanLine); + + this.isFirstScanLine = false; + } + + private uint WhiteTerminatingCodeRunLength() + { + switch (this.curValueBitsRead) + { + case 4: + { + switch (this.value) + { + case 0x7: + return 2; + case 0x8: + return 3; + case 0xB: + return 4; + case 0xC: + return 5; + case 0xE: + return 6; + case 0xF: + return 7; + } + + break; + } + + case 5: + { + switch (this.value) + { + case 0x13: + return 8; + case 0x14: + return 9; + case 0x7: + return 10; + case 0x8: + return 11; + } + + break; + } + + case 6: + { + switch (this.value) + { + case 0x7: + return 1; + case 0x8: + return 12; + case 0x3: + return 13; + case 0x34: + return 14; + case 0x35: + return 15; + case 0x2A: + return 16; + case 0x2B: + return 17; + } + + break; + } + + case 7: + { + switch (this.value) + { + case 0x27: + return 18; + case 0xC: + return 19; + case 0x8: + return 20; + case 0x17: + return 21; + case 0x3: + return 22; + case 0x4: + return 23; + case 0x28: + return 24; + case 0x2B: + return 25; + case 0x13: + return 26; + case 0x24: + return 27; + case 0x18: + return 28; + } + + break; + } + + case 8: + { + switch (this.value) + { + case 0x35: + return 0; + case 0x2: + return 29; + case 0x3: + return 30; + case 0x1A: + return 31; + case 0x1B: + return 32; + case 0x12: + return 33; + case 0x13: + return 34; + case 0x14: + return 35; + case 0x15: + return 36; + case 0x16: + return 37; + case 0x17: + return 38; + case 0x28: + return 39; + case 0x29: + return 40; + case 0x2A: + return 41; + case 0x2B: + return 42; + case 0x2C: + return 43; + case 0x2D: + return 44; + case 0x4: + return 45; + case 0x5: + return 46; + case 0xA: + return 47; + case 0xB: + return 48; + case 0x52: + return 49; + case 0x53: + return 50; + case 0x54: + return 51; + case 0x55: + return 52; + case 0x24: + return 53; + case 0x25: + return 54; + case 0x58: + return 55; + case 0x59: + return 56; + case 0x5A: + return 57; + case 0x5B: + return 58; + case 0x4A: + return 59; + case 0x4B: + return 60; + case 0x32: + return 61; + case 0x33: + return 62; + case 0x34: + return 63; + } + + break; + } + } + + return 0; + } + + private uint BlackTerminatingCodeRunLength() + { + switch (this.curValueBitsRead) + { + case 2: + { + switch (this.value) + { + case 0x3: + return 2; + case 0x2: + return 3; + } + break; + } + + case 3: + { + switch (this.value) + { + case 0x2: + return 1; + case 0x3: + return 4; + } + + break; + } + + case 4: + { + switch (this.value) + { + case 0x3: + return 5; + case 0x2: + return 6; + } + + break; + } + + case 5: + { + switch (this.value) + { + case 0x3: + return 7; + } + + break; + } + + case 6: + { + switch (this.value) + { + case 0x5: + return 8; + case 0x4: + return 9; + } + + break; + } + + case 7: + { + switch (this.value) + { + case 0x4: + return 10; + case 0x5: + return 11; + case 0x7: + return 12; + } + + break; + } + + case 8: + { + switch (this.value) + { + case 0x4: + return 13; + case 0x7: + return 14; + } + + break; + } + + case 9: + { + switch (this.value) + { + case 0x18: + return 15; + } + + break; + } + + case 10: + { + switch (this.value) + { + case 0x37: + return 0; + case 0x17: + return 16; + case 0x18: + return 17; + case 0x8: + return 18; + } + + break; + } + + case 11: + { + switch (this.value) + { + case 0x67: + return 19; + case 0x68: + return 20; + case 0x6C: + return 21; + case 0x37: + return 22; + case 0x28: + return 23; + case 0x17: + return 24; + case 0x18: + return 25; + } + + break; + } + + case 12: + { + switch (this.value) + { + case 0xCA: + return 26; + case 0xCB: + return 27; + case 0xCC: + return 28; + case 0xCD: + return 29; + case 0x68: + return 30; + case 0x69: + return 31; + case 0x6A: + return 32; + case 0x6B: + return 33; + case 0xD2: + return 34; + case 0xD3: + return 35; + case 0xD4: + return 36; + case 0xD5: + return 37; + case 0xD6: + return 38; + case 0xD7: + return 39; + case 0x6C: + return 40; + case 0x6D: + return 41; + case 0xDA: + return 42; + case 0xDB: + return 43; + case 0x54: + return 44; + case 0x55: + return 45; + case 0x56: + return 46; + case 0x57: + return 47; + case 0x64: + return 48; + case 0x65: + return 49; + case 0x52: + return 50; + case 0x53: + return 51; + case 0x24: + return 52; + case 0x37: + return 53; + case 0x38: + return 54; + case 0x27: + return 55; + case 0x28: + return 56; + case 0x58: + return 57; + case 0x59: + return 58; + case 0x2B: + return 59; + case 0x2C: + return 60; + case 0x5A: + return 61; + case 0x66: + return 62; + case 0x67: + return 63; + } + + break; + } + } + + return 0; + } + + private uint WhiteMakeupCodeRunLength() + { + switch (this.curValueBitsRead) + { + case 5: + { + switch (this.value) + { + case 0x1B: + return 64; + case 0x12: + return 128; + } + + break; + } + + case 6: + { + switch (this.value) + { + case 0x17: + return 192; + case 0x18: + return 1664; + } + + break; + } + + case 7: + { + switch (this.value) + { + case 0x37: + return 256; + } + + break; + } + + case 8: + { + switch (this.value) + { + case 0x36: + return 320; + case 0x37: + return 348; + case 0x64: + return 448; + case 0x65: + return 512; + case 0x68: + return 576; + case 0x67: + return 640; + } + + break; + } + + case 9: + { + switch (this.value) + { + case 0xCC: + return 704; + case 0xCD: + return 768; + case 0xD2: + return 832; + case 0xD3: + return 896; + case 0xD4: + return 960; + case 0xD5: + return 1024; + case 0xD6: + return 1088; + case 0xD7: + return 1152; + case 0xD8: + return 1216; + case 0xD9: + return 1280; + case 0xDA: + return 1344; + case 0xDB: + return 1408; + case 0x98: + return 1472; + case 0x99: + return 1536; + case 0x9A: + return 1600; + case 0x9B: + return 1728; + } + + break; + } + } + + return 0; + } + + private uint BlackMakeupCodeRunLength() + { + switch (this.curValueBitsRead) + { + case 10: + { + switch (this.value) + { + case 0xF: + return 64; + } + } + + break; + + case 12: + { + switch (this.value) + { + case 0xC8: + return 128; + case 0xC9: + return 192; + case 0x5B: + return 256; + case 0x33: + return 320; + case 0x34: + return 384; + case 0x35: + return 448; + } + } + + break; + + case 13: + { + switch (this.value) + { + case 0x6C: + return 512; + case 0x6D: + return 576; + case 0x4A: + return 640; + case 0x4B: + return 704; + case 0x4C: + return 768; + case 0x4D: + return 832; + case 0x72: + return 896; + case 0x73: + return 960; + case 0x74: + return 1024; + case 0x75: + return 1088; + case 0x76: + return 1152; + case 0x77: + return 1216; + case 0x52: + return 1280; + case 0x53: + return 1344; + case 0x54: + return 1408; + case 0x55: + return 1472; + case 0x5A: + return 1536; + case 0x5B: + return 1600; + case 0x64: + return 1664; + case 0x65: + return 1728; + } + + break; + } + } + + return 0; + } + + private bool IsMakeupCode() + { + if (this.IsWhiteRun) + { + return this.IsWhiteMakeupCode(); + } + + return this.IsBlackMakeupCode(); + } + + private bool IsWhiteMakeupCode() + { + switch (this.curValueBitsRead) + { + case 5: + { + uint[] codes = { 0x1B, 0x12 }; + return codes.Contains(this.value); + } + + case 6: + { + uint[] codes = { 0x17, 0x18 }; + return codes.Contains(this.value); + } + + case 7: + { + uint[] codes = { 0x37 }; + return codes.Contains(this.value); + } + + case 8: + { + uint[] codes = { 0x36, 0x37, 0x64, 0x65, 0x68, 0x67 }; + return codes.Contains(this.value); + } + + case 9: + { + uint[] codes = + { + 0xCC, 0xCD, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0x98, + 0x99, 0x9A, 0x9B + }; + return codes.Contains(this.value); + } + } + + return false; + } + + private bool IsBlackMakeupCode() + { + switch (this.curValueBitsRead) + { + case 10: + { + uint[] codes = { 0xF }; + return codes.Contains(this.value); + } + + case 12: + { + uint[] codes = { 0xC8, 0xC9, 0x5B, 0x33, 0x34, 0x35 }; + return codes.Contains(this.value); + } + + case 13: + { + uint[] codes = + { + 0x6C, 0x6D, 0x4A, 0x4B, 0x4C, 0x4D, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x52, + 0x53, 0x54, 0x55, 0x5A, 0x5B, 0x64, 0x65 + }; + return codes.Contains(this.value); + } + } + + return false; + } + + private bool IsTerminatingCode() + { + if (this.IsWhiteRun) + { + return this.IsWhiteTerminatingCode(); + } + + return this.IsBlackTerminatingCode(); + } + + private bool IsWhiteTerminatingCode() + { + switch (this.curValueBitsRead) + { + case 4: + { + uint[] codes = { 0x7, 0x8, 0xB, 0xC, 0xE, 0xF }; + return codes.Contains(this.value); + } + + case 5: + { + uint[] codes = { 0x13, 0x14, 0x7, 0x8 }; + return codes.Contains(this.value); + } + + case 6: + { + uint[] codes = { 0x7, 0x8, 0x3, 0x34, 0x35, 0x2A, 0x2B }; + return codes.Contains(this.value); + } + + case 7: + { + uint[] codes = { 0x27, 0xC, 0x8, 0x17, 0x3, 0x4, 0x28, 0x2B, 0x13, 0x24, 0x18 }; + return codes.Contains(this.value); + } + + case 8: + { + uint[] codes = + { + 0x35, 0x2, 0x3, 0x1A, 0x1B, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x28, 0x29, + 0x2A, 0x2B, 0x2C, 0x2D, 0x4, 0x5, 0xA, 0xB, 0x52, 0x53, 0x54, 0x55, 0x24, 0x25, + 0x58, 0x59, 0x5A, 0x5B, 0x4A, 0x4B, 0x32, 0x33, 0x34 + }; + return codes.Contains(this.value); + } + } + + return false; + } + + private bool IsBlackTerminatingCode() + { + switch (this.curValueBitsRead) + { + case 2: + { + uint[] codes = {0x3, 0x2}; + return codes.Contains(this.value); + } + + case 3: + { + uint[] codes = {0x02, 0x03}; + return codes.Contains(this.value); + } + + case 4: + { + uint[] codes = {0x03, 0x02}; + return codes.Contains(this.value); + } + + case 5: + { + uint[] codes = {0x03}; + return codes.Contains(this.value); + } + + case 6: + { + uint[] codes = {0x5, 0x4}; + return codes.Contains(this.value); + } + + case 7: + { + uint[] codes = { 0x4, 0x5, 0x7 }; + return codes.Contains(this.value); + } + + case 8: + { + uint[] codes = { 0x4, 0x7 }; + return codes.Contains(this.value); + } + + case 9: + { + uint[] codes = { 0x18 }; + return codes.Contains(this.value); + } + + case 10: + { + uint[] codes = { 0x37, 0x17, 0x18, 0x8 }; + return codes.Contains(this.value); + } + + case 11: + { + uint[] codes = { 0x67, 0x68, 0x6C, 0x37, 0x28, 0x17, 0x18 }; + return codes.Contains(this.value); + } + + case 12: + { + uint[] codes = + { + 0xCA, 0xCB, 0xCC, 0xCD, 0x68, 0x69, 0x6A, 0x6B, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, + 0xD7, 0x6C, 0x6D, 0xDA, 0xDB, 0x54, 0x55, 0x56, 0x57, 0x64, 0x65, 0x52, 0x53, + 0x24, 0x37, 0x38, 0x27, 0x28, 0x58, 0x59, 0x2B, 0x2C, 0x5A, 0x66, 0x67 + }; + return codes.Contains(this.value); + } + } + + return false; + } + + private void Reset(bool resetRunLength = true) + { + this.value = 0; + this.curValueBitsRead = 0; + + if (resetRunLength) + { + this.runLength = 0; + } + } + + private uint ReadValue(int nBits) + { + Guard.MustBeGreaterThan(nBits, 0, nameof(nBits)); + Guard.MustBeLessThanOrEqualTo(nBits, 12, nameof(nBits)); + + uint v = 0; + int shift = nBits; + while (shift-- > 0) + { + uint bit = this.GetBit(); + v |= bit << shift; + this.curValueBitsRead++; + } + + return v; + } + + private uint GetBit() + { + if (this.bitsRead >= 8) + { + this.LoadNewByte(); + } + + int shift = 8 - this.bitsRead - 1; + var bit = (uint)((this.Data[this.position] & (1 << shift)) != 0 ? 1 : 0); + this.bitsRead++; + + return bit; + } + + private void LoadNewByte() + { + this.position++; + this.bitsRead = 0; + + if (this.position >= (ulong)this.Data.Length) + { + TiffThrowHelper.ThrowImageFormatException("tiff image has invalid t4 compressed data"); + } + } + + private void ReadImageDataFromStream(Stream input, int bytesToRead) + { + var buffer = new byte[4096]; + + Span bufferSpan = buffer.AsSpan(); + Span dataSpan = this.Data.AsSpan(); + + int read; + while (bytesToRead > 0 && + (read = input.Read(buffer, 0, Math.Min(bufferSpan.Length, bytesToRead))) > 0) + { + buffer.AsSpan(0, read).CopyTo(dataSpan); + bytesToRead -= read; + dataSpan = dataSpan.Slice(read); + } + + if (bytesToRead > 0) + { + TiffThrowHelper.ThrowImageFormatException("tiff image file has insufficient data"); + } + } +} +} diff --git a/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs new file mode 100644 index 000000000..52b11613b --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs @@ -0,0 +1,95 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Formats.Tiff.Compression +{ + /// + /// Class to handle cases where TIFF image data is compressed using CCITT T4 compression. + /// + internal class T4TiffCompression : TiffBaseCompression + { + /// + /// Initializes a new instance of the class. + /// + /// The memory allocator. + public T4TiffCompression(MemoryAllocator allocator) + : base(allocator) + { + } + + /// + public override void Decompress(Stream stream, int byteCount, Span buffer) + { + // TODO: handle case when white is not zero. + bool isWhiteZero = true; + int whiteValue = isWhiteZero ? 0 : 1; + int blackValue = isWhiteZero ? 1 : 0; + + var bitReader = new T4BitReader(stream, byteCount); + + uint bitsWritten = 0; + uint pixels = 0; + while (bitReader.HasMoreData) + { + bitReader.ReadNextRun(); + + if (bitReader.IsEndOfScanLine) + { + // Write padding bytes, if necessary. + uint pad = 8 - (bitsWritten % 8); + if (pad != 8) + { + this.WriteBits(buffer, (int)bitsWritten, pad, 0); + bitsWritten += pad; + } + + continue; + } + + if (bitReader.IsWhiteRun) + { + this.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, whiteValue); + bitsWritten += bitReader.RunLength; + pixels += bitReader.RunLength; + } + else + { + this.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, blackValue); + bitsWritten += bitReader.RunLength; + pixels += bitReader.RunLength; + } + } + + int foo = 0; + } + + private void WriteBits(Span buffer, int pos, uint count, int value) + { + int bitPos = pos % 8; + int bufferPos = pos / 8; + int startIdx = bufferPos + bitPos; + int endIdx = (int)(startIdx + count); + + for (int i = startIdx; i < endIdx; i++) + { + this.WriteBit(buffer, bufferPos, bitPos, value); + + bitPos++; + if (bitPos >= 8) + { + bitPos = 0; + bufferPos++; + } + } + } + + private void WriteBit(Span buffer, int bufferPos, int bitPos, int value) + { + buffer[bufferPos] |= (byte)(value << (7 - bitPos)); + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs index 0f893448d..cedbbe35b 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using System; +using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Tiff @@ -20,6 +20,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff return new DeflateTiffCompression(allocator); case TiffCompressionType.Lzw: return new LzwTiffCompression(allocator); + case TiffCompressionType.T4: + return new T4TiffCompression(allocator); default: throw TiffThrowHelper.NotSupportedCompression(nameof(compressionType)); } diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs index b62def1ea..665e4aca2 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Formats.Tiff @@ -27,5 +27,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// Image data is compressed using LZW compression. /// Lzw = 3, + + /// + /// Image data is compressed using T4-encoding: CCITT T.4. + /// + T4 = 4, } } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 468989d19..7eced53bd 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Buffers; using System.Collections.Generic; using System.IO; using System.Linq; @@ -311,7 +309,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff { int uncompressedStripSize = this.CalculateStripBufferSize(frame.Width, rowsPerStrip); - using IManagedByteBuffer stripBuffer = this.memoryAllocator.AllocateManagedByteBuffer(uncompressedStripSize); + using IManagedByteBuffer stripBuffer = this.memoryAllocator.AllocateManagedByteBuffer(uncompressedStripSize, AllocationOptions.Clean); Buffer2D pixels = frame.PixelBuffer; diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs index 86a7560cf..5348be8ce 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs @@ -1,14 +1,12 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using System; using System.Collections.Generic; using System.Linq; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Icc; using SixLabors.ImageSharp.Metadata.Profiles.Iptc; -using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Tiff { @@ -348,6 +346,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff break; } + case TiffCompression.CcittGroup3Fax: + { + options.CompressionType = TiffCompressionType.T4; + break; + } + default: { TiffThrowHelper.ThrowNotSupported("The specified TIFF compression format is not supported: " + compression); diff --git a/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs b/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs index 3254744bc..96a3e8dbc 100644 --- a/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs +++ b/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs @@ -12,6 +12,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// internal static class TiffThrowHelper { + /// + /// Cold path optimization for throwing -s + /// + /// The error message for the exception. + [MethodImpl(MethodImplOptions.NoInlining)] + public static void ThrowImageFormatException(string errorMessage) + { + throw new ImageFormatException(errorMessage); + } + [MethodImpl(InliningOptions.ColdPath)] public static Exception TagNotFound(string tagName) => new ArgumentException("Required tag is not found.", tagName); From 3e4b5b262ae77295c682431de5c6116638116d19 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 21 Nov 2020 18:19:42 +0100 Subject: [PATCH 077/275] Use memory allocator in t4 bitreader --- .../Formats/Tiff/Compression/T4BitReader.cs | 66 ++++++++++++------- .../Tiff/Compression/T4TiffCompression.cs | 7 +- src/ImageSharp/Formats/Tiff/README.md | 3 +- 3 files changed, 46 insertions(+), 30 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs index c37de8031..b09645a4b 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs @@ -2,15 +2,17 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.IO; using System.Linq; +using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Tiff.Compression { /// /// Bitreader for reading compressed CCITT T4 1D data. /// - internal class T4BitReader + internal class T4BitReader : IDisposable { /// /// Number of bits read. @@ -42,6 +44,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression /// private bool isFirstScanLine; + /// + /// Indicates whether we have found a termination code which signals the end of a run. + /// private bool terminationCodeFound; /// @@ -49,16 +54,24 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression /// private uint runLength; + private readonly int dataLength; + private const int MinCodeLength = 2; private const int MaxCodeLength = 13; - public T4BitReader(Stream input, int bytesToRead) + /// + /// Initializes a new instance of the class. + /// + /// The compressed input stream. + /// The number of bytes to read from the stream. + /// The memory allocator. + public T4BitReader(Stream input, int bytesToRead, MemoryAllocator allocator) { - // TODO: use memory allocator - this.Data = new byte[bytesToRead]; - this.ReadImageDataFromStream(input, bytesToRead); + this.Data = allocator.Allocate(bytesToRead); + this.ReadImageDataFromStream(input, bytesToRead, allocator); + this.dataLength = bytesToRead; this.bitsRead = 0; this.value = 0; this.curValueBitsRead = 0; @@ -72,7 +85,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression /// /// Gets the compressed image data. /// - public byte[] Data { get; } + public IMemoryOwner Data { get; } /// /// Gets a value indicating whether there is more data to read left. @@ -81,7 +94,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression { get { - return this.position < (ulong)this.Data.Length - 1; + return this.position < (ulong)this.dataLength - 1; } } @@ -207,6 +220,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression this.isFirstScanLine = false; } + /// + public void Dispose() + { + this.Data.Dispose(); + } + private uint WhiteTerminatingCodeRunLength() { switch (this.curValueBitsRead) @@ -401,6 +420,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression case 0x2: return 3; } + break; } @@ -959,31 +979,31 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression { case 2: { - uint[] codes = {0x3, 0x2}; + uint[] codes = { 0x3, 0x2 }; return codes.Contains(this.value); } case 3: { - uint[] codes = {0x02, 0x03}; + uint[] codes = { 0x02, 0x03 }; return codes.Contains(this.value); } case 4: { - uint[] codes = {0x03, 0x02}; + uint[] codes = { 0x03, 0x02 }; return codes.Contains(this.value); } case 5: { - uint[] codes = {0x03}; + uint[] codes = { 0x03 }; return codes.Contains(this.value); } case 6: { - uint[] codes = {0x5, 0x4}; + uint[] codes = { 0x5, 0x4 }; return codes.Contains(this.value); } @@ -1067,8 +1087,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression this.LoadNewByte(); } + Span dataSpan = this.Data.GetSpan(); int shift = 8 - this.bitsRead - 1; - var bit = (uint)((this.Data[this.position] & (1 << shift)) != 0 ? 1 : 0); + var bit = (uint)((dataSpan[(int)this.position] & (1 << shift)) != 0 ? 1 : 0); this.bitsRead++; return bit; @@ -1079,24 +1100,23 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression this.position++; this.bitsRead = 0; - if (this.position >= (ulong)this.Data.Length) + if (this.position >= (ulong)this.dataLength) { TiffThrowHelper.ThrowImageFormatException("tiff image has invalid t4 compressed data"); } } - private void ReadImageDataFromStream(Stream input, int bytesToRead) + private void ReadImageDataFromStream(Stream input, int bytesToRead, MemoryAllocator allocator) { - var buffer = new byte[4096]; - - Span bufferSpan = buffer.AsSpan(); - Span dataSpan = this.Data.AsSpan(); + int bufferLength = 4096; + IMemoryOwner buffer = allocator.Allocate(bufferLength); + Span bufferSpan = buffer.GetSpan(); + Span dataSpan = this.Data.GetSpan(); int read; - while (bytesToRead > 0 && - (read = input.Read(buffer, 0, Math.Min(bufferSpan.Length, bytesToRead))) > 0) + while (bytesToRead > 0 && (read = input.Read(bufferSpan, 0, Math.Min(bufferLength, bytesToRead))) > 0) { - buffer.AsSpan(0, read).CopyTo(dataSpan); + buffer.Slice(0, read).CopyTo(dataSpan); bytesToRead -= read; dataSpan = dataSpan.Slice(read); } @@ -1106,5 +1126,5 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression TiffThrowHelper.ThrowImageFormatException("tiff image file has insufficient data"); } } -} + } } diff --git a/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs index 52b11613b..13f7eb794 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs @@ -29,10 +29,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression int whiteValue = isWhiteZero ? 0 : 1; int blackValue = isWhiteZero ? 1 : 0; - var bitReader = new T4BitReader(stream, byteCount); + using var bitReader = new T4BitReader(stream, byteCount, this.Allocator); uint bitsWritten = 0; - uint pixels = 0; while (bitReader.HasMoreData) { bitReader.ReadNextRun(); @@ -54,17 +53,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression { this.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, whiteValue); bitsWritten += bitReader.RunLength; - pixels += bitReader.RunLength; } else { this.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, blackValue); bitsWritten += bitReader.RunLength; - pixels += bitReader.RunLength; } } - - int foo = 0; } private void WriteBits(Span buffer, int pos, uint count, int value) diff --git a/src/ImageSharp/Formats/Tiff/README.md b/src/ImageSharp/Formats/Tiff/README.md index 2eed880b6..ca9078ae1 100644 --- a/src/ImageSharp/Formats/Tiff/README.md +++ b/src/ImageSharp/Formats/Tiff/README.md @@ -10,6 +10,7 @@ - [TIFF/EP Extension (Wikipedia)](https://en.wikipedia.org/wiki/TIFF/EP) - [Adobe TIFF Pages](http://partners.adobe.com/public/developer/tiff/index.html) - [Unofficial TIFF FAQ](http://www.awaresystems.be/imaging/tiff/faq.html) + - [CCITT T.4 Compression](https://www.itu.int/rec/T-REC-T.4-198811-S/_page.print) - DNG - [Adobe DNG Pages](https://helpx.adobe.com/photoshop/digital-negative.html) @@ -41,7 +42,7 @@ |None | | Y | | |Ccitt1D | | | | |PackBits | | Y | | -|CcittGroup3Fax | | | | +|CcittGroup3Fax | | Y | | |CcittGroup4Fax | | | | |Lzw | | Y | Based on ImageSharp GIF LZW implementation - this code could be modified to be (i) shared, or (ii) optimised for each case | |Old Jpeg | | | | From 7514df6ddf3154040f99ea7bad1afb4f9d791950 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 21 Nov 2020 20:05:12 +0100 Subject: [PATCH 078/275] Use Dictionarys for terminating and makeup codes --- .../Formats/Tiff/Compression/T4BitReader.cs | 729 +++++------------- 1 file changed, 179 insertions(+), 550 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs index b09645a4b..cf7ff9caa 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs @@ -3,6 +3,7 @@ using System; using System.Buffers; +using System.Collections.Generic; using System.IO; using System.Linq; using SixLabors.ImageSharp.Memory; @@ -60,6 +61,136 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression private const int MaxCodeLength = 13; + private static readonly Dictionary WhiteLen4TermCodes = new Dictionary() + { + { 0x7, 2 }, { 0x8, 3 }, { 0xB, 4 }, { 0xC, 5 }, { 0xE, 6 }, { 0xF, 7 } + }; + + private static readonly Dictionary WhiteLen5TermCodes = new Dictionary() + { + { 0x13, 8 }, { 0x14, 9 }, { 0x7, 10 }, { 0x8, 11 } + }; + + private static readonly Dictionary WhiteLen6TermCodes = new Dictionary() + { + { 0x7, 1 }, { 0x8, 12 }, { 0x3, 13 }, { 0x34, 14 }, { 0x35, 15 }, { 0x2A, 16 }, { 0x2B, 17 } + }; + + private static readonly Dictionary WhiteLen7TermCodes = new Dictionary() + { + { 0x27, 18 }, { 0xC, 19 }, { 0x8, 20 }, { 0x17, 21 }, { 0x3, 22 }, { 0x4, 23 }, { 0x28, 24 }, { 0x2B, 25 }, { 0x13, 26 }, { 0x24, 27 }, { 0x18, 28 } + }; + + private static readonly Dictionary WhiteLen8TermCodes = new Dictionary() + { + { 0x35, 0 }, { 0x2, 29 }, { 0x3, 30 }, { 0x1A, 31 }, { 0x1B, 32 }, { 0x12, 33 }, { 0x13, 34 }, { 0x14, 35 }, { 0x15, 36 }, { 0x16, 37 }, { 0x17, 38 }, { 0x28, 39 }, { 0x29, 40 }, { 0x2A, 41 }, + { 0x2B, 42 }, { 0x2C, 43 }, { 0x2D, 44 }, { 0x4, 45 }, { 0x5, 46 }, { 0xA, 47 }, { 0xB, 48 }, { 0x52, 49 }, { 0x53, 50 }, { 0x54, 51 }, { 0x55, 52 }, { 0x24, 53 }, { 0x25, 54 }, { 0x58, 55 }, + { 0x59, 56 }, { 0x5A, 57 }, { 0x5B, 58 }, { 0x4A, 59 }, { 0x4B, 60 }, { 0x32, 61 }, { 0x33, 62 }, { 0x34, 63 } + }; + + private static readonly Dictionary BlackLen2TermCodes = new Dictionary() + { + { 0x3, 2 }, { 0x2, 3 } + }; + + private static readonly Dictionary BlackLen3TermCodes = new Dictionary() + { + { 0x2, 1 }, { 0x3, 4 } + }; + + private static readonly Dictionary BlackLen4TermCodes = new Dictionary() + { + { 0x3, 5 }, { 0x2, 6 } + }; + + private static readonly Dictionary BlackLen5TermCodes = new Dictionary() + { + { 0x3, 7 } + }; + + private static readonly Dictionary BlackLen6TermCodes = new Dictionary() + { + { 0x5, 8 }, { 0x4, 9 } + }; + + private static readonly Dictionary BlackLen7TermCodes = new Dictionary() + { + { 0x4, 10 }, { 0x5, 11 }, { 0x7, 12 } + }; + + private static readonly Dictionary BlackLen8TermCodes = new Dictionary() + { + { 0x4, 13 }, { 0x7, 14 } + }; + + private static readonly Dictionary BlackLen9TermCodes = new Dictionary() + { + { 0x18, 15 } + }; + + private static readonly Dictionary BlackLen10TermCodes = new Dictionary() + { + { 0x37, 0 }, { 0x17, 16 }, { 0x18, 17 }, { 0x8, 18 } + }; + + private static readonly Dictionary BlackLen11TermCodes = new Dictionary() + { + { 0x67, 19 }, { 0x68, 20 }, { 0x6C, 21 }, { 0x37, 22 }, { 0x28, 23 }, { 0x17, 24 }, { 0x18, 25 } + }; + + private static readonly Dictionary BlackLen12TermCodes = new Dictionary() + { + { 0xCA, 26 }, { 0xCB, 27 }, { 0xCC, 28 }, { 0xCD, 29 }, { 0x68, 30 }, { 0x69, 31 }, { 0x6A, 32 }, { 0x6B, 33 }, { 0xD2, 34 }, + { 0xD3, 35 }, { 0xD4, 36 }, { 0xD5, 37 }, { 0xD6, 38 }, { 0xD7, 39 }, { 0x6C, 40 }, { 0x6D, 41 }, { 0xDA, 42 }, { 0xDB, 43 }, + { 0x54, 44 }, { 0x55, 45 }, { 0x56, 46 }, { 0x57, 47 }, { 0x64, 48 }, { 0x65, 49 }, { 0x52, 50 }, { 0x53, 51 }, { 0x24, 52 }, + { 0x37, 53 }, { 0x38, 54 }, { 0x27, 55 }, { 0x28, 56 }, { 0x58, 57 }, { 0x59, 58 }, { 0x2B, 59 }, { 0x2C, 60 }, { 0x5A, 61 }, + { 0x66, 62 }, { 0x67, 63 } + }; + + private static readonly Dictionary WhiteLen5MakeupCodes = new Dictionary() + { + { 0x1B, 64 }, { 0x12, 128 } + }; + + private static readonly Dictionary WhiteLen6MakeupCodes = new Dictionary() + { + { 0x17, 192 }, { 0x18, 1664 } + }; + + private static readonly Dictionary WhiteLen8MakeupCodes = new Dictionary() + { + { 0x36, 320 }, { 0x37, 348 }, { 0x64, 448 }, { 0x65, 512 }, { 0x68, 576 }, { 0x67, 640 } + }; + + private static readonly Dictionary WhiteLen7MakeupCodes = new Dictionary() + { + { 0x37, 256 } + }; + + private static readonly Dictionary WhiteLen9MakeupCodes = new Dictionary() + { + { 0xCC, 704 }, { 0xCD, 768 }, { 0xD2, 832 }, { 0xD3, 896 }, { 0xD4, 960 }, { 0xD5, 1024 }, { 0xD6, 1088 }, + { 0xD7, 1152 }, { 0xD8, 1216 }, { 0xD9, 1280 }, { 0xDA, 1344 }, { 0xDB, 1408 }, { 0x98, 1472 }, { 0x99, 1536 }, + { 0x9A, 1600 }, { 0x9B, 1728 } + }; + + private static readonly Dictionary BlackLen10MakeupCodes = new Dictionary() + { + { 0xF, 64 } + }; + + private static readonly Dictionary BlackLen12MakeupCodes = new Dictionary() + { + { 0xC8, 128 }, { 0xC9, 192 }, { 0x5B, 256 }, { 0x33, 320 }, { 0x34, 384 }, { 0x35, 448 } + }; + + private static readonly Dictionary BlackLen13MakeupCodes = new Dictionary() + { + { 0x6C, 512 }, { 0x6D, 576 }, { 0x4A, 640 }, { 0x4B, 704 }, { 0x4C, 768 }, { 0x4D, 832 }, { 0x72, 896 }, + { 0x73, 960 }, { 0x74, 1024 }, { 0x75, 1088 }, { 0x76, 1152 }, { 0x77, 1216 }, { 0x52, 1280 }, { 0x53, 1344 }, + { 0x54, 1408 }, { 0x55, 1472 }, { 0x5A, 1536 }, { 0x5B, 1600 }, { 0x64, 1664 }, { 0x65, 1728 } + }; + /// /// Initializes a new instance of the class. /// @@ -232,175 +363,27 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression { case 4: { - switch (this.value) - { - case 0x7: - return 2; - case 0x8: - return 3; - case 0xB: - return 4; - case 0xC: - return 5; - case 0xE: - return 6; - case 0xF: - return 7; - } - - break; + return WhiteLen4TermCodes[this.value]; } case 5: { - switch (this.value) - { - case 0x13: - return 8; - case 0x14: - return 9; - case 0x7: - return 10; - case 0x8: - return 11; - } - - break; + return WhiteLen5TermCodes[this.value]; } case 6: { - switch (this.value) - { - case 0x7: - return 1; - case 0x8: - return 12; - case 0x3: - return 13; - case 0x34: - return 14; - case 0x35: - return 15; - case 0x2A: - return 16; - case 0x2B: - return 17; - } - - break; + return WhiteLen6TermCodes[this.value]; } case 7: { - switch (this.value) - { - case 0x27: - return 18; - case 0xC: - return 19; - case 0x8: - return 20; - case 0x17: - return 21; - case 0x3: - return 22; - case 0x4: - return 23; - case 0x28: - return 24; - case 0x2B: - return 25; - case 0x13: - return 26; - case 0x24: - return 27; - case 0x18: - return 28; - } - - break; + return WhiteLen7TermCodes[this.value]; } case 8: { - switch (this.value) - { - case 0x35: - return 0; - case 0x2: - return 29; - case 0x3: - return 30; - case 0x1A: - return 31; - case 0x1B: - return 32; - case 0x12: - return 33; - case 0x13: - return 34; - case 0x14: - return 35; - case 0x15: - return 36; - case 0x16: - return 37; - case 0x17: - return 38; - case 0x28: - return 39; - case 0x29: - return 40; - case 0x2A: - return 41; - case 0x2B: - return 42; - case 0x2C: - return 43; - case 0x2D: - return 44; - case 0x4: - return 45; - case 0x5: - return 46; - case 0xA: - return 47; - case 0xB: - return 48; - case 0x52: - return 49; - case 0x53: - return 50; - case 0x54: - return 51; - case 0x55: - return 52; - case 0x24: - return 53; - case 0x25: - return 54; - case 0x58: - return 55; - case 0x59: - return 56; - case 0x5A: - return 57; - case 0x5B: - return 58; - case 0x4A: - return 59; - case 0x4B: - return 60; - case 0x32: - return 61; - case 0x33: - return 62; - case 0x34: - return 63; - } - - break; + return WhiteLen8TermCodes[this.value]; } } @@ -413,229 +396,57 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression { case 2: { - switch (this.value) - { - case 0x3: - return 2; - case 0x2: - return 3; - } - - break; + return BlackLen2TermCodes[this.value]; } case 3: { - switch (this.value) - { - case 0x2: - return 1; - case 0x3: - return 4; - } - - break; + return BlackLen3TermCodes[this.value]; } case 4: { - switch (this.value) - { - case 0x3: - return 5; - case 0x2: - return 6; - } - - break; + return BlackLen4TermCodes[this.value]; } case 5: { - switch (this.value) - { - case 0x3: - return 7; - } - - break; + return BlackLen5TermCodes[this.value]; } case 6: { - switch (this.value) - { - case 0x5: - return 8; - case 0x4: - return 9; - } - - break; + return BlackLen6TermCodes[this.value]; } case 7: { - switch (this.value) - { - case 0x4: - return 10; - case 0x5: - return 11; - case 0x7: - return 12; - } - - break; + return BlackLen7TermCodes[this.value]; } case 8: { - switch (this.value) - { - case 0x4: - return 13; - case 0x7: - return 14; - } - - break; + return BlackLen8TermCodes[this.value]; } case 9: { - switch (this.value) - { - case 0x18: - return 15; - } - - break; + return BlackLen9TermCodes[this.value]; } case 10: { - switch (this.value) - { - case 0x37: - return 0; - case 0x17: - return 16; - case 0x18: - return 17; - case 0x8: - return 18; - } - - break; + return BlackLen10TermCodes[this.value]; } case 11: { - switch (this.value) - { - case 0x67: - return 19; - case 0x68: - return 20; - case 0x6C: - return 21; - case 0x37: - return 22; - case 0x28: - return 23; - case 0x17: - return 24; - case 0x18: - return 25; - } - - break; + return BlackLen11TermCodes[this.value]; } case 12: { - switch (this.value) - { - case 0xCA: - return 26; - case 0xCB: - return 27; - case 0xCC: - return 28; - case 0xCD: - return 29; - case 0x68: - return 30; - case 0x69: - return 31; - case 0x6A: - return 32; - case 0x6B: - return 33; - case 0xD2: - return 34; - case 0xD3: - return 35; - case 0xD4: - return 36; - case 0xD5: - return 37; - case 0xD6: - return 38; - case 0xD7: - return 39; - case 0x6C: - return 40; - case 0x6D: - return 41; - case 0xDA: - return 42; - case 0xDB: - return 43; - case 0x54: - return 44; - case 0x55: - return 45; - case 0x56: - return 46; - case 0x57: - return 47; - case 0x64: - return 48; - case 0x65: - return 49; - case 0x52: - return 50; - case 0x53: - return 51; - case 0x24: - return 52; - case 0x37: - return 53; - case 0x38: - return 54; - case 0x27: - return 55; - case 0x28: - return 56; - case 0x58: - return 57; - case 0x59: - return 58; - case 0x2B: - return 59; - case 0x2C: - return 60; - case 0x5A: - return 61; - case 0x66: - return 62; - case 0x67: - return 63; - } - - break; + return BlackLen12TermCodes[this.value]; } } @@ -648,101 +459,27 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression { case 5: { - switch (this.value) - { - case 0x1B: - return 64; - case 0x12: - return 128; - } - - break; + return WhiteLen5MakeupCodes[this.value]; } case 6: { - switch (this.value) - { - case 0x17: - return 192; - case 0x18: - return 1664; - } - - break; + return WhiteLen6MakeupCodes[this.value]; } case 7: { - switch (this.value) - { - case 0x37: - return 256; - } - - break; + return WhiteLen7MakeupCodes[this.value]; } case 8: { - switch (this.value) - { - case 0x36: - return 320; - case 0x37: - return 348; - case 0x64: - return 448; - case 0x65: - return 512; - case 0x68: - return 576; - case 0x67: - return 640; - } - - break; + return WhiteLen8MakeupCodes[this.value]; } case 9: { - switch (this.value) - { - case 0xCC: - return 704; - case 0xCD: - return 768; - case 0xD2: - return 832; - case 0xD3: - return 896; - case 0xD4: - return 960; - case 0xD5: - return 1024; - case 0xD6: - return 1088; - case 0xD7: - return 1152; - case 0xD8: - return 1216; - case 0xD9: - return 1280; - case 0xDA: - return 1344; - case 0xDB: - return 1408; - case 0x98: - return 1472; - case 0x99: - return 1536; - case 0x9A: - return 1600; - case 0x9B: - return 1728; - } - - break; + return WhiteLen9MakeupCodes[this.value]; } } @@ -755,83 +492,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression { case 10: { - switch (this.value) - { - case 0xF: - return 64; - } + return BlackLen10MakeupCodes[this.value]; } - break; - case 12: { - switch (this.value) - { - case 0xC8: - return 128; - case 0xC9: - return 192; - case 0x5B: - return 256; - case 0x33: - return 320; - case 0x34: - return 384; - case 0x35: - return 448; - } + return BlackLen12MakeupCodes[this.value]; } - break; - case 13: { - switch (this.value) - { - case 0x6C: - return 512; - case 0x6D: - return 576; - case 0x4A: - return 640; - case 0x4B: - return 704; - case 0x4C: - return 768; - case 0x4D: - return 832; - case 0x72: - return 896; - case 0x73: - return 960; - case 0x74: - return 1024; - case 0x75: - return 1088; - case 0x76: - return 1152; - case 0x77: - return 1216; - case 0x52: - return 1280; - case 0x53: - return 1344; - case 0x54: - return 1408; - case 0x55: - return 1472; - case 0x5A: - return 1536; - case 0x5B: - return 1600; - case 0x64: - return 1664; - case 0x65: - return 1728; - } - - break; + return BlackLen13MakeupCodes[this.value]; } } @@ -854,36 +525,27 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression { case 5: { - uint[] codes = { 0x1B, 0x12 }; - return codes.Contains(this.value); + return WhiteLen5MakeupCodes.ContainsKey(this.value); } case 6: { - uint[] codes = { 0x17, 0x18 }; - return codes.Contains(this.value); + return WhiteLen6MakeupCodes.ContainsKey(this.value); } case 7: { - uint[] codes = { 0x37 }; - return codes.Contains(this.value); + return WhiteLen7MakeupCodes.ContainsKey(this.value); } case 8: { - uint[] codes = { 0x36, 0x37, 0x64, 0x65, 0x68, 0x67 }; - return codes.Contains(this.value); + return WhiteLen8MakeupCodes.ContainsKey(this.value); } case 9: { - uint[] codes = - { - 0xCC, 0xCD, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0x98, - 0x99, 0x9A, 0x9B - }; - return codes.Contains(this.value); + return WhiteLen9MakeupCodes.ContainsKey(this.value); } } @@ -896,24 +558,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression { case 10: { - uint[] codes = { 0xF }; - return codes.Contains(this.value); + return BlackLen10MakeupCodes.ContainsKey(this.value); } case 12: { - uint[] codes = { 0xC8, 0xC9, 0x5B, 0x33, 0x34, 0x35 }; - return codes.Contains(this.value); + return BlackLen12MakeupCodes.ContainsKey(this.value); } case 13: { - uint[] codes = - { - 0x6C, 0x6D, 0x4A, 0x4B, 0x4C, 0x4D, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x52, - 0x53, 0x54, 0x55, 0x5A, 0x5B, 0x64, 0x65 - }; - return codes.Contains(this.value); + return BlackLen13MakeupCodes.ContainsKey(this.value); } } @@ -936,37 +591,27 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression { case 4: { - uint[] codes = { 0x7, 0x8, 0xB, 0xC, 0xE, 0xF }; - return codes.Contains(this.value); + return WhiteLen4TermCodes.Keys.Contains(this.value); } case 5: { - uint[] codes = { 0x13, 0x14, 0x7, 0x8 }; - return codes.Contains(this.value); + return WhiteLen5TermCodes.Keys.Contains(this.value); } case 6: { - uint[] codes = { 0x7, 0x8, 0x3, 0x34, 0x35, 0x2A, 0x2B }; - return codes.Contains(this.value); + return WhiteLen6TermCodes.Keys.Contains(this.value); } case 7: { - uint[] codes = { 0x27, 0xC, 0x8, 0x17, 0x3, 0x4, 0x28, 0x2B, 0x13, 0x24, 0x18 }; - return codes.Contains(this.value); + return WhiteLen7TermCodes.Keys.Contains(this.value); } case 8: { - uint[] codes = - { - 0x35, 0x2, 0x3, 0x1A, 0x1B, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x28, 0x29, - 0x2A, 0x2B, 0x2C, 0x2D, 0x4, 0x5, 0xA, 0xB, 0x52, 0x53, 0x54, 0x55, 0x24, 0x25, - 0x58, 0x59, 0x5A, 0x5B, 0x4A, 0x4B, 0x32, 0x33, 0x34 - }; - return codes.Contains(this.value); + return WhiteLen8TermCodes.Keys.Contains(this.value); } } @@ -979,73 +624,57 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression { case 2: { - uint[] codes = { 0x3, 0x2 }; - return codes.Contains(this.value); + return BlackLen2TermCodes.ContainsKey(this.value); } case 3: { - uint[] codes = { 0x02, 0x03 }; - return codes.Contains(this.value); + return BlackLen3TermCodes.ContainsKey(this.value); } case 4: { - uint[] codes = { 0x03, 0x02 }; - return codes.Contains(this.value); + return BlackLen4TermCodes.ContainsKey(this.value); } case 5: { - uint[] codes = { 0x03 }; - return codes.Contains(this.value); + return BlackLen5TermCodes.ContainsKey(this.value); } case 6: { - uint[] codes = { 0x5, 0x4 }; - return codes.Contains(this.value); + return BlackLen6TermCodes.ContainsKey(this.value); } case 7: { - uint[] codes = { 0x4, 0x5, 0x7 }; - return codes.Contains(this.value); + return BlackLen7TermCodes.ContainsKey(this.value); } case 8: { - uint[] codes = { 0x4, 0x7 }; - return codes.Contains(this.value); + return BlackLen8TermCodes.ContainsKey(this.value); } case 9: { - uint[] codes = { 0x18 }; - return codes.Contains(this.value); + return BlackLen9TermCodes.ContainsKey(this.value); } case 10: { - uint[] codes = { 0x37, 0x17, 0x18, 0x8 }; - return codes.Contains(this.value); + return BlackLen10TermCodes.ContainsKey(this.value); } case 11: { - uint[] codes = { 0x67, 0x68, 0x6C, 0x37, 0x28, 0x17, 0x18 }; - return codes.Contains(this.value); + return BlackLen11TermCodes.ContainsKey(this.value); } case 12: { - uint[] codes = - { - 0xCA, 0xCB, 0xCC, 0xCD, 0x68, 0x69, 0x6A, 0x6B, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, - 0xD7, 0x6C, 0x6D, 0xDA, 0xDB, 0x54, 0x55, 0x56, 0x57, 0x64, 0x65, 0x52, 0x53, - 0x24, 0x37, 0x38, 0x27, 0x28, 0x58, 0x59, 0x2B, 0x2C, 0x5A, 0x66, 0x67 - }; - return codes.Contains(this.value); + return BlackLen12TermCodes.ContainsKey(this.value); } } From 250ba56fa578f9eb20ecc8121bdbe2ebba3bb32d Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 21 Nov 2020 20:19:56 +0100 Subject: [PATCH 079/275] Add test images for fax3 compressed tiff --- .../Formats/Tiff/Compression/T4BitReader.cs | 48 ++++++++++++++++++- tests/ImageSharp.Tests/TestImages.cs | 6 ++- .../Input/Tiff/Calliphora_ccitt_fax3.tif | 3 ++ .../Tiff/ccitt_fax3_all_makeupcodes_codes.tif | 3 ++ .../Tiff/ccitt_fax3_all_terminating_codes.tif | 3 ++ 5 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 tests/Images/Input/Tiff/Calliphora_ccitt_fax3.tif create mode 100644 tests/Images/Input/Tiff/ccitt_fax3_all_makeupcodes_codes.tif create mode 100644 tests/Images/Input/Tiff/ccitt_fax3_all_terminating_codes.tif diff --git a/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs index cf7ff9caa..c31eb8793 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs @@ -174,14 +174,30 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression { 0x9A, 1600 }, { 0x9B, 1728 } }; + private static readonly Dictionary WhiteLen11MakeupCodes = new Dictionary() + { + { 0x8, 1792 }, { 0xC, 1856 }, { 0xD, 1920 } + }; + + private static readonly Dictionary WhiteLen12MakeupCodes = new Dictionary() + { + { 0x12, 1984 }, { 0x13, 2048 }, { 0x14, 2112 }, { 0x15, 2176 }, { 0x16, 2240 }, { 0x17, 2304}, { 0x1C, 2368 }, { 0x1D, 2432 }, { 0x1E, 2496 }, { 0x1F, 2560 } + }; + private static readonly Dictionary BlackLen10MakeupCodes = new Dictionary() { { 0xF, 64 } }; + private static readonly Dictionary BlackLen11MakeupCodes = new Dictionary() + { + { 0x8, 1792 }, { 0xC, 1856 }, { 0xD, 1920 } + }; + private static readonly Dictionary BlackLen12MakeupCodes = new Dictionary() { - { 0xC8, 128 }, { 0xC9, 192 }, { 0x5B, 256 }, { 0x33, 320 }, { 0x34, 384 }, { 0x35, 448 } + { 0xC8, 128 }, { 0xC9, 192 }, { 0x5B, 256 }, { 0x33, 320 }, { 0x34, 384 }, { 0x35, 448 }, + { 0x12, 1984 }, { 0x13, 2048 }, { 0x14, 2112 }, { 0x15, 2176 }, { 0x16, 2240 }, { 0x17, 2304}, { 0x1C, 2368 }, { 0x1D, 2432 }, { 0x1E, 2496 }, { 0x1F, 2560 } }; private static readonly Dictionary BlackLen13MakeupCodes = new Dictionary() @@ -481,6 +497,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression { return WhiteLen9MakeupCodes[this.value]; } + + case 11: + { + return WhiteLen11MakeupCodes[this.value]; + } + + case 12: + { + return WhiteLen12MakeupCodes[this.value]; + } } return 0; @@ -495,6 +521,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression return BlackLen10MakeupCodes[this.value]; } + case 11: + { + return BlackLen11MakeupCodes[this.value]; + } + case 12: { return BlackLen12MakeupCodes[this.value]; @@ -547,6 +578,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression { return WhiteLen9MakeupCodes.ContainsKey(this.value); } + + case 11: + { + return WhiteLen11MakeupCodes.ContainsKey(this.value); + } + + case 12: + { + return WhiteLen12MakeupCodes.ContainsKey(this.value); + } } return false; @@ -561,6 +602,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression return BlackLen10MakeupCodes.ContainsKey(this.value); } + case 11: + { + return BlackLen11MakeupCodes.ContainsKey(this.value); + } + case 12: { return BlackLen12MakeupCodes.ContainsKey(this.value); diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index a58ba53f4..abdde50b1 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -511,6 +511,10 @@ namespace SixLabors.ImageSharp.Tests public const string Calliphora_RgbLzw_Predictor = "Tiff/Calliphora_rgb_lzw.tiff"; public const string Calliphora_RgbPackbits = "Tiff/Calliphora_rgb_packbits.tiff"; public const string Calliphora_RgbUncompressed = "Tiff/Calliphora_rgb_uncompressed.tiff"; + public const string Calliphora_Fax3Compressed = "Tiff/Calliphora_ccitt_fax3.tif"; + + public const string CcittFax3AllTermCodes = "Tiff/ccitt_fax3_all_terminating_codes.tif"; + public const string CcittFax3AllMakupCodes = "Tiff/ccitt_fax3_all_makeup_codes.tif"; public const string GrayscaleDeflateMultistrip = "Tiff/grayscale_deflate_multistrip.tiff"; public const string GrayscaleUncompressed = "Tiff/grayscale_uncompressed.tiff"; @@ -541,7 +545,7 @@ namespace SixLabors.ImageSharp.Tests public const string SampleMetadata = "Tiff/metadata_sample.tiff"; - public static readonly string[] All = { Calliphora_GrayscaleUncompressed, Calliphora_PaletteUncompressed, /*Calliphora_RgbDeflate_Predictor, Calliphora_RgbLzwe_Predictor, */ Calliphora_RgbPackbits, Calliphora_RgbUncompressed, GrayscaleDeflateMultistrip, GrayscaleUncompressed, PaletteDeflateMultistrip, PaletteUncompressed, /*RgbDeflate_Predictor,*/ RgbDeflateMultistrip, /*RgbJpeg,*/ /*RgbLzw_Predictor, RgbLzwMultistrip_Predictor,*/ RgbLzw_NoPredictor_Multistrip, RgbLzw_NoPredictor_Multistrip_Motorola, RgbLzw_NoPredictor_Singlestrip_Motorola, RgbPackbits, RgbPackbitsMultistrip, RgbUncompressed, /* MultiframeLzw_Predictor, MultiFrameDifferentVariants, SampleMetadata,*/ SmallRgbDeflate, SmallRgbLzw, }; + public static readonly string[] All = { Calliphora_GrayscaleUncompressed, Calliphora_PaletteUncompressed, /*Calliphora_RgbDeflate_Predictor, Calliphora_RgbLzwe_Predictor, */ Calliphora_RgbPackbits, Calliphora_RgbUncompressed, Calliphora_Fax3Compressed, CcittFax3AllTermCodes, CcittFax3AllMakupCodes, GrayscaleDeflateMultistrip, GrayscaleUncompressed, PaletteDeflateMultistrip, PaletteUncompressed, /*RgbDeflate_Predictor,*/ RgbDeflateMultistrip, /*RgbJpeg,*/ /*RgbLzw_Predictor, RgbLzwMultistrip_Predictor,*/ RgbLzw_NoPredictor_Multistrip, RgbLzw_NoPredictor_Multistrip_Motorola, RgbLzw_NoPredictor_Singlestrip_Motorola, RgbPackbits, RgbPackbitsMultistrip, RgbUncompressed, /* MultiframeLzw_Predictor, MultiFrameDifferentVariants, SampleMetadata,*/ SmallRgbDeflate, SmallRgbLzw, }; public static readonly string[] Multiframes = { MultiframeDeflateWithPreview /*MultiframeLzw_Predictor, MultiFrameDifferentSize, MultiframeDifferentSizeTiled, MultiFrameDifferentVariants,*/ }; diff --git a/tests/Images/Input/Tiff/Calliphora_ccitt_fax3.tif b/tests/Images/Input/Tiff/Calliphora_ccitt_fax3.tif new file mode 100644 index 000000000..e50ee6f2a --- /dev/null +++ b/tests/Images/Input/Tiff/Calliphora_ccitt_fax3.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a45c92b187e7a59247ccc50f418379e91fd169b39fa7fc8a6dcda9b092fc3013 +size 125776 diff --git a/tests/Images/Input/Tiff/ccitt_fax3_all_makeupcodes_codes.tif b/tests/Images/Input/Tiff/ccitt_fax3_all_makeupcodes_codes.tif new file mode 100644 index 000000000..09be31655 --- /dev/null +++ b/tests/Images/Input/Tiff/ccitt_fax3_all_makeupcodes_codes.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3b1a0af9ace55d5d4b86225cb569a8632b6a6bb621fa1dc56a7d3d6404eba7bb +size 360 diff --git a/tests/Images/Input/Tiff/ccitt_fax3_all_terminating_codes.tif b/tests/Images/Input/Tiff/ccitt_fax3_all_terminating_codes.tif new file mode 100644 index 000000000..a2da71cf6 --- /dev/null +++ b/tests/Images/Input/Tiff/ccitt_fax3_all_terminating_codes.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:faa92346ccff9cc7e3275b31cbc4ec054e27d0d0ed20a215a22b6178c2d7adf0 +size 564 From 0bb81659027dbf21ca982b8fb784c9b6d685ab36 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 22 Nov 2020 15:02:00 +0100 Subject: [PATCH 080/275] Fix issue with CCITT T4 with white runs of length 0 at the start of a scanline --- .../Formats/Tiff/Compression/T4BitReader.cs | 63 ++++++++++++------- .../Tiff/Compression/T4TiffCompression.cs | 27 ++++---- .../Formats/Tiff/TiffDecoderTests.cs | 16 +++-- tests/ImageSharp.Tests/TestImages.cs | 2 +- .../ReferenceCodecs/MagickReferenceDecoder.cs | 2 +- .../Tiff/ccitt_fax3_all_makeupcodes_codes.tif | 4 +- 6 files changed, 67 insertions(+), 47 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs index c31eb8793..043e5b313 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs @@ -55,6 +55,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression /// private uint runLength; + /// + /// We keep track if its the start of the row, because each run is expected to start with a white run. + /// If the image row itself starts with black, a white run of zero is expected. + /// + private bool isStartOfRow; + private readonly int dataLength; private const int MinCodeLength = 2; @@ -78,14 +84,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression private static readonly Dictionary WhiteLen7TermCodes = new Dictionary() { - { 0x27, 18 }, { 0xC, 19 }, { 0x8, 20 }, { 0x17, 21 }, { 0x3, 22 }, { 0x4, 23 }, { 0x28, 24 }, { 0x2B, 25 }, { 0x13, 26 }, { 0x24, 27 }, { 0x18, 28 } + { 0x27, 18 }, { 0xC, 19 }, { 0x8, 20 }, { 0x17, 21 }, { 0x3, 22 }, { 0x4, 23 }, { 0x28, 24 }, { 0x2B, 25 }, { 0x13, 26 }, + { 0x24, 27 }, { 0x18, 28 } }; private static readonly Dictionary WhiteLen8TermCodes = new Dictionary() { - { 0x35, 0 }, { 0x2, 29 }, { 0x3, 30 }, { 0x1A, 31 }, { 0x1B, 32 }, { 0x12, 33 }, { 0x13, 34 }, { 0x14, 35 }, { 0x15, 36 }, { 0x16, 37 }, { 0x17, 38 }, { 0x28, 39 }, { 0x29, 40 }, { 0x2A, 41 }, - { 0x2B, 42 }, { 0x2C, 43 }, { 0x2D, 44 }, { 0x4, 45 }, { 0x5, 46 }, { 0xA, 47 }, { 0xB, 48 }, { 0x52, 49 }, { 0x53, 50 }, { 0x54, 51 }, { 0x55, 52 }, { 0x24, 53 }, { 0x25, 54 }, { 0x58, 55 }, - { 0x59, 56 }, { 0x5A, 57 }, { 0x5B, 58 }, { 0x4A, 59 }, { 0x4B, 60 }, { 0x32, 61 }, { 0x33, 62 }, { 0x34, 63 } + { 0x35, 0 }, { 0x2, 29 }, { 0x3, 30 }, { 0x1A, 31 }, { 0x1B, 32 }, { 0x12, 33 }, { 0x13, 34 }, { 0x14, 35 }, { 0x15, 36 }, + { 0x16, 37 }, { 0x17, 38 }, { 0x28, 39 }, { 0x29, 40 }, { 0x2A, 41 }, { 0x2B, 42 }, { 0x2C, 43 }, { 0x2D, 44 }, { 0x4, 45 }, + { 0x5, 46 }, { 0xA, 47 }, { 0xB, 48 }, { 0x52, 49 }, { 0x53, 50 }, { 0x54, 51 }, { 0x55, 52 }, { 0x24, 53 }, { 0x25, 54 }, + { 0x58, 55 }, { 0x59, 56 }, { 0x5A, 57 }, { 0x5B, 58 }, { 0x4A, 59 }, { 0x4B, 60 }, { 0x32, 61 }, { 0x33, 62 }, { 0x34, 63 } }; private static readonly Dictionary BlackLen2TermCodes = new Dictionary() @@ -159,7 +167,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression private static readonly Dictionary WhiteLen8MakeupCodes = new Dictionary() { - { 0x36, 320 }, { 0x37, 348 }, { 0x64, 448 }, { 0x65, 512 }, { 0x68, 576 }, { 0x67, 640 } + { 0x36, 320 }, { 0x37, 384 }, { 0x64, 448 }, { 0x65, 512 }, { 0x68, 576 }, { 0x67, 640 } }; private static readonly Dictionary WhiteLen7MakeupCodes = new Dictionary() @@ -181,7 +189,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression private static readonly Dictionary WhiteLen12MakeupCodes = new Dictionary() { - { 0x12, 1984 }, { 0x13, 2048 }, { 0x14, 2112 }, { 0x15, 2176 }, { 0x16, 2240 }, { 0x17, 2304}, { 0x1C, 2368 }, { 0x1D, 2432 }, { 0x1E, 2496 }, { 0x1F, 2560 } + { 0x12, 1984 }, { 0x13, 2048 }, { 0x14, 2112 }, { 0x15, 2176 }, { 0x16, 2240 }, { 0x17, 2304 }, { 0x1C, 2368 }, + { 0x1D, 2432 }, { 0x1E, 2496 }, { 0x1F, 2560 } }; private static readonly Dictionary BlackLen10MakeupCodes = new Dictionary() @@ -197,7 +206,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression private static readonly Dictionary BlackLen12MakeupCodes = new Dictionary() { { 0xC8, 128 }, { 0xC9, 192 }, { 0x5B, 256 }, { 0x33, 320 }, { 0x34, 384 }, { 0x35, 448 }, - { 0x12, 1984 }, { 0x13, 2048 }, { 0x14, 2112 }, { 0x15, 2176 }, { 0x16, 2240 }, { 0x17, 2304}, { 0x1C, 2368 }, { 0x1D, 2432 }, { 0x1E, 2496 }, { 0x1F, 2560 } + { 0x12, 1984 }, { 0x13, 2048 }, { 0x14, 2112 }, { 0x15, 2176 }, { 0x16, 2240 }, { 0x17, 2304 }, { 0x1C, 2368 }, + { 0x1D, 2432 }, { 0x1E, 2496 }, { 0x1F, 2560 } }; private static readonly Dictionary BlackLen13MakeupCodes = new Dictionary() @@ -225,6 +235,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression this.position = 0; this.isWhiteRun = true; this.isFirstScanLine = true; + this.isStartOfRow = true; this.terminationCodeFound = false; this.runLength = 0; } @@ -313,14 +324,32 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression TiffThrowHelper.ThrowImageFormatException("t4 parsing error: invalid code length read"); } + bool isMakeupCode = this.IsMakeupCode(); + if (isMakeupCode) + { + if (this.IsWhiteRun) + { + this.runLength += this.WhiteMakeupCodeRunLength(); + } + else + { + this.runLength += this.BlackMakeupCodeRunLength(); + } + + this.isStartOfRow = false; + this.Reset(resetRunLength: false); + continue; + } + bool isTerminatingCode = this.IsTerminatingCode(); if (isTerminatingCode) { // Each line starts with a white run. If the image starts with black, a white run with length zero is written. - if (this.IsWhiteRun && this.WhiteTerminatingCodeRunLength() == 0) + if (this.isStartOfRow && this.IsWhiteRun && this.WhiteTerminatingCodeRunLength() == 0) { this.isWhiteRun = !this.IsWhiteRun; this.Reset(); + this.isStartOfRow = false; continue; } @@ -334,25 +363,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression } this.terminationCodeFound = true; + this.isStartOfRow = false; break; } - bool isMakeupCode = this.IsMakeupCode(); - if (isMakeupCode) - { - if (this.IsWhiteRun) - { - this.runLength += this.WhiteMakeupCodeRunLength(); - } - else - { - this.runLength += this.BlackMakeupCodeRunLength(); - } - - this.Reset(false); - continue; - } - var currBit = this.ReadValue(1); this.value = (this.value << 1) | currBit; @@ -360,6 +374,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression { // Each new row starts with a white run. this.isWhiteRun = true; + this.isStartOfRow = true; } } while (!this.IsEndOfScanLine); diff --git a/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs index 13f7eb794..ae15a8b61 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs @@ -36,6 +36,20 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression { bitReader.ReadNextRun(); + if (bitReader.RunLength > 0) + { + if (bitReader.IsWhiteRun) + { + this.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, whiteValue); + bitsWritten += bitReader.RunLength; + } + else + { + this.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, blackValue); + bitsWritten += bitReader.RunLength; + } + } + if (bitReader.IsEndOfScanLine) { // Write padding bytes, if necessary. @@ -45,19 +59,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression this.WriteBits(buffer, (int)bitsWritten, pad, 0); bitsWritten += pad; } - - continue; - } - - if (bitReader.IsWhiteRun) - { - this.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, whiteValue); - bitsWritten += bitReader.RunLength; - } - else - { - this.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, blackValue); - bitsWritten += bitReader.RunLength; } } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index 3a40d5ce2..5e04906bb 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -18,6 +18,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Trait("Category", "Tiff")] public class TiffDecoderTests { + private static TiffDecoder TiffDecoder => new TiffDecoder(); + + private static MagickReferenceDecoder ReferenceDecoder => new MagickReferenceDecoder(); + public static readonly string[] SingleTestImages = TestImages.Tiff.All; public static readonly string[] MultiframeTestImages = TestImages.Tiff.Multiframes; @@ -29,7 +33,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public void ThrowsNotSupported(TestImageProvider provider) where TPixel : unmanaged, IPixel { - Assert.Throws(() => provider.GetImage(new TiffDecoder())); + Assert.Throws(() => provider.GetImage(TiffDecoder)); } [Theory] @@ -79,10 +83,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public void Decode(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(new TiffDecoder())) + using (Image image = provider.GetImage(TiffDecoder)) { image.DebugSave(provider); - image.CompareToOriginal(provider, ImageComparer.Exact, new MagickReferenceDecoder()); + image.CompareToOriginal(provider, ImageComparer.Exact, ReferenceDecoder); } } @@ -91,15 +95,15 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public void DecodeMultiframe(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(new TiffDecoder())) + using (Image image = provider.GetImage(TiffDecoder)) { Assert.True(image.Frames.Count > 1); image.DebugSave(provider); - image.CompareToOriginal(provider, ImageComparer.Exact, new MagickReferenceDecoder()); + image.CompareToOriginal(provider, ImageComparer.Exact, ReferenceDecoder); image.DebugSaveMultiFrame(provider); - image.CompareToOriginalMultiFrame(provider, ImageComparer.Exact, new MagickReferenceDecoder()); + image.CompareToOriginalMultiFrame(provider, ImageComparer.Exact, ReferenceDecoder); } } } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index abdde50b1..fa9e4e290 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -514,7 +514,7 @@ namespace SixLabors.ImageSharp.Tests public const string Calliphora_Fax3Compressed = "Tiff/Calliphora_ccitt_fax3.tif"; public const string CcittFax3AllTermCodes = "Tiff/ccitt_fax3_all_terminating_codes.tif"; - public const string CcittFax3AllMakupCodes = "Tiff/ccitt_fax3_all_makeup_codes.tif"; + public const string CcittFax3AllMakupCodes = "Tiff/ccitt_fax3_all_makeupcodes_codes.tif"; public const string GrayscaleDeflateMultistrip = "Tiff/grayscale_deflate_multistrip.tiff"; public const string GrayscaleUncompressed = "Tiff/grayscale_uncompressed.tiff"; diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs index d411a6fb7..17ad3e990 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs @@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs MemoryGroup framePixels = frame.PixelBuffer.FastMemoryGroup; using IUnsafePixelCollection pixels = magicFrame.GetPixelsUnsafe(); - if (magicFrame.Depth == 8) + if (magicFrame.Depth == 8 || magicFrame.Depth == 1) { byte[] data = pixels.ToByteArray(PixelMapping.RGBA); diff --git a/tests/Images/Input/Tiff/ccitt_fax3_all_makeupcodes_codes.tif b/tests/Images/Input/Tiff/ccitt_fax3_all_makeupcodes_codes.tif index 09be31655..6a1153bac 100644 --- a/tests/Images/Input/Tiff/ccitt_fax3_all_makeupcodes_codes.tif +++ b/tests/Images/Input/Tiff/ccitt_fax3_all_makeupcodes_codes.tif @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3b1a0af9ace55d5d4b86225cb569a8632b6a6bb621fa1dc56a7d3d6404eba7bb -size 360 +oid sha256:60b64bd3c24437eb90c0a17a4328e997702d7e4c0889ec90abde092ab9b490e8 +size 546 From fbce81b6048f963fae086f1f5c8702d37850f947 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 22 Nov 2020 16:04:43 +0100 Subject: [PATCH 081/275] Handle white as zero based on photometric interpretation --- .../Formats/Tiff/Compression/T4TiffCompression.cs | 8 ++++---- .../Formats/Tiff/Compression/TiffBaseCompression.cs | 10 ++++++++++ .../Formats/Tiff/Compression/TiffCompressionFactory.cs | 4 ++-- src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs | 4 ++-- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs index ae15a8b61..8c16cde68 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs @@ -16,16 +16,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression /// Initializes a new instance of the class. /// /// The memory allocator. - public T4TiffCompression(MemoryAllocator allocator) - : base(allocator) + /// The photometric interpretation. + public T4TiffCompression(MemoryAllocator allocator, TiffPhotometricInterpretation photometricInterpretation) + : base(allocator, photometricInterpretation) { } /// public override void Decompress(Stream stream, int byteCount, Span buffer) { - // TODO: handle case when white is not zero. - bool isWhiteZero = true; + bool isWhiteZero = this.PhotometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero; int whiteValue = isWhiteZero ? 0 : 1; int blackValue = isWhiteZero ? 1 : 0; diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs index 33330beba..1a2b814fe 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs @@ -14,10 +14,20 @@ namespace SixLabors.ImageSharp.Formats.Tiff { private readonly MemoryAllocator allocator; + private TiffPhotometricInterpretation photometricInterpretation; + public TiffBaseCompression(MemoryAllocator allocator) => this.allocator = allocator; + public TiffBaseCompression(MemoryAllocator allocator, TiffPhotometricInterpretation photometricInterpretation) + { + this.allocator = allocator; + this.photometricInterpretation = photometricInterpretation; + } + protected MemoryAllocator Allocator => this.allocator; + protected TiffPhotometricInterpretation PhotometricInterpretation => this.photometricInterpretation; + /// /// Decompresses image data into the supplied buffer. /// diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs index cedbbe35b..e15cc451f 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff { internal static class TiffCompressionFactory { - public static TiffBaseCompression Create(TiffCompressionType compressionType, MemoryAllocator allocator) + public static TiffBaseCompression Create(TiffCompressionType compressionType, MemoryAllocator allocator, TiffPhotometricInterpretation photometricInterpretation) { switch (compressionType) { @@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff case TiffCompressionType.Lzw: return new LzwTiffCompression(allocator); case TiffCompressionType.T4: - return new T4TiffCompression(allocator); + return new T4TiffCompression(allocator, photometricInterpretation); default: throw TiffThrowHelper.NotSupportedCompression(nameof(compressionType)); } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 7eced53bd..5c253d4c8 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -276,7 +276,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff stripBuffers[stripIndex] = this.memoryAllocator.AllocateManagedByteBuffer(uncompressedStripSize); } - TiffBaseCompression decompressor = TiffCompressionFactory.Create(this.CompressionType, this.memoryAllocator); + TiffBaseCompression decompressor = TiffCompressionFactory.Create(this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation); RgbPlanarTiffColor colorDecoder = TiffColorDecoderFactory.CreatePlanar(this.ColorType, this.BitsPerSample, this.ColorMap); @@ -313,7 +313,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff Buffer2D pixels = frame.PixelBuffer; - TiffBaseCompression decompressor = TiffCompressionFactory.Create(this.CompressionType, this.memoryAllocator); + TiffBaseCompression decompressor = TiffCompressionFactory.Create(this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation); TiffBaseColorDecoder colorDecoder = TiffColorDecoderFactory.Create(this.ColorType, this.BitsPerSample, this.ColorMap); From 8122bed91b8b520a165f616aca3c83849188e24f Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 22 Nov 2020 20:07:37 +0100 Subject: [PATCH 082/275] Add support for decompressing huffman encoded tiffs --- .../Formats/Tiff/Compression/T4BitReader.cs | 47 ++++++++++-- .../Tiff/Compression/T4TiffCompression.cs | 9 +-- .../Tiff/Compression/TiffBaseCompression.cs | 7 +- .../Compression/TiffCompressionFactory.cs | 6 +- .../Tiff/Compression/TiffCompressionType.cs | 5 ++ .../TiffModifiedHuffmanCompression.cs | 72 +++++++++++++++++++ .../Formats/Tiff/TiffDecoderCore.cs | 13 ++-- .../Formats/Tiff/TiffDecoderHelpers.cs | 6 ++ tests/ImageSharp.Tests/TestImages.cs | 3 +- .../Input/Tiff/Calliphora_huffman_rle.tif | 3 + 10 files changed, 152 insertions(+), 19 deletions(-) create mode 100644 src/ImageSharp/Formats/Tiff/Compression/TiffModifiedHuffmanCompression.cs create mode 100644 tests/Images/Input/Tiff/Calliphora_huffman_rle.tif diff --git a/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs index 043e5b313..ed2fad7ed 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs @@ -61,6 +61,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression /// private bool isStartOfRow; + private readonly bool isModifiedHuffmanRle; + private readonly int dataLength; private const int MinCodeLength = 2; @@ -223,11 +225,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression /// The compressed input stream. /// The number of bytes to read from the stream. /// The memory allocator. - public T4BitReader(Stream input, int bytesToRead, MemoryAllocator allocator) + /// Indicates, if its the modified huffman code variation. + public T4BitReader(Stream input, int bytesToRead, MemoryAllocator allocator, bool isModifiedHuffman = false) { this.Data = allocator.Allocate(bytesToRead); this.ReadImageDataFromStream(input, bytesToRead, allocator); + this.isModifiedHuffmanRle = isModifiedHuffman; this.dataLength = bytesToRead; this.bitsRead = 0; this.value = 0; @@ -302,7 +306,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression this.Reset(); - if (this.isFirstScanLine) + if (this.isFirstScanLine && !this.isModifiedHuffmanRle) { // We expect an EOL before the first data. this.value = this.ReadValue(12); @@ -372,9 +376,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression if (this.IsEndOfScanLine) { - // Each new row starts with a white run. - this.isWhiteRun = true; - this.isStartOfRow = true; + this.StartNewRow(); } } while (!this.IsEndOfScanLine); @@ -382,6 +384,25 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression this.isFirstScanLine = false; } + public void StartNewRow() + { + // Each new row starts with a white run. + this.isWhiteRun = true; + this.isStartOfRow = true; + this.terminationCodeFound = false; + + if (this.isModifiedHuffmanRle) + { + int pad = 8 - (this.bitsRead % 8); + if (pad != 8) + { + // Skip padding bits, move to next byte. + this.position++; + this.bitsRead = 0; + } + } + } + /// public void Dispose() { @@ -601,6 +622,14 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression case 12: { + if (this.isModifiedHuffmanRle) + { + if (this.value == 1) + { + return true; + } + } + return WhiteLen12MakeupCodes.ContainsKey(this.value); } } @@ -619,6 +648,14 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression case 11: { + if (this.isModifiedHuffmanRle) + { + if (this.value == 0) + { + return true; + } + } + return BlackLen11MakeupCodes.ContainsKey(this.value); } diff --git a/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs index 8c16cde68..6aeb5af81 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs @@ -17,8 +17,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression /// /// The memory allocator. /// The photometric interpretation. - public T4TiffCompression(MemoryAllocator allocator, TiffPhotometricInterpretation photometricInterpretation) - : base(allocator, photometricInterpretation) + /// The image width. + public T4TiffCompression(MemoryAllocator allocator, TiffPhotometricInterpretation photometricInterpretation, int width) + : base(allocator, photometricInterpretation, width) { } @@ -63,7 +64,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression } } - private void WriteBits(Span buffer, int pos, uint count, int value) + protected void WriteBits(Span buffer, int pos, uint count, int value) { int bitPos = pos % 8; int bufferPos = pos / 8; @@ -83,7 +84,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression } } - private void WriteBit(Span buffer, int bufferPos, int bitPos, int value) + protected void WriteBit(Span buffer, int bufferPos, int bitPos, int value) { buffer[bufferPos] |= (byte)(value << (7 - bitPos)); } diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs index 1a2b814fe..fb05a9f25 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs @@ -16,18 +16,23 @@ namespace SixLabors.ImageSharp.Formats.Tiff private TiffPhotometricInterpretation photometricInterpretation; + private int width; + public TiffBaseCompression(MemoryAllocator allocator) => this.allocator = allocator; - public TiffBaseCompression(MemoryAllocator allocator, TiffPhotometricInterpretation photometricInterpretation) + public TiffBaseCompression(MemoryAllocator allocator, TiffPhotometricInterpretation photometricInterpretation, int width) { this.allocator = allocator; this.photometricInterpretation = photometricInterpretation; + this.width = width; } protected MemoryAllocator Allocator => this.allocator; protected TiffPhotometricInterpretation PhotometricInterpretation => this.photometricInterpretation; + protected int Width => this.width; + /// /// Decompresses image data into the supplied buffer. /// diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs index e15cc451f..3a0e5e6da 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff { internal static class TiffCompressionFactory { - public static TiffBaseCompression Create(TiffCompressionType compressionType, MemoryAllocator allocator, TiffPhotometricInterpretation photometricInterpretation) + public static TiffBaseCompression Create(TiffCompressionType compressionType, MemoryAllocator allocator, TiffPhotometricInterpretation photometricInterpretation, int width) { switch (compressionType) { @@ -21,7 +21,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff case TiffCompressionType.Lzw: return new LzwTiffCompression(allocator); case TiffCompressionType.T4: - return new T4TiffCompression(allocator, photometricInterpretation); + return new T4TiffCompression(allocator, photometricInterpretation, width); + case TiffCompressionType.HuffmanRle: + return new TiffModifiedHuffmanCompression(allocator, photometricInterpretation, width); default: throw TiffThrowHelper.NotSupportedCompression(nameof(compressionType)); } diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs index 665e4aca2..8a33948f9 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs @@ -32,5 +32,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// Image data is compressed using T4-encoding: CCITT T.4. /// T4 = 4, + + /// + /// Image data is compressed using modified huffman compression. + /// + HuffmanRle = 5, } } diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffModifiedHuffmanCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffModifiedHuffmanCompression.cs new file mode 100644 index 000000000..1201ab66a --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffModifiedHuffmanCompression.cs @@ -0,0 +1,72 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Formats.Tiff.Compression +{ + /// + /// Class to handle cases where TIFF image data is compressed using Modified Huffman Compression. + /// + internal class TiffModifiedHuffmanCompression : T4TiffCompression + { + /// + /// Initializes a new instance of the class. + /// + /// The memory allocator. + /// The photometric interpretation. + /// The image width. + public TiffModifiedHuffmanCompression(MemoryAllocator allocator, TiffPhotometricInterpretation photometricInterpretation, int width) + : base(allocator, photometricInterpretation, width) + { + } + + /// + public override void Decompress(Stream stream, int byteCount, Span buffer) + { + bool isWhiteZero = this.PhotometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero; + int whiteValue = isWhiteZero ? 0 : 1; + int blackValue = isWhiteZero ? 1 : 0; + + using var bitReader = new T4BitReader(stream, byteCount, this.Allocator, isModifiedHuffman: true); + + uint bitsWritten = 0; + uint pixelsWritten = 0; + while (bitReader.HasMoreData) + { + bitReader.ReadNextRun(); + + if (bitReader.RunLength > 0) + { + if (bitReader.IsWhiteRun) + { + this.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, whiteValue); + bitsWritten += bitReader.RunLength; + pixelsWritten += bitReader.RunLength; + } + else + { + this.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, blackValue); + bitsWritten += bitReader.RunLength; + pixelsWritten += bitReader.RunLength; + } + } + + if (pixelsWritten % this.Width == 0) + { + bitReader.StartNewRow(); + + // Write padding bytes, if necessary. + uint pad = 8 - (bitsWritten % 8); + if (pad != 8) + { + this.WriteBits(buffer, (int)bitsWritten, pad, 0); + bitsWritten += pad; + } + } + } + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 5c253d4c8..bbf361e0d 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -210,11 +210,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff if (this.PlanarConfiguration == TiffPlanarConfiguration.Planar) { - this.DecodeStripsPlanar(frame, rowsPerStrip, stripOffsets, stripByteCounts); + this.DecodeStripsPlanar(frame, rowsPerStrip, stripOffsets, stripByteCounts, width); } else { - this.DecodeStripsChunky(frame, rowsPerStrip, stripOffsets, stripByteCounts); + this.DecodeStripsChunky(frame, rowsPerStrip, stripOffsets, stripByteCounts, width); } return frame; @@ -258,7 +258,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The number of rows per strip of data. /// An array of byte offsets to each strip in the image. /// An array of the size of each strip (in bytes). - private void DecodeStripsPlanar(ImageFrame frame, int rowsPerStrip, uint[] stripOffsets, uint[] stripByteCounts) + /// The image width. + private void DecodeStripsPlanar(ImageFrame frame, int rowsPerStrip, uint[] stripOffsets, uint[] stripByteCounts, int width) where TPixel : unmanaged, IPixel { int stripsPerPixel = this.BitsPerSample.Length; @@ -276,7 +277,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff stripBuffers[stripIndex] = this.memoryAllocator.AllocateManagedByteBuffer(uncompressedStripSize); } - TiffBaseCompression decompressor = TiffCompressionFactory.Create(this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation); + TiffBaseCompression decompressor = TiffCompressionFactory.Create(this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, width); RgbPlanarTiffColor colorDecoder = TiffColorDecoderFactory.CreatePlanar(this.ColorType, this.BitsPerSample, this.ColorMap); @@ -304,7 +305,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff } } - private void DecodeStripsChunky(ImageFrame frame, int rowsPerStrip, uint[] stripOffsets, uint[] stripByteCounts) + private void DecodeStripsChunky(ImageFrame frame, int rowsPerStrip, uint[] stripOffsets, uint[] stripByteCounts, int width) where TPixel : unmanaged, IPixel { int uncompressedStripSize = this.CalculateStripBufferSize(frame.Width, rowsPerStrip); @@ -313,7 +314,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff Buffer2D pixels = frame.PixelBuffer; - TiffBaseCompression decompressor = TiffCompressionFactory.Create(this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation); + TiffBaseCompression decompressor = TiffCompressionFactory.Create(this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, width); TiffBaseColorDecoder colorDecoder = TiffColorDecoderFactory.Create(this.ColorType, this.BitsPerSample, this.ColorMap); diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs index 5348be8ce..6db776039 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs @@ -352,6 +352,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff break; } + case TiffCompression.Ccitt1D: + { + options.CompressionType = TiffCompressionType.HuffmanRle; + break; + } + default: { TiffThrowHelper.ThrowNotSupported("The specified TIFF compression format is not supported: " + compression); diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index fa9e4e290..7f16f4aa7 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -512,6 +512,7 @@ namespace SixLabors.ImageSharp.Tests public const string Calliphora_RgbPackbits = "Tiff/Calliphora_rgb_packbits.tiff"; public const string Calliphora_RgbUncompressed = "Tiff/Calliphora_rgb_uncompressed.tiff"; public const string Calliphora_Fax3Compressed = "Tiff/Calliphora_ccitt_fax3.tif"; + public const string Calliphora_HuffmanCompressed = "Tiff/Calliphora_huffman_rle.tif"; public const string CcittFax3AllTermCodes = "Tiff/ccitt_fax3_all_terminating_codes.tif"; public const string CcittFax3AllMakupCodes = "Tiff/ccitt_fax3_all_makeupcodes_codes.tif"; @@ -545,7 +546,7 @@ namespace SixLabors.ImageSharp.Tests public const string SampleMetadata = "Tiff/metadata_sample.tiff"; - public static readonly string[] All = { Calliphora_GrayscaleUncompressed, Calliphora_PaletteUncompressed, /*Calliphora_RgbDeflate_Predictor, Calliphora_RgbLzwe_Predictor, */ Calliphora_RgbPackbits, Calliphora_RgbUncompressed, Calliphora_Fax3Compressed, CcittFax3AllTermCodes, CcittFax3AllMakupCodes, GrayscaleDeflateMultistrip, GrayscaleUncompressed, PaletteDeflateMultistrip, PaletteUncompressed, /*RgbDeflate_Predictor,*/ RgbDeflateMultistrip, /*RgbJpeg,*/ /*RgbLzw_Predictor, RgbLzwMultistrip_Predictor,*/ RgbLzw_NoPredictor_Multistrip, RgbLzw_NoPredictor_Multistrip_Motorola, RgbLzw_NoPredictor_Singlestrip_Motorola, RgbPackbits, RgbPackbitsMultistrip, RgbUncompressed, /* MultiframeLzw_Predictor, MultiFrameDifferentVariants, SampleMetadata,*/ SmallRgbDeflate, SmallRgbLzw, }; + public static readonly string[] All = { Calliphora_GrayscaleUncompressed, Calliphora_PaletteUncompressed, /*Calliphora_RgbDeflate_Predictor, Calliphora_RgbLzwe_Predictor, */ Calliphora_RgbPackbits, Calliphora_RgbUncompressed, Calliphora_HuffmanCompressed, Calliphora_Fax3Compressed, CcittFax3AllTermCodes, CcittFax3AllMakupCodes, GrayscaleDeflateMultistrip, GrayscaleUncompressed, PaletteDeflateMultistrip, PaletteUncompressed, /*RgbDeflate_Predictor,*/ RgbDeflateMultistrip, /*RgbJpeg,*/ /*RgbLzw_Predictor, RgbLzwMultistrip_Predictor,*/ RgbLzw_NoPredictor_Multistrip, RgbLzw_NoPredictor_Multistrip_Motorola, RgbLzw_NoPredictor_Singlestrip_Motorola, RgbPackbits, RgbPackbitsMultistrip, RgbUncompressed, /* MultiframeLzw_Predictor, MultiFrameDifferentVariants, SampleMetadata,*/ SmallRgbDeflate, SmallRgbLzw, }; public static readonly string[] Multiframes = { MultiframeDeflateWithPreview /*MultiframeLzw_Predictor, MultiFrameDifferentSize, MultiframeDifferentSizeTiled, MultiFrameDifferentVariants,*/ }; diff --git a/tests/Images/Input/Tiff/Calliphora_huffman_rle.tif b/tests/Images/Input/Tiff/Calliphora_huffman_rle.tif new file mode 100644 index 000000000..e0a39d248 --- /dev/null +++ b/tests/Images/Input/Tiff/Calliphora_huffman_rle.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1a4f687de9925863b1c9f32f53b6c05fb121f9d7b02ff5869113c4745433f10d +size 124644 From 72ada958c970c30e0e7937df7f27c99defaeadc8 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 22 Nov 2020 20:10:14 +0100 Subject: [PATCH 083/275] Add Tiff specification pdf's --- src/ImageSharp/Formats/Tiff/README.md | 3 ++- .../Formats/Tiff/T-REC-T.4-198811-S!!PDF-E.pdf | Bin 0 -> 365889 bytes .../Formats/Tiff/T-REC-T.6-198811-I!!PDF-E.pdf | Bin 0 -> 112837 bytes src/ImageSharp/Formats/Tiff/TIFF-v6.pdf | 1 + 4 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 src/ImageSharp/Formats/Tiff/T-REC-T.4-198811-S!!PDF-E.pdf create mode 100644 src/ImageSharp/Formats/Tiff/T-REC-T.6-198811-I!!PDF-E.pdf create mode 100644 src/ImageSharp/Formats/Tiff/TIFF-v6.pdf diff --git a/src/ImageSharp/Formats/Tiff/README.md b/src/ImageSharp/Formats/Tiff/README.md index ca9078ae1..f2fa861a2 100644 --- a/src/ImageSharp/Formats/Tiff/README.md +++ b/src/ImageSharp/Formats/Tiff/README.md @@ -11,6 +11,7 @@ - [Adobe TIFF Pages](http://partners.adobe.com/public/developer/tiff/index.html) - [Unofficial TIFF FAQ](http://www.awaresystems.be/imaging/tiff/faq.html) - [CCITT T.4 Compression](https://www.itu.int/rec/T-REC-T.4-198811-S/_page.print) + - [CCITT T.6 Compression](https://www.itu.int/rec/T-REC-T.6/en) - DNG - [Adobe DNG Pages](https://helpx.adobe.com/photoshop/digital-negative.html) @@ -40,7 +41,7 @@ | |Encoder|Decoder|Comments | |---------------------------|:-----:|:-----:|--------------------------| |None | | Y | | -|Ccitt1D | | | | +|Ccitt1D | | Y | | |PackBits | | Y | | |CcittGroup3Fax | | Y | | |CcittGroup4Fax | | | | diff --git a/src/ImageSharp/Formats/Tiff/T-REC-T.4-198811-S!!PDF-E.pdf b/src/ImageSharp/Formats/Tiff/T-REC-T.4-198811-S!!PDF-E.pdf new file mode 100644 index 0000000000000000000000000000000000000000..40724dd1e36989cdc0424fd1753bbff157b1f0a6 GIT binary patch literal 365889 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%0AHN3qED!ze7slKdzq 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%0A?$`<8m%>IO%{mLUQAabEl93G}9yD3Ke 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 K3fMQk{_!6>UWW1j literal 0 HcmV?d00001 diff --git a/src/ImageSharp/Formats/Tiff/T-REC-T.6-198811-I!!PDF-E.pdf b/src/ImageSharp/Formats/Tiff/T-REC-T.6-198811-I!!PDF-E.pdf new file mode 100644 index 0000000000000000000000000000000000000000..32fa877b13f104a4f4d94701eda484e8948c05ad GIT binary patch literal 112837 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 literal 0 HcmV?d00001 diff --git a/src/ImageSharp/Formats/Tiff/TIFF-v6.pdf b/src/ImageSharp/Formats/Tiff/TIFF-v6.pdf new file mode 100644 index 000000000..3e03f2338 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/TIFF-v6.pdf @@ -0,0 +1 @@ +%PDF-1.1 1 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 7 0 R /Resources 3 0 R /Contents 2 0 R >> endobj 2 0 obj << /Length 924 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.)PbE+n*d"(B:/5VsAh7F7`A`[2fg)*t--BaCXH(QjG]!Jl9**#h/>:q.2d /2nLR#-c0:32gZu^l!mrbRSqu0nVmu6:i#Q.4Q73"DCc#:(8'M";8;%JtiqqN2BSe F\6R:44&jS;mE.\BfDNT@c<"bPVK.0mM00'ik#^@`J,,l^n?X#2],8@!oV:6,paR+ `_c`[=X4uI7TSGaAuD!=%*&GuSAnJCa[r\4aA,Ko9:42'P#rj9%PH"^2:lY$R"u1e _8ZQ`[0Lbh)3*_.&O?QbF>Or^6LlRedWFRYa8?06?3'sBXikJ:Lr;])MU^5+3 W3WfK-S&S4V`97#@ZmqTOQJS,!XS/a%]j'82n7m9GhF:WLi/?r!C3M;]*"_Y19oiK Je15a^gb!c@CTet`Q3[JYQ:KN;#cNMJEnkK;L!9tU93?m'F<]GRO/;lR&hI9ibV4I N$CH\15p5$dLZP&=Agc/Jl6q3#!nJ_gWmYAi&FD?@fSC4m?h]/7ZcOo%nZ9o73r'< 8B/$cFQaVeUZ75X)mW"$'H8gDD2>;=+D@nh%E[l@*!Z*l8KjheTPDarLp@b-UOn@a O219`T*D-2C`XG"6#^k3X6J&X$F^Jk@0WYHWn84[mN@JM"9Soq?&f$.5W=Y<"-CMN MDEb6%WN071CYQK#.1Z1Yh6dMJOC\$$\:nc)$^1UC`8'PQsD[AnX,MnO#!@n1^l-a 5WHnA>R:=f,8@+(@<`^=mg[10qh/BF6Hg!M1n` endstream endobj 3 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 4 0 obj << /Type /Font /Subtype /Type1 /Name /F3 /FirstChar 32 /LastChar 255 /Widths [ 278 333 474 556 556 889 722 238 333 333 389 584 278 333 278 278 556 556 556 556 556 556 556 556 556 556 333 333 584 584 584 611 975 722 722 722 722 667 611 778 722 278 556 722 611 833 722 778 667 778 722 667 611 722 667 944 667 667 611 333 278 333 584 556 333 556 611 556 611 556 333 611 611 278 278 556 278 889 611 611 611 611 389 556 333 611 556 778 556 556 500 389 280 389 584 278 722 722 722 667 722 778 722 556 556 556 556 556 556 556 556 556 556 556 278 278 278 278 611 611 611 611 611 611 611 611 611 611 556 400 556 556 556 350 556 611 737 737 1000 333 333 0 1000 778 0 584 0 0 556 611 0 0 0 0 0 370 365 0 889 611 611 333 584 0 556 0 0 556 556 1000 278 722 722 778 1000 944 556 1000 500 500 278 278 584 0 556 667 167 556 333 333 611 611 556 278 278 500 1000 722 667 722 667 667 278 278 278 278 778 778 0 778 722 722 722 278 333 333 333 333 333 333 333 333 333 333 ] /Encoding /MacRomanEncoding /BaseFont /Helvetica-Bold >> endobj 5 0 obj << /Type /Font /Subtype /Type1 /Name /F5 /FirstChar 32 /LastChar 255 /Widths [ 278 278 355 556 556 889 667 191 333 333 389 584 278 333 278 278 556 556 556 556 556 556 556 556 556 556 278 278 584 584 584 556 1015 667 667 722 722 667 611 778 722 278 500 667 556 833 722 778 667 778 722 667 611 722 667 944 667 667 611 278 278 278 469 556 333 556 556 500 556 556 278 556 556 222 222 500 222 833 556 556 556 556 333 500 278 556 500 722 500 500 500 334 260 334 584 278 667 667 722 667 722 778 722 556 556 556 556 556 556 500 556 556 556 556 278 278 278 278 556 556 556 556 556 556 556 556 556 556 556 400 556 556 556 350 537 611 737 737 1000 333 333 0 1000 778 0 584 0 0 556 556 0 0 0 0 0 370 365 0 889 611 611 333 584 0 556 0 0 556 556 1000 278 667 667 778 1000 944 556 1000 333 333 222 222 584 0 500 667 167 556 333 333 500 500 556 278 222 333 1000 667 667 667 667 667 278 278 278 278 778 778 0 778 722 722 722 278 333 333 333 333 333 333 333 333 333 333 ] /Encoding /MacRomanEncoding /BaseFont /Helvetica >> endobj 6 0 obj << /Type /Font /Subtype /Type1 /Name /F6 /FirstChar 32 /LastChar 255 /Widths [ 250 333 408 500 500 833 778 180 333 333 500 564 250 333 250 278 500 500 500 500 500 500 500 500 500 500 278 278 564 564 564 444 921 722 667 667 722 611 556 722 722 333 389 722 611 889 722 722 556 722 667 556 611 722 722 944 722 722 611 333 278 333 469 500 333 444 500 444 500 444 333 500 500 278 278 500 278 778 500 500 500 500 333 389 278 500 500 722 500 500 444 480 200 480 541 250 722 722 667 611 722 722 722 444 444 444 444 444 444 444 444 444 444 444 278 278 278 278 500 500 500 500 500 500 500 500 500 500 500 400 500 500 500 350 453 500 760 760 980 333 333 0 889 722 0 564 0 0 500 500 0 0 0 0 0 276 310 0 667 500 444 333 564 0 500 0 0 500 500 1000 250 722 722 722 889 722 500 1000 444 444 333 333 564 0 500 722 167 500 333 333 556 556 500 250 333 444 1000 722 611 722 611 611 333 333 333 333 722 722 0 722 722 722 722 278 333 333 333 333 333 333 333 333 333 333 ] /Encoding /MacRomanEncoding /BaseFont /Times-Roman >> endobj 7 0 obj << /Type /Pages /Kids [ 1 0 R 8 0 R 11 0 R 14 0 R 17 0 R 20 0 R ] /Count 6 >> endobj 8 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 7 0 R /Resources 10 0 R /Contents 9 0 R >> endobj 9 0 obj << /Length 2573 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdnc;Y]"\6L.)),"!2.H +?Q#eL4ad$kTDZ!C\;1FK;9VL(f"6R]L.Di2=$pl1HZmL\e#=N$a_aj_#t6(\mo^C 15q:0j>W+:8BD3(WXK6<]VC#*1:aU:^,HMiHkQt'_iR,=4/8Q30g3nAbiprNTN%8t "-Rj%]VB/om3e!/_D2V=$m[Ncn!Mo<]k[G-jghW(36sD^"Akd#Dj]IR5=:pC,f#S% &29)=OBW^F@7/Q9^i93X2iM7JLC$8pX#1L+YIo9#%WRTZ)F@pX`:ZD+A)8pjoE>Cb 6\D_P0fjr#=Z.Sp9@igMnal<-WfEg1(5*:lDXFAP&DT(QihOOE5158JoT!\aHoR.] 9RPu_C!q6u$ua%jmLF;Ami=;;PAV>5:"-jSN6o)h3D+1!SEW'eRY7C=bn:B#YCLBs ^bAG+&A9e(W+m:V.8)/C2U%pJA>Lh4=pBmn5a\1&UNL>ballQ!hZXL[2[#..=QU[_\N&H\E6kBJ/eNeL(^a`?cT-WqK4e!,f)%_#2)*6Y%[BHJ"9K"G2G)8u=IqiHGighp&b]e) 6?E-]SEFd+j/EM,V62_:&ZFeaf^id>2[jT6RONrb+J(%G,7_I*k0f!@S`p.TR\.IW S-J_up'Q/8qo86JSF5`Xeh`U(GI*5,)H%0knrI3taU@cIb18DUg&In+j-* c"OU^Oipm1'q1K^>Am\*Fo.cqlt\t]VD5EPKAQe"dItDlm!R]-Z]'Nb(n;*sW7pD/ ::ufHBlBkZprD-b8FX9$LPt3g5$6VB_#CG$42(HL9q-OUCW$bmY>EThdkg8c0.QZN>2hje*cWNR#4@*L$KJpDCh4nP#TbK2Z'GC<$U_XDW i5q<09^`(1Ce6qSDP,A2]1CUP?7+'G)g/I7."SVo`>FPf%oLb@LX\+H<")V3'An3DIMP<6Fp\pl?73e,32UARoY\4GDc&6X(mAnJ9PL\? VG)WZp.?1>=ssmJ:bi6!kebNR"Z2-kM\-^S`QJ6>-G\]?0*XQM$%`@_MYNd,p7BW0 1:n$iZ:Z6)g)WO">R^r&$AW\udDHRZK]ekr)5aAb#nd2m0pRljTVi2jr0;ciJQddg 6uugq.6hDaoJ:"(B1&C,IIlQo#3>-G&TVkiLOBpZ<$[FKERG>)kg:3H"C0&n77+/& U5%[G&d'].%('R3"rWOd)H]a+6&bDg0Z'ZC^hF72rDA5hVAp.$A""%@2M=Nq)j!$M W:_S/;d_j+%a],)&i:KNdrZ0qlG9MkEct!+S-qY#F]"Ee+,)b9#QeCF%)J@38[,*j WakLs_1nm%qPF@+Hb#fPn2)$6:'^nH7Wk\`d/r$-%Om$sOl8H++^ia8%lumA2!"%+ n>Hn+njBFqV#))`9@B7A>4\/DS"CnRlCTh &=AdRH'[cR>aQ000+9jt#9_J;:#dS4c)Som`onfoF-f7T#"(_#*bfCMWbNl\kp"(! O6be*%lC8s$Bp<,^H#14i@3hXa=.J,oR;MZ_*E)ISiWm[U2lR+.7pVGQNna(0F2Zs hZ/0F"*5:h%&p?\CV5[sZ[*#h(=+0\ endstream endobj 10 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 11 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 7 0 R /Resources 13 0 R /Contents 12 0 R >> endobj 12 0 obj << /Length 2266 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd&DkP^\]mSu;/Q.ZE\`8> iWF:[8JJs+`l!A(0L.H6%J%mT5W0HV#/HmbCNQj"C5CthE1KBpC,m/iL32Y?Q9u4H 8*&Z]*>%FG2]WBPN#Y;1`RbQRZ'a,7r]mNe%2o 1^t[uE1JA5npp'TX%Of.]s.u7&\KaCDAtc8U[43L@@1rAL+6[]Kdn^"Ub[tWWeNG8 !c[#R\8Kr.)FP/_N?cOGF0loN+gb5GX^R_-(^7a[@#`1*Jn1;U,9Y^?6DL47TZ:ru Z5Ufi9[u#5&N#:`8aRE+dj5$VDM*YJLh-\.J8:b?N5Ko$2U%%$5VS28,&fT'",-k= 4&R&T2H#\pQMA`9XJ!@(ntc!59-SoI1lf]c^8['d@*&dR'Mtm3%\kS,*!Z"/.pK\" >Z"26CKP*e+Z%;PMQX+,VE0Z6punC\_`\J@<'u'R3NCBQjqq.L`O/sR!PhMOc8G8V @8ld_es.PC>$s0;l&''a9+@H8aGNprbGt2YFOr7+V%I9hr/;ofb"4<;[F: nRQ&AgHX.](lOdOsZDO6D`o[4;C]LgrE[&)6s\^Mk$*V+Rn.(+H/#iXq\N_kd_N [WSa2Z,p&7P-\PZN2L)Vd`/Jla)DN.G:%p3`9RncWt?ZE>9t]b#;RLXE4/tUA%'^l 2W7"-H,,(iNKM1X&.(09cWOGfY[aBqd2\Lh#5f`N(YDJbBuG]D_lpS)RD=kRUDViG:gL$;bMu@d3Sh>g?*G2! qbIR9E;gl9eF@P:D4Cr"3.qhK_l23G?J^1,b5-OteW+te4I9sj:S5;`d\E[aO-GPY KXr$'8fT>)jdia+]Ymlt2\%"\1migIE"3N3.Bd"M44$fRScdnq9tA5([1!NbF:A\< q=Y#X;PK;`Amsj&0pV6h"TotG4(ns2-H.VCe`*&j:l3+S2i?BVbK()8N&A-:K`"2N E%qCem#Q>dL4Tfn+P-mT$obScWh4C7PL=[c:N $pgnWA'4$24k*NT#`6VHWCVuPL18B[X)\=S5ajDs/KL5+EJLf5U9o'U7N,bc5DBWG +;>$<9o6hIYa)=IVJm*`(eG'c;4P0%,l@]lDL'SPAu#uMEo`\h;C&4:0K?fI*@t\n 0c`Uu2K061G[h5f>i:frkb+QJLJ]1F)l>#gWC(,fM^r[NXak-q@1#tiV+#HgZ4smI ,!7HYi$JW&(EDG)G$g8I8flno%VG=+J+!S]P$-0M?"b>dK_?9#M.BU%EhWuH+R>sO bZs8RQ"2#1)aES:(hO/R@3h^k'%YqHVc1VZ;Ro4\8#ViM`=\]FAp^==Cm1dP7!qoC 1;7nt'10c5H;pFG*U6\o$nX)ud0b>$3W2J0-oYc>+KG]\Sl^M0<.cX\)QemH:^j!F ,*%P>,8^S%.00^.$c4AeOOC+m:dGPA^bc6,5uSIk.p9,slo5-(%Hl6UW3DETBH=9d20[7E6=U8ja'F#[XISJq5"Um_]4)ehCWE#j4`Q$%a)YlF)o/I%B 5U(l+TcG>OANP!7i3k]5\*!oLS5rh-[AUPa_FR)u!AG5m&6'Kt endstream endobj 13 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R >> >> endobj 14 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 7 0 R /Resources 16 0 R /Contents 15 0 R >> endobj 15 0 obj << /Length 1914 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdB,\ a$Z!%aE95aW0_Lg*:g6ob$b'K\Y1k7hQ^>T(M3fHF9D*u'*&FpA<,@_eLLi<@Q!)omC8Dbj- bVm#.mA'T[%@!,+\;(*ki%mbtA.8hS04cIA4/2[o)`mPMA:@)DQE%D0pjX3hmi 2\)T:_T7bB`>&SCbIFJc:bKEgQ]?0Tktjk\+Nm66K6hZ/"i12%faNL+oJ[6HPUf5V ;8DPQ;qE/Gou(>R@fm/LKj50tCAZTP"9W&K(>[Sm;"_T,d%u0M'sfrW;<)W8YLhCI mg3TY\DLs.,?,"*gN>=unjF6]4u0mV]MT(hY%fknbg[Zq'P2N;+'Sr+\Ak6QK*nRT g&l^42H=7%WLo0sY%0<#/6d#s^JtAS*,.(E+%V24K@2YlOOKb,"VrAF&8Jam+r[:. (?qK0e[RjlZ6OfHZ%i@ti%dEud,mBL'q"(85OfTKX'/>U6*9%>Q:dL^YFF4T"9Mu= *U@3Qo^chXFAh#8?u0#_7KJ`a(rh]HNu^`Qn/EKp9*C;$aO3BbLe2W'Sq3Q?PijNj 1;pTg0Q\;=dj)18Ps7tL3)D'iC"2&c:.SXRLq(pG0H#V6ccJGukYnEp=-5'\jMae6 ;pK\b=grOPSFR@-ib]5DF:TP2:'S_Zl,.f*2W:%CL=?W."*3?1AVps%'>d;f!+KE! NP;I!TY+5L!X0(Md@gPD]+<8-@jiH8UP'1h%B@oW7Ploq!88FKg::pnBjNY1YlN*j ,W>!3fcM"%KGkdR>(P5U.47P#?npjS+i)s?'J1+53rqE3I8PbGAptL.QO(sG^V]]> $r(0=2Jhe8^k0;e&39eAk^`iC3#5T5,S#YVM8(n?itK[R6ZoB2Xl;r#D=LZMYr6\ X`cQBZp,srn8j.TbVFqblCqJcS84h$5DDGiK3IUM=mGh5a*+@h=LU/t&*@,`%C1h54#'i]c@[*RP%YXog6H$;Q3Xq=/hK(0Q ed)AYNH?Qd&r-Sd#M0+GFod>##+96>S+7OFk4(u#orQRW$0ruR7@GZ;,imO&3185\ r=e=T)(&Mk5YtWQbBY($,)Y!(^k.?5-Ig8cd^D=kCrMZ2T+I'hO/0kd15C/7I*hCi 5:sP?T8!CY=Q!ncdF+-YLou>;P8mOQI7T*3d\Bd#S3Z&Sa9P/^LSt:I;Q(![)\pe& l7rf\Ei*-hUXn_hS!\")Xc0N2q=CV3N]\iKgV+b2PIuNYGM$/P]Vh-U]^~> endstream endobj 16 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 17 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 7 0 R /Resources 19 0 R /Contents 18 0 R >> endobj 18 0 obj << /Length 1081 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd&DkP^\]mSu;/Q.ZE\`8> iWF:[8JJs+`l"07ctkY<6u`4Z@72%9Je>-5aC*#Y&/gC\P.3qA.5`*i[IM?iL*7fX g>uC-31u4ATUg'-42_o*U]uce*4SC%A^?g)m;]Z(k#*mmanimUR,/Q1/W'pg+f4&R 2&o::;aX.#5sRCH9Ld2#]Z_KR),=d"Oefpq%,hd/1_bkBTTnNA$[?.sY+\,/2N/kA^+q)]#XGBm ".775_*__i'+Rn*i\A26Epsj`lm,mUY\6q/n@WZf;Y.RNMJ!rh_\h#e^Fs\N2\)Tr `%oE;["^K!%N+?%NR?t01^p-poFMLb6@oaud)bYQ8+84hSISG7kVpHm<*:S<$B)>1 UM4[E3K<'HI6:8d'D+=V_[MV<6W:p8YE"]Co(tW22+'KbUJ-P!NWXgaT;XPrk9VP7 Cmc'jBYl\W]MBcr*'Fg3[e?tYI5mo/Yl,(W-Y_jq3CBh>$OAtlF=D"T&hG[o%^t-] p=<&B5s5]F+RUqg&l_dEU(uIVMQX=5]U",SV1gGPP0;Gt]u,L[3^-?4>S%2ui5dk& j)pUu$mW/Hpl7rX`t;UB"US@lcj8e=6A&t6Yp[knH]V"5*>910Z#q!k2cPFDQeUPN de[18R,g3J".86\@L_D[D=m5[$uUIaaE^9T0LSL9 endstream endobj 19 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 20 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 7 0 R /Resources 22 0 R /Contents 21 0 R >> endobj 21 0 obj << /Length 2245 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdU!n 6AQnH],o&%c.a`YL*hg&>TjG?[8>#<%FIg2 'T8]YP`]((_nBX7[XFobg7ejN(kiGeVc\gC3.qhK@sHZ*%5dkD3ELj%G+g=Ec-(1? CcH3?.MlZeBNd^@W"]U`(rRT4\-pZeeTV28U0O$Bd)4[?42CAAp1>[rimt&_,`&,o Qk^#Q:rF)QGl>=<@1"V'PM!,TOP\8#Fkqr7ih4^X<]KcaOK+]iKp,rG=E/KFH;3.O ?3*W'nK_"DCpH2mh`b:$P,=aKDHK7U0'[R'*Toom("tmXi--<*=Sd*=m RL:UlP/8FqL6D!G0F!G3Begp>1l#TG#Wtmu3H]@iTP,o.?IQ)k>mcD7ljEq6LG$)J 7e;k!]9_dYL7;I\_UO25m5nO5K+*&35W6B7rI$a;:d$Wt3DZm7p5\gp5>f25]Mets h;dWQ"h?52&B#$KNYEsq3']2e+U4VE4n3R?nm+<4O/6Y1Mh&"2N>p15:-,ThJ_W#[ [J8Gp;5A&ZoSS@AXN\PS^/JP.TjYN>N?5">m(0'!;XO ,m"9IjT85Y`X^s\p8d5t:I@_aIR`#e0]jL6e<7t(%d@_^7sGmY#[or+a>:ZZdN4LL eICK)&/EfJ!^iF]61H)oJd<@2?47/s_"8J[TKr0Lg`VBUR>?HFKSi-a:kc_Z0Xd:? KF_e]:=&b(_9sVIiM#ga0]F)dL^NlKr*YElYRhK+ar;PWAUGo=.KZM-M2=2?%Vi@- LB`A@i>^li/4DEX@s4[(__5_d(*f6_`Z8O1a,4['#XKSn*.rKY%L%Fo6/Y$\jik=l &$`&n*2*'fK>T4dRcl`99mM4@-."FSnp&J%ftB'c cnq81Q:Ac[+S?msAM3OQQ^gCBVpKZfSPtWE:2,(j!OV0_Wo,>IH4']_W%g!fh7'j+ /<-.h2-@rh`1usV)k>Z2+Q1[0M4Oj.N(&?4ginB!lbMDN]R;Gs_I5jJT$%8Yrj,Jqmb+fr_0`\Arl,I,SfCSe[k==r%goRs>#~> endstream endobj 22 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 23 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 27 0 R /Resources 25 0 R /Contents 24 0 R >> endobj 24 0 obj << /Length 2280 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd&DkP^\]mSu;/Q.ZE\`8> iWF:[8JJs+`l"07ctkY<6u`4Z@72%9Je>-5aC*#Y&/gC\P.3qA.5`*i[IM?iL*7fX g>t[m<1e4\[_elXJf15uDDR\U@LAcYUh)NV9fRc>Ph^@@hB92m+>dR>n3S<$0-o%c CQ6AYnu1FBF>Jp"Mdh:raCmFFg]jO1J\RR1%>1VA3Y"ah^+b3A&AgGdULL1*VC@lt T+-\QatSiPE6:1$K5a(:%1;mO2iM7IWp?``!kpS._iWJD5U1p`j63S!:l8p0NK@B^3NE1BAhF6@l^T_j_arp`_oOh+"iI+. atpi>0Zc)bd83+E3MR/B1(PnNW"gW=Gg-c;+TUYq-].DETdYHl>S%/n=Q'YuJHYX0 9@GH<1s?P*q^2LZKV[]snCZHp%HCjQ1XYlfZR0oWG@`&uA. =Pgtjl6SbR`!Q>T"i7Eh'U^;30]c)ieI=u1d,LktW_C^Nm6"BENEn:LO7:@SN_rb8 lO79O^bE;B!j":SisMadYlb9dA&;<'aUPe(m//oc2;K315u;)3Y?L1=0Z6")R*-/T &=mtU$q!]<4$fn]cnLM*RXkgI6BI3N/5A=Z7DbgqbSK!A'K@qgN_&:K0*,m6_FK0X #)+Nak^O8DuT\V93%UeEZkeik--4c/2;H\u+#cfcOJ,-5Z+P.H8%Hh#D$9rrF^a3/1n?+ufbE*badZVN)/eF^nS684 -==K!3Lk9C#70%YT0CT=o_r5V._U-TQp[\73nS,jLZ1.-mPmTE>FUca\M23k=kR+c :(HdI^Y4Vk=Bi5S*-Po'eLu/gbTID(G'kOBK7&_3cTf\=:miE)aGJO=@,,AV*^N7p*:M]k4$E`:jPW[MpiBojq JVj[['%59MHVMW[SWhKN:fOI*;l_Lc*^Jr#)roKXTpRVtAS`=5!Zt.;9m`FMddGX? &9m27\uhGiajn,%5$OT4/VD9QE9V`/'/VWImUT*I' %&*H/EJg/`!eV's@<.A;_6I67GaTIZ`*/(M[P%k3U7YIao5-4@2?l^)+4nG'LJamS 8O1?)(b7XlKhsLs'St4:E-<_U)SImreeDi<3H9<00%KD;K*Z!^COA&F2$d2p!k:7m5V\"K OI542q*3%WQ3NHC),%nGER@b[Za5D:k3-NKlN(6JOPUh9W,4P'h-a(?.&S$RF&,*L 0rr5X:g4%1@LEdR+lk81@q$F<8V$0J`:+*Q3gWtUq*7;Y#+-Z]?OYo,(b$A@ULOrM ;h<.L/nleF''*DXc!7I#H=[ct7"&UE*?h?'E2G(GAu:AlYLUe.RauBg-#P-=(mfsg`Cu@#B!EY-=u>k*ZiL;J;/:iWLK'ufM\DLdBL8PZ @NeD/>7t4s,d/=i!3RjCp8n[nUY+WW#V-6'"gnqSBO 4QG1L0SZS?phQ-AN*+3)nB!Cs[0tIV%:OCE%Pj=k T>M#C/6&kS(s%fW/#[S!G#UK(qd0o5$P6AHKmTN9iZ0V@IAW^(2^.H(6](qDNgB8P Y,d+tC*IR]8i;O-9MJt`3,,RSnT@mbs*Wg)"U,==3%sK;b83ET/6&#ib':@aYF6H- OX1,`)Iui^71'LLNFg"bpiUDXah.!uJ:I~> endstream endobj 25 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 26 0 obj << /Type /Pages /Kids [ 7 0 R 27 0 R 47 0 R 67 0 R 87 0 R 106 0 R ] /Count 36 >> endobj 27 0 obj << /Type /Pages /Kids [ 23 0 R 28 0 R 31 0 R 34 0 R 37 0 R 41 0 R ] /Count 6 /Parent 26 0 R >> endobj 28 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 27 0 R /Resources 30 0 R /Contents 29 0 R >> endobj 29 0 obj << /Length 2564 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdArXKUT[9m%UO,6;2WGXs2\(B=lnXSgRn+)"j"N_Z6YKA.RLX&[Gb6Ak'C)S# !e`Zs$=aq61l`.ui;"cE"$ZSl_[+[K$mQ>919Thk>/R)7KHU\W@Cd0H5,&nN2N@m< V\DPX!4W^G69*iH$%a:s.gTPr`$Pp<'1=hk-pV/m"Y[4P((JL:q'jVlE7ZrN!XRF6 %\oCV+q1_9fG:XLTN%8;l-rTB#pU@F5R#9F__Oi(P;8b&FHc.jNU_[!<@3>E.Pd!J m:N@hn!nMeW"^__3R-RuW7+[Cg/AC&l$M?k^G8Mo3NMhsEmbG+FK2dRkh>81/+ds- 8PE&.bqpsA39iELPB>bD:6Y`E@(sNiL_*\)"X5_:,Ga2q_rGFQ3K@STc[jhk]3MnC p[hTk%TT/f>>j#$;R&W1LtJ^9pml>.e's[(%?6!j5Ve9C(t",VA!OFM9%NDIo?4RU :a%2r%JLW,eWaCpi(q/G8>KaWhE/^i<=>Es3&85aer(9^U.Jj:hcu/n4sQ%;p\#7O @=^]&UCP19D#5Jt*)N\BC*;Y"NJ\LW&6a^Pb7Oji4sVu8""o4U75ZSM>3#Cc>hd^a fMN716`WY.hm7Yp3fK9j$bkV#Ad5LuisYIpia[5RFi7dbl(s@MNeT?L"(:])Q"BQ5Z]r]f/\j1Vh6S[jHt=er]AJO6XO/4JJZ -]"a#&gV$A3>dJapsGQJBdb9V!f'j3#S3>'CIk^d];@iphjOEQ8,ZsI[V/p="*JMC Ho&->p@O1T$f<2T(m1k:44"bjsm9n'%(tN:c]eUg:nP) D+',9$bQaKWRSg>Iq`H)):ms,Q&@5McL$^Z+Q/L%$Ijr.RL?M\Z=]>J6sbG71o?X\ `(I'37H(r55NZf";+&;o-?[l`;SD2b*<- JCAs_b5aCD#8H3[F-:DN@Ep!+'^1n%#^&c@fHDV=_9*=($\>Q%,G]_X&iaQ7b_[`2 $T'GeJ=r"PCeY$99j@lk>bJs<.0cH`'GY95*5P0O$@sV<+=UQI&2FMW7=ah.kq%(@ MA(`'ec5gf$@G3&h@EAQ$jN+,#T^ErASa?Q*tl;k%uS(iC^KPfONcK)g4XUpKd=tL ]bXu($B(+N)c&>@'LN7r8`>rN0P)LKQWhBekN&^sH#j2k!,.tJ[';/O"AkHuOsg@9 .A)tP10-D+=YL&@3sUpciD`Wl8AQRK:uoD?j'R,3BG:VKqOpWql[>UdT/m3?(QA0` -njC6Zfp2?gaaPL!R*F)-Z;]qWn)cE.Z)DM#&WCoTN6/G.68aje1&$D7J(m&27_Q` Vhf4"1g&1+7,AbJY]"?nJUer?UfZ(Ympg8GCdM:Us3G2aBef^Fd-CjD)g*/0mMrF< A(\XkV^ M83#AmdpkX-XOc>42)h'%W#TNBkb8/gQe'jhu^2iM&mKs87s$;"NQP1kn1L"R1p9I Cms\8*j:l-W.Sh*%$468X=!/g&pq]<0rC3M77n/a\7H0b65[-e#oJ=BMYR<5m4)")e=DfOId,B4B11m? /iNE&d]q"_@2<&aZYU@WFWrP^)_i;uom_6ogbT*7&N@+.9$2d4*\6e-UU7H endstream endobj 30 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 31 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 27 0 R /Resources 33 0 R /Contents 32 0 R >> endobj 32 0 obj << /Length 2932 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdKL?#H34,\a)ifhn Ei@cc'e($?jhcU@Qp>pUSO=4Q.?Hf32k6r36U?C\+G8ls+p4Nlc?$6%9nl$7bWp51 REXelRF?#KT]Pbh%2$h,E][@H4*mTjfj?]YN?[,q5[#lY7;eiP[0K4)Yo*tVlL9>P F:]:4gp1?NS4BhuZeJ[`T5ECB*I1tr!PmQl%ZWM6/d2?%bVM5rTXY]DK6u&G\k_Sn ]F'3f?C;(M"&q4dO:]L+\S^Qk3fIRAlHqsmZS<,H!uTBDNT6Ls(BafOc87#1@nQA\ $PZYX[(uk.&.\6bW2"pJWa7Hk,El6]2UV>QNG&/m(*BbEso<(X#s56c\CDPeU8 ;A^g5W"S[107[_FV]l//>T+o%6C`o[[4#@=.?2UE)[K_HMj5'b[jo,B$mQn9o;HLckm'SS;Il4+C!q8OW=H8=m4'8G I%Vl[,B"BYXdgLhNWT\djgmFrfeU&J6DuWR0,&dk/)r>@1(E9URe%!BOQ89`C>m5nY. *,X[@;Ab)g!8'-;];/]\2f5eA4:Eqmfn;L`'/cN?D&dc2q#c4kTkbZ$>+MRjru*h= >aB$,]/qKK.a0B;N&`i?&qR%m^J$L46^ieg0b2>FC(N2)4%%#%$/!TDK1i+$kW#Rt 2K3\r'?NM]jHmLP4Q-_b*>6&"bH`1K9FpOQbei"P7hL;"+ecqbZ4%^#R&F?F9>$.nFX"8 2@eeVap^SKjFRV-Co'8f8Vqk<0W5i(N0,b!L@@(+WW$cVOeFoCq;%7PGp#@eS,oX7 Lan*fW>.1eF:,-NX[!f+$^kB,Ud81bEXkJPcja'6pk#RNiin^l?s)_@@?G"K%)Jl" "i/d//L=Z;S:_5u=ohg:^KM?F>\:^LVN\M*XY6#KD Q^Om\ja\4!G?eTXgAn#X&^OLDmd9it[3GiL;d:6.#U&sC\a2u[L7^"'U4f1)1Umk>:^?AK( ;e7hdC;"Y4c/]&HZP2F9aJ>>%BN_cYac[a9VAQj>rLIB:AVceV!1PY\AIS&?MP/nF FUWsI"W!2nRHn6!@'#ZeV0&CX;ke1>_C,gYeM2eOeD_k!nh]6X2&`5Y,-QqXF8+'- ,c.RP%&#=/W%Oq;&('/KZ>Us#55)PrEL\%%$Xsn(A6IH]nB>I^[O%>$o*"^0mJAl9Z*7MnpgC+l0W V2gH.<<^35)?^VkQ;aY'EFI7J5?!mp-@V50!4AsV3EU$mV#_"1h^ds&m3q,EaNbH! ,*VNC#Vse7%-MmbLRJq"Fp+,ZZdsjN[fMBr'a$T/O[iTeKT6.d?U"/oW:7mBTaCgK[)Xl@QUb*%KI]7FhED`$`@8t=R2VUXn'<&FlM(A`-Q Q!eL1o`0Vs&88L%._*1-2-m92#?+/^:TCH2WKO8L#KNCTAW&Qq]E],5F)!_g2Zu!_d endstream endobj 33 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 34 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 27 0 R /Resources 36 0 R /Contents 35 0 R >> endobj 35 0 obj << /Length 1051 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdW6X 5h6E4>e9Yd_ja^8$nsNo*Piscb6((*"#S:iZCF&"1jDM&_DJgLmeakfLa-0=gP:V9(2N%:&2q[,W`r(i*jbc&]bp2/>.(5X*QcZ@. m<&gk`*6b8j)b`mjLI8!PdW6i^?ND9]?::`c'!b8KFRl]B3]--,FqEP`)3q2RYTf1+ZPO)[4UWA.^CARp.lqcHYc^qs=k_=%lf) jpC$jB@30(4s[u!&N1RfdOmi^FhC endstream endobj 36 0 obj << /ProcSet [ /PDF /Text ] /Font << /F5 5 0 R /F6 6 0 R >> >> endobj 37 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 27 0 R /Resources 39 0 R /Contents 38 0 R >> endobj 38 0 obj << /Length 545 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd$'>':#e0uCjd!WeUqC,E>W,B;+Ka(5UA m:@Ls]IP1*1h\476_*Fe5+k`NJceTg5\)^BN@%"APF-5K)oMtK^M)Sj3ZQ,U%%^K3 cSD$7B-U!+dNHr"U"X:L\7.4K8*V:WeoNsW+\j!9d\03>C)7)u>P+89bNN-GKu(98 LBXB->`,\fj#52hn-Wdi3Q%VD&.E&njKFR'1d!!6;%gV8Z];iLX$e1!Je0EL?pHW/ .\Pob7,nR-'tj^U3YnonTN%8;l-SMS%V%c\3fT0(juSM)KM9Q=Nn].BN^3['3=Y!k l7oq*C5rLSW#>^~> endstream endobj 39 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F8 40 0 R >> >> endobj 40 0 obj << /Type /Font /Subtype /Type1 /Name /F8 /FirstChar 32 /LastChar 255 /Widths [ 250 333 420 500 500 833 778 214 333 333 500 675 250 333 250 278 500 500 500 500 500 500 500 500 500 500 333 333 675 675 675 500 920 611 611 667 722 611 611 722 722 333 444 667 556 833 667 722 611 722 611 500 556 722 611 833 611 556 556 389 278 389 422 500 333 500 500 444 500 444 278 500 500 278 278 444 278 722 500 500 500 500 389 389 278 500 444 667 444 444 389 400 275 400 541 250 611 611 667 611 667 722 722 500 500 500 500 500 500 444 444 444 444 444 278 278 278 278 500 500 500 500 500 500 500 500 500 500 500 400 500 500 500 350 523 500 760 760 980 333 333 0 889 722 0 675 0 0 500 500 0 0 0 0 0 276 310 0 667 500 500 389 675 0 500 0 0 500 500 889 250 611 611 722 944 667 500 889 556 556 333 333 675 0 444 556 167 500 333 333 500 500 500 250 333 556 1000 611 611 611 611 611 333 333 333 333 722 722 0 722 722 722 722 278 333 333 333 333 333 333 333 333 333 333 ] /Encoding /MacRomanEncoding /BaseFont /Times-Italic >> endobj 41 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 27 0 R /Resources 43 0 R /Contents 42 0 R >> endobj 42 0 obj << /Length 1333 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdUUdkbpM/KJAQ1Z#RL]6_1]o*5(d=i^F@I$)Ao^"N"?OSLo>&.UF=k@ abC)T:@s9L]A/a1UN"j@NanGAdgZC-j?&>GR_C^;_^NU2.`5bF3fUC"ILm"3Yj2i' $D9XF%1u,r1(E7`BJPRbU[6fi&)cC%`%c@[eL:cS>/m;:JmSJTO9f)&$\:XK_?IeE `kY,4#iM7<&s2<9KFoX%1s:rLlPtnHf]EtXJP6:!!NgAbgpi3ACtpc5@sm2si+n)# KE8#b`h;`]P(TJ=A";a*BIfsr5jl5!m5p2G.7f2R88.OgW;Ff$ZLFRuP9o3*hDUAr @++q2@k7h9U!PV4"Mo%o+rI(4AR`8( ?8dh=S2J='!c+]Z!"TgC#TT6=2LCan>3ieH/1DjgA8PS&& 2XSqSc%`$76qmIEm6%;1%RV9)2[n^t`e04*Ai_j\F.c_7m-uC[R OCSqj_q.-9jHu,dC._DdmgUq@(bS:,#Qo_$-qP764Hu<:;FA\WLl(esQ0d$>ZhJqu 'KWo$rr>>P(3PI]+MGh.).*DOJY^Y^.@3MR_id/N:;O?C-7G@IJ9)4"XbNXHm_dng +aYd`eW;Nf~> endstream endobj 43 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F8 40 0 R >> >> endobj 44 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 47 0 R /Resources 46 0 R /Contents 45 0 R >> endobj 45 0 obj << /Length 2318 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdbC+ukYTV>R(*g66 iPsB@FB1@1bK?T*CllkZ$C=FCU'"!25[cTm%Aje%IXGtt)pV_FTX[6Yka`/&d/f?c @lg)QVW4M/_B@JCU"_;\A9%@Ae9E2=c;n9;[H]gNfc`mr(`=P/bCI\=%N$*9KaqU_ 1(:/Q]J9TlT\^4g9)II*N2)3ooo*Y\hD5X@'ME1:>KQLE8t2E5NlZcV-CO6N]%'2iXbp1_9u=n>RXlP$=qHa('YV3K@T? `9]=;ReeK;:\`Bq4FrSn'e;%-Ic`[D^t^`Y6#ugX7S)$BQ\[FiB`LMQ.*ehQ_n:(J 3UViD0h88@iq^BlTP#'qX.)Ukoj5uB\lHigYZ8[)n3cn4k^#!](B?8;sAF&qZ=2,Y#[dp]1m VcGKT;Aq"8Z9I?&!oQpKg\#%-:8-8ipM-l\OGRm>JT,HehU#dQi.bq"OtHnjn:KlR /u=ag>8G^F3=d85`hErq%2h8:cI9:s\g%L!2ib/t`38.&X5#_=TkJ0W#2u(^)6UIo Nm37[\B6]=NdI=$]oj[a%HQr(<][8+Q5oIo,7OMO:^gel)U!$DGW4%!h/+ILK93r0 %1o%Y0of0f^S9gLHGglWk9pE6TW+qEfu`O4uMN1CJAF,hW2"V_e\^/PdQtSQfod2[gcl)nsOEbi(e=-4N:< eKpJKd:7;<8(&fMED#Iq*D>Ko$PkAg@mb:81W\:SfrTXDqVR`FBhS5NPlP]3Vei$3 !-HCMToF'Z8O8uW#'!eV/j]RF8t=gIN1`-^0jre(,]tMrJH=JAU"'\tKcG>pU\/uS @.+6;'[f@R<^^XMf0FJ+;IEH#:drn?'nZ]'6;0QrLrbMW+Af\-;7m2D+V^ZT)W'H; =U%/K-H%$)$ViB3Lu=pE"A$*3U0&k$W3O$fK>@ngn1oe3#uWM;Jln]B-4WbWneGR# (dlEj8kDnq*Iu"o!pn2=k&s"@Lts-LPT%>a4/*Iq=>f"o^Y Wj2b[Fc*tGWW#q<`\F49QFPo%dX('nj:_5UOUh"rBX+Ea7`YdX,D53KiQ2BGnC\iB b+qXD<^n=E-r4B\LJ%+57\D+%p84pT%5JE<87UBnB-SXbcE^?j,-sRH3RnU*bq2Pu b:AUkYs:q_GaKd8%-B[],uWe<,EU5)_fW^CZqM\oZ5E^;)R,u-2J"$LR17>4Tj1:8 hU8tq38;QjJKWUB_ks=(l!i4UdoHdk;aVNXuffTq[78XZpkKMY(:Lnf0Ou)0dP;lS\ C!0A/D,Lm>.-F]oTfSts37'Ka@N,n!+Ul/<$7(cUTO:+cY7NH1!fJP!_5!*k-e8d% @*BLN!]s]7VDs>lb_b:U5+uZWI8JM[[S68IDGf@\SFguH(p!BZ#(;4I'SftqeMN^[ U:H"hI5r2rCogW6*Qo`a;VZ<4%Bu!h.&^$W%sW;O;K/dn8u45g0JYSbiGsO,Z?/Hc 9.3<(/r(\,FC$1%AYj+mOQuCr?9)?uQJl\c96S9VlW4ScWO0%P134&]'FnuBZV\G& 'VAp8]5duV^O-g=dopUeCuM$lobrq:E-A""89)!&[K'Q=PT4S9:@li9*Eu@,,0W'0PHRos/*dCP+Mq6YF-l5Z9rT_aMai (FEhU3$M#?E-!BrY!g.&3r!'G>.+kU^8Q1>D4^F,OVuCgW`nVi&O&HMBV,jc$925E +Tii0~> endstream endobj 46 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F8 40 0 R >> >> endobj 47 0 obj << /Type /Pages /Kids [ 44 0 R 48 0 R 51 0 R 54 0 R 58 0 R 61 0 R ] /Count 6 /Parent 26 0 R >> endobj 48 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 47 0 R /Resources 50 0 R /Contents 49 0 R >> endobj 49 0 obj << /Length 4218 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd1*"Po (=A^1>VV,+N1eV/JDDE*Anci!=>ok1(Kk^]kj@eS1l@:^_?4CpBf,ZVObUV4b\=-.U]2>?SF`olQVe9r&h/IN8D>s"; al2^`r?"fkZd4b*#")7RL7C.A$q8&pFEcjb]$J"ZrMe%WrrApq=BtbVn1\VX2=O92 (qH^]kkd\?Hn+s+X@d4I/tp2k$U_-3o_q#AK+RmQcbrTXYX-J5)8kukZG]4ObSZ#K d?F;5_NDFPNCXW9LDIf6e417pO`UmD`/qcO"\2u'rMn+]d^V5k'SH::mkABi3@(jDdIbO#500e[8B.:-Va#hca!P D)GJ9Z@&i>=0Y1g@**E]:F*kV'cCORFlXDd;R2Ml@963V15N*3X#j%QX&NM(^@q4/ 6!RG-Cpcr7A6X.Z1M,5E;V+%?-jh)3"G.c;655/2!N9KH>5.5kVZoG_!C!Jk`:Ap6 WSIH2aQTgaj,h>;N`_Z7O%O[)khn*3*]Bg)a[/sm0-YYb1CdnCkR\3`1*/Ic!Y=Q4%&4;^T]:s2k_b(fN4']qR1N1;YH.F:^p%^D5f>BN@iWPm%$N*_4uL(W 5@IOpYm@>4f5"gh.+KJNG&]`r1j>Xa+^TE-4Y>ObG->K>:Gr%^kY5(\nG91LX:[R= SPK76LV.O_R$p EYB=ui:u7N!dnVHPat7Yi*i=[GECo25bo)';DZ7Hk\]BK2p2[AX?(XME/Yl8@^k1( :5Pi2@\]8!7AL^*?MXb#E?6X)3+Rf(H1bRJg$:80>oM96BoVQ[.js=0 $3:125>g8R`ea!POMl1"%lns>EXG+ej*kA=7DV$+*"CdZ2+/]#WV>8S5oR-O3K%(n(P/CI&LgR39Ki"*ehs+FkYji92/5 87`9J$)1#S4Z=_[i1*`/F&+\>a]Uaq-ZdFMc<8IQ_2PRi1Nn2l)>cR0*2R,!bS^Ka jQDtq<,0lqb6Cf4e_9r-RDm='QEm%DW660&:6<=6k>4X=;?1j"\B:4_*`?7llq,9k NiCX7SnXW<3AENS;j4O_d*[&?lQk&OQSsWH%6C@=etLTkF\W^BAi^9BVlW;Z9on+, 7b&3oW;@X4264-Ag*itOKaYN]Y3F*8<+Nf@ ht:N_&,*V9]"Y%VRf9M_qO4+(n3Uj^7BtD6]$pO(?>LpRW:fl'+,Z/e/,t*$YZ@/RfS\!KPp2j'<8?=jq/h;]YQ]_`eFU#o>>@DRgl4^`%HLRd^96Y;S d_.Ss[od/P`Gc-JW"@n<>AMmmfsln%[qt QS1M__Itk:ht5WVC\@$f\:VV,r#8QJNZ:4q'S4>JoKqQek22;2GjT2hH*(?m%Kh9OZ,$fc,-_d%]+VnJdN^PF,M"ia'*%JK-)LS I0?&3)L>3R6=:3:,rKe7`Q?/$A5&OGFs+k`;MP<_FRf>7kfsKL%4Zq:)>h5Te;PW# ;@N%\q?JEe:?c!YXS$XPe)]7cCRmQsFYX\1>,:uP!\:48aTmV/X(#nEEsmpBc^-^4 =!.s1ZI>DQ+j,![O[VR:p=XdEHS\PiQ#3EX-o?_Uqb$:T:T?;'gl;1eGj[/sFo_S5 1VeMtOV!aF6W"F*ja[mFcp/B&5+ZW-FGL,qUU>nAlIbu(AF="1i\W_>MHZ<*k]jlrLX^h'NGM]nKG^s%;Q6 ?^l0:btn2K$d129SAqKaJ02OFlYII.e,IhLFd=0=mK^5Pk1>*0POkZT)"MeY$/@;X Eq.kmSsta3i-U6&J_f>Gpj<7fHg`u`IE^Oi"qBGcY:%LPn&"]=,N1^kQ1A49WHkYW @!W?^lb;Q#F@Vf'3/u&lp[PerP).k2':pKUdt'<#NY[J N,Ss+1QU2fYIsoa&q2M!N\@>\DuB_*VL,MDr[]1U$m^Gg$Dn2f"&"Z^-N\s:o`Ql' ]9#S;&2Jh+5-1\@[#=:'coA)H0#25n"A<5-j4;-s;Zs<[KhP7VBO\4s2[0h8J^%`8 n8AEUBH\cVJda]DOF4Dbre7Rs[]i*q0MMjI._fq+\Ib/:Y`f5a%g^62#OohAcr153 k[)J:De8X^-2`m7+H`VNE$/a\+uYUcI!-kS"pn6Tm.(UEp)bfa.De7u0iB69 =E0=I(#Vq)0kLP#CDQ:OT94<9iYWmi)BmM"!Naj=.=.Fj:]pd."VI">!`2(CaC]Ej (t#Jl`kZ)9$Q"pd6"dMDZ(bq`f,CN($\2+X!6-F;jp`Hq%Y3]9JAQEd=q!lB/+O=u $7K\A^(&2cRfMNDJB6,'>*`o4/VN\9.,LA+p]LT>SuL0^n_+Ep>R[91/5e;[dQ3jE Z315V%%u5]JEJ&l`!jH<%=m(?i9h#Ha93,(";2nnT]4:*g^J#oCI96hE[rModjOM4 )MVT;dTW13?RTrQ..WQNEgn6qBe'C2Rn=r@nolG?]V=Q%*td-/%/M.LH8>ZK_^G >p.8AG>[TT_A$V?W!rr]/)]+kZTsumS8.S21Zkh`-GJB"OWkuY-??L0d[UcpQ?"+f CU8(>o0<`b_@FGs-mMi71I3\#9-koR*iVRZ.DuD=VIL4_&$_oVi2-pfBFQD^^d..0?FVnp$ZZA.F-3a[!:I&h>-`?umGg F!V"p6=Da''[.[bCQk7t-@VK%HR8<"7T+R\o<\muW!Q$h3YDZJia"t7F#($H#'=%* jj-C?^IRj\bi3b91l"Vp)bF",O!%U1oUeSEV'cSQ-UtHQbk.Gm`!%uO8:h3eEB;8q OI?jPc@YW3bi'N'q"\q24449NPT endstream endobj 50 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F8 40 0 R >> >> endobj 51 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 47 0 R /Resources 53 0 R /Contents 52 0 R >> endobj 52 0 obj << /Length 2690 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd1;lVX ;OH6]>AKKnjS8OAb\miT(<$D$ajA1OM$lct&Pe-l'UTdhj_D'O^/$O&Nb24dM!:oN :Q:Y2I`H&B&/PKb5llm*T.UI=)pnlI=08ZoNTVp3id>;q)$Rm?ktmTR)VZk(&Th(> WTTa._?3P`b'9Hoo>XO>^PZSYnL/tk#_;@-^4R!RY)Iu=j?/38WkL4!A!9)9."`XG ^5@J>?E+Fm"'G4RmIP#k%3o95lAhtH`35+EE'Q1=_^nbG%\mi83SKL3UOa,?,J-q\4cr[6*4K\t=Vj;T1Haqp@n_8+X7.P$0Y;'\ r.D3rBOZk'l%m_1J"5cZA;k5UGCq5e`niFOIRP1Q"Apc`EB+a>1uV?C7U0 bHNa%$i1H..4IeqLK38J2"ia:m2l2(&KG&L%YMhg"qAJ>g7\u&bF)T^P8l@m^4Su? cjn?SC+;^?"dYd\cJuT%#(^g;n[tf#@9Q4DIAelZUP"+;9>cC*2VI+@kV1[Es?huRXcu"/5g?H(2IR'uro5fZ".:UtR'n\2 h!N%g2RF<,IuX_7LiTD&FTd^R\o%M>3=M-Qj##4Y/N)6Vkf@6s1#:AkU9aVYd')t,90^cl +U0>Q`3'5<&.X.!%IO^Mi7p(T%UY&]34=pI0T>P#S2[e"We0No".K'S*#"J=!P/+` ;ulh+,#0NETotsfZEdA\`I/oN1no-0Pa=o`Ag*-NXS0MW9lZg9N9rDE;I9KqbSgp= 3;FRPV"S-K8RgV.Y:.a1NXL5.7bS\Tn]Gb@*dYl Rj+NP)Q.a$6Cs(O:ke!?['O/hK;6IqWaN_5+@'.(PZUt%$!@oDllU!/6a@1:"0K,W/_L.ZIt^4]Ag*(^]#/>n_b K4NsLAC2,X^bdRZ6:;F7;=#JM.#?k!HN1amn&"CIPMQQdni_o_!/;l/Jt4N)?&OqJm/'oT_3$JmFcPka:Tbf##`&R%=s Nn+X7@U)PMA-Q#?$U[kO-a.4[#BdtEi5&b)J@:7WWSJB8r=T:]+L*spVYT4#\5D5@ 73OW_OJa$f7L7+)5'e=kdAU,(7ll2f"/<;b"WfSb&Z_FC5`Qm9@k*`=BdGI3*_FkB "Ufhl">g>lg=b&(7qO$DG?kl5PHc8@bf#,fC:mbEC/;*NQdt*-(gLV!9i7&7f%GlI t/=L3auXE01;&9)e@CloKg -*0r^eGD0*bkTSkJ5D+LLIVYGSYJ!,)5o9/"i/eUd'c3fN;N]'TpR`&D52 NA7.hCA\QQ5=V=`l(YEO(di'?%&JdZX_"g$hL.:2&==.9.OKZ%+i+<_9I'c1U_OA1 +EL1&Y1Qrr64cl6XXa#k0umX)@YJTVak6*llK%AkTm<[!l2OhO?!e"#M(WMuh:Mop @*_2Y5)Vo8:4+s7Tj-Re+mJBe@*/tOqXc"M&L('2?:$sA+:(8D.KiZG;VP]EBW`0l ;G&9;U8(*f6l=A>LRI=-#U)JGpl5YRAFY60Xg*\E7]U%Mh+=JB-`$kg%A#o_;9JiEd d=ZIIJ&)>mefi6X!XK_Po84$b#(:.*,/6n&;*hdk0JLjj:sdkqAe%+[q%PO=S#ogT 671@?4BMla$EPK)7AG(jbVO2k\l6-%!Zmj@fS["1<5V8/Ms)@`(sc.oA@_1V:R588 "!d/:3RI=V4RJ>M8NS`u_#s/C^V\pX1,KdtMt*9mDE.oA\528HS-k)_H("@UMhj3m g2Ea,CGc;e1:u]rC0R&mTF))NAqJW**aJT endstream endobj 53 0 obj << /ProcSet [ /PDF /Text ] /Font << /F5 5 0 R /F6 6 0 R /F8 40 0 R >> >> endobj 54 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 47 0 R /Resources 56 0 R /Contents 55 0 R >> endobj 55 0 obj << /Length 2673 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd1;lVX ;OH6]>BQ3#jS8OAc8R?i&5C3-!kjOc@_&6eV5D&C^LN/pWY ^5@J<)Ti6bQu7U6%W]9B%\m^Uq[MR>5VWd%,UCni_deH$_2g-K(T9$0+S?1rX4%Rg 0KY*N3=6GBkdk`///1Mb;lV4)_db[#/+/Ldo=ZklhE/8F36F9T&"CMm[r(HL2o^q) 4YOI-j9ddSP.V_E8C4skbTSDTlU!] WQY];m%[4B?N2PE@)=Z_c&D5OVdit[:VeER_VA#SJ^<`4&L2G3F/gB1E-71OtR($O:,[.Q"G 1I0D+g97lC0]qYVf.CkoNNK/%YmDXg_D@.F]tif++[lmihRC#;-9bp$SO5Pk"EZ]D XPi8HVm(MC0YKBkm5NP;pnf;CifL,(LdS6L1u?j%dcOno3#`4$p:_2A2*nH[-%lAc X$8l0(uHEsqOD\>tNT1e) Z!Zr2_YUr/B]i6$mK1,SNamL`a:=SHKVDb!ri*\i/Q-&GVQ!2,i:q'Vn"e_uc7@YM nJo5;-emj-7u5ZTUQr^I9.lR1_i#6b-4a" Q.)/@FKCIB/2Louh3K?H:4mVi87`>HL!Zb2J8>d9#Bg(hpV9c=BgY`'R5;?t*/3Ad &m5Ob\J!=s6_ABH)8m/Nd`c5_]Kj[?*e*lIc*.H2:I%+@LJ7&%Vf_jNS7MhWGu2h[8h 1o69CjJn/u_'C5O)Bk6HXY`c*W8JlAG:\>Dq861;1h'=g.i"-%Y'KKG%E*kmdsVr>l\YJHL3>5kRmC/gfcf 'B1W4C)KGdP@"u+':4RK=_#h.knq:$=u`FU3ORmXC#.EU%LMr24bM2keX^PJCM^/S F`OYNlV)\Z*D'*@+q0?>,9[1h`*5KIG(H&V,Et#%"%ppp!JiO7APEG/d_AuJJ9_WC Of21r=CGcP[0-KN)hThMCTZE)10-F._*Els%MaA1i.5_q<=F=]TP6D5SH'cYAE$it k>:b))PYtK_Cdm\@>V3NbufM>,7;Ud!4CCnH2LZ>FiXtM.,Q+MULf'<_HQ3c_qFWa $rc^k1P\6aSl*g>4r6`N:B 33>0]36'R=;J4"H[5;B[ML-Ol73H$WOTTXHG/uX$[D975&Q@>>n[hZ6)euNKIFl@S%!oX)OOq`fZmSX?B#Xb?3CDS -Y(,IoEk'c";cIDm4aN`U,cP"pG/*I^gp0Hh&EHGD!qU*`O8,d\_Ni9fE5$p4mh@c "UQ``8)RoI6+AcrZXfES2#pI1ZO3T$9KDP.i.R6HA5e''0q53JHNRk9A;ggLE8A+E -6R9fM>7f*!6fB_CAg6W*kP,NHu[KV8ij7?+Yc>V<=8\PFbeYhL?e?\Es?)Y/nM.F j#OM">:RDpc?H!:o/PkVG>BYT305ls~> endstream endobj 56 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F8 40 0 R /F10 57 0 R >> >> endobj 57 0 obj << /Type /Font /Subtype /Type1 /Name /F10 /FirstChar 32 /LastChar 255 /Widths [ 250 389 555 500 500 833 778 278 333 333 500 570 250 333 250 278 500 500 500 500 500 500 500 500 500 500 333 333 570 570 570 500 832 667 667 667 722 667 667 722 778 389 500 667 611 889 722 722 611 722 667 556 611 722 667 889 667 611 611 333 278 333 570 500 333 500 500 444 500 444 333 500 556 278 278 500 278 778 556 500 500 500 389 389 278 556 444 667 500 444 389 348 220 348 570 250 667 667 667 667 722 722 722 500 500 500 500 500 500 444 444 444 444 444 278 278 278 278 556 500 500 500 500 500 556 556 556 556 500 400 500 500 500 350 500 500 747 747 1000 333 333 0 944 722 0 570 0 0 500 576 0 0 0 0 0 266 300 0 722 500 500 389 606 0 500 0 0 500 500 1000 250 667 667 722 944 722 500 1000 500 500 333 333 570 0 444 611 167 500 333 333 556 556 500 250 333 500 1000 667 667 667 667 667 389 389 389 389 722 722 0 722 722 722 722 278 333 333 333 333 333 333 333 333 333 333 ] /Encoding /MacRomanEncoding /BaseFont /Times-BoldItalic >> endobj 58 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 47 0 R /Resources 60 0 R /Contents 59 0 R >> endobj 59 0 obj << /Length 2286 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd#,^Fu#Ni"I!PQc=K#n@'o8+1h25q mDKi(9dj9^NWX=mW8:`(1E43-_Lcd?mN=P/-p7Z$I,Ck_d$mr#?6VC<44X/r,f0(>agbK!7ZE-*q!oQuA3cBQ2q*']5ZL4`l8G"[V&CAEUN^G'#kWgoMbW!2?*#.;(:hYM9NbqCeYIfXeGh4J+T^nW`D%OY6 [-^K(1V4mUAuc?@&E-:mK:p@&c/W?mYscu)bWI1W7(9+&-[V&fLl+# 8D+U@@19frf\Gs*j!2N$6SUa=)Et?'jt_n5@.Ru+Ml9J;][b)_<&IO;BK.g.F42Li ""Z8c6a6>NrCjF0nTcU@`I-#W/*MkAN[YNWZFB$WC`&n?DFTMr^hBk^%ET5RMI`C\ C:+0FTPPRHLlPYn$6X'd^G#pk(`96nJ7c59BL85HB$6W"auCgG6AQnI(gsp.+rs`E 2F936UGgB6+PeuEaE!apCkEuM34='?BX;,4K(W"W4EkjUC*;^oWfK^742<]^i&RhF d)7h06XA^d%e\X@>gT_!#j4UZE5FRE&1W64W9G#j"ss%1USdQk2)5+1 F^_Mg!=\W@Ps6]$ZWQ;hc43,e<^B'#M)m8Ybrfp*lPQ1Kb)de^j#m8LjCpcbF(Qs0r.Ig5,l2M2JK!JiU`NO?;U#S\&rJZ]/) -lN2,AdSS']*?2Q&0`?:9nG\a"X.t;"K+Yo9<9NKMC^O"Y]:3sHnbt:+=DjcW=InN %9>0I*5J4D'p2Y'#=\9lK&C7cdNKN3c"X\X0P7FPCa^J=`Zl,#:_hg58If0C*/=.i n5gQT<2>LGWH1bie[_Cu.e.&Q"T[se\7Ks)I+F('d];?f4pFl10KM(]$3Osa%B--T UURbDPA8$\@BJ]`8$l@8Ec,WU=N-EO?4F,=0;];-,i[mKMsR;`'c>/89\_$> 0sDFGP#Mbi'Vg[cdGMudFWOhIHW#EK90OQ7+]N+tOE,b;8f*0@A69t:Z9B[..4Hnd l4&q4DH=pU#q-A,l#JO*>_J?7FB.DhRcgJ,PG31X1a\X5/q)U/Y[16Q/g SMS+m6lGb/BIs\j&;J9S#`D=[NAdFe!+ZOpa]U@ZQ)g'-9%=jH%\SnlqR.l%#2:j< 0ZG(M"#AsE,:!V_lUV]m7"E5'1DN"9_0?/cClV`dZTQ?XGWZVMS*Q3@UU5HDYm)R[7GM[a$nJ;q"sEK7OXl"m+e1@o7J/cV\8R\9/PLa!WZ@:n W<1Vg!XXRCFUu9Jh*@sT8@:Uhj4k0RH`EEm:WFcGWq,84X!~> endstream endobj 60 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 61 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 47 0 R /Resources 63 0 R /Contents 62 0 R >> endobj 62 0 obj << /Length 2020 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd1;lVX ;OH6]>BQ3#jS8OAc8R?i'^'Qf-5]![2k5O*&HS!9F"s2E-OetD5]8Fa$)[;>o[7;+ #RL`70pp['f+?3hdt,\;F.o(Wf?n9Y?%X%$"eupbbSuc`%*%e9EGSKT)i_jH!U-E9 3?*h_"qqlUKPau5&r#RVI,3YsO.5#jQkdk2C.]>DpK.QF:mp"N?^+bI'jKn4]8huEZ#gtU@ DH`'_7&\h6'F9R(8GY<+0]qFPUBod;%?8!\U()ar5WTA5!9>hoE!o&j\)TN`1.]d% Ji4'tHoBA!.@WNUNIrJ:0Y##LcN!>0u!8N\^Lo#P3#-S:t)@"$hR#'DY@%mju9Cp1lL+73d )25o$mr5#BW+m:V93b5PUZi+.P##tSr9OV@*@;I-6[8&[NQoY"2@cKqgc;R:.fb8W 9I_1$KFC3](?2 [js?;!8o\p'YJaG6>\9h1>qRoVV&`\,jabVPKb6;7=dkkS*$aK]$TPRV-'q.'1Mi! %.em#3#0S@]f5a*m\U1T5e%3%2@L$_u`4h2dI FtZ#+mdR3;]YTD;nHAs],A;`2!4]]g@;q;\"Yf:-767\D*'C,p0QJ-;I&_-2$0%&F f^#J6`.i7M`U@HJQJK.q=g(pOEul8eAok9+m%S#p_H aJQM72IO_/T6)K,&1;T9Fe(Un"g5!C3qm+MdBg9*&6[jHo.KL'"2];;]\&iG#f2Rc >(2E]0btn[$]qW&IZFUi]Jk^a=kc)0N9V_'5hu1#]"%jAm"d2mT!266`F[49ALtV8 Z_:f8J7K[8/Rf2X(:Q26@l!Rc*UQ"/8ji +fmUUk-fqG8RH*`d@H"GkT*DS/ljP9"+gXSB\<4UBkcMk'*-D$+-E3+7f:i01P%EK Gub>iaIoiCbjiSa+G#B*/&=%a2@[n%Dls"cL2bQ]RNl#iYnRm0d+HssCG'mK&osj; EnP"[B9`q)n\<*!g_bm)0`]_d[q4k"Qf4hOoJ#?[:u$F?6Juh2Y)NMbD#p8N,pj1o aZ2mm%SCgrQ*,DZ6"k*!?MTja7s(+ZKK0OuA$-/V_:_4HHCiUMGm_Mm3gt@;=r1r; 81.SD-f_j+:FnS7+J98KjK]Ui'akMM":u+fCY'gnMZ7aY&]WksBn 6OHjne,[mtU501EQuNV8d1/N/)cfRQgdMZC3KpG4QAPu9RanRY endstream endobj 63 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F8 40 0 R >> >> endobj 64 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 67 0 R /Resources 66 0 R /Contents 65 0 R >> endobj 65 0 obj << /Length 1883 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd':#e0o0RQ(gLb,DfWsPEjCg\DGRSU %-)Bfgq^d>'T5F*6+G-84V/$W&Q&RLoJ%JHM$:^dUq0MO8@VNqUS>&C<@r6V-jD%Z7RgLc]:Ml4aDq!9Q&; id=obNR<\)l3%23dQ&[oE*UeokX$!"0TJd9`1[TD)B+ASRSk%^/\kc<-0!,acSPtI MPIB"/=t=u8:sb"b36E7NO"MLC5!r$qgRJ5DC^b.]V,Hr(ks4JV!:,2b\.j*MlXS] N3=2D5J7S+f_7GpbN/Le&&\&C^[MX:!l4'Lq_>pn1a*/lK6/[R6Vu^/+:RcJSJ"1[ !8/&;6aQQ]=&_T87>9-pUFuj._I"NZ"!!Q>Lo;0MOYuJ\k*h#QW*="i&jR?cKEH6_ Os62A%ZDPSUKjZ?2Y%\5JZ'4Z^eJ6pS;9g(U/lpraIkeW-J.fJP,'LX8M<_n1M"Z_ BMr(L$%mPT#=$Q<0!C(g#f@u`86jf'c-9]I)@!9nD)u*BUMZ7)/_XMSNT4^2,S"hH aT"K)kf9(QD!#RA6Xr0k11NTOJQG'Ud-2;S\[nk#NT6NU0TEukme]QjBL.X1COI(1 AV$4eLH^QXBV,`d-sVIBg>!)qa$Zf`3(8Iq%P^NW#hH!aqV>C5kUQG.V8kqOi%U0` $GWQ/UP'+57?qQ,]]RE)2ABl0QM;""gIUfjN'g.h#nK,kDDI0mnp)?E@RsfQKEDeO eA%L8f(uH-AgF%nh)'6j/+j1j0T9L2Q,^[nT.KfI%G&T7%-('1Uia,]36ue9^G_]L UEJ\>8XH;;L9RtQ'f[D>e?g.6e#R29Y9K(b4-!d&paM5nDE0(lF)+hQ$NN'hMDb,@ VH3:m)(blgkhmF*Lj>"O2^-Qu,A27g.0V"#!glLbiCGFSJ3XbY=@Z49 `TA%NSH=Y"SNeLgri-Ojr6RJ>G]+>WL61>LK.(sQDA`bi7V_-!6EbSVKrIGdi!Yh_ 893Z,aM\Y,baV15-`CUlX"`>2?8-(Yi5cfP-E,2HPOs=?ao"8W`+mI/(KJ[bXLNkr %+1PN=Jp;G^m6&%+n0P(:MCB;php$Ke(,n_asP10i)ld330>Ut*OE!*c/`tYP3`'a aWr@ThH;KukoNM_R!dW5F1=cC(/LWSfb4peNd3..2oJP0)$;*X";2=,'^1/J6O]NJ $A:/VOP1ae$HQ@8TqWbKTQO1!"aim,LmVZ`q937^#!gD%5U?Z/:e*N4.)Vqdc[s)fE%VWl+Bj%6":#;Gin);23BVLAKeC/5,Re EJFOJ7ft7q/$kt6Z-rjUVeM,p0Fi@UBFbKGo`PVYmKY-"OZlf>'J^@$'reS_6)[," KJ8E!\JcDSc\,5SY7cddBcfg/oJC$aDN;W%jr,ZoZ:T^1FV0glQO*Ca"Ta94;:GBl *@j3`9[GesC!rHka>RAD5.sj'OL.8e!<~> endstream endobj 66 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F8 40 0 R >> >> endobj 67 0 obj << /Type /Pages /Kids [ 64 0 R 68 0 R 72 0 R 75 0 R 78 0 R 81 0 R ] /Count 6 /Parent 26 0 R >> endobj 68 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 67 0 R /Resources 70 0 R /Contents 69 0 R >> endobj 69 0 obj << /Length 2368 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd1;lVX ;OH6]>BQ3#jS8OAb\miT$47#:V$Pjh&/n;q$F-g#F>oW3VaEb7j<-&^.b%/nhaD/+ #Rk>:WPI1o2jo=(AO[ 0H%n%-8Vh>b%0JXFiD0kLr(8pR!Cd`!Jo1]NmsC."fsH@%mju9D#$60MF%!Le@cTd#CQ: l9:<(#pfI70.i)c4?e\ioV^$?]-T)8bed]=NPh^Kf-fMWm6XOMb.]Wl",!uT4qO!6 0T:kCOK2:^H^d$Tegc_^9%AR=A%Wt(Cojd[m/J(0!^OqL2:2TaK!6&T_!qUpb\:Uj em$HB-.;opPt^btdM)sSaQTgaj-@bI::%V(nP=ARIsqTR-;qR[3?UV6]U36C5iP@5 bjaKJ^ebq#e$SXTc][Qq]]R'@=aFKn<_[-q:Hb0hk-;oVS:q!"k0^%m+@+l/P;[iO hUY-g?mYT>-Q^DCUe-m=2lL>oh]YIqJLME[&VFZk:)A=]H*5Pb@cDO+0f@\>KH^jX d)OQF6c_M:\atL/OQZF_K(J2Q.T@STq0$2C4L=bX'I?Z.Vne7d\>Dlm(p2f^2*^ko'u>ZNbTGU0+Z4uT"nnDJX"Ko f"Un8OXk\FOn1\:dL;]\`-BRmpHJj/&:tHQH*&7f2n!H0g+hOjJAn&^0]$hNpi@ip ('D*ZQ=WTK:iqF#)8$e)`8Rc^Jl#,X_,*r<#uLqDcu%)fVnhbd)q?`1Tli\CA *9,.MBJ5#U_q5h1TR7?"JA<3t^H[r<=qQ'Eoa;*5[90N\SgA ]3*cZh)'V4cNZ*kaF+R@UEY04ILeNuD$X#*,R"ft33ec.$&\(nPHB^+"*hKK@g%CG ;An<3QH][\A?(Vbj9DqeD\lX\3XYs[2PFV@(*RL?OqIC!]nB+-h_EKU"0ajBHdD!R5Q,T$W? ,p`V\_%)%O0V,372WR9@@VjmTc8NoH6(q#G_$4GJM,t:f?oaMaNL^2>j:r.kL79rE G7q3f7[^XCODXu<2ILAGk>T^'%*8(Bk%dO*XXjVV3Gp^)N7OHWAIar1d%bd+5g/0i 8h7i6Y;mTWfo(5bM@;Bk;lYQ\qh6n_3DgmL1n.^CIUKR+K@%.n+XVe(#2md0a!B[c C6Wt?D2BsD3i!/l'nA/L`3BC0*!e)lFG,hQGLCg)9kHgcQ;Z'h945pq%Xiq>:`u]g Ma/:65TmH-0G%@E?##AXca%G3KgrYj`2?%LC6MD-elhNNgprM\k%8W9+GN8F23jSM [Vpee*Xns:oq(badD`.ZEGdmKLUSa'0UnI\lci%r+e"88>%MhgeQ"B'd*.c-&/5[( AI5&`27QIr8oJf/]9_#[>Ddt;X4h?coZ!n170.a-a]\oli#Diq4q=80#R4]#)M=P. %WBO)X+W7O50-r/bXc6!!2i=Yd&7`VHJ^.EnYJJ#-.*RMGnR^u3 ^,cBmEsR#]@TRA#i"*/1?eWM3 endstream endobj 70 0 obj << /ProcSet [ /PDF /Text ] /Font << /F5 5 0 R /F6 6 0 R /F10 57 0 R /F12 71 0 R >> >> endobj 71 0 obj << /Type /Font /Subtype /Type1 /Name /F12 /FirstChar 32 /LastChar 255 /Widths [ 250 333 555 500 500 1000 833 278 333 333 500 570 250 333 250 278 500 500 500 500 500 500 500 500 500 500 333 333 570 570 570 500 930 722 667 722 722 667 611 778 778 389 500 778 667 944 722 778 611 778 722 556 667 722 722 1000 722 722 667 333 278 333 581 500 333 500 556 444 556 444 333 500 556 278 333 556 278 833 556 500 556 556 444 389 333 556 500 722 500 500 444 394 220 394 520 250 722 722 722 667 722 778 722 500 500 500 500 500 500 444 444 444 444 444 278 278 278 278 556 500 500 500 500 500 556 556 556 556 500 400 500 500 500 350 540 556 747 747 1000 333 333 0 1000 778 0 570 0 0 500 556 0 0 0 0 0 300 330 0 722 500 500 333 570 0 500 0 0 500 500 1000 250 722 722 778 1000 722 500 1000 500 500 333 333 570 0 500 722 167 500 333 333 556 556 500 250 333 500 1000 722 667 722 667 667 389 389 389 389 778 778 0 778 722 722 722 278 333 333 333 333 333 333 333 333 333 333 ] /Encoding /MacRomanEncoding /BaseFont /Times-Bold >> endobj 72 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 67 0 R /Resources 74 0 R /Contents 73 0 R >> endobj 73 0 obj << /Length 2539 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdc;?DPOBeB*W3?P6T:K/VGhK]jrgB(%Yfl:#h!NESW'G^!@6B-1Y9^GK*epD fY0Xcg43kV+n((P#%'BhhU`^,n>0!r%eO@mV8Ck]/d=V7C)!fOW,S+bNp)EG":Cp" 1^u'^b47[?^d'o?K*^Q$":>JI)@Z^g\iKrr(u@<5K-3E9$ng$g%`@VM/TaR8=J OGM^!X6K6&/rG6Zg;5H#N0(Wc6'uPD-,Dk)3=Y#QdP<;)4FlW(__C4.`<3WekJmT* ZKCl'A\'mePITdcJEs>%oro '\H?$aRtLXW.b=jh%U?/#gD[s0\Fug3Y'Zq6Hf#Bds]s#,n#`lPMk4T`boAqUB'6%F"6-1[TnjpmYkO1&(%8srjSNTadLD3+=\j'6`Oba;1d psuX-+TUFZWe7.9$:<+Yfb_kPGg\C-"(:[8N5LjpjghY',4E0)_LaK#0Q!aJ<'rPc ,2t]4._:)4ru]>*OI[\?6Rr$\TL'l2fo,KoSA $%BpR%[KZE/kaXqT<6'Qr7M@7q0Nr67)F&l2b\GeQT=sc&3qTHBMOldK6kI*Di*kk -b9WdER75??o`tK9G12hP'?;@3johupX7g.FWlmtWK?!;"1h\`83s(/_LbVJO!+X:g"SL2UKn$ERm2VB^Qo(GT>%#7Qk`6p7DC/ H[u4hM?)=3+,I4OgO`.=Bga3nf;-%]@MM<>dL&H5cAPGFBESc50?BW.[Z3n9cJ!H3=q4 c3#Kn'qr6VD<.*R*WV^U<&8_]`+Z'6(>@[X&4@m`lo#u:C(hdS*@h.!2(hWB83I1-\UE=ZaLG1O9 C5$9#eXi`U"<5uIBjjeV0sN(*``VOMA:XoKZIWGcfql?_h7\.]1Jga\5n'M=NftL!d\-Fk;q_S92fo2jaMQc6M+PG$4Lp/$+i+OS(jtr`%C@E>LmiW f)!B#TEoTG$+)/?r(C@9mYI9TJeF[a V]`/%92CDM9Rs%.ZKYaY@7]l_2RaOKmGj)oHYi(1XC5Qm#<4mfDrt]S'o+XZH_:j& 'njQa`#t_)a^W,K5peKh+G@p`SatUQbI*9[kM6*18k+$CmJ>A^CTX)5=@0$\_Q2oA -53bhQab'e*b\5,??GejBu*b-/,YH\od3<97ai`PI%78( endstream endobj 74 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 75 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 67 0 R /Resources 77 0 R /Contents 76 0 R >> endobj 76 0 obj << /Length 2626 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgds+P8fhj']_YNGb]M 1'@]hcnpcCK;>]0M?q)($q$uWOgL*(;%5]!U\O,6K:(L,5L9JG3K4+HaOe(W1L_g; 9WIHi,ft;W&r"[D5lr9(S:$S]M0Q!9`pGbH1lP;!l7n8/F>c.\6ce+FKEGoQs#Qo# .0)SPW8,u-"/+#EN5Knm2@\]XW&')5o@Pf.DigqW]T[,\7 :QDKZ2UDPn_D]"9UtdnhCAef7dBh'DoK>u\[1p]_k_aqO.1gC&^rSKO(o!^@?Ac>P BW_mUaV%.%0IU5(g`d335eYpGSWF`! #jok`\B[DTjTp(7i9QD5S&>jCX:>(P+kDp?(l"uh5,d8b!qQD%pbekYLB4_/PK@6d ROs4$!+[%'li=O0$4qI5q@8.F\.jW@kbod\fEO_[2VFUb'NHVL$]qSa5\6oE$$\as [X-*]\E)A2P4=>q%6Er.aAQOn/5dFV'@cL"pt3B5WgT$kn1>0,LA73D1;acMjR^B0 rBmsq$L#Ng@he8Y%ZZRndDq6V]3iZH]IbP8kV.aIl70\6!Q'i-W^S0XGs`g51Ef#< 0Z6a#6dY%9_l,E'O"<'VWe2_EGDp8Mf((qNGheu%_lUUI.$6loYptACfK+tT]YZ@^ _p&S(8R)K''Q-c(T=V`qg\WME.Kqmd4jb?s`"%mT%f_1k8AjK7?Ul"RSAUe*+d=`C U'e82ad41u/DmQC!5]0>2-C,@LD^Y26.-#eFC@I$Kf(J&%81):-Im\eQ4@=(Tu^F@ D@2mi.*_mfXT^*a)Df`:a$GeE+i&Z$Rq!3H)EUN)DQNuOcl3AqaZ>/aj;\7]DGC6u :C9Bc+Z.,^PEZ0o%B3@FSl75;: *+3Ib#rs(f6qTtX.3g+\I#:LM;=Q^\`)W"qG"l@q_C-]9?V=%mW&!Xojn&K.;aUO:Vs<2"l&*MhaW6`V\]FLrM .@Pn1*0IaOB]/+,bmC17Q%\(aaW>7mV0K1oCC"#basNDS@lTW4`1%p7#->e65fbHa U+hmj(eU/iRB[)DX,<].R2HE#3)3B1Ph;I9P:'bE2['3nSY#?(%,d4$"T]`$LPTPZ J[dQa&LOqCVO5INN2`G40IoXCfe*;ghf#J@?U8:'9B^>+$K7d9-L0=//5m(ENkHPJ k&E+!Rp0'HT\BPUrmpD^;i3;)Rt:X-'>e]YMP`SH(=W[#=9XtPE+[=I_mUHLnU7mT L:G+ko`VBf1pprJ(u"Yg$8%K?$C98tgREA;/HSHf&K7uAQo>7Xr#rUOK"+/.K5';p dCNp>lWY<@'eD,hDc0Kl*-6XNN1+Db%H>I2M^NH_rK`S)R\#XRP\Yu83#04#7*[>,)F]If34SPR.L52,5D ^N/qK'"DN6/5QISp$p%QbPIDh(SGc/X]@^l+U5]KWq7!W#r/mi"=@m\_iuiMZR+RC >:1e5;)Qq1bSp2P@VnuHJel>0+JWI`TUiDIG'$!YO)8Gg)7=V'iX2Bi$uMP*>_/-' d.K1=@GCC/oF]-2(:)YV>XsY?1TY>2f_.ge@ihURFJ[&YGTXJ)5ra-H#\Z5WRO?JM cPU4Sa*Btk"U5?@q?G\J.Ij[;M!n+qrSo endstream endobj 77 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F8 40 0 R /F10 57 0 R /F12 71 0 R >> >> endobj 78 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 67 0 R /Resources 80 0 R /Contents 79 0 R >> endobj 79 0 obj << /Length 2631 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdl0AGZqhU!GdYT.Nq-k 2\(B?FP%'*8FSBoM\*ff_P1X5D=R%k!U7bKI3T7O`kmi=O`9P(RUXpL""-Cqc4Cb>01d5cO Cp=KqGj5hjd>QHGN$BTYm7_N#C@oGS1KrB?lCcHFNA0;33JGmtT&mNrRU.I3@sg[" [LWr/3fPd,A=l[qUW#>X@gLelN%Y#r2oZI,ZnoD!HfGl*PTorJTOWjI)m$2'%cX4V D.ng>5\R>Z,Y91/>;RM?+;tI#\sS<*W*l>(TM+kVKcu#)S;8nNU/lpraJFb4UJ0)s O/+1U8EGR7e""Ve1f.!m-_iOA16'0jD'+d*GhN]7oh45l2Do8h,6fQsG2P=)",,%# W7)h:,p@o1LbWq62X-28FnF/+_\eRE;?MQj.^SZLHE[MRj/19_$M]ABN^DZ&6E'U@ &Sa7_EsrihiNiNn@:?*DN/#q"-l=Rpc47`B!'?rKt!'4+b_W8;O/7HCkF$j 1:X.nc&6:gBGA;!TPbX$)*5'ZAPC7t,\B>5\ge"X'HMr]ZplU5CndTnSS\DKEOKE; R#i\Q6Dj._!O?mek^g>oS[:(Z^hpN6+c'!["^+Ii'a6o8dQ.kIKchhn_h5#$meb@ WiKc>b7[Z$DcaH%[I.".@m!Wd4bcuJubVoL?4u?B"Ob,>YV,85- ZD`g]WeMha&L0G\Q?lH7=9JXAm3AN4F^N!$I0QY$5.C0>&-;o/aSGhJGFV&g\nV\* Op(o?_!Y'8Y!'n:0SVAc*.oL]8#e\,U*k=+$PU?_Ko+g%E9NXZEMmE&+U4HG2Ydi0 PQ_T2lB;im#T?jLBu.X`,UdmOPB8*WkT$dii8f@q&pcdY]K<"K`."E@6:T@Y3J,;e hU$maDreT%UM+ob9)NdZdJ4BnQC?:Ln`L2j!N2$m`CA7f\'$ ]3!F=@;Lue8AUBZ6hCh=W&4cee?MW&<@\BWWKhHS#-._BXGDJ?=Gl%GX0=jmmSo1;I;K>0'i7*/H/m)RJ"O"^5b?CCq;e[cXUH.jSZ,Yk?*lf0\b3L1PQu"X^'E EDodhGg\mfYjtMOAiB$E,9*M!_@?jS%!HS7iKWtQ>2^,u\ (sK;B>0&hORKO)!0QsNa-d`Y55iDsoq7fR(6L?&@/D:6g4;gfV]j\j.-nuJafg6iD I_i:r"&O"I!C01G9q^@M,%0?oK<[@nJSVt[`n3RB<^g"SR[0+QAK0E#B_G.;R"]^FB*Z+%IAM*2k\15X^G"<*,V/J(3\$X':!C9.mf:T VPSm"9^3WpEcm0\lI\_,D&WA]/n!\oK*rq5$Si3tf)k4D.!DcT FTtk&dZKGafB_BKA`t=8SjJ9VXdaaN/VUKs0QVGUq0]hUqVX?ueTq7=D(`7hi>%Gl g<8l,/XIc8f&k*h(s2&AnJn1+o26R(K-VjV'jY-toF7u019q^@hLfTjAa_5]EYm%2 fPjYo09u0JGM;C<)tS"<+E.YGQnorl;1L<"LoGPH7:9@S>&g*r"uc&Vj,5^4XsKaC `?p_E#(pG!Fd_f1KoSh??"88BH1M% Z^/H9Ka05H.0L8nB7^C_e$bH*hgIq76I)FWm$C^e':GBU*X]nj=5dE@A&: _jTBh&$^K/E6 endstream endobj 80 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F12 71 0 R >> >> endobj 81 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 67 0 R /Resources 83 0 R /Contents 82 0 R >> endobj 82 0 obj << /Length 2341 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd-@KERKQK[+-h hdEhUA2%MJM!r8!9N!lXF6Ms;E#Z*$Ef 'F[?:l8"(7:s^u4!<^g_%E!Nrfj6 m__TE6dFo=5H8@pM!63edj-06L$5@=uYI2lTB-B2k&dqaE-s`.s+q>9C>m:f^d<]6.F7%)fWU(/OBZ1*X@a)ts9Gn-hGPTu.WgotA6n ii5&u!/*&QULL1*VCA$a+YTO,7hrlqFhLMQ2"PW^W>F8=AO5Zt2Q5B_:f&PL'79OF 7me5r8.J">n2_sI)m,'nU>Sl8$\;'G^LS/R087D>iaE;6Yl$g!A(Th,)C_$45HHkk @UFH"Ys(=^Tq!6;)^3ut?b)i<:.!2L+d1Cq=Y'a])Hn;'D/-!Ib4Ga]ajQ)jTs6K[ LA_"B__3PI>pp_hJu?MS@OnUtI$BAcES2aO%ft$;"r-pQ?%LTpXdIld!9f/2nR#(C ((J+3>fE%<5"lMAf`kjL*0YrZ.Z,Y.RK2-ueK><#`'IKUPut)PJoV:"c!%6b)Qj+' #:h'1K<+*a6-+Yq!NQrF\X.9@enJMq$V70-6GO#bOiScJBj3\G^nsDTfK*G318dP, ),a=1:GC1iYBV2Weks:D$jacR0OXQ>P6Z>+.-I?@)Z8)9,)&Tk6cX;6%"i"tO,uB" 0#l\l+0'j--!9sJ,MX9.LcR2o"sK+?;\Vg'r)MlB=Xubn)bqTfOX=$FK,CbKbGb`) ;/h)U\A'/H$bR[h-"'91K,Z"U9/=g11PFig),'5/7BTdp]Kf:*$/1s[O!#?nE*s"S 6lL=Tr#:8d@//m%g3\lXdhMNfAnIE29f$3T(6WKF?Lpd/1JHLOn?*Zs+nhBOe]Q5_ m3<%,1K.hpil)IFndF0q"U=lA:9sUShc&kKP*?L\^n)3k47@NP6A@=NTFN%@dWLcg gP!KF)P\As,)EbB4C[6DUP*8e$<+Y=M/dABIT$nnS_q9k\ KG3jN<>i<`C%sOj'agZ;!mHeY,=t2raIC28MR9(qGXS$a*Q/]W4#8HK@3,f2_ Qnp-i"^l'+Q$@%a7aX01?CkGZUMioIeuG%#.W$W?1pIgjb,1T,Z(7q5`==sscT(1> /Z\M-T=k'.oYIZc[oPfi;*S3^K*Oq;L?@[0-P0e\"Qn1X+bt>8G5X5~> endstream endobj 83 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F12 71 0 R >> >> endobj 84 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 87 0 R /Resources 86 0 R /Contents 85 0 R >> endobj 85 0 obj << /Length 551 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd88?TV8JVGik4fP*@:%&Q;kKZ%dj =3N$$1#tVg2K+V1nH%)q+?#U%JcedocX/G(9HrEr,gC^m$Uu?(Xjg094<4o$*?LPC XY?9Gk7:G6b&07B\D+.EaPZ>@cuXKfreJYF2%+N1I`K,E6%N/a30ln.YZ2Q3oiJ7aeI!YE6C2 endstream endobj 86 0 obj << /ProcSet [ /PDF /Text ] /Font << /F5 5 0 R /F6 6 0 R >> >> endobj 87 0 obj << /Type /Pages /Kids [ 84 0 R 88 0 R 91 0 R 94 0 R 97 0 R 100 0 R ] /Count 6 /Parent 26 0 R >> endobj 88 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 87 0 R /Resources 90 0 R /Contents 89 0 R >> endobj 89 0 obj << /Length 2908 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd b[t=IktmWh(?7k$bFW&f0S@KDjWc4ml=i6r9r:j/d-#h/e"r/BNZ'.u&7I:\+fMt& *91>"EdY[?;$rX`%0i#`K5+oJL^]#:R>g2`N)/@s0oZ8@j=n=N/,hQ-6Q>=S\^#`J 1'@](bRc8KMe:\OlLGXW-Ah6ErWUL/MLgd:&8neda>duQU7lC(MRjpDXq2&a&kImK T`AgI+p"?Q)l\XPC5*'74:>b:C.%g0cm.B(]@T7sMqg!uedu\n0(5"[+TnYtjmK7% R>lhDl]LZO*K@"WmB1YEF)#iiTV_H:`N@_DUdYo4\?9%SeB7\E2idC">F&;dX\tlf h5>)d%^7353iO'U?7(UU?CVQ5UO2\m/64SU+t+ZaW&2)8NSEU^r?aA'e];fZ+ed#g k 2PnP5=$.a6Dom)*-ZL@VFHBO=+eBYk.U-'ZI:A7GMlTeK\s:r5(LdS'k::iemt^aQt;\jqN8rX;."E17Te7CP o#DM?4!A.9V6ja`gFL"H3k4.;!f$g+3!Mn=8!(MrFof"_dIO8-ffcPJQHI@*>Yb*! Q(Dlkk;L'k6Km3i=b$bbUhC_5T^tmc1jQKs:D#WDpCcPXiIc7$MEr;f#I7ESCaY*'?aHLK&OM00Ys%Z>g3$X 2/(6u?tMc3=I3IjL02*ZT.h\&mt,E/'C2HmUX9P!&TPb/nEWm(DUds2KOVol7N?#) a>j\]"+sP[6&_7K+h!+JB*'(n)>QPNlmPliVjXN;$L[k://e>Jgf5h(*7WW@t?S=^D0T?cg\C.D,K>XQ<@P"b=[UWopD4dc&Tu]P)GF[(HUSgN"=N U/mPO-11+7mb]6U6XY2:LWoVE((,a=`1M/n7Snil%eF;l\l:^T6'P/t'FGXF7,TnPJ,~> endstream endobj 90 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F8 40 0 R /F10 57 0 R /F12 71 0 R >> >> endobj 91 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 87 0 R /Resources 93 0 R /Contents 92 0 R >> endobj 92 0 obj << /Length 3007 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd1;lVX ;OH6]>AKKnjS8OAc8R?i:lM;6&W#^o&022a;ojL+Ej(slkV616(l\U$.29AtV;`'/ft>UL5VeB2d)+R4j+<+- 5Tp:'%\[9D@g,MF&md$3lM=tXKF.:C(^quH`nem,Hd(9)ZuWf<3ai)33A7Q&0ePQ6 !i!gA!LRbY1BW0U4?1Rn`]"#&aPJu1b+R>:L`lbOp:/8A4Q4/@A'd51;3O7*NH&HM hH7ZDg6%C_%!498Ys@jq0;>+=Qc;sf73/pTgs:>F!tAI3M@JL;@G%``O8&@k,2: d5Of>X#;1\0m4%.]t!]J\YP-H:QY2]ND''L.po-[%0k:lUGSB)i]*Do4JuD22M0A9 '%RqTR\E"Y`3^7ie.*GXOYk_N-6TJ=';4PE](hJM_=4XtlMJ1kXKM!]HtqPDm\)nj +DAZ3c5+gSKJ0(R(6tj?X3MGG$JTajB)14,KNd3dO/)*X]/N1.o\H9Mo*m,crHXND +[CkjagfY[@m#>SZ76-\&:u?e@RuZe$=Z8l.11tEQm[a-3lQ^S!g&"-?ber6N6RfV @qHcVn7?>dfj;.?0V[*-%m!.-*kZV(Fr\,I1N-[92\dgte-"G\mHHqrf)?]?P6=;t dLA0FOQ$g,PqZc'(m.l@"&U4$;m-Jd1I^pT<@]s9.\U9.VfnffEM,66\WnBqg_l9L <_F"J)T,W_Q]DJk:'SDd,1b9>(-u5;6(IVRAqCoFitVkPq0k4],fRZc5Vs$\P8b9l B%2C/3m]GJ07_\r,H=EpA0Lo`j.c#Y]>IiGOsHNLWq]iB/Mq$T0\eTCs/ob8mt!'@atoPu5ITF=8<8CZm'XPU9X 1p7NKRajSr"1e;n#/m0(';D2Q8Y]=FH)X-IF]XmJ>W8RFeAsFC5%i\H9eR(KL*dPE :Yf._L7U4V'h6H#I8,!HEDFQsjD72q1drRX1oc)knqlYn;!Pp1Z>)HGK6-!!aXL=8 3//8QTODDcI5Qc0<;#Kr86HH_1eEEh#f/el3[Vgp=!QkQf4i;UNLDi[Ur'Sm;ThB^ :/tK%g7?&N+:7G8aB9l;)oEDjHI;9PeMBp7bmG,aTRr1.)D=QT%'Eukg)sD$]9=@Q,=IkI1Z]20CbWH%9kRZfL>[.Z$,U;hU#,g=bQF$! 1XD2anWa?X9UcR#5ln0(gZK\VR-_Z=2s,LE"M1u'fW3n4VT0\9l\GfRlk))`VPc3c MkDPf5YN_s-#,g$Bl58\dZ*+[)_/e:+65ZrC[!gj@q*TLZJWhLd+P" iu2,GH5Wr$%,mipk6JM/2-=dXkm_07kg6:D1dfc+CBa=NE/AOa<5LnQQt\0c$qOi148g`qnVo$m6H0g9gPqto+LB_0&YEb3 ';MO:]Lq>8Fb_XH!!MKhZY-/^ZU&&uJcN*bHQQunR/B)2-#?1cbZCb$U@1+anstn# 4L`sXmp2naGd.+A2>*)H37(g=T'S!sqss`%jL_##GgfajZRJc1[^L B6<9Kbp`soRhUp#%5`7OqmB>WXh?'47&lNqpc57)c@l.N=MB2l59R,A8oN$N^Rj&_ 'L8[89kPXGK\H1&`I$q>'10^TC^%F;as-odNC)B^.~> endstream endobj 93 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F8 40 0 R /F10 57 0 R /F12 71 0 R >> >> endobj 94 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 87 0 R /Resources 96 0 R /Contents 95 0 R >> endobj 95 0 obj << /Length 2312 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgddfkBPJ=f @=p%jWZp(l&45C_gcWT9CW&/kUM>TC8IpEb#/?-B9c5It8@g8W9Y3/'#3p:SjfWKA ;Al6I_IAqQO(-$h^b>k&&2o0!FP8UuLd3m.j.KYkU(\G($4,,0`]$70FDBP=WtZ+' %$'BKTS*EZB!8\#o"?L]UBor1c>^kd%B,`48`G,j"ge7q0u@ufW9Z7$ X>hq!:ZH.PNSsliU($XaMQ[Lcm#]p`EbfFH28_\s%)7F&k?s69.r-=e_dMeD>EqRa ^5@JAC`"cr11'E3'Drq;Z*YOskb[9Xaud^%KdCFtFRm/p>3Y$upJH8?k_1FQHlh9rRE.S2O$XYAaqP/c MSHm;O.;>K^OmiKN/Z*6*j='n+P\uq@_;cG<'^NB2i_k0`:d15/fXFr216(hs,XhgrU$IpW0Yn#p(:AZV9S1%W3(R%Wh+>ri+ fA?F4Rt(isKJTO8Y-p]]r(F,BoIEt6XpK1RIF#\K5:>[SdAt%iJ`N@f-iF#Y/-9Q? C'2<)&unm1G@5t_#H'FS'oK5SYVRL26Cb6oHR *a,[TnK=]6bQAGVJZ]%PV77kRb\S\<:gOlgLY2mi6m#up`%ZsCfGt>34[4cE-:S%n qdE'[C's#I^R.9LK-5)fC%2(U;"2lU'?GElZoYI%puu+q?Z6?a2TGAD%U<1`Mi`TI 4UCEHS4nrBnneNL\?=@fWu?(RSbtO7=S63_i`F*J675%@SV'OZ6`dD>5%[,\@j8I< 1?F*5oV.q0K-r&:\PF=B8Ij94bpUQ:c4bq'hueST4gA\gRJ(6De'f$ANA1[@`,]ZgN8!U?jQ>RP!0gC%4h1UP> 7Yt!""ZNKr4$dAJ\kC5k7-LHeK-Q\(&U?#=-d?+qErn\DD&u4=^m-Q@KTQgDRUC]o CbPDZ*Q*+RClqT>0XX$K;F7:7`"nA2J7;62Gtpm`qD,r2+oE>M[`2CYg*0Ob&$uE/@L(HVWPqiQI#KF(! FO!T:7W(lGhe--tT&<^=EZ\OjmRUo<4$N6`2G-8d/4']qqJZ*O= 'S2#P6k.&je$hN]C5F!72YeEgT74aUAM!1k?J'!/dd: &Zjp;]AF<-%$%HM1'Ei'OTJ*TN7lW7@)Ti2Q?m<,j\i:Z!5k,,D0sJ-a<:QAN.YUu A57CTX+:C6'IGD@dE0BfLDpP!EgL;)h-+-K"RH9p1h]g>_.DskRAu]G"!T;1J2Obj ^p),^%+$p;@%\R,!+J7Qi$cBl#T!W"JI/51:L,YbQq[84%RJ*%aIr_cKJ8Q%Ee@,J ;p$10d)p%Q]Cud"5rm17f+-kZ2+$Jl#.HKAHtMG2%KY[Y#&qq endstream endobj 96 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F8 40 0 R >> >> endobj 97 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 87 0 R /Resources 99 0 R /Contents 98 0 R >> endobj 98 0 obj << /Length 1913 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd;e_.R+?,^A"t\HO5rq"3$tfGY&B\lO$a6f9BZ;n:W0ZQM'0)o$ 1`MCm"47:t*$?j@24.i=E)J,;PmK_(TVre!$A6KCo*6GVeFj5Y5b9D3O2LVh5L&+u \g>Z6SF\Id^hi[t+c&sb1ef+8cElM\dQ@r5A/tQAc#OfD,,)>Fn"Y^h:62n/E8EW+ j.OblKG!_%k9nYJa:Ki=@,M\W#U^kg"7NX"qoj 8C6EX;=o/k:Fc7>9"Eti_QOGk0Uaq\EigH-<)2q!MU]c>9'6]E8D#g'&bta#X5f[; YLTr'GRuZ)j#k[cWr6p+=&b^-.T!u6^,H0%Uk1EoG)0BB/sD0D:TRRc5Wb!"gQ"2@ d]O198J^.V@"\WfX?+:;dZ2kcaFdUgZ(\4HJ$uK>EeQGgZ[C[WiZQB"$oHb>p,gC3 b>8Y#SH@0FG.lK@70@-YU`c`?'YuJaCC\:V$sOXH.:Jl`P-VoV0UIT88D#7Mj#h_< Ls8\UNZpIS@#C]e=u!$9%FSQ4%1;u*5=a-)Lb02Fhae+A)"h+B'AcKSL]#]+)]LmVZ`@[R63#%5fcQ'U&pPppuo.r6_jON?&+fr0Kj /[?;W5)]jM>%EH>m4s<1b)"`eD?YH)_KC$fgUiY*n**6N[fRM@dtJ9l*Q4rO(na58 O%5'6]K&Ith]0JjL?,1r^#R'DSEa@$#c0:.VaqO5+m&u:j;PVfA.=usZ8I;HgH8EE GF_+55U[*!TP%M]:g+Tp5FFpV)aL/)u.Jq.(lr UuK4(7p$qE(5*g_`[3a'R4cSAUW)_!(>Mt_aXB*&Yj4&sTOA6uPl".YjVCLh&s@p^ Asukq)*r)e3\sV'0p"X5=2I_0ZTT@.R:T.UW+@M[#sp5`/Odh\#]W^)I&Fq" endstream endobj 99 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 100 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 87 0 R /Resources 102 0 R /Contents 101 0 R >> endobj 101 0 obj << /Length 2901 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd1;lVX ;OH6]>BQ3#jS8OAc8R?i&l$4F$n>gF+e-`2o8tlHE3WE&Jch^@^/?_S":&\HeB2hD (sV3sr:o>o3`q?p/K*GT+>Fp[Rc+8ejCL+/p4ghsbZT@M$Uu?(X[!hi(_"FRXN%`E bHI#=Q2[CT>mf^5Ni(8;PiNrGRC7HJ\:]P5N2$Zu1cI!"8.Z4^^gn/`64hR+%Ut(W .?#Kbi&2";Er%6h8U.2c3UcacYroi)6IE0B48^poULRj7Z]*OU(5u(75WC72.-N]# Tkecb5m(f;1TTQGhE&Wl!73E[b4oQ["[N-!VSdB=^_e[q11'[_nC!>Z4%Rf^n$2tH ,nE"g\C*,iYJ^#5^i.uZ"?-@=SJo?S95l`)_dlm&3A3a%4Ju+uE$PbLOJqoJ%X?pp Nc'duli[t0\u<7MZcJKOUM?bl5&`dC*Y$#W*^$hTF(?3oTr;RUdF]1h0QTmr)KRl3:B@k2d8A551B6"Z9&5Va`4%N1$0BnV3T]36n#'F1q*C:OHK i)%7E@;q;\"X-FJ.LB3n42-T:+NqP*+iai)MKD9%a`m7S#/3f]?9K=gS0e`>f3 NPNG8%3mjZ3Xq*N^Di108CB4[!V?!L,\N 0Zk@VWBj?N7+iZG3!]VJD[u+f,*4M<8:e<_4+nRm!tm'k@*'-Di%ut!1.2`-g'AlG .#G;-g_-@u6+(J''eFPt6!Oao\+!Kf94Ck<[i=Fh$>3,AVh9Ep1^)F05N J85S\X[>"%&&FH"CdaMLOTJ'u$"8)c+dg![:`BSZKHYtYedj336NdKB,_X*&'G-AT $K5eS+RmLU6We7:5\N"Z!7dl5BS.5El$FTd*]:m7R1l]#.M3H)6mP`6MbPZR &h(J^?5*?;AAHe7Zq;=bWY[gRna@Ie5]@/Ua*cj!c%Zej*.nR?J]dVU"/>Yu',"_Y lofJ/KH,XVOE*[EoWPc+HAi;4YE^MD3LA*%7S7YB.P%dO"\F2$_/W5;8W4?WF<mMTjeLA3.,="8Qf>7k*2?H9 V/N7Jm=h#oWN+[J[$mY?G]4-'V/+k>b<^u5 F?[o&LDWo0(#fV"9[[m@P&``u.b-"4B@QgokQHmkZd-6qHKe"gQ.M-_=*d\6_bK;(Tu+If)YOLSMf5k6#i;4qlG=(B3Y#5C>!QQ8gf,"=HE13= @%5c$PV]6@!>u!T8^^^tmNkl^P7.[pmoPJ8d:ABUZn03rB%+=%XpRXajf4k4E;9tn Q4hLl?u:UY5t+H>>uj]fF;pBVEdF[M>ZZ76IZZ*b2Z]?QX,scrg&PErq>;!S0PVD+ @pC^iEr6"E:fCob_0'dTFbM4hDCb\&%&'5W[lWVFb3O:Z8TYM@9I_.s62E#cd?KFOg:kD'W6)J*u4 /,J7f.n@%1CS37/c\/Jegn'Yc@Qd8ua>*hP9:KZL"1P/J1a@lOES%iXi$!uJCr?+# _^>_t4=rg3ml0#L=A&Ih"R3nEsg)@91`]*_t%LN+$ENU1pPP5aKc/4Y& XolVP/.dD+&/78]bScJH\3?jJP#Sf.bDhi+7%p5qmbR)haq7rUEgYH-&3t>)[eM8* nPP')',>UtMeSj'4$U$@7i6[O933Fl!eMom*`I[s@14E89k&1,4!/)XZm$m8U=tL3 1_'+-`u?._*I<#u1L\A-K%)M\dqR2g<@YVPp"JQ`S!A+,o3p%SN@ZA6NZ"(*u\tmD@FTNbLk$`mEGIb7uD[(T%m:bA30M2+%[,eaKZs)+YVG~> endstream endobj 102 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F8 40 0 R >> >> endobj 103 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 106 0 R /Resources 105 0 R /Contents 104 0 R >> endobj 104 0 obj << /Length 2249 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd0cXB; 1uZ*I;]CaTD/uT1T-Yp(NT!dph<8B.)r$.BRt0R;-n#p+B!N#Ek]7:Pc0!K+aNAQa d*:,d#_R3*2HS*mPG+4ED`J_0VX>mp5]X#cGUDH2d*]Q^RLH(B.(XA/YE\3l.Nj7^ .5$kl<0;>4Bl=iHU:4C;^h6E.bKSam!KbGb$mSg7nFuB\H]*#`9"9 ff"hrE8:X!#iIZON`0>[%>2_e3XtTuZk*ZkBYlR+j!5nT%Kh$Uk/;6iL7Aa68DkkS __C6Q^l.ET'`kOK2#O\S+NcJK,H06U1kDu.&-o.%$M;#F.bqgjTo.W]#!m3G)bXsc AM^3hn6E8m9T(574m=_.+cYN4o&Pbi7%g5W9/;&B=.Z;C%5m1SU[GdKUki653%cQtEe$T`DS:#6Bp,N/7Vo;Y3+; CkkKoe9m@F%k5t!=b_&a5Tkqn(m6/r%paA`6.Ck\4ASqi17tW-Gg-mPl-SKO]W^@W 1Q3PtdQK!4_lTN>,@gj6[LTUWfiiKoWtJWu1Z:_cN12Uo"VFF/m\*K?.1ST:SL&#K _jg_d]8gBK1_'"sS1uP`mMn!mb+q]f[L)HG2IiM$`\n=%,,dd4uJijKXR SVJUGgQpHE(=h/`^juKErX>R;_Vbj''!X7VmutLiPTg&1mWi hpOV.+H;"`T6k44Yf\s9f\g&3L26._KMV1*^(3$CjUPFRM] 9+c]dQ>HH0+`crO_f(@0BT+\<9Bm?kQA,DG\?O'0k9s*fHYq\<@q+1u.OMg(5k%A\ "me)J@8XiU22bFg$?]Sf9@F>c>K[c34b$PWjaghD[O$oD=]T0n?-Fl"4#!]cVj&Pc F#\.gXPWPD(ie:8#L<%Hb$bsuA!BtdA-(4cXG0'fln!oYBUTqIJfm;k!^ld5@8&,Z !lQ,5\0,73eSg$B3Z?0:ed^jAT!^q3%"Y''JRQZK+GKUX%)UBq%%K8FZZt]lWF$-D ^st(lcko-4W]6]n&2A_!*R=ge?:X[Y\gL%*h(&U8Ln*_#;m+H5P:ZqH396Vs`)R[: .:m:7&[o-LCIJjL3lBnI][gOG"rkcsFPWCHcVAPnudX &OXDq$mA??Y^klkMu/T4"@YU:jXnOd6j<8,XhpQ 3hu2c=J=MtB`ZF+2R&l6=W8r]-pF#gk[)M""X%@[Ho&@TB/Spn?O@3=&fno2:Vo]I TdJHJTo+_GK;`EON&j`r"nmP9F'S8;2LPPtH52rZ" B*#67;>Ql>.XsC0?p0MmJ0]k_3dbVW0.Yo4.&aIKd 3kg*>M9068`9fsf\@H(=84<(EhM4/J)(]7O['j#Q=aF[^Xg!(%6lgP191KD-h*R`E ^'$>s134!7<$J_"OU!Y\7pg*2RoB#CchhP,,:oLi%XI_PGkZ("KanTN"J=<<786nD 2+7qAPTT"g)loWMY1Lql^@QJd"0's@."2pFbLR%PHllqJaah3B)N'YchM>rj$(I$p E,&)j[*96NRcCl)ih]'Hk/-Bupg=t*d=!So[&64U`kRgrSK\8tk,%!4'gm'nRZ*'c 1GH^&C)jQ'37&Z*@"N8>-u! endstream endobj 105 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F8 40 0 R >> >> endobj 106 0 obj << /Type /Pages /Kids [ 103 0 R 107 0 R 110 0 R 113 0 R 116 0 R 119 0 R ] /Count 6 /Parent 26 0 R >> endobj 107 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 106 0 R /Resources 109 0 R /Contents 108 0 R >> endobj 108 0 obj << /Length 2630 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd,fa.Dk9d!VEkAfi+Og#%$VD2Uq0MO "%GW9NAt)rNEkT+*B'S>Wn(so)_I_5dW5s=c3>0Fd.>OA)3*f(D$G`4*V5;]mX^bj Cc6F[L/XM!jCp>`q\+hB$A]h4?QpHZFB5%=ZCue\)kfiE7ae[28Efk09oEUE2D05> 3Y"L_gc;rUONKKjDRue64-&q#3]7KcfJecRqQ0=B""<[%NfJ^ioqt/r>.t>p;I9$U cPYLp25>5_mYp0V_DJoEON'5=UM>W$-kA]_&2`E'`pSkK=BlCD_gE7,`3-nq2@cK+ W&')/'YH7m&_$m*^o;Xk0Y">d(cOH2X,dFp,@b'd\dIcST.A/rih-dVR_LmL1Y`j4 3H2iYSrGC6g6]a8#XKcTj-;PM%LOI?(q$Tp5WD+sB\N'V6\*i^d*B9q7FDX(Q8[5B oQmbPj$;$N%(*NA3R:Gf>?]1+dWhBIP6/J_<$Rop154'$5lN!CN8F[_WHa*^a^];W ;7l-.>maW_"[sKD'[+KMeC?q>;1oZdk4-/@T]kdZQY=aLWr\;m2GT;RD]>Y>'DVSW +[pHLNd(hAq2DJLd8Q#L^reQ.@PbJ/Nroi2?n=n7EKO8I@%]986A@:U]R^/"$T43? 9aMt%4=a(Lm)!+6iUu#t+\Ug?#_Yt&P,7H:atKA!9]X/\Tlca Mh+WJaM;kQ:oZso[PBA9HnqB(Da!$`Bm,U2B?11Bl7q;p#,<%s'H3qG$rrSl &.ZjeldAqj)_@mkTg4;WCO'NsOfS1`fJ6XC[BAFl0F#Xk"Tl?!Ms4uP-o[*U47+1] o)pB"%#Ya-R=l-u_Y&paL$\>hddpYk%\mkW;r/MW4?796`o44-O>hp:?41%UNmZSr nALk.Os>-feM94@Q4sEf?:f^5$5<.^K#gZQB5?P835Q_5rd/o+E.e+5mX(G!8X-r QQ4T+TU/TSJPGW!B2&,p@Ej&\7TE'76ZPUQP:7,[mS*0En_*[m'L9\*B[F4/bBi=f90Shu`SpP(KEtYRu =dk\nKp@1!3l9R>%pYZrNipnfdb!`R=2B`&Ar0>CSIjTsBRNj0NQ<8rOJim"79F62 E:t]T=]cOE-*34SYM1Qt?5bCkG%CF@Y[p+%D"L,T2$%D5FI%7>`C$tUM TL,h4`$h#e7Y%PdZ/^5[V1d-R?@d\NAIQs?2C+K[XE8@#Lg_!KO:V~> endstream endobj 109 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F8 40 0 R >> >> endobj 110 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 106 0 R /Resources 112 0 R /Contents 111 0 R >> endobj 111 0 obj << /Length 1913 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdmS=-7`0S=9H07] N$E@k^qaS%QO4#C[0;b?M`b@3LN/*>lk[)?P8oXnL+2&;47"eg`WMQX)FAfRL>$f\A#NcVlq.i. Z(\+2a9E'i3B8BYUGU7)ZipRf[*:jpePRo.?ALJD4X/d[.,-/n2b_IM)8e3+KeYRT 9:"_NmYdt-NR#/AmlQT*^@8dP$i9KY8Y!RQ^!D>lrNnHT5:\rp>U6eo:a@\r5m];R #.a@W!O,f8FPKGj5Nqe>iN^L5^s0Xke(K2M86Xkqgm"U3f <]:_f3k0FcO?'6ME'Q0,,7UHPNT5VE[-_nljtr*Nc$\P3%T\rWmc,)43"7+Jau0A) ZE-+3f%_Yn##qn'JR.dS7t_(4aqGOZ_4C6:`j*0(F@+NL9m5H^2g,M7ON1h;$ih`Lh5!2UW\2npq_kcOCCIP1: :8dCk(?k0Xr66l(gV39m]f[YN2K@=:9+4Rj5f,TaY>5iO*+O=""c5,Y (049K1S[:8($6lFS/P/U5&f\<2SF&p*!;:@Iu/F<*Uq8n>X;>!0N=_[7)51RfPh92 6+N3o^)04iaF-mMp6Hi>*E'4g9]hlF^nn\:_])E.MZbpsT[F^qEPtN5N"?3?hK'HH 1c']:0*`h7rErgo2pRsU-\bTm*jaLH05)hik3uK)J7qQV;,A7j`Y);Ypk&p(TTh.c bffla;,#qm=U':Yh%D2mji2ion$ 4Q=*qal@Sub@PD),Dc^OZeee-KW6?icM?N_fo\ulW4:@8NgugcEm)_OOU!jU*;dm# aD$2dOBRcP>HN&>#f?`eDS$9I/h86qJ?]*#$=0kpGC[\A8XZ8"%L>BK@>+).#L_EM A+#/nD=EapH5^@6D?L9A2P%2H)49VtL5D;o+(Od[es$_Kn4k"CLjf[4l>_p1+=Xa\b9A#?hiWY^ *LsN'4O`8V7>?IS!n6.PTIKE&a%Z,jf'nWk,$d>GV<+U7\JT$p3dAqJLbalP45)( hck8B&[=1,7"kDhA4;?Q*'(D.^H,T;n2k(7]L'3V[+OY\J4/7M(h2''67E!Z+9~> endstream endobj 112 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 113 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 106 0 R /Resources 115 0 R /Contents 114 0 R >> endobj 114 0 obj << /Length 1682 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd16i6% O_dS*2]W[iAPp!%7T)%%bDThc8G#e3*/m?rJoT;o@PXI,#9t\8=E?6eNe]=idpdDU D%5W9!JEaE1n=feP3Gf.+r6,I="nN0/"Y+$[$ki$X')S\lo&'87"W>h7'N]gu,\[.us:RXKZ(ML?MPm 'JXNYU_#_-X6f179q,nMKs08-s1g9-$M-P:9Gn'k&P;LY#_1=k12ml"P,(?RV_[Pfh %Vl_sYDe)^bV5'P@-A.ui<55R#m5G?1!7\Ko:Ag=XiK,grJ2[As?5Zq-.;)hupdC)L4V!,jto0a"^D)aI5m=Ta[dVYr3A&qX-g+\K\2--D!Q@\E:_1Z?DpSHK!"VBLd?:6?9s;/G,L c=:DM&.,*;ehX=)V(+AS6CW-Z%c[q"QW^7Uj_.\r3nJ,?(;7!l1eCB9I\g*ing*;l 8;QVkA!#+uAVr%hG](T.TmAEE\geSQ+ehlTktH-eQ\[W"l,R7MW6h2j'WC37N@dNj K&WQ(`&/oag^0)'GsqZiCQ,upp?$ukD4G?lOD]kf$lFnsB\M4oRH``aUT&qbhQM?26G34c\3=UEAuB$GtFJ+Q/#/ E8Z6'E,[2inMMiWPctLPD+276",8c:X].q$ABt"^"D++Zer+iXMcn:Rinh/2j.UG1 ]u3"jQu#,M\6\4pb-*QE]hCK?Gt$4&Pi=[d=8=OnJ[lYeU^.s*&gi/qi%h_f2X61s ]k&b+#:.l29GshOGn4I^"&pJjbi('u%3=m[a/IJs"5%6p9I'c1U_OFYWX45=-NnXh "&_M/?fe("'pZjn_Ju/O!8nfJJe9$S'ROW!KI8-Y.](Y:_C#Z2hR4]s$X$#%%4DDj _=oMe<:bHiMdVF??$VR )XCA.Qr?8]8[]DT.q*c<2[m2?O>6k~> endstream endobj 115 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 116 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 106 0 R /Resources 118 0 R /Contents 117 0 R >> endobj 117 0 obj << /Length 1529 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd%LbTMdP(m6/YO0e?S3p>== c+'o/b6!6@!Uj%=tf3NE_Ji/R6u1-.ZMr&,)p UPS1lNWWt;2\(B=l8afu,W[lq$[\b.Y-,CBQDNV(L?>Ear+loR/f^r/-NhSKh5E[\ \M"AdS3Sn?j/CWW%FM8e2[j2(lP%125gqS<_^s[-W9Ftkl:N_j,aXBu.__qnECcNW `8b=F.DoYgmP)29!g&#<#ZClC4R>-BKJ@:VC)XAYK.rrkLL-/,(W7hdk@sr6h89--G ?n:e1P>+@9CMPAqchkBE"tpZ!o7miTfGK>m>N5J2G>%rU^i1gHS-]7AUae[6]CU.u Dt$RN4:GV:)a'fb@MC1)[;e$\%VVLlNt_4#FNRA-FZkBkm,'Ei;iHSp73`Pj,Y;-h 5VS/713])&nL3N\WnFo?SPp"@`uRO-,gPj/j1BIn99Lk#-I?dbU(g%+ST$9D1/'O_ NWYHC3aCVDSJf[nDH@?[),ulkB22.kYO$.E_D7./$AYQMDBSN[7`1DdKu,i.g`>Z? ALtNkhLo/Kjc"o#FTW&j0iha6B*PGrTZT%7X#!k7Wi'Khf3tV!?d@4:*FcBV>>8\6 Seu:5#:ak!Bf8P-W=OugV6b$n[edZ]";`/\`SCA2+T>Z3LoGU20-1<+J\_m\B=&M< Q1i;O>5piQ[/r!8VSQ@s#_(j2L8@8I`!]6=@Y"-00o)@'?%X2/%#KGC8+=Wb8rF.9 1_;/s5-C4rk[@kmdQ1dgl3MaI0CJ4'OV`JFbT"D]lT%9oK4%sndZA!3"mddaZp\6Z NA6/iNPo:*q,XL<2obpW7%i0<$E?F.nUYO5,Fn_)e,3N*q"#PY.fe2;COko?$(Fk3 "c7j3JW'm;TTg5qQt,rM+gs$n"`03Z$,m@YR11=X[YRZS0n(O='I'>TMh=VU%S4\[ 3#"E6A844saT**a7%SNmH:!&gR@9k7XDefqF(o?;;^b#7TqOf1B(2om)8OCje>ej6 -1iCC1'.~> endstream endobj 118 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 119 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 106 0 R /Resources 121 0 R /Contents 120 0 R >> endobj 120 0 obj << /Length 2113 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdOMbXDf,E]11lZ!J&HS/eek-Am4`IX<=M\-hJ;p\3Osg#S)THq6-RFm$T7K&HRoJ F>25RVG;nN[V7aX%XK-5P[/]n$UukOT)UD\L?MN?c&.,Zc4C5^?tpS_!1\>"TN(q;/i3]tMN(fS"Gq_TS0P5AdU":e#XhT#\<$hWc&+k:i&1ug(s;Wc CA?XD4cKP31piAPF:]h;/:au6K;uXH%H.<>1kd*/@MB9F87WWm"&%#kb2]-m)MW:t M"n!1e$oqtK.;7-9?sTRm0,/iE"i`p(ig<=1-j;7%$/#*fL8Ko4X!"p1;2_e3f\9#l8AbI]=@SsK9Er&e'r4eTgn>Ek973r6`fhgC7rl#561lY(H8@Q@cPB5 P%a^+l8h@:UNNpC!2Zq#d7-\_"/,G:dqpEk:3Bse3_jG6OW6XH'YHG[_#UJo;NFpG D%;Vd5d--A_K7MmQP?-5X6Z]L+\Tn)@p.B^I2O*K6[8#7NT68W3F$UtHDEbYh1H1s [D5T^Nh_k%2\)(2ff)&i@-jpcf/5Z20<7^r*'okmqT[l_TOBm`iOlbsTdf?Y7FB"9 (NT]noU?j0JSdM0a5a1QU'(S&auNJ5,Hq^f,B2N]i.Fd?7FRK1@[YQe+En4%$p*() ^N[n3@Q>'$/",>(V&6f)3qc-J@9GQePf:ge+d]DnQ2.CEftNjL;l8(u5OS@0Xuu/,(?U LZ%S"5I`q8L)CM(\OGeiDV9.MR(GH;*B)14hYoPr_f4eU@UgE)7h0$6Oau\bG+lqF ZV00MlLiqgqfBHan^.]6R*hgkj)(^CN+,O2UQ-&[dFDp?ik@>!QDZJD!MW7X@AG]E ;M6!N3TPIF2T:0$#h#Ldk.W$K5T3 8TUKQg.m9@D.8@G18BrXFkVJU788aPJq/ZHThC2bD6[FZ;\WViBo=[b+N50")F[7GN2i0^)@A -o<]<()etU#`1TpODuR<^dndj8CL/k<+4ju_KN`)XMo8cB*%:$#!h.6,3_52JVd-4 cKO7WaPa2^&IFdHZlGH(0YC083FC"l)`.9!NtYW=4os0rV$3O?hD9R29f2Ps^gT&q 1`VX!;>R&uY5(A*_06:J'JIS8I*R#5%)L:+L5F"0'5P"C'+)1\UGbPQ>g5sIb_OF\ $6A;?emahNYKqL(VC(kTS1NB6aGJ/&,Z3,ke$Vfq3T3)m4N*I7(4+5n[WC7aB+5OF In58t5[/'pTTE>:as-pRAp"NPnc!7N3[D7?72@oBM5R0MgI@se;kp[a2:OW"3iNhD %%t%me%+^7C..cll=-.03Vu&*]B!&C%GWYD2M3I_$`^QpU=H9[72daf>]hpteHT_I T$:6'&DhN>HF#\JjIS'tTs10AGtT[$8Zu(XRT=sf:+KV.Vedtr44$eil\s/`[1\9p C#)2DPk\/m;R=hf/rudPJ"r;5ZrLlL,3nOdZ.,l9>&,?bX\md]iPL.KWnVF7YKq7Bch]k7.1Nha.gZ]o6=4Z6Q,'tpHK*DSO%QQK U-LXq\_9Y#C$Q3\\2BSpg?"kb.ZJtjf@I"TCW0e%J]5:;U@_WIePb_kD0DTeJWLE~> endstream endobj 121 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 122 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 126 0 R /Resources 124 0 R /Contents 123 0 R >> endobj 123 0 obj << /Length 2944 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdN!;X%#B %2fP.1uW,d>nss47MiR6\5BK^lum_^L?Ea:NI_)J2i`N`hDU)2l6,eq85c/r#`]2- 3fSceUt\&Ul3`;#+D@\g-/tRXjRPo6pfs.HSTM220PD;W!DlZ3#\ ;Ak4?7HQUf'96^k$=QQ\'F["3:c@N?@,[e9:'92]1Vq(Q'S`_$kF('l9hUdd!R7sF fmuE7T7mA"?Yk[i`-\LqR_Do*1Vp:l2JS3d-8j\(2-:P%)^_t["WMWjgH0.%";D=A ^u_@EH6`T\js.*/?ua)a^D+\^*g=NRqNd7C"WC]L3G%08bHp[]D7O3(m#!KN,ja!F W:PRRB:,X$DIUo)XV/n5AiT1?B>&R))YhO#1FsKZ[o*mr1^t.J2K&8snC:lilt_+/ 3#m8)i7LL5X"3h^+,Jon\'-VemBFRp%"$#qo_>Y&8=,&s:fJTNH0F<'_[()h%60$G 8(RQ>b8Ukjlm/QAjDBOSj7U>/d'OVG;O^_5T 33S)od"MrpA2eH:KK\pgCW`@e2@KjO)^*5"PW@u4l\,!b2:P&rBqH\ZTIC"_K`_Es r<*]h+T_Yt)pVi-3sU$",,Qc0(fG2Z:U(t1A:/U=CB#c[#biNQ**:BFYUf5%eM69@ aBu4_-0J)b#FN#riTtG@iM88aKIj26J7L2sSiI#8YkT;eQ<'nU.&eIe:XGbC7]1Vm .77dbj'j`i.&\o#1Ji.Nllo^'0m&#"[i@aa?]>>UY8]ah=GVP'2?q<.7:N3;cP2UM LrFXpR3$AjbPQdg`W/7N(^jC"+[cRjf1WIDD2t96QiXX()`*^!"3Ys!!S0%q(oR]_ M;_@4]leqB@6:9T\j'*cpU>*E.V>.R>=-.!P_(Q-kZBgt65S<'o9o:)$F\uO48'Wr[ Ui_odVc][nThJ>kBs9FL!1k'\0sX$*B\ZC;eO8N!(5@Nh1E-F[9d:f\VWSqkPShM< ;iO%6@Ef0#\s@@eJboQbc*K.h<:]!"@njA"ZrX/DIq"8hWY34eXH)&X\4\kjREg[kE=4KmaVbr4 2l?\@4A6mT'VnE\Og*L>:8>KofG!NuZUgFR\2bZ5BOMANJ=eMO[=-;RA.5OGn&78V D=!\+Ybr3D*5[,003uJ&CW+oWi)#ij8klC9s*u6C;M6K__%C;0) 7QVDDNMJJ7f&Fcd(^=e`n5'Q;iU:9g#_7g/)+kDNp-`--AV&%L1I`hW)ATVM%Pj0D ;Kb94OQQJ9Z0KK8*Ko(^>,*]DV+)lf%DGqHNc0@W";lD.@8rP/.5TH`6*`^AlkLBp "Y%,T)&]dG0_(dI3mRE^m>!Y&O\(Zl-cZ79bhS6)'95],N"PPD_eT4Yo*ianpF*rf *dm6J-Z8dJgIjPMEhdCa2A)W0S9NY1Ld_kfH0jJPeLm@*C+?neCtM&X5;^8=0F)Sg (i1GmT*k3g^6lF(UKs1-F@elV'c&#\3EU]]$*=Umq&Z7uLJFg?5`.M7.32+-D=>$Z `RH\[cu@!>,dI:%q:SI1KGC:<^9E9pa[j*:_?_hiH@9ri-*T<7LEbYsQZCP<"!+Lo U69?:10E4a3;pOC>!4b]%eEDj5::^5h^L1Z?maR\nTn&(A*0+[e,,E0s4dcArY'XAX"Yp(sD7l4un5 b!N!>=rbin3i+0o)t^q`cW&"q-`?]ka-,`8qn,ecCf3_d-5LZ5h8;63hn!Gs5l2CF :eCj)^id$X;T`]L62ShR*0Z8MXb\_')6P2&1ue=*Z9-]iR0fQl(lGRb#/s0C(2SZe =,Rkt:F6@X&Pk5VfIHJ_-G^t'\(I8LWd6#7[)EXL*koK;pZ_sl65S(uTaQlrdP837 5W\cjDL)p^KaR2<"uXcFaDVQiAE*ncJ.Y endstream endobj 124 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F8 40 0 R >> >> endobj 125 0 obj << /Type /Pages /Kids [ 26 0 R 146 0 R 262 0 R 377 0 R ] /Count 121 >> endobj 126 0 obj << /Type /Pages /Kids [ 122 0 R 127 0 R 130 0 R 133 0 R 136 0 R 139 0 R ] /Count 6 /Parent 125 0 R >> endobj 127 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 126 0 R /Resources 129 0 R /Contents 128 0 R >> endobj 128 0 obj << /Length 2625 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd:e&+jh[WF/d<76__useELQT+g6Q-Z,:"`/h7nG jf["A53QB1)oAGgJjI';+?CDkjth:iiC#GjNe]=i]Z_KRQqV+C!Pm-.17JCN':!t- F>s"hK'Vbg=tu"ApGmTX#T?p7(eiq]GgL=GK`UKV*!Uro5V<3@LghN]_EbUFKd,`Z ,lsa5<;E@C"75pmK?A<8#9h"d!=E:$Sgb#.N$etLlo8Oa0+H)7XKs?sn0\I79,\cS iBe+k6AnB>(obi.ii#3N;gBXCCQ_lu_'&r%(5FFdI3:2aF'8df@B(fmn.6#S8D9_jaA\<73KA0Rkl_*oX?)@pnm$6;e)%+gQ,ED_Snc%%`h^mmtMTd>kPq5L;$/]]hJ0 \C0H+@%YU9iN\k,An?k1_&n=g+n\@P("[fP?%T^fBE^a))5a:TQTEb5GkbS3oV@b4P%k [4\,;\9)r:d:e^;3bO'pjBTNdF=sEB]@Vr3?E,Nc/4m,Z7JeM]GlLl5OJE2%d;$CIRYkK4D->(to j>8$id@+/!%nN_<"9H&JVuoY;:6M3(!.eE?=5k&t;Of9+I9j\1\C;$=JVAe%-A(Br !uho5kA8I(7/@O"KNa@..Dm2c!,.:::YNb!uF1*Z)$5sSprZ0ikOJfJS4 EK)0A1`@W[3_.:P+#n<-n]G@$%Ng#_$mF9,L0!))j@RjMZDhBJkfC3)GT[HhUbda[ TH35`rXQ%rh7s3pHY']FC^ca\RKGuG,.,Q'd:&mS(6$Zg=lu'nrM_368f4LqbEN%/ &5),f"59:?%J4'R28eJ^"))fdKaH&(7d>+-6n*J9ksc_;l<(&oBFHMq#K]R,X=)Nq HIY$`[6j]u9nc;,poM*HV%6D,SI5(t]6)T,s(Hm&f%]"mD\ @'O1F'c+PX,JuN7nVU&\/AiHmX(N`+Y/]"&[G3L5%'DId0&X!I*fUhrDgX+k$Tr/T7!4@M E;^lo@XjLXN!CXh">]O'K91\Y7?G&A'*nAi&J>*l#alAQKq=3bKs4$SaVpuYE/oL4 4@d3,o-&9gCb'5;$BKpXaLKB@JYID,bWMW_B(uH;QEP:/dC6;*9BMN9,KB- [J@IRh?0!EZiX/Y,/m4oKNt3@B$e%AbtsHB)8M0l`Z5j:;(D+9SE*.]R,DG'lTpa\ Zc01R2XDu ksArlC0k[Tl;Z"@$`MT?.ZbNh##/cT_$OpKJM4:_$H<,9u Hop\JZ(lp1K82-@7i_76h:d2d%Pcoq4KBa)CV!=<*[/%4`2;qeS&GE #"dn([:hOF2&b.1ML>`[qSI&B@4DH%(9ek\@;Ab30Wg.C#+pW,L_?p7>DI&l,.j,$ dV9^S8P>!b!'h+h)V9E8FUDX&*QTuFD[b19Tu"VV?U?1D+ endstream endobj 129 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F8 40 0 R >> >> endobj 130 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 126 0 R /Resources 132 0 R /Contents 131 0 R >> endobj 131 0 obj << /Length 2417 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdBbo,>#cL"fG`oUDS`o&V\5aLA*6d2_-;T8qB\57iG&,ZL`1MQpiKE=\N-bBb5[Yh5ii*[PE@6halc&De'1V2j4)Mk*nEu9dhTu/F@ UT"#aO15VQ3mY/gSA[?XU.fkhN1sk>1QI)Q5q#\I/!)e#UL;^c%^(4Z&3,\+OQB)/ ,cN.jmM+2!N?aP.3kJ!ngq&1OjEqae;/*F\,L8@0J6&dl#S1KKha&?[k)c`PU'< <%?:AT=#'Fhs) kR_:L;mmUKNp9sp-e2nPAdLK3=qD.F"^4%45lg8P%]]J"T]r`(BjhDp,EP!H2/kfB aVhaa^9.ai%q::B`P+(N"+]]<":dJD=KCIWDE4Udk(?f%Y6C(i9d TN[@=@D^-lK;ZA[\n1nq8poC!m=tD1?e%I`JD$m3na;h"DN\h;kmh_$]rOdEm*28B j$t4M8\MQJfJL,[`pfOsWVrVi%V+a7X%_]X -(qJ??m'U.*T]E&2XXiqG>&0f3Llc+fG5.h=NpRXP/DhAQ(p628WY]_#&`uCMbeV: Cqc@W&8TU;NUi2+B/]*b6%qpLc^?CI]8bs CCJ/JTg#.%dgDd48KlGCXMCe<;B+t*b5l#r[ka''dqXX0'CKMjK`/0rN)$P\7' ad!mQFF"k]P(-:TOaS6,GT7t_Ee@)`;a!)AekH-cMotgr,(Q"=96;Xu.1Ig4'n[7M &981Rfe/%&nlV+QG7:102QJ*t*RZ?OC[/8b3Oi=0TlQ&B!7Vp=i,#24_D3CaXP^[9 k*Ud3'i$AmI'9ioi1)$H%Wl!qb4ct6!Q7$77p[6UY;HLSi!e6^5.ohg2LJg _ROngp7,WQEX=pH/TUp4bS_"R-7"6]ZPj7gK`q[[i6!8Wk[$TC@1BP/1F[jI)FMBD B/K\@&IJSL6'+&Q`AH;HW,uU5Zu1fb%-"bl!pnZXE2SIVj9H$rX'QE^4,)2d*b3P* :JqQ`i8!go1[<&4EVVh3obS_r#_A9Y/O;9%'I)$`"&RtH7VkX-UN=M5;*A>V8@$6< HHg'f6p*FD7]FI@!EJF-YA!:9PE<[*6.oFfF62LcXVG*i.bc?UA>C(\L.O^.nb$p8 8p/3gXB:*9>n8+&TL`P1RR;X,E6>C#[`.2F2'S.&\I,_ph;25cauE1D&A*A\+Q=TYhZs #,9h?]"8A+_=4,I,1FoNfT"9);Ch".o`loP!#tL&BB6[\2)T)"3cQ6$1H;@]2//9EA=CVPYlrB??kC([N1^\g#Op]Z("=]!X OqMmM!n*4@.H3B4PX9%lR0ABnLA&#>9B\eeA-;~> endstream endobj 132 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F8 40 0 R /F12 71 0 R >> >> endobj 133 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 126 0 R /Resources 135 0 R /Contents 134 0 R >> endobj 134 0 obj << /Length 2375 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdlk[)?P9)pH/$(5U+S&6/iR)ZM`Y E"Ej[g-.6MhJG4l]B;3m\6XN=$UukOT'%^DL?MN?/Z&UUc4n#ZTPbX$!UCTVKE2(_ 'F#_=+s[PI85_'tJl!@8"[Hl0.M!uX&0-H:A/m%T!dLLj":chF-4/[NNt'"2r'K:# &/7MH$.)$X*!Xc';Ab)o_ji[r@Y:m1NNb1P1Ia]]fJ'>#P=db]_`6pA-nhW]30(#F `]'S%]K$KV&(CXL*bG0[2t7ae[:lVtXO//nr90apt\`hK0*@N)'dUFBp^ (fN7*?"8TIEO@\!N7K[JBO]U<(u#nJ.7paONO#Y"7_Doj1PFaIc9miY8T'0VmQ8gU _f=hb(snr<%m?[re'[eX"$lsH'bs%$"=4CATl`!u9m.LH&ra(X;[0-3CgS$hdA1V! 6e7cPi5bBqdMYT=1m($ZpIq80S%4ZHUjW>Nf?U-";>ip:;?M.?8Tr[9>BV3 )0;,81Z8O]n-gH?fJf`JaP?9Eel$YaN2(Snm:T&kch+rT#O1>2:,ftP)>dl-m?!\b 1Valg5]fZ\I40l;F9huKZjb,<\?aK&ls9GO%LsVRW6j>'5llB>U^T5G,ALZ'OTu*+ `/90!:@,kH%1;$D2\I6n`fCE<#t.0=&Fu1o"1bZ>!Ak>kYe!4)+:SOJbSgQ@AAX?- qh6/CNe9T->tDe>#8'LZL;4]A^h?RGU^4^iKnRL+K-#ot@##^(%`uu3+!`*sOrNWC DdXJIJ?"bL.^`H_PN&a+NoZo=_".i*,UP(I3Yb!%UC5D415:nE*!VdQ%jCHJLb*ir 6th\?8>%Sn+NtcD@Lbn1+^gP?1%Rj/_\gH5^f(iCHf3u94c3>8LV2#t/qYIMeD&'CB5N1++TLR*$bi+7'borOnK @#UQD7g&kFWs;_8"(6NAiMi-aNYXN.R`NgU$7up,+p"Kb'&E5_`h'l*PGSJ7OgKu3?a>:LBgY6 .!62tVgY2!=g5-04/4cKaXDq"Y]W54mQLr10e9_C".b(M%HQcu#3ULV_o^.3TsX:o 0^?j3S3mWkZm#YBVco@)="8a.kWLC#iEqq(%rk9T-//J&3?K63f;7:*:eLAqRM=RQKa4PSXjcM^h`;.tG]*&4Y P.cFlRR(.K=KX.!%$th(XEsI?d^X,mF8AA?%.q^h!!K4H?h;4HQ#7GLi?LSRk+ES K6%Yl(`A><0l4rNG"Q&!!Lc9)9KInUMA\aR.0fjo'K&A]/>gT1Lf1#cK4>B$8o_A$ e.a$g2r\uo0iWbniC?+&EZ4OJ3LX]OofjS9XeZWrH.J endstream endobj 135 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F8 40 0 R >> >> endobj 136 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 126 0 R /Resources 138 0 R /Contents 137 0 R >> endobj 137 0 obj << /Length 1945 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd1;lVX ;OH6]>AKKnjS8OAc8R?i&C.0uOd+XC2BG6j6S/)HA.Tc]a&=G25[hruYI9R4`geUt `h?BFfV0!A'T5I+&X]68FZ(0B\^QtZfQElb"9t'(K[4!\:eW'rktmTR.LLa=fWPC2 `"ZiXLJo(Taii9,\eHrE:TN*pp:Hma#E'uI#%>(WQT(sdbi-(7fO>Nt".H/ENJdMh lRV"?bh%LMCQP4nYab.I6@f-H'FnV&^,c]1pn8$$_l29D%&8nLT+%K):soh`)!!N6 %Z&mm"X.s\mtOQ;DVV"%MuLmbBm?.,NfcnS`,miU%7%kZ :5s5L42.\UM'4;3'a5k6ZQo$/021q'gge5N" ;EcHk+"q[8^6[dV0[UktQ'*o>=>Bi7"IPOZ!koV9E\Md;>W=TAXp>?^15\R[.N^pU baBA^Hm'd4L0/!r'@[1dkkVUfrR6b89M2nl$,9L76?V^^L3gF2AkQ@<\ASg>g.q5H[9mk5fW&a_sBGu5c idqBqZ(NN>E3&#MYRD\^18PQCY3$EA%\^_9]88t1CQ=Cpt*l9 7\&%4X_TH^_QW3E>etDT,EXl=R5Zn%*;QB=b)eLd7[_H$<,OEYn52h&RI_hh=s'rR,/6f75F7)0-:T@:AQ1[W N51$%,4o?dj(*%]Oi"iioQYjI)l'+b.*n('LR[U1l@Go BN$a]@\JZ:-C9;kb`Aet=U:f3J:I~> endstream endobj 138 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 139 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 126 0 R /Resources 141 0 R /Contents 140 0 R >> endobj 140 0 obj << /Length 2117 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd!&R#`U^.(%k"%&7n=1'@]B\e_C3&6d)`lA/:7%Dt0h 1d7ZOcn^TU-sZt3W#4NV#4!duK8.4".MfrNX(DQni;hYq,`&6c&-f4\BH%0@@NuV] Jqt=r0?aNC#)4UJ+;omLU@0;20q`S-LCDltO/+1U8EU0_3_YlrNoRc31_%<`h(U6^ Lu4p:$\oW)aN?E^\P?XtPe%-!Ua1r9"=uKkmBe)5^8"?hr4d19r\VDE*")kc&kc@.7U3&1I'guau^$,ORe&/891Xg%^o*kg'`I+N$DZ:b'="h T`FBDN^K8S=]R?:fJ*iq`Og'!_4=_K?o8U!6oDCL;UsHqF9j@UfenB4NqEt9m1&`I /eD2ROOnd7UWD:QX@!a)&2ebY=u+cth0G_kE'jG?-1K+]&8/nZj:Dd\]^e'ql4U`i _e\X5`hRKlJe/D%\EYh]Y:I)-e,qn2AEdjmi7$k!B[S\r(.d+BB4G>t26659WZ=AR .lB)UOin)fY!J.]b+f>sO#SF:7(282!a#aE^U\j-Q,B!ZqOQJcO`6#3S8PeN:`MONCIlkZ4"c5Xf6YrXHB!N,HiP B-Je#_jGsd%RI(0.EW6YZ6'*'#)s8$!o/W(%E=KW5ViF((KZDq?%jD ^sFK@-VKj0-^C'l%@X^QjT[N+fOBIJ817c)hAdCM,51)tp+ARf8aMBOcg+[^7OHOXAd5LW[]fpmIm _#uY$E5EZ"4E>;sc9&l\!n%huIul8gTJ%_j$l]m-,>od:/@e_q=J=qD03,)8(+<1K H/:kr[:9>nntt6qF#FAA1'U3;;'c8J569$f'SaV+&Mg :5'VEG/ K@$&'2dl6YFjlW'.0Ik4ACLH(9jWsr,IUC*o?LsGJ.O=uOrHasYmTHc1*J>:$HTOW "Z36F8;0fk5;+m67;q"P)$-6MQP-E=S\.B#:RS=5Kme9MK4e6L>!u@* INk1>Y@3%loq(8G@)o7G+XISd1A5a*_OCnBK"R.$Xl5V;U4ReC0fu/:Lb^\HE,>]U Cfqq*1c+XRm>CbP?RMC58,@uFK545SdqZ*Ag;HYKq<=-!Ycj.RaUbT[#r_,c!MRjY5X5~> endstream endobj 141 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F14 142 0 R >> >> endobj 142 0 obj << /Type /Font /Subtype /Type1 /Name /F14 /FirstChar 32 /LastChar 255 /Widths [ 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 0 600 600 0 600 0 0 600 600 0 0 0 0 0 600 600 0 600 600 600 600 600 0 600 0 0 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 0 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 0 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 ] /Encoding /MacRomanEncoding /BaseFont /Courier >> endobj 143 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 147 0 R /Resources 145 0 R /Contents 144 0 R >> endobj 144 0 obj << /Length 2876 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgds[ SBc4;/KXda^]R%c8<40FK#CQ@d9,#IDQ:T@.,m:I ,DDb'ea).l'iTr^Wbn5ld*^:o^YOX(3G' cY:a'o4ZYdp8b+=$o/n)6mu<(NJm,_D_]\DQ"LN<)k5=ZrK$:AUVRn 9Q,EhNA0-DV\/Oam8a"D*QVqQS`UIhb36lhj7!IY(<,X4&/O$P"iVhDX0.G@"Vq"8 +P:icl!rI?b2%1)>7K6R?/`3OH/?%7nU4c$rVqE+H!Un9oaUUAQf.rL'o$/O%O%DH (sq0#4[WjLRcJ2^9K?Yu5j+Eo\ULP=Akh9eU10gN0$P^\rbl4X>crI.RL*;8/LdPR i6p*aTP\F\$1\H4[ ==][0QB`Nd+?Z@%e27@%:g84UL!)2sXTp7l&tqBMWl.#O!iQA9!T63oE[)N87/!(* D0dKO"Zk2ZNmDZ^#GB-S!XMs8"$g:0"]9aS;o!.:_AEbRGbG>oN,4,[D[pPN3!R<7 DHc>A%>\^1Jc^$6M@j/u&\V_E#c7]1Qmc7F]sjPr[Wb=h>mUPE4/$d(=Gc5a%k+dr #BR2rU1?hlYiP8j`ZG!;e=7oka?,tOWjID+),m]R+GA[M^-M`bC6*&b)BbnWR8F5A Ud-(I^qprk\7#moLaNC'mK`m:!"F!'(Bon*fh8jnQQaR1TB8Cphk2A-!Rrhk6@g?> UiSCD">L,Q0XefRBj)m`BEf*JG,K'O":ju+46:3if6NR0^pojZ,`%SG6$"BP0XmkI 5a508&LY_SDY^95[LH>q^gun4-F*BkFlIRY_6h-'K*XjM&!m ,KNs4_5<]XD-'HijN-?E7R.I>R3@ub(aPoC%Rl8Ie[jJrcJ+LEd]RQ?_f7EXXc?E_ _5`s(I!.#+4]%"RBXS[QMZ\#2Z&H'ia13ULi\B/#7r15Y'n^Pf44?L_qF+I&\nNC[ LQmqSa+mL.gFj"Q%L/TV)ImaL_dl1EaKda/f!ec'h.<`4c4UhC$:Bg_GAhH#d=R!1 =3T]s42AdfXIILLR@5%m#c/Oa=L/VmAOjS02'IM"MF8)'=\3=0JktMknO3Gb$7=_/p]4BmG@FIk$+S&9[RA+?F]W*J`+0tS7 ADDp<-AE)_)*r#Ln,>p3KO#V?qc8d@\5CmLj)C%8f%Pqn8JX^I"0G2qQ1Foc(\tL( GGME]LnU%qF'Je[$MS+cHcbHc+!9Y8?7d9B~> endstream endobj 145 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F8 40 0 R >> >> endobj 146 0 obj << /Type /Pages /Kids [ 126 0 R 147 0 R 167 0 R 186 0 R 205 0 R 224 0 R ] /Count 36 /Parent 125 0 R >> endobj 147 0 obj << /Type /Pages /Kids [ 143 0 R 148 0 R 151 0 R 155 0 R 158 0 R 161 0 R ] /Count 6 /Parent 146 0 R >> endobj 148 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 147 0 R /Resources 150 0 R /Contents 149 0 R >> endobj 149 0 obj << /Length 1939 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd1;lVX ;OH6]>BQ3#jS8OAb\miT$49e\!O0^a1S"s[0a1B>U9C)G3XY\qbn2uUk+aX6kh2aa $4[9(_3]%E'Sm/F.fOh8D_`2(JciQ]7Ahs[9d_P*^u05lp!#<,/bU;=(lP,r!,bV@ 1QCNhL/X5CWBmO%`#O5%0]$h*$\SZCH.'FS-n^/)YDk+V_@R-4kfj-f_kT9p_9I&` j=B9&mP6VO1[Ye_6X[bcNGIaV'g/RM/BfF->F%@1%WbONKrjl2d10T+]F[12dcV7K Y=`-GNQg]Pd'RX@K%'o4;cjSt6*P%O[R$!k))dJqA>Y(^=DSkb%BFmZ3.V0k$9;hi j>k&o&G#oln]SqN][8cDQ\hICehVrSW8S&sYdg&.AmW /'].?#<"(Dh#=X[+O9[XUMs?J7$ERW'L34NLcCEtS!`eWlYC-iT5*2j!5YtE*)YhegI$"Pjcs);b$u5RU16.X35=AKD98M\i<)eV8`E'sk )3#CVM.8cq>h*B1/A1.D3["Xl43o_=K,Z6''QT2Y&N*'$867*E,Mk1U2^UES>\Qm> Nhc[u_db8pX%EBqU_ubk[(rlt,:)"4c09W#1flM&k<)O=j'('WiNh_[#q^OdiXMXi W;*g(*$^kZXQ+=TN6h2gl?F@?)-[;'Z1!4A$'^QW'b.#1?Q-\%QJ*)AnK=lDE-B:a?uFgLZu?k]\Dtmm/)qc060%DJFqdjDFsK-.P!JA6Rrd^Bi?aql YcdWDGs41-jncWt7#D"1:(3iP,cbGa)[!TY%jmEQL3$$c'L?%9#WONQnC!)`lFB.# E&X'/aDf&?aDl"(#TpiI&OL_9BkdAO2PRqP'j:Dl7/:"0_jn`_"@*BLLbaa>U)X_c lqmeZ1T>Su(ns%-BZoK*Z3Tf\%/J(58KeU"7+DXkOp=&Lh#.C!W"@3&Ujk*F69rN\ 5tH!HGpqnI5kWtF;@B$s!=U-)OJilp!b`';iCBh3fGT6RE"*'E2n/;@,/Y:-3@+C' *4&>cK5%,J8?isoA^lKt"8-1Yd#.-!lJ+^3hnPBdX1JC]1$ S<\\IFqP$.YpI[8-P%M[>F[)&'GFsOEF>JCX&GI\hLigm!Os.8e9(;,pEskLB 2kR(*F<5[8:ka`O;@*g=-s/6p!LAG/8R2rLg=&UB>iJG2dVu(bqF#k.Ts^QKEp;qE VtC3'U%$Z2<(kVobZ:PT8sm"-'HEX`kThZn3dV5Ji\_/dM+eY`0]jq6WO-7sJcLKb WJr\q'_-F3&n[dTg7/VT_,FD,c@=,uehXBfQacQec6XJa/*hqmU'\?;cq&3f#^AJ< Ku/#!3ep9-V?4DNq@Q79!<~> endstream endobj 150 0 obj << /ProcSet [ /PDF /Text ] /Font << /F5 5 0 R /F6 6 0 R /F8 40 0 R >> >> endobj 151 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 147 0 R /Resources 153 0 R /Contents 152 0 R >> endobj 152 0 obj << /Length 1895 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd4N^fF-rBsIK2Af,"]BH`/ bjF0),3Z/\3c_B\7ADSuCf!LPFgh1>5ARc_.P-``KdFt,5klb.87o.kn&oP&!MV%J 32AjOb4Zt:ZO@VdaJe/TJYKY>2^Cs=.1@PsU+.'A8n-YbT^_^pL'f&<"Pl"m658=e :*YST.BuEUjuokn'Tf$q,G< :_X<8 @a.D@'tn/;eI"`F<_uIQ@:)t*`>l89bB(p(<3$1`;EN/*;SZLaQAmAAbF&t#]Kk"ZIf#5Dij62@YVu[iM1_A/;9RIq6*= @tN9USi)12QQU@=KbQ,BLYFeKI@sKImTGP')4$CXjEQBnN*o_H5>'LQUEQ2ZpeF2! @!:D5X^mkY^Sg)eem#=][GIs4inGMgZYd$nJW_ROR]],Xgmis.PLNakFE$_c^pMY7m7+\=Xe@kZaUm f5pjBEO=Cl7_-Ja+j2`XjSaMSSFh*1J\+hYfj,YSKra_:[^CF, SE>=bq]8MSWEgSFYKVAp#?PPm@>,FoX]n<'l;R_D_=VaiT9m4dTgH-8f1X)#KY@E# G70pQ\_)SP.),od#E9\1N(cq;ZN&$&>eh]a=DZX!6A7M:SlUH*' !au2,JFFlBFF$+P2\'j[/jb-`'UOY(@sgTf8n'?iii"%W[~> endstream endobj 153 0 obj << /ProcSet [ /PDF /Text ] /Font << /F5 5 0 R /F14 142 0 R /F16 154 0 R >> >> endobj 154 0 obj << /Type /Font /Subtype /Type1 /Name /F16 /FirstChar 32 /LastChar 255 /Widths [ 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 0 600 600 0 600 0 0 600 600 0 0 0 0 0 600 600 0 600 600 600 600 600 0 600 0 0 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 0 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 0 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 ] /Encoding /MacRomanEncoding /BaseFont /Courier-Bold >> endobj 155 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 147 0 R /Resources 157 0 R /Contents 156 0 R >> endobj 156 0 obj << /Length 2008 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdrej0AL'>gR1G)/ltrE9([hVpn-<,@Q8FCVG^JRI(3W>ns:?94EO=FAa\. N-"!1)8+([/sQ`.h-PRuZ4mR&Zs-)Hah6MM3'bnC%-&NNflce([O=Bmm);^h:ha0" DNRL7S[kr,%so8E482iAN#N0RK4%dI"#)8gjB-XqF6<>J;JVuFU#/RN^,fhM&M^*ss8l[IbEpHU@4u1$i(q&[u ?5eQ=[#H>!WHqO\9@=aZ(J6HQgE93G*3V7odT!`AafJ1N0ZacF@*o1PUt%1po6hW, :/ZbA.T'-of.>=Uq\(pn'HJh]bcQhS)UWWf74(rrI-F5JiN5[E;3UAH0%4q7`C!G$ D+dQ'<".Ya:9pVi=B*N[B-tfE[0n`do/LYob>n-??r`K:T41le4LnfF<`HD1<'dI& BMl4'+J_bU%9mi,'%OHV=7R9Y6rrB+N;mP9=u-XJH0F% #&@D4Nak_^<6uoF.T6_K8TSJn$mQH/&-bFFW#%8k!1GU$@jkMANWB."k`0WbVLlRo X9-;5&0dPS#*2ro+!dqg0$LY:>?N5YNAFFW7!LQ4fstt3Eo"4,rs]Q]:,L_30'R/5 jYkD"T@CGI)la+Rk_$bG4/f3qZFNaUbk+2OCXs@dle`j75!`uKikF>do)8(e$i].* CaU1'.EqWEam=i](6S0lHm8s7X]."a6]h%CicVLR7MJ95A6.mm0o$6Gd@8l[`'kXb V]]kOH3khW\E&9R.acfr054,9FL"h59ji98AnoGd-mds$R;tb$#u'!`!a+3;8W$ba U`?1L:rCfJ0l*bqfoU_q$GhrY6VD;6/_"65?5#_MA]1oAaBY_XF>>&EdM/!GD[p5< 5#K8#q8&-RB^re67Z$.CCoSGVB>#RZ>X;^Fs.t^G(nT Oi,/#2C4n$0SW&HdH.8oA#28Ye5i%aaqH\Y6&A4=^48oR@5M_!"e=rT?W;>J@Z1IR @hW!KVK7G.+`5=Z>F_U[^O1K><AJUh3!Ws=QaCRBh@&"W^Z5>

@)\%P]lRXMI+2!l`)?VSb+>"FgU:3ucK\U+PPm((V&[9,^2;^k[Z,kBC +/k&'\nsp:qp=nZXo0<5UubnPI!kFe::FQDLo;0MOTt,%Uk=EG0O"UQUQEV&MW"Qj OX,aY%[r,#b#FXZ!Mb=F/aHRN#6Cc2U'@8]+H5"t#ZGWs2iZjFVS`\\ElU0';F60b N^@&1%&7de3XlVCM"s1?Gj[MN:;W&,1GNV[#)=Ua+Y't&TPPH!6,GO?K`R,L^9KFa ;%:bZ_Gp97/qXuX#4LPY%&pQX!BEjF6LQd+LofD.c>+h:4"9Vlof]-mlfRG-%iB7( :m3aHQ*0a!T!ZhiV!tc:'TDQRbt[R?5LXK7bpU`Br=7UYPe>aF3f;:%)cP%Y5tV>= *FSNE!Mq?C>C/6]<*]J<#"rf/ ?s0q_T5E1 endstream endobj 160 0 obj << /ProcSet [ /PDF /Text ] /Font << /F5 5 0 R /F14 142 0 R /F16 154 0 R >> >> endobj 161 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 147 0 R /Resources 163 0 R /Contents 162 0 R >> endobj 162 0 obj << /Length 739 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd25T@KS646_gArcA(ii#f:XeYMmu[Wa`*F+VD`35&]IP0e%13A+2N/m%5W9/=Yf*`*A#S0u,#Qc8(+"QpCMC9:U&_ a(^48ot@Wri8qkoS4YWSE29f?%5(o$iK!F(^Fhtgo8GUg%c'6+3K0`@%tkQH5k>*l *6UcJO2@V*%Va@9T-t$ZLp98i"Gj7tD$WB\_;gX7,/TGKb:3cB+RN"B0JJ5?NMC endstream endobj 163 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 164 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 167 0 R /Resources 166 0 R /Contents 165 0 R >> endobj 165 0 obj << /Length 2710 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd(!XohgPV.j6!P)oDD_$qfNT!e"JYKkj2k(82o=ji_ 52)D7@KQqZ^3)H(9S*A]GhaE%SIOa80oDq'O!+M]1li*HfJsF/#kIZ&%l/sENT2]X 3]kOE+;V5"MPBA?B\U$P[k6?\oo(+mi`+W#fg!UXc8_ONPh=! @Pf<&f4K[ZOLk/i/&<*eKRY%KX'f1e.]-^u/[#T;`bsDdEX=KbFT?6#M:I [)G4*W!rcC),\5?e/G)aCP&=p5L#SKTP.k]Pj.rj:#Ik&UkF+_)I3IGd'5\mAlt.R #([o.&$mLR6D5@Q=9,'7,f$4H'=&`Td4-,g6VsldRXRuR!AZ6=.]n?'Ub0D#OTq)^ f[bjt4f&;V2O)YI_g>K\mX^QHk"tT!Ej/NBBc&/7c;fZt[1'"a#Gfi2'dSqu=s9Qg VkE;o'4F3cNMYU3@+$&-8RG*[F=J%KNee.3@5S&(re#m`lO#rp0lW!:51iV'bjrt> `$cN,oMCt%=m0uVXYiH?@YeH]P*kLtr!-2GHOj45#_7Fe*jB36e7lU_#>No@=@3iM l5b5KE65u,>"et(c^ZE_)Pjl^o\(F%LPF116hWeW>IcnQVp6NToZhLpXK7:XQ4P4- cjCunWD;uDK>Wdf)9U#UNf.d:/`h+36^g+GW>Rjk3T_40dTTU>fpeIsjLh0Bh$"Di 3%@9B[V4`G/+MabLIYDiP0.!4dR$VH.4A8n[#m7EBc>+D_^F!pXapau/]nXQD'\JF Pl[1N7EOpIV1W*?En4I$)9uMe75C5^3/%HKN80IV7@A/n.Sgr\8eF4*%WbfK6ktGQ $.BkY)hceT5]t:&d4+_8F[cI!r2=W6d%K& -0O"/A5.quZ6_/4HW<[L.ffec::*`2*k9BkBX.jnoE=e_6gfteC$S)CYkXj4+Ff$: /TGo$UfBHbU9B0I4jgfC._,VbeT2kH@>8Y@efa3#i]fbnjJmt\!e+8M-piUj&47ds %aC79V@dR2-$I58K3_&4ZCE\J'EisrXm/iAc6V1J5'p/G^o(ih"Xkg+=--#5%jd'S =*E%`:A#mF:&_o"`K>G-RP;E+6&[R5(6D2L"22PuQ;.XrNWs]O$_.to@f%K-(K/mC(cP!s>2q0(a-gf) 6"m/BX"+l);:6^[OVY#f4:$s`Z2>)l0delMKp=&M^H]n?KsATCZpVD3AbE@?l1q'M %on^)KmdA6A71:Y9"d$Cfp9PaFi4sAJcl*Zq^\LC^`:Jl5C;OgT&rb`,_E>\#ji\. oH5$GQ1fQFrgCf1FLk'O0Zk7Cp%1+rf(e;[Y32tf#A/@13?; endstream endobj 166 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 167 0 obj << /Type /Pages /Kids [ 164 0 R 168 0 R 171 0 R 174 0 R 177 0 R 180 0 R ] /Count 6 /Parent 146 0 R >> endobj 168 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 167 0 R /Resources 170 0 R /Contents 169 0 R >> endobj 169 0 obj << /Length 3142 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd1;lVX ;OH6]>BQ3#jS8OAc8R?i(s]$(Od+XCOlq\$.LcgA+?,^A"ET^Ws5%)rqubgL>b Rk*Y2BoqUi3ZQ'B'60+!F>Ok0-QrMPb%As>be,/,SAS;dkR<[h%JCnr(lP,r!,bV@ 1E43;"uC^L?UCK+pfr^UFQR^(BJho#+h6[i%W/Q3k;j(Qjqg+q"2KmKD^usR4\9n$ 1ah]^m*W7;B\Lrn9_6ml.YB]G1q69?T$`KA'6O_oK*k()_X0>"3"?%"IaY0Q'Bm`O ET=X*Uek:8W4^GE+qtL(W7isn&(n`S%7@2%2RC[:N\i.-!k^:u_k>it8V$lr-`ch0 L,$B!lTd6hZF[cC%KiIaYJ5"MqC2YFF"5A0iY[CqMLqY>WXL,6neT-\HpBVY:<;N. \reQOrBm^9JC]_-\C`]9m`X?;9M,[^1oeh?MWTmC378Sfo?<*&-)l.QWs,W;WirFT UIHu]'O])e8-g[b^rS/92^1!XKj_QN"$k`s8qWr]0o7IY7h\pAJDFT^#V,n/0MA?Z )6(^UOLm8He'@U:,1o^'"9Js,7(LT"rBO1YF@-YM:6=*a"f$dBBFM>4I^(o`@(Ifo :FcXH>`_n%+l,tK@MC34o8K\/m24R95@_>T>u]*+<8%di!9C;ui;g39Th1D]!\05" L?#k=!eF(hQ"jZHdFe(KbH^/"1MFjS2M`!Kb0j6 15R6C[*am19rff1C'1:QEIB,:`eIV4B?=^fA:$)To*obVo5t_*0XtFHF;AN;/_J9_ qqm+I.j:5&2>J+orT1QgD/meS'aL]l%@K]6,]CRO/ibISm+nsf]3d2+q`]gq0nsh"?WIaE>RM8N*^e/WMnr:M+g<>L,Ffa>iI5"l/>S\3._1mB 0O63=&0X#0pXc.$I0iKq*3s`.']H!>m+aW,OPm!e!Raj1PkG=6.)M?[jT^i-M&mo\ O]1717dF_!,@K:Fi*iR?aY_O82Vs^dj>1,"JUJrf#n5+@E=SsJ;E?&W&^]Rk618Yn 34TqFb7o^8Tnu.XEl8iTC)]a%W&H/n*1;cTA8NqJXgKCLI,%nADls"/8L(+k!NMR#>f*OZndifNES7Sr.#=j7>&_E FTtur)lWsl";I@d;OJM%,7HA&J]TXG7)0jDcn.DY=\OUM),6ot3+gBS.L+AU*a9lF @u>NVS76k`O?9V#X.8e%OGt_offGhfkhmZgkbVbbQm_*WbcAX<%>tng%>>B+54i)m Ba&Aa:,ElZ,KDt[OU',0_PIU)C(8uX!RY1G.`Ct^e&_)g^PH^:h<]+8b7V<;Z0j$nYW6.k%oIrj^M\]i?P_95uDEXsVC?JjtZ/l/EjF9FOo 4.uGoRq3:KP,Il(>^M9nd<2J0L4`SH#"0Egc?D.Vjn,/o^5A95P/%n`fNMqo=$p2Q %d;#5T6+eY'a9h'#&l?/",Is>b@3G"HB96a$^$W1X!g9X1NcnW)Vc,Y:[O["$@0QRaOI,pc6L2n-j/rdOek"_)<(rKa(;.tlh Rd`QO2WZUQk*bR>qpNIG2L<)\+-+l^G`@>ZDfjshJ'RlS>pp=]_\U3N-R1(K@5NiUO\`/Y*iG.Ir7q@)GE,C[s6q@9]d7:U./7Zu2POTPhF 7"dmc.o^(jiiU6poa5TqYC`j7dPDAlp.l_p$Aqo;VN<6KfZ!QtlIrs>KJR:XC]fhN aMrShhW%C(8EW7rTKqmpc8pW'b(g endstream endobj 170 0 obj << /ProcSet [ /PDF /Text ] /Font << /F5 5 0 R /F6 6 0 R /F8 40 0 R >> >> endobj 171 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 167 0 R /Resources 173 0 R /Contents 172 0 R >> endobj 172 0 obj << /Length 3031 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd1;lVX ;OH6]>AKKnjS8OAc8R@-M@:]f8@2k@)9aF+8sup9:H9:$V]c([:inCZ_uh0efXN@A *6e]ZWNaGdWu]D3PF-m>?S\2_#0H\:k\NBLB2cqBPf]FJPmL>>oP,+`)mASr? V:4^P@KGg?F1^;tpu2B)\!Cqpon3Qk7]a`o4_'$!3K@T?`&9.2TKo%X:akHe%&7e* &.PWI<]$hm9=!r>94H`pKug:Zap%m6DM9C]GgN+:"&"NO63D:.hi,I"=Vs#+**,!, j!\/d%2!\cif9)`5b^:?!9>oGK+&6nVurL')RY4-da\h;KU+(N89K@ZDcFcj"ka@N.VL9i';pqm%Gh[RlI0oX%YYDUM>c1';_@;1,6FX pghab%7=+G4fGE'hD`kB`\ELUAa@0'Ecn*;%T=nd #`b"qZj+FAUd#F&l$P!@6N^lf(QH]?5Vifn*""T`0LKm2^_;Oh3']ioKcVLLPB]&< (VTEghl]6(3fPA7gN$;/7))d.'SU:h8Blcu@D4?L^F]l"PlNMJUC2)rgOhKO;)dlu _Q#ooZG%K<@S!FI5(dt01/i%)Z5Gq1#hMOGR,)CGKbW7G?,a%5#;Ur>kiOqL.GbQf $ofOATVu@BmaDX9[Lk+Cng+Lj63A2TCq)iqMl+nSlRnOdfu,6^X7iBP-0S4sqc@'' L"h\Rc30K6c4_JIgpu+/:cCA;k8KsLdQ>tu\?sI-U-\G..,?r9S+cjLl9ZW]`(Gh_ c<_>X!"`e2p43M5H7WL*KE?f23XT@6jbgoo6i],'F5pcG_4)X*+,MqXM\f7#j"";% 15O-(5uUO!2t.8h]].D6"PG_/!GKH<5>%`NGO2tqEui;pB'HV84l3[NGXT. 7.-"o!,ah@_(IQ7D"%O@]dQB&^o?<5(liViAZHh0S?uON;X8QRiJ?^5er%Y>fOcML nT"[2go6;pb#?*lpHb>\[0k2t*TtV'[PX*&=TBeKb2+;%"Z5),NjRk!W]Ch*Q($Qk86%X,Q.Rd?85\d@n>hbq$6H:XQR,c[c8/FJ-j9F5/^=hEQAV?5dp5"cV#=%(JHcft)jr<* A[o47)CY0GHjY1LUT-`j?kWKjPAm_.'HV#*BP1Qn_'"IkS<%?)LnZE-Vm6'4;.oZF OpOuY,U@\;QOD"I7eHn[6ro!*FC^?V/i:jB2To?R?X'Cp4NY,/rtg)+T,QNX%p)Pofn;aO\oOn5?+<9^U,L]ksMpk!K?>8+Ac-Qn^`g: 0cu'ZCo?=s9p&:>)mX(V7Y;tt3YlbKZ1b]:T)1Q00L_M\g&coW'6B>9/DV'jD)!3` /Z1#f.)[6f&L+;G42o\`"Op^s%5MK'VXd@*nEE!;WCVnZ\:oI]jLV)>[Yq*c`i:@\ _PP*Q<@q6*];m)HljadtNt1R++BsKa^&2@(Ptd!CH3c.Ha'fA7I7g@\$WdDh?\f,%>fJMj*qE50*+J_$K0 OW/S_h'7k":kB(.bTd]E"r2<$rZ$$<,9BD^G`ei0A_+qfmLo>8G"T1b@2>lo:uVgd U*q`J`Z,csE,EZ(EJPg3`#OC`>ZjTpN\UUNFV8.i'`jt'[;D-g`?P>7$k\1;"q91P Ps)gRPn0A<^A]dV)^iNXV+b3/i+E*Oflo?XXe*^A,,.`&cBs18jH%6$>sT:lhIB_CNcBK5_tm9 b.YbV9d_!k"mRLq3f.Gc84li/W`Y8hbdn+Zp]aD#j:mWS!bK^70;+1F@KH~> endstream endobj 173 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F8 40 0 R >> >> endobj 174 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 167 0 R /Resources 176 0 R /Contents 175 0 R >> endobj 175 0 obj << /Length 2705 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd1;lVX ;OH6]>BQ3#jS8OAc8R?i)%@qA.CeP.WgaHQOTCEtA29PU#0NX(>m`Db6jAjGLnnrN SB;%7O\FtZ1n4[R1I8\=Ct0B'jq.X'O$YfF/*?FWK!C+[(f!O6BXZ52(5t]0>(AO[ 0H#l/=bA0PGT\;j3o_pqK]XC4)pDr"iOdmq.Y.T`1b7Q&L*VY.UP>kUG0oZ_X@QFZIe9MB`:ElMI9p9V!4,bE-m`pcP\qp4*6d+:7 35]^W19=:lC_/D/PCPS#n#5&"c(Sr]30(*,`"4tl0Z?NjY5E\j19XS[,"7@C;]+&, @.`:bo)R2n$r2n+&-N*SN'&iZ;[I#<.9pp7]FPW5W4_[9AeYN'.*OBUm@3M\/5ngq .6blM&/MTJlTV$mdsErI2<("Gjc+n`H6J:<>[&#O&o8?7H/rGi8oacM@m`+\WOVLbNj2&fQLuS&^?9%#unJMLu2\%u)7# D\7c5+ClJ+Nj)=sL-_bC,R]/so4V.tWh!;T"3P`sLEXZV&.*r5S0R7fj^3WE94f`^ L!%OKV?I$?H5*Ub^hsiRe'r^d]]"J'P9osd^Q/ZAFp.=>_1EYfg6e?UQG#$j;1)_L )aFsI+f5+4A+;;i#,^+7/&4]A+K.+bWUS'k>L$p!e-#lSKiGUFKKE/613hK?8lDEj .ZMi`E@P`W1,9d-&?PiI1@cZ[3Q&!uLA&D_lKSpJE9\SZ^hWYsg]b#W;/s`gUL)S' m7"L%Nlnb&d%UU":ob%rM!8jA[X@c[6FNKm=XDG@ZA7S2k]%p@]rZ+LP439aMAV,_ k2)4B\1rIZZ2Q#u:`GNp[70;t2PW2tq@1:r5VGQ"5m'k^"$?cfk[Ysr%Pqj>2,8$ .m0k4i.?Sk%RYO_:6@@hUDTG7T]?_AW-.PD6oa2R/.j9[r95-pN0k+6dlO^R)_'FB X-*0*9.GBBWPi6d($ijr`d$2Yj89/b\f=JLE&TG89^BThF\PK*/>,GO'`i83"fe"% WPe!@%Z"g\`V$%32We=BH(]tL:&$:_AAr#IJmuNg.7/6]S5(NR2+JEOXu$?>6[Kbl ER_6^ohZTe)KqJ]#t<'0&:k2\.j7f&;3Zm2n'hI]?0ZPFLGTk]d>Hod%O)3Gf,h:q Z/l,i))TSu1_F?L+=p?[3]qB,Io,InG_Z=KbM^fjROb(qM0/TdFI\JG\\'q@*&nt5 Ju_Q2f0\e'q@`BXb`-]!K;P)h:UiZRiUC\_!JYoq$1A_V*&\:&X[WM+p752dm[Wf9 q*dd_&X-)G/+PT.L/lT4eB+@hVQd7$\N1Vl17saD^3Hg]5m^jC[[q2=:V@WJmuY%- -d]+._r0tke$MUR[%..:E,'g=J8#N(I/8UC[V6=*"-;Er2q.HM:lQ*"1l;`/LH+f$ gbJLt6+2imdq0POZ*]LEI9NKs[KnAU@!r$^41W

&.=7NQ;\Vf4ooL+%e)e[JlXI HQ=Ahr8OAkILZQ,:2(G7<&7Q#1'^h6=/[r5Z\adh:fQdtS[#2b5g!iWF#-W<-(p]7 dS86A4#)jHW"*6pi#JeNETTq6Z\TJ%+K<"Ki`rH2P-0V`g!\eYNAn2!-o7,ZPp_hj *49g5eP\g$A6hVQT8K@m*@F2smAOMZp@t60FSeJS$rqU&W=miu07-CDfi0tA.oW'pB$1D*A#aT!E\>)fFr/d";2;6e+^Ugn.tRcNNM=ks5INVt Q1uPFR,q_IM:SCG71V;Np*R/'#)]+E(38nPD.Z4614flfkKg]]%j_iB=iCrW09)Jd Fh3fJ=c0#U"CXa-3Y4Bp+:S5`moe!0MjB$(To=p,lq +EX#2r:Yj[JsIq@m\0k/;OLXf>m$1!/6PM$g<2L/ntm$n!L,\igogKKA:;<%FjGeF nSc=AAr?q=7,)JJMlmLF][+ZdAa>k\Nimq,9CXO$ZW_e+6)$:E]`/1tc5fY6F1X;1 p$*2^;Sd2=URAR]eVRd^NWrcCU2fQ\ 9iU]2=I<<"V'Brq/fJ^U#Lg@c;WH8iKUs@;&G8CE/^W(k-:eDFO(pP5bV)Na+l*UI nm:61)P4CgpHu5mSI%O^[N1*k*b`_0!BN'#3@X7eQQnZ9d+?R=(+!%?Nb# endstream endobj 176 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F8 40 0 R >> >> endobj 177 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 167 0 R /Resources 179 0 R /Contents 178 0 R >> endobj 178 0 obj << /Length 3198 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd1;lVX ;OH6]>AKKnjS8OAc8RAC)CslHGo22W3LNVi;^o5s?Sn:k#*Wc.b%*?U9W#_KJBVpi )\(tYG_!c@d[SOZF$r,o@PjVY#6P9r?u@cV%Yjqk-`*9G`!)nJFB7^l(_"FRXN$QZ Ej)"YSq@?Y,CCRA*r8[f#>ENaZu)Jt^Bq7.$a?;PNY9cQZ[?[%f&1 WrrJ0i&D1&qLYE#ck;AR0F1PnN3,Wk5,aBJ5X5kd8_Cm@ZloPBlb+d@c8I3$"'@>H8;#MakbH/9^dMd"ciW9 <@(.X[NcK*&3UR$i=96R)IFUEZ-n1Z@54>tf8Ot;^D^.ra56e62JlP[6s`i1e0qTNZL^\H:rGssIji%Wq& k!nX9];r&r?J>a>Z%N1?hD%J0;q[ZD):`Au\O."E0TM;l7D1UgX&CFh?u=ci"p)j\ ]Z?NSF>8fD<"G;\rIZA^M)G01coHCe%>cV(N.q9K'9ioC/E$Fj>Zfq^c'e-JNA[to Q"mG<\7lA0e.;f!:oQdSBh&^]"bqP0`fWhU?KQnr"i+$_*6VAiVTCq)=4Hi1;Kk;j N%q,lA8N. M`*D5I:SpZY8/6;2M-k>)s99@:7:*=X08Bt%"M+T?5WL&CGf97MI/o/c#Nh gMh:Q&C@SjF4Q9P8t'h$MJ/Ot2-:sCG-3[mA/fT1DU%H\>?#T*0?/u3VgA`EFLnB^ _nQ`qpek_r/UGBo+DKIjf/G&-O=*Nsjb('a$4'0OT!6[8?uus36'N]7:sT4Y.HjUM +KJ[H]kP]leXe8Iigg8=#G5f#P6fI0WZo1I%n,`X-cR>Df2GSm<&CM==P[2JFM#c@ =>_C\lV*"M=-/uZ5#l;meD=MpLFY/;7+.#@q*+/.`'?o gkq;Aj!sEG..Z<0P70HHm2F%V[qu9uW^=';0N5 LiGVVePM;/U=sfM3gdE6^)2,$i?OnjqWe8rS_p 6;+-uL?CaR)%]Ag5EcjZescMgdP#%@=pjEq4`kMm_L]s0q/8>RU;!(!*H=Ra0]ct` [XNdYr+`'Qf:1H\ingETe'9B,6>+ji)F?oITu!R$WVFEtL+gg#gkSec5SM%V8CfB] 3iWW`#5,9uD^bssAF"Qq"+W_5JWq=J,N/`4I"piIC7kU&250XJ64jmC5pb"p"]`XD/lWen!B"o0U'c]I=Ucs5jJO2@)N%Bq 'q]2%N3'uA_n1e\<=DT'UP37g*Kk?HoU'Tkpt\I.!C0g.\s-k\8/=:b1<4s7SPNm6N_)RCrC<^ lrXGm!@:K@0KSI<')H)6#bhMG+9~> endstream endobj 179 0 obj << /ProcSet [ /PDF /Text ] /Font << /F5 5 0 R /F6 6 0 R /F8 40 0 R >> >> endobj 180 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 167 0 R /Resources 182 0 R /Contents 181 0 R >> endobj 181 0 obj << /Length 2900 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd1;lVX ;OH6]>BQ3#jS8OAc8R?i),8U*Od,'T(IF)ro4`rX+>KA1VZUK/K0qR'%*%n#K0!Er ),<[V!N14@3#mqLUQMeP[PFAuRnBBG'J:=4F'=bT2Z"%$N(),tKY5063c9TU9c/Jo WR^0,B/:uu60pS%H?K2UJD$go)%GbWNh=CrN+8gIcEhAop(odK"1!n=?u%/-#%>(W bR-]fE8OUWbY[%L\Hn4i:sigk5d>dP_Pk)`6A# c81P>-Y&f70TEuo'*Z"*Lesa-NU(%8N=#C,n[tVQmcQd;@-GtSYe,*/6XpKc+cJct ^?*:N&B>U+EGO)A"gO83^5@J>?IZ;te:ei(NB!m#3`0F"k.TfJjQMD-WCA?X?^b@'"e2E9L`]?*+ 6B70oB\K3dZI#%H/(!_n^C<_4'#(N-1G$ia%-+G.,!7YCb@\GMAR%#c[7&nPc:oL7 h$kj@NpTClF!1hA5snFGKEHY4s1.QUVcM86.*a`s:#O_d\PIQ8.P(.qcHqH3XKM;W "*j)h4i7s71Kd'k7K1PZN/)q(H16)I5RiCXQ 4lq8)/Zeu!:g4.S69/")gbZG.m.*.FE:5E_dpo5MTkt\OGjSSd*J:*j!YT^=agmC0 3*\'$GnA%m6tMT^d=T>`2K?Y/68p-Va$]@$6[a$fE+i+*?p$-pQ043Ql8._n:t#s@ R^TH$B&")pY7=8,5U>r2JDU1qS$fbu/)-7Mdg_O8M2U").kqE1EU`=^N]Rj`J_W#d [5_q(.f;)>BsTj1Yj*9KD.#TOjp*@+MpM)1K4JXPnP+#2NO*VOXHiujJ4a(]%G@X8e"V# LB,aD'Qf'Q&g\\m&D\VmXG9KP#W8>:5:BDHDEZ1iR$03h'GtQ-?]#\m^4hN(S29PL 8D+t.jK*`pdMtJf&LPA9GdLH&nUd^F6_&s)d.!n_.=h.Nc%]M/m/Ji i[@qFRfT-Nmq2g>"i0V@P<")[[6s"@H*?`83'!,QXP:Z-9Uj(SELdj\gM%kbac!g#e3l T\5tf4WPYULo:TQft+d3FXK=a;P:K=aJ.K F/nf)%%^Br*Y#.i7+*'2,+rDe>#VjuD;qiX'h34rH8A.B>d$%QCfag%G\c]"YK!o< 8ZB7lRHi5u0,+?A>kGp+-\N$c,_jWHj\&$8dYT2VaVH/oTLUSNH7!.<#k4d'UTb(, 1;%AJ"/SGqQ%$Bglt%_ZRH6Rtf+`f:A.)/6$AEDLj!7/9AR/$eFc-l--1L'7=F'=1 &&Di%WkjB+(#tIcJGM:3L6p[u<'%N(e(^?*31"H@>"I`5;e![HOSQ:4-\U$7._;'% =>CBRA2mo*EZ(gV-S,#c\?AREQGEW.D[ZAkA/i&DUrgMER#r"$7T3M>]]lIl77'tq %]8$n:=>@`haHV:rUg!_?+CUT]2Okh$.P\6C"aL$QF]]WC!1^r-mWD[]drTkgQ.dpVmJ `6`sI#5:H7+'L.6Gl?jF3BJb]SZ:+Nm'-a;lB%">Rph79Li"HmM42';@ErIK^LPr^ i^>niNd*;th%",HDSp:2031)ZWO>b$JOLSc>E=&RJV:l8hR#EtC_38.Wo>'/gOk0pJD_X3_H jgi/uMOGC\>=`1mdl(`ia:Z:B?D%N,#\=SP6A$b?al0t5K[0+#=_q$sj.bLhLJ'-r 7Fb"V?q]tE1!E[DAZ7L6brX?&i6?d7Y$o8]r!)Uo'b&:.*Y%)*2EG6'5fa3~> endstream endobj 182 0 obj << /ProcSet [ /PDF /Text ] /Font << /F5 5 0 R /F6 6 0 R >> >> endobj 183 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 186 0 R /Resources 185 0 R /Contents 184 0 R >> endobj 184 0 obj << /Length 1551 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdete`=QGI1nf"l3"='RUNp[;c8RAC)@WN)r:nEN1*"4:dg>)# @]u>LL1@3e5\AW(%%;$&X&sGpCc4ThVV'tb[r(E3rXK$\fUo)l(u.-*aA]H.YGctT `];1t:cjX]A)'-SUQ`q)NGeX;oE57ec8RW@.r6q&_g(D4%T"D[">fOL>i7:!655/1 .#;CM#2ulg[lH_SC9f-:URp\($_m4?[-^Le-O_S#E8a<),E.F=&h%K71.^.BaT`6E T^%9q;m7`55m"(9KEHJC&4Pm]k:"'jGiUfCT`FL;-f[VZ5WhD,aSh.Z$cG'("*2%0 7d<$'$):$hU?K2;:`:p_%>[phL4QjH'F1d6'G(BKi)&EhJT-]'"X-FJ.LA@RW"i"j 87ijj<8a];VcoQ;geo`oHN:cmT:tVpAE.c[u=CYC^oo3Tff(]u(?D\JA lk\6!/WXGqH%%ni;aEc4C0Jk,kp[+/Xui#)!d1Y$%&5Mnp5YO-_0eb2FibcIDG(HW bOhm&1.g)_ZmR^A5im@&DF9C9lf"YWJB=!5$4pX,f\NpPZ]+'.mhf`_4jUJ5"$tqO /h]Fd5m8+Z@uKbb9MI(oI8>-T3E1!dLLjFI;]L )$][fdMFDaU0UM#_<=e5LB4n]jRt^+akKN.%,&S2cNHl14],2CCQ<,WGV_J:c4.Jt :DOQs]R$7""(?Fk<&00F4<(7.A"=&;N4" M?fm>,ki8M"ZpC`%OY@29AoD4J7tV,"quB=6O7-1O.lG"68k[cWmLt\o^N[ZV%40" AIL!*W[?ckDTW"J2=3Xh1prqG9\Xb=cZI`$W-7nVOUtMYkmiI@@2G>r8nl>fW3n>3 ePX3B?Rq)rY#`,r9Z]p^c&HqU:f%o/J8]kWe[@(U[R(ep'm'V\*0f[n^r\;a$Ou)a #*`m3>QTXa;8LC25cEf_)k'a!a;boW4q'b-Y#Z&K0<:qDl.-5%F\7lI";5/W_JhdI P0aEF2ep!2]=DO6p#!p4KL-]873H+D~> endstream endobj 185 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 186 0 obj << /Type /Pages /Kids [ 183 0 R 187 0 R 190 0 R 193 0 R 196 0 R 199 0 R ] /Count 6 /Parent 146 0 R >> endobj 187 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 186 0 R /Resources 189 0 R /Contents 188 0 R >> endobj 188 0 obj << /Length 828 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdb0C%R>`Ll@F/8L%''0*e-#ioTJs?^@,1E'lf_r6+\W:c __#<(*Z2`[3\(S"9.CXqZ/L9EP9o]T&D/CG//j,Dn;&"!+t/+Q%48N]DAe-/kWg!j&"l0?"TaEg,6bqL \.jW@kbod.Z;d46^9W>u4Ju)MEZ@^bd%he],']V#M3CWb2T]Bk[^Ih"Kab'$Blp's #GFd$/qjR""ZB*$Lg_=<0:_%G/)n`aca58@gsWRs932h8c%BQbaGm.T3(Rp+MW;6" >FO`QDNh&%\ojc/ot@NfU(gW]4-'>t2*GE9VI9JE2iZjZaJpX Z?,C12c&3)s,(b$5lnQK`".;FQp?O[THF~> endstream endobj 189 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 190 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 186 0 R /Resources 192 0 R /Contents 191 0 R >> endobj 191 0 obj << /Length 2453 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd+p AN&/[[Nc*g5[cDb:)"q\J%r&8lh39iJ@?BRW\,E+)kg#2b4E:i!6dB^5lft"Nana* #)sj@_DAU9Er$uQ@u7`!\BH#?ZaV%.%6pUff`s.+!DPeU4;A^g5 W"T$D+\6!MV`-@M&K(g.\8]\;W;8i:,`X!T\h>7k>WOqK)a61jC[)8\a=Y#ELc92; A/caAgE=TB%Prk#3D"Iuo=`C!:A_[Wdm-"3%sZ1\N.Cum2pb>^\='o7dWhB4'&(:( ";7s*k;m%O;b)k/inj^/:^`Be1LW]HkDk0[hE$VU5j:R2NmL$N%\jTfT=U"Y_Q!d\ ;jum[cX>Zj$nZ/J3Xp_a`&A.u>tXu\""Z:^^T:m&W4@f`(,cVSGj_R?omMH#_;g'm &.K<=Znf>@P?9cXZj=T4d&Ql@2`\e!T%&cfKG^Aj+k$3:"\F$E_2=I6ljur'bJs9> 2(7?I\^#nI5gWpC?"LS^Lt'`R"*i$$?=QYT5`0'GK'h2%Q%r'uc369gV(PFKQ]8n] g?h2"TNE+eTtjbJL'Xj75eH'?o6h(c-+O1FK.,/(^"H-,3]Zo^]m!d"`^2]BbsWFt "h4I4'LDN14"blFs^LF?)AN[Gb1EUPS1l[CE%>=c@PlUI*s$ Q%fi"RhfBK5^A_o#!/u+/%Q/$[bF'a_iWG)eTY-:3m19ClIZQsmP($U(d8T&3b*O! f\,OZ!QX>&%_&UrY'n)@N4>_-[n%*_JmZp5hc_f_1&]_H7>>&Q;NNmoUqI+j!8)T9RLFY$D%<936S#6n..#SCfJ!NBP7biG)-+"l#a%<=a3$&9hb&66pc%>U=U0JI#K85VGX$dl^@A/GRR 6Vgs,*'$7%//t*-+:*Z)ko,cTlj>"=/3tO*ce//G*Q23Sb/"Ah,/bLSG\gG(`<%TE E`7SJ-nC!rZ)"V6$<>:dI&CNBZ%0cG'qe5`"R%`Fd'O]YYTO(aJo)QBDa[O3(Hp`] 1SD`U)?Xc64ic,h&i`L9_d\4GO3q6&;!PrNP62#e5+9?3,3@+TfN$6hEebdYp`6^M ;sm5f!noh[=s;g4;k6h,F1rY^@dYc*M8+ea<`L!;dX6VJKG6hZ6=`+dSEIkT"a[%Li*0gQ'm2h?L;F2$( 7NqiX=-/Z+/FP#\Q]jQ[?504h8U*V&TnP'A1S&O6l*F%W_IKGu&Mf0A=sN:5Pc!+= @200QXAe'O44W?>(;ed>B:3\r;m:pHpiD!T+_[1WE4!Do>6P%J.>(Gp>T)`R@,]oG #.'p$/P6g@Cj6LZ_m$,cG[b2R;@G$X()K<18gI*;)TaE/",/=Gtgk3LO?LU`c].!+Lq1epF$Y#15X5~> endstream endobj 192 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 193 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 186 0 R /Resources 195 0 R /Contents 194 0 R >> endobj 194 0 obj << /Length 2925 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd1;lVX ;OH6]>BQ3#jS8OAb\miT(<$HnOO._k[pI#V_U2URBfUtYL@^hWfO`WlKEMrsZVGBo )pUq,%1M'je=7"Q""D=XAMS(X7oQ:TbTJh`obIGVhfHEdd5V2jktmTR(lP,_h[EPZ 0B&Bpr,X!2b]5Y36j=\-Vg)i0fV_4a6%n1GN'kZO3P@e)kVOcA3'cbi:+N,!5sq.$ YI.lM>29UI.f(Uu8U/01Ne;l7qDJ]p8CIucMhK^mD1bU\`P\A*q7pk-C?!$=9H)Rk K0\]^X?idh%uuPZD'?h*'<3"n"/IjcZ@ROae=>brdIC.A4@Sm@?l(K" ,K(-p8A[9`ZV9CAcj(!o:(Rp&d+6^iYl_X$N30/sho"i.Nn[O="R\4.m?!psNT7,k 3K@uoZEZe.R+V(:Y2d1a=*dc2QYeA%bd bo&%XD#+]<'9uod?&id%1>;O\gQX,ECcOg;!Li)8V=b5'3K.H5au7i:k_aqO.1gC& ]LY"u(qb1k.cGr\0\hd9Lo/$##,.]'&-d]iDPeU>65V,%W"T"GTa4R"V]l//>T,6R MO]QA$;ralUPUHeLa8.>@E:3?J8;r8$&5P7NIs<8&.J_VqLYpk'>VJZ$lD7J,,(rG ]!8hUm)aG!Pun_+\+%cGcN?_ri+UY/>5"tXb&r:EUN#IO2F>!iEkW*>'G*6F?Eo)[ apS!$?;$_n.1P=IjsW3`T7c+J'R8!UH*A.OWr&r4bMROHQ\AQ;_[2&SG5AK?eI\JKmjL:#OM&78],l9;(XYHF<4'g 7"-^^)[=8\dp:Y>C8Fc?Zbr/Xc5EVj+isRJA"juP9&!!7q>S=L]boV9DhuO]gI#(b p(#/bC3k\H4#^J,l-f;,5mob/%'P-To:)(6A1F%SfmT\#k?%]j6@@Lhgg)B6K]I=nNiQAAE#L3]C%83mt]P1^ntM+iMs d\J`OCbm&4RZj16&;q0_lb[=SJ>C*#;sBf2U>qth9(NLE:UK)WLq%eEX?T',f\Idm 54DYLgR]i[VSOOY#*%[`;T4*=U8Nrm6OCt#-M3rMR$sdN CB3VJKOA9EbQnYqjAa!_,!3QCELm^nQ2!D-/q0*kVJdk#V $!47_f92V+AutNq#!grX"5EKu0)."&T]@%u48oQg?3d\<\_2Rb?4*D.&:]B73L*`r Do#7']rD_>h]"'#YH+"2L#bG7@+^BU@n@g;'W@iDa*n%XKiuthYH`Ig&W_HZY6ZG4 h>h25R3e!'1F_+3B.sE]cp8(kP.f(YMXuI^H,mE#XPPXtOI(68^(&C(4-Mab/GL=o G+j-<,+(ggrKdZgoj$8`EH(FGoXNJhfj\2.)38UlSu/YN!Xpkm:C\MmA>^6,ClsZm 0P/7_>/f-aE-uZZ&B\m/+9J(;GNY9F`+6V#=F1^V13d=9iL7:tF*WK+Mb/%$"GOFm ,3qo_M6htl\(8T?$m!3cP_et)Q%3BSAVbH%cV:FIH.7jQA=G'i*Y^^/dneYubma/S )6+og04?XZ%5T=pX0^(Y$0:Q$!.a1[K"jQHC0@+$h@g-I7"$>Dq3uC iM@TM#IbXm,?f#:kV6V*ehX/Rd'sl+]@ukj8=L`\F`)U\<+*!^R9+TVQj@e]n#)^T 3=lWDMtfuF&.gCA%;o%]dop!b1u(X d1&CUD?Qcs7JK.cA3aR7-L`l5;hQ+P3V&V[4TkTniUPaoZKqLRTmCV"Z?A3?TXO)G S+'kqAs<^!e,?B[1'.~> endstream endobj 195 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F14 142 0 R >> >> endobj 196 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 186 0 R /Resources 198 0 R /Contents 197 0 R >> endobj 197 0 obj << /Length 2475 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd1;lVX ;OH6]>AKKnjS8OAb\miT$4`sppr$\p0cEf=dqOL)Bf;MVU&u:GaCRAf$mt!WJEU[M S@/Z$AI.((&/,F?IBeIDCp0:N2:Dkj`aq+M36&kWJCn9j)9ei@lp+`/(I`5P6%I6> R3NZr7o!L>bLEd7%^ko%^MUL6)%8YA8_UnY]oCB(2iQ#iL9V6RprN=A_g(AV%Kd*= 0SBOqdPHm)l@.F&m:b2hYG.[hQ];O%k6i\h!9BfOE.H*2\#u[RoehAHg1g[+NMU1H=*udpsN/-UX)h2aQK9eBTU>R)ooX@78d' L,4K(e-n.GCG\gS/GLqq_#b-4cu=6J16.E7jY-rlTKtQJ;]rsc%'pq44-!#J^=gns b96CLJO>+M:GSGRgSc]cfK6rF><(9DlM0NuX5b&DloDI-5hERl6giXXFIefr4.#\3P1H\]^R.\LeoVU2WPc1@MmCp1X8]5p1d Q+^1@eukK`-WVsKT-=`k*89h_mfUM,]RbTRKeF['3'+lmCL/#kNqtSdA.3uraX,q? 18-288G"ZpA#3qtMO9L8'FTId=.>10c?Ekb".jpc3_hd#q_b`'_Q$_cpu"*Lj%cEX bK`MUUgEL"@9kXi\ltHSO?7kC".`bNi<)#AIa8#Bqi&mXBg`R2bcFV)[6u^$Li:-YZ8sq2;/u[Qllg_ *7_[,W:-;[5'TB$i]XO.oNO%m"TdC8L;\PW:nrP1lGY**6kYF!MM_NH/LH\a2Sp,A XFu[@j;T?IP+AZIgt+ra+5`HX78GXB+"%-\?M55mGFO)'(B9,P,5V4oW)\(/&%#7k# 7'`9l)#NbpDJ9p'`\?IK.oK%A']uNs9-9:pDF2T.q]Jidj+BF&Ec@%mO iOkaB=Qo#bks86D-kJ^dk4YD*@VZM0rMGt3:r@D'8;=fE+gc]\S_Ui7Mfd5K^s*<\ $KX9:M;M<>-/`/BGQWM(!7%,>p>q2aVrN9@8!Ot\=p;=&6+0p%70Ue BYdA$cGtk^?:#1#X.DSUV94pm]cNFo(*5j(RV\(4>=-:8'XsgTX^"D=#N1/^6g"B# JH0["Wn=R+31OV%RhMV$]Zpa,rA,NU9nc-d>Ebe+lUa8`l>M#4"J=f%Ph'DR(Z5onr1bH*qRM 25c34_,hcVa(`_]e=4h5SH3Qd>1E7?)Y_2ob'?&k,Of]=Enn]P .<50K3l6IcS=0QuFGQ_klk7bI.4lp7'Eko1_O/I]^5#c'=Aj;Z@X'$@5g)3$Fgr;f 5]f&oHQtFO6YoH@a+hZ#2l'Eg&mAbiOu6;M/86mel1IKX;qAY:KPHuU6qu#D+KG^c Eu.jC\<+17,#RB0Jl!/PCD:363WMGifu/n`D"Y>12dk#6:!Z#CE endstream endobj 198 0 obj << /ProcSet [ /PDF /Text ] /Font << /F5 5 0 R /F6 6 0 R >> >> endobj 199 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 186 0 R /Resources 201 0 R /Contents 200 0 R >> endobj 200 0 obj << /Length 2982 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd1;lVX ;OH6]>BQ3#jS8OAc8R@-M@)gO$'7DK1nG,Qa?Y?UF>o!^@c7>!hIS^'"ZDmTTU$]Q $3u3'p?[03'Sm0qK1X;OVDebjL5Q!Jj]"&,N$]cXP^X`&)sg'X8`%1*kFE$UP3Gf. +r6,+d9;&Z1Wuthc/qXoK]h-^:e\F[\YkbH2M&Y?jY\9-l8fCd4)blr&+[Hl5_0%I "@2AT5VS2Ao/b2U@?YH\[k73!i&m&S)CbnUCFC#(@Cd/s"V!h`juQ84lI_&jE9K:m +TQ$kUFF)W3@jV%FP]@/4&ZWtX^e[,7VW>a2E>_1c85Y6n3Af=,I?Yj5(b+Qo>M#_ i".ooJ3`o>_jcOW%F0dd1GeD+7Ynqjo0C\[LeMA9NfKU/YtI^,&L%CRW9,k^_49gT 1p=8unYkt?;A9O,.*eh%do>L%KF75eZpC8i4)t9S4o5]tlse0T9LH0&t:\ Gi8s(P;q2Rf2Ns/+u?TRmY]+\;cqX0&-P,s8C8-5i4*iTO\*c86e(fZ+[o#ecnVl0 /@Gp#cQ'uFm'JWI:-:H5XB.f8+%QbRfg)R"_!tS`mT6$NBS*&mSdLuV]",M,sP.]#FL`icAFm(70$^@82p4666)n$q6P ]MISd/8+efJ"f9WPU?ToQH[LqcSV,TL1fndru,,pY.ER^=.F`9a?t?3L;Z/@K4O0@ $r*R/eS?YXei?HA.9ANPHQ#uZ%'sBKRC+.U3>%R$AnTI#J\nn/1PMU6qK(VkKG@Lt a$l,2>?L/0bH1%l=Jn)/lP0W5)nuJJos$aY02]*lYWg_JiX^X!O]'_il-jP&Y44eI qrQ9K06e1d,3V0k&^t`&+(LJaJO"sa:'7PjR>g/XE>f8'GhHH=Z]8^8"0,<1@-G=X ;?e+4&2u/W8 -jo?I#9al6J`9g'"sD:4&EoYqh6ASgCdEe;VnA'7asod+FnMc@g;2bW@S^^n^+!*I 7N?(iNbS."`7N^]MsOZ-OaVkK."p9idiUN3!D8hPfW'M(%onb.=@]6+O-N+MNt8Z^ Q3d=cid$Ui%bSFH`m,*j$s\RmH:c7-I)4hL05pD-Teo+5,GeX^OtP$P6n(/+,lD". ;V=[`.m=1Qgk#CJ#6j4X@=p#8?WFK?>9$VEG&NPF>VV?tV&>I3,*pWejG?ffYOI643*g pRafnKo3.jJ)SBB,XZ$b")2+%@DFcEJq?h:ENBHo,m-PVL)<9p6k$,a)F\_-nDqcu UeDsU*90!t9+iG5o00H\3Za^l63[mRGtbjQr-45oMG6]61BplB(m"3l;F/)WLG2pr $$pJY`ipt`_ihml)XKH*Tg4AYi1,lj1FO!W7=kM[$Z!^_5&6E?'gTc?l>!#qehH9b )Z^WuXCbd)V.PGu"rYu;7S5$S+-:H[JrZi)[U=\mR_nne89'1]V8O/P@[ebPHpkD6 D)GLoqN#0C0a3`si`or`?Q;RJ`e/U]i5@Sj&!P4q%_9?*7S^+%edEY-?JBH'6-F@.rG!N1aWJ[]hu>=dGaAK `cY4:bKQZQij#7Q<*U&e7nI=1L$hJs?8o %H4^1g)"f(%_cQ+]$rg3`s1kiZu'u)]4qR&Xa)B&YptL4+Og"6gs(^[;,mtsjm[:" )4D",)OrLS]J!ko-H(]07qnC:*G3Q)\Ht@AmtMe%S+6kRc=f_123_guG-.76Kmune Yi/B endstream endobj 201 0 obj << /ProcSet [ /PDF /Text ] /Font << /F5 5 0 R /F6 6 0 R /F8 40 0 R >> >> endobj 202 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 205 0 R /Resources 204 0 R /Contents 203 0 R >> endobj 203 0 obj << /Length 2416 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd1;lVX ;OH6]>AKKnjS8OAc8R?i*_kfp"T[=#4.M9X9)ac7@6*Sj"-I;U9Og#L/*7L'OQ7NF )Csl)@?eWL+ma"(i;jR7D`4*q7T5`#LS$Ak:>F+6lMD,.N(),uW1`9]2V7b;1T@VB +K>\U\inX$J9*K(](Z-QDPNF5%*%e<_,/u"%AO3[YI?!ScI@dr!8,\ZNT`Xj3A'Q7 1C```Ej)oGK9GbbE2piQ8jo"9:"kA02TM?N=VMam@d=f+Nm!+Gih%D 6\%EV%'GEAeAP))<8KRZ",,-__^!\YaH'l!&.#RTShgZtF=9.7.M@Q_\5"0(l&J-R qC2Y6C[h9m+[DDH$K5eXjZ/BTSK5bT!:(ZGndEo"_'9N^dRWZZRbs/Y+Hl>s_^q<: %IZE1+TrJ^cSU=X]IP?<<%uE^30STj0LKo^T.i0"6rtDGZuaSQ%ZgPkdPW[Kcnp^A 5[.?EUL;q?Ks\Eu9oQU_=>. QJg3EJikTbp66AQnJ^gJ'( 9Ud.AY)LABj'8fDqLYpW;Zst3$lD7J,,iP,)_ngR;A_/rZh"'kWI;K^ge_KX_:99s [j5tD.EtiZ#mDHi*#hmmiB_#TdKVsYq8+g?H]K'$&Q9#H?[K?e!76Xp\%)*,^bn37 GS^GsjJGqg%UTV%i0tiS%m^1c%&)#q##Oe0+fJt,@&8@)!(.4t-uTPL"sSi.q@-!B 1G-\l)FAO^8kYk'-`iOH6l9hP&ja9hMuk@ZdLR':Zhf-3FI/hf7N+n'Bb*Lo.Pp[C &Ad+,/WT4d6C7IR@e;X8;">\r",.d@e5_R,65;u*;&8,b9*_^G,t9kcI1'S"56P:_ "s]FaUWVs-Z(12PU?[1Pi?fl0>gXHTVU#?`B3u:ad7Y%0*0*E\Mm=BJ!guAQ(ORZb jl@eD^r[]b"@8C=W_uEV(.q59C^XXD5(Wc"$c,_:8^:WrUo8*Vb4mFii1quZ5a4g6 UoB70^`(EP?5Z+=n,s>fUeKH93l.nI-1X;8"H_c3A#hkN;XE'ipmWSi7*c&MUqFjf 7N#d3#*s;F1Wt&b+m:1H5(jVc<5Z+DT@u -8]4>o.IQ'e%7uDFVJ0tMR$?`I)bCK+ek3a4&A!V8h:mt56`H7++SB)%)m%;d=C1L \OTP3TREguU4ko=qc2dsg1l.hI0/*6(T.6US5i$,`ApRjm=a.uF)@q4G[ab)n8ChJ m<63q&Ght[lnHbu`4c;=%O!im1(WA&-F`c"^fgm(f3oOX:sEmer,>AP)mT+F:K-S^(npHYS;>fS;] hI\(m:E5+SK.);5)5uXX\`QEQRc/L-2si#u0O0oWn[fZ(0\L endstream endobj 204 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F8 40 0 R /F14 142 0 R >> >> endobj 205 0 obj << /Type /Pages /Kids [ 202 0 R 206 0 R 209 0 R 212 0 R 215 0 R 218 0 R ] /Count 6 /Parent 146 0 R >> endobj 206 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 205 0 R /Resources 208 0 R /Contents 207 0 R >> endobj 207 0 obj << /Length 2462 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd1;lVX ;OH6]>BQ3#jS8OAc8RAC)Csrbl&LHcS/i)>6TH"ED`3^%k!LS::g=^UbbmgqJE`L6 *;!E#!JKTV2pWm!;]pVYC"r)E#6P:TKO['43MHTsXNP7=$Uu?(X[!hi(_"FRXMLB$ D;/qe=u1/jCgf%;%&31.6a[iCbK?<5iO_4GNPPUH1(8c(G4pfGGc:C$9lj_!^l>;0 gq`AtkVp1)qpsU6OCRF,2[[G6'uWD-E\ra?.f0$(DX*tc1=%.N2%AiH38>!r]J/J. c)BJT6S7N_1QJ5Dau9fiZnVTsYcJKA^+/uX1+2K/CCRt[U50Ss!h`N]Z2oRs,+rG'6lW51 iXQn3.&^?T,,:7B#)_@>glWk(9ZX5cI_8-BB0 jtX5Q'8[jCUM/Up2t-oi0sab6HJ59r`W(JHGV0M[\C`%02EpYO?E4Rr#kH](.hd:A %5"VuYG_Pl^I=+l>--E$iG5JX@Q9j71QGYiDmep6BYlQaUR59t#ZYR,R7SVK0pkV> r*[^Er%j;mD'G(%+`BHr^+q)]#_)WpY2%n4DloZtcrE&]kG?=*f(l*Nd\P*j[d"9Q -4'Q.;&KN>93EN4\5rmfZWN$VkpiHl<&Z2Q-[>[s W(>h1HB)pIm#Jg6L`V:D&C'Zs*0>lmhU.>R2"b)@s-9ZQY0>o8$2ZQT(l?di#bggd P<^0h8]t4%R=ZC0>)Cr(_FmSuTJe#Or#Rop$piu2]#=kk_h,0_LZ%V)YYZ!cH=mIJ J.NWt..1Br#"Q`(/b) 3_(?Y8=d%!KucXl32^SF`J1tK6Rb'=VXVqj3-9qmZ:AlMaU(>db23[[=E)Vpa?Ln' i8'T,%jt52Wc`t66[LY*iNtYQ7?!45gL#b2"Xns)b\@^1BjraCb;\?E'G_6rIYCV\ ]JB>[TZNRRY)+U(<6feqPIa1.odZbn,nuP*!AtAGTUH,1)bU$dG>^<*!X8]Gdc@#n i1N#"M"`29>CQpY5ldDXN,?i"?[#=7YI3ll_'0_/"feCEPX<+]&]tqS7ef"Gfi/oe AAM0M%%nkr5T#MK!+Kg=4s1V_#QlJeJVX1p(++KFj=.a*6-qY7-Uu$d.Ec+MN\H\H )_r>%lXZuR]UHk((gOnL%Ecdr!7bmR(';Z\MYUW]"fS\,3q*0N-lES:Z:7l8crrh' C+X#$5fbG0@k2\?Q3G4;$?GG:@*BMjij-0&.dnX!P_5hu:_IB77S+./'blBU(._Wb @qHU>(m&>hae?I66>q@gl`)l%0imF5GiWE NLL%[V,8Ze"=Bf`,YpMI/`\f'H5d]GA=2Qb1EkK@%qkZH6':Rk8-oqj16Y#H*PCY0k4sU7mjXtdF_hlm2`R?K!2Jm>)RLj?Mf3Y#5S7pQ=Oi8dZG#J ennUN_W(YaX&1Ggj]CJ%_NR&LSH6YiRCU+N*PK&p:&HNJ(1u5MgL`]!9[Q9o>@s?Y A@]a;j?s=S>6@5GEWh@cS5t/^9r!2&DHraeo7$QfFF].EX>$B!clF31Ebci>in&og DO;[@,$H >=P>(])u;mf`PU/]ZqA\hDCVlOGqtXZhH"*#,6<`ikT!--ZUV2r+9ePHGV.qiqse) T#.c%CqHkHP=?4^6OkXd81@Gb2'`$#ifc&o0pjq8qr`&O`l,m[&Dh5(&/u_(<(J[D 2FMV2'_M6:3tudeJ,~> endstream endobj 208 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 209 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 205 0 R /Resources 211 0 R /Contents 210 0 R >> endobj 210 0 obj << /Length 712 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdh)qC9(i+dt=`5&0l42iXSb )EEgm(u57PDkia47'`K(4Mf28N]@LmJC'q(JkM^D9sOlt&.$Wjg7X*#i(tYq6JtbH 7Y@6A2@Y4PqC2Y?I)dNC6#61o%Y830%54dW)M!>~> endstream endobj 211 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 212 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 205 0 R /Resources 214 0 R /Contents 213 0 R >> endobj 213 0 obj << /Length 2427 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd<"i^$5\W-H%S$&pQ`=OSr,kLcT4C2I-n6191H_G%nhRDIJ6Kt&b);]*/kIg) p^AB/lk_SgqZ;J&XO8:1/Rbsd%lciTC9f-:X,dM)9?Ud"[UU7kgSjH0FMU>HnfZjY Tr;qPUMPjOjgb[gAfIF8E42-#ThH4?#2tqG*ioC$O=g\Fd"iJE!oQrjMkZl/O[3[/ jp&q:T^nXF_8pUF8qNFl-]16d_(btbTN%87bN`oA,Ofb$)8\"bW#2_3D#Ou>W8.V% 8IXNR+G:8i@.Fe'Hb^1%WX-s,e#F%22?X,.FT=mL1XJ0]5leb]%Kg+f(d,]a<3O0# ZaQO>,87ZC/)ptp)So6\c4i-S_e.L]_]:p!f+/$n=[iF25Tu/_P>jINO)7k]":=S? 2@PjalKd6*-+l=!0/u3D$UV?h/-p18>/&?Q(*\&1"1_br9#^iGp*QVE!ojCpSZEi.`#`!s HXqmsQ^]\QC)5H3CF]*?6k`VX$6e"0@FUtm=HO!/R::]%\2V1TNtnD+a_N'D1dJIi #Slk?/5!m&`=cL&6Ym&iTZ")d--Ce4@b,8ZJD!5sZOt`4E""76c\CTKW)*$jBBsP= #3F!-K4&U-iQaXhL(palb0#Ob5Vak/33XV.nUM%uUm-RjR>QXe8FP%NT>9b82<\/f>+;7*b6TA`hOGu#gdU+Ci G\]@]*:pGN5cgU#h[XLC)N[d)APLedGK(>QQQTi-%LPBZW2UpQOCVtHb0H/]oGWjb Amc;\nM(;?k*Gc,Y!XUA"9E\*"jhFeBPMH>$'Z>PV&g$mq3D0f;K8I86UT"E-_b25 q1j"/IKO.'7\I2VdPO3!6%Y5pl4fS:n5O\*_-`4rahtWT(E=E7X?P+]>=(qSjc#Ug 1PmXo!h`l\;p8.?0@dZf0MtP1!`T@(m/P3N5S!kqDPj,Q[Sp"'$I\SUU"#%@eTl0B .k4Z!'rJLiK;o;M7;T29q*Wf-hAZK//[%"BX&X@#LaR#5'nBg([[fpB=U.C-;P]`9;_Z0ccPX1j8P3W3Hgh`ZGdpP d10oq+b]or*81sHN0&/%?/hY):FA6#^B=`JJo5U[3J&+?]o[!&I>ZVGSU8Dj7/FEn(H%%q5_@As/gZ #:nrP6c>.ObQ\&7X93HRNWgr]EgA4eKLE$WXGQp=-[cR;os0#Q'<1;lVX ;OH6]>AKKnjS8OAb\miT$4/%f!RT5$0q6Fc$6bYhF?,-c-S"qa=[=g5Z_i?]\g/CQ )Kc)`+dC,gYo-=B%B5MhBA)K*>"\L\;cGdsi<*M*p)kq(Mk[[cTYP1ZbjJ5X"1c)* AETGBB1",$a<3G)#6Ncui:HW5K2J?7-3N@qMCH MYh'K/)t_l&.Oa``nJ6R,(_M%"/t/^2IfBXdQ.aMK\6jq8D#gjZ6W2KaUR.UZgM:'U)/P";J(*)V&aH7mg8+ 6W?!-j-ul'KE;ih=OEn-eG)IOj?4F"5t(Y@NpQpaj[rNKfch_l2;k2FB1Zd9a_Xg8eE%Q2?'> 2;NSP947p*EPG>+iITM=Ro:lEe-!_Ci+&#-*"H(1fmUdI]g69n[fC2d>-l0reIG=# co0VBqfk'8T`EVd=q.=$YE&"Kkr0k!9r?kX/KLqi":_2Q=H(ka:d!)*!k^;%nM=<_ %E#KN6k&U62!`.[ln?e!P*UEo67N0h ptCl]A^0hY16Jpg>?.)VZXEj,2E%[>L8$'54T_:TD7$)M#"oX6YPI`#6&U`_'s'=< _BO>);"q$53=Th!"USnL".hPno1J9^%%6-S#<#hko?8],r005N4jl`e&Xb:s0+Hor -]T.;9?VI=4;aA,cl:Pnj*F"aj-l8(OjYDa%ZNUZ'CaQk9ouJ0^MelX$'ia&D%l]D%RJW -r\C!,DO:&*6T$j1s8_CWq_Pd,#\S1%r*B/`?o<8"BC?@LuOn[XPlktR4/J_PcWMq )Iim@N(cgY`DqMNB9AZ1i2.IP_aI-hEVQVfAHUVaH74jF<^$+p``f\gP=nUEL1Bk#h4R\\ TttLL0VE5KJ8#MpPqE6U:fp_S[X@%),D7@H(f/L`"Ybbe,Ek0+)]G5W/+_2;80YenA9jl3+eD9?`U^**H+c&qr2V+K cS1f."dn8kAB_:1%nK@!%&p`eQ1J)t6`=^7lTJXFWJkY,KZ";$=TT`Xk5&_)ju#h# [n#ITs+2[%%O?iLQ;b.V_6>X$f`HXD0dL'@f;1^Kt5f(,9&e)rs/dBOmp>9W&ic]oQV ([+1#Wj,t"F]@(]m/A!TX&T_5haKDl)d#(;5pm7[#=I,O9aeD;;H;$o,K<_NUWZ;\ a]0c$hf,.(4cQK;S.R&_3MCK<[I.V=>9d&sAA"nD;R;QOQ/V`l4*h`Wf0B\%Dt RmB$mXma9@3^J`)0Z2eOh,`@",HK[_l\nAkJ XBW>j*_hBGB#Ge7@A8#dMG9n*iLs'qkS?$gcI[sN=:VtG54)lQH+8c8B"@Rf@]Y/B.Q+I?fUJSE,:%\TP2IiYP0)9+i7/#X,V?OT/JQTgjW5fa3~> endstream endobj 217 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 218 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 205 0 R /Resources 220 0 R /Contents 219 0 R >> endobj 219 0 obj << /Length 2642 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdVoaC*#Y &/gC\Q[^n?C,5.91ND(FL*7hd8tb`hGpM0+%RYK^3fIngMPG`rMZCYs@C*th0SlZB [KXLRk0(&=K/UB!UO%ST-n62HU`c[YaTSihLgn9,!N9b0LO>:0>@jWe3F-98""CBj/:B Pg(IK's)mAN2)3oTT#R`5U8q=.-r^L;IIp/N>-436.!$HQCpWRBW)Y5#tSZfa)Y!A 0T9K\CEU%FCoFOrUG@4g%'THY4KAh>1_:(*GhP,R".Y-&NPd#k0TP^sedtD?Us:u, To+2"bB.4jD;1b;E?-@tSUH/V,@0>79J?bd;kV3 UR8,>bE($g[SBGm\p QGjd%\@X^7$M:jaLeL0I@M!H&Ad'ALdR9>Oa]%PuPQV)jd7VjIa?\jI)dU^EBZYLs 2\#n;h&q`1R`3t3Je7N0LgtbCgoOSeV'ca^Kpgdlr4pdpC+r%K^T_WLL@`r3n3AJE 2(pI+k?9*Jgp5n*XJXMVDSW48)a,cb?+4,/0_ENd"X;X,qIs#\(`]=h_b/*oXhc:t #O95J;rJ1B0JL9bgm!H$@gnN@jK`j*7[.]\]G\IYL R4>%TZ>5a^HuI)r[2?BW0&XWj[JQ%E5[Na6>]eS[IK`\dL?^c kZ':;Q_d3DJ'E#o?nZmC`F\%sc$Pk"APpST0Z'Oi;)0JOpsu>jUgMuR<['7%#7uP2 @KDiR/uVmn2C(1R_LcJ1MT-`.(C5n'+qZ@+bf6CpqL1__N.%ik.OI!'$eX0%-cLAX 3iOTE6G#q>?*pU^u6)5BOt90hlIPSJT2""UA\ &2f]Q8T7bKJ8#N'W?NY-ikLt!DCGn>4"@`kftnE>?n-0H18L6#",)8a.>@N0p(93I g.4-&)*KP%k8E)hALpM=:PN"=)'p1b-D8ab`At#1f)')dk/BpPI8Ec'F*p*V^lP[CZRk2Bh4iu%5V^u iKkmX]glpb8?pYE\N'J"cD;@3Winm+^61?/=Mb>A;J,qRRu0:>b*T1OTY UftdE>?Y$9.SfD@,$kosijW+4raESfqX.\_[o8*:A[5.pCo(t\/4p+Q_RfNK(GYr" :f>q*ertWe9OPVNOKZI#;U<:5rj%j/6LE_Q[ao)nKo[OsIE_q\.1pIaq$LNT-Auh*) dK!jK3T2cG"mQMu)HUsuH$Yh`4NI3]rkneg6Gl0r%,=-EI>DM#Q^<>_K:]##J2De,~> endstream endobj 220 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F8 40 0 R /F12 71 0 R >> >> endobj 221 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 224 0 R /Resources 223 0 R /Contents 222 0 R >> endobj 222 0 obj << /Length 2109 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd1;lVX ;OH6]>AKKnjS8OAb\miT$4_/6E.Y#u2B>JG;j`*PDEN^b"qr>Baeh'99KH*tQp>aM =3a:!3M>SQ2k80^@s5N'D`SmJk2!SDfq]0!c22QN#>AqlFUYb3J@&cG!3!bK\KkYSu_%&c??cR\RU8$3K@Sta;6prHbPK>0,lId-/o<: &2p_bbW!-9Gj_R?i<"B^NIrHJq):Yhi]<&NMdHjP:2?HQNA#A23JU8kMLgd:&8ned #WQW;U7lCHMRFX@Xq2&a&kImMWN,(l#)h3&LrcN&U'@8`o%_$3To,@r#&@[KKIP!: C)O-p1,tGm."Y"[L&n"_]W,rS7)"C+Et/ujiO$&&0aO^m*'<,[_N'OgJ5R5=TgP>* MW%2?7gtBm;D*iY;Z+==V\qDf>9uCGD^]S9.3p(:(m&LW!K5g-EJH5!;N5M^%p#<4J0K)9aC_LAB.ZGE, 6a6JT&2[ma`ncX^&r@4/0rg:F_.ZdeI_[G?9W``.Zug]RX]'h=/Z *XigX(+;EanEtc>K!2Z.>mMbr^kiLV]C8\54l[rX1@,u.%D01L+%sTCThccOoN`Tc 95XFL1A$G@ha$r>(#Mj9DaTic&-Z?ikKT%P05Hj\NQ__@BK@2l&8:G=Tr*gp655h7 RhS72]1l%hc0.N);sNU!.n?"f6Y`S&"9T5,Xh4Gp?`NjJ((.Sl4k%.bt[sq-c!s/_us-YQZ4q(F,^:#rsMXg=Jlt)0M6YslsU"),2 bgi-4.g'.bQW=*[%Brr.I1_C2=3sOO\$QFG=EFgl:f-/fB*/3CbSrJpF>CSM[.;=d +=lh[&[A2n$/<;G3mb+[T0o48iO`*F0(E;\pQ"D\":[YM]IT^WJplW/,n9#@R(q5e E?I<'M$HF0Z<4JQg8Y6i71$1P+Qj.PP59b?@A\bmNmSJi9/bs,XN;jNbfqXV7?%W"_"AegPXmdA@$N`7^AH*-9o9/:@\M('4SgE/cPj%KWf+PS6$4j,BNc P'kQ`@rd"U@rFH2`4Ec50eJi>_j)C"BEl>-0$=#*9@ANN9!_&/%G50-7WfQ@+Pn-! JN=93YkJ@[``LV -QIr?$DK>MRH4Fob*19]@dJ8fB3a3rnEOpeUtn5sStJUVF$,o`-oMZ43J%7`I%B26 7BJ-"LRO3%g2WUDVkKKB8\A&q1!r/*:,/2(`)iAF98(em)Gd4>2bf6LFG@A'T7cMh 6P9P49ce;+!76H4'37Q+"sMCSMa\;03DO[meC`JcgaPR/?]m#ZdoOO-6SMr1CcEku ;rS`+PH]lSnOJ9%bScKo!k!eG+U=2`P!tqh2=>ioG XZ.*U9WV2k?;]9ReZX!#V[-I3;s"rp>]R1ZJ[(3/WijI]-/)$-U:+,1l!>.hG-FmP `)t6_HnI:FZb^D(>%HT-/VUP;(?41WlfE/iHci=S=qFu_^r.l`l3pb*2Z]]W~> endstream endobj 223 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 224 0 obj << /Type /Pages /Kids [ 221 0 R 225 0 R 228 0 R 231 0 R 234 0 R 237 0 R ] /Count 6 /Parent 146 0 R >> endobj 225 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 224 0 R /Resources 227 0 R /Contents 226 0 R >> endobj 226 0 obj << /Length 1687 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd1;lVX ;OH6]>BQ3#jS8OAc8RAC)CtG)AUcaY1S"sV!oTh1Beu1fKu$<3W$"&<:Bja+bZtQ4 )GYVGhMT$b3h&f4;ilOdAe5rAkC)4"b$d$go7g+bp)kq(`ia@QTr0qF1?^P]")69I F:`E0"s5#T].1ED6`bnl&LHFUh@L*J.*OBY_kV)uN2l`U3QgdU 3\-17,=Je1_^M*4%)\IT3Y$4@]J>:1aQPC8@sYaJ7B/Z])5.=7"[Xp/=D^A/1a/(MM'&o @g=0rQ"C>SGap#5'j\9Z8p9u+-WnSXE#Vr%peZZ>n`,ENNOC%G:PL#[mc'*+@)Aq( ;s<#/%-+;Is!WF%Ql+YPD$_%@G$8i=^QN,R3=64ZBFk>Kk!adX,NuodeDLKFJQHW6X]?_VN$QKGOiOZ9B'L ]^NkOj$EhmF>^MdUGWFs0\W:H=:E3=3B[i$,+U*R Vn!^A;q8_OhOlg:c7>e>`Ql2KNmsPnGFE5uPd:P7m-)YS"hY+F+LNjl^Sm.PLS?_] UDJ3kNNK^MU#oHh*Ll&:AonRg6"\d9mFL;MQg$T&9ulID8piW]i&*9IHpqVGuuP'2W@;a*dBF(kg%q_b3e)rST(HN2%,. N?TLe74?+p@SF..0rk!c/G1p2'_jOb7Wghk\=&8."6G.NDrU\lW-n8diK2 WR^396'CtL//BKai8hZj'DPuIXdVFp:sL.](*O)tT:MooZN?U=4t&.JZ;YB>fh-+G d\3""VIM+oWt1KaV$G0jGZlDo5m7dXijoo(B3IZ]3!T`K329*s>2"!*L9[R&Z^R"^ .tCCa`HS.,4:2`&?VR^=qm-CWQ;E`1%MHaZ#(r6H-RiCe;)/3cQ^669A=H1ec5NO4 [$M;N3_WBD,X_de&e>$&M"An$.VN9'8/VF~> endstream endobj 227 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 228 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 224 0 R /Resources 230 0 R /Contents 229 0 R >> endobj 229 0 obj << /Length 2681 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd?.2',:TZT5&,EdX6N.Cu='Fpn*lDkAHb=9p: @c1,q%4tL]$n*>/J2[T5$MoP^XBI*bV?rE!T+-E$>pZW>=uN=mJXC>/6/kS#,=8YG >WP=e"GAP36R]f+N<'%;1lh_efFSI++Qj%@OI4l\"9N]72iXSaLbat=kZeZ)Z[Cdq d'PqL/4ltL'W?]pnDN1jZqMf-%1;mI2N@"ckR^P\9][%g6Q#)H7C"o)&e,0VJPX52 n3Iu;0do=^;j9^H:b2)E=&BHckbo4Bo`].i%I+$X2_7=ho/W`OA92&&@rTao0KFJ[4V^W=@mL5VeB8(uZsG8u5M81t,Hc2UU!&R0W[Xc m:[!q($Z>="E6.2O_m<)9:hfEta(`j;IX.nZZ&$Yqe<*Ir,YrM5WXoAc D"D=Hg($!Fb;eRn#%R.Ia.^L=7KFRNo\-7;>L[!)1<2ui`\n`:`f4qM_qNl>(aMT')QtA^_BFne,e3pQ# _]m#T\(!ZGhJ$[t^#+M]mF\[TnW%rTep8b-7>bTYPM/`R]I?/UWY!m>f#gu^.2`DG E6ntanehmfV/%rb6lUb'j95n@:UYej)%W2m88=q\^pjdn(>AOX&4+u,QTV!@98dFk SPb/=kh(b[A0Xa`h_G2/AOX_sjePDJ OjcR9:(4ed5=ANh>^8kS8nK,0k^MH\;c?Zs\S\q-jOJmQ57S(fT:\ODOhh$A67cDO 5"KF@iUd#/-&EBNT]EjTE,0l`6:\T\AJc\MGZLcS.\Xj!:ZONC%<8I2Uk$CDR`ZRD aJ&tjQEloUCk[.R^l^'F[eW4Ak@=TQpkD^8Yn9W)NG)113-YA#"*u\%.HEDmB]S@m pehCV$9KMEI-0U.:M5a*Aj[uT9@FNRPeia4ad)-1*qT8ukXBD;KqO-'K.V",o`,(j C("A4@X>!lL)2llp]E&+M==+'Z[b>1BQ,PO7=I%m=ogj?OD>);neXqJP:]_o\7Lbn F?E^f9c#W!Q3^LN06tX4-,cj!NM&'SonKS`43UFBQ@c-)eN4m_'jO:<'rZl?,1T*6 Vc+p\a!<"-*Jn*r0dnu8Pj[?=Rt[#YnS:h9?is,c&3#@ag`*#5aUS?g3.NjG)fk(= a>qF(U`>12"/L&F)$N(^/H)-F^itT+g,pM!)Zs!jH&4!7,]c3f)3XP)K.K>Jci$WU dL(Fu_>?Hm5l[Jk!"?uWQp$6Nb"W2U&DW,i:Xa=&`amm@.sbh9$UHZX,#]@7B%D6)o6 _5nFtlfW[lej4=%@V=FP'E`:8rkK?V*+2$.&?:`u%Tjah(&AaW!G6l/:1d!V[!dV3 VA;BdBGUbt3`GP;.)Z-^&RnF.N*!C!B2UH)",]ML12q/A=RC?c)_;G?IVNAp1%[1Y )\2:7aoRY`-^i2: T\=ojb[$s/.5`)t/=tCNJZPmPjD,R<&Q25n>V=;H(f]duJ:XR"7kQM;Q4h5LN>2kA d,2mp4;*(@;WK-(mEJK-'$;% endstream endobj 230 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F12 71 0 R >> >> endobj 231 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 224 0 R /Resources 233 0 R /Contents 232 0 R >> endobj 232 0 obj << /Length 1863 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdU0].85_'tJl5#_UJ%,3Qk(Xq O;s#gCn<)4!dLLj96057.Kr(NAAYI/,178X-otQeM8ACRl3@@":kVS!o][I0ZdXNr 4Ge<]q#gei@fep*5 U4T(u_<>Yce0Adg1;[cg;*24-!]I!uLeS!fZSH3K-As'M>_Q(.GiUVDFML0:X5b/g 3%0D-gcAAd9fOkg,7*)0=@56r>qA@dN]%@,]#6<4K*]ISg>]'n3l",,"%:GNFcmTG ZOfqp.`8>,"!S.rE&,kn4ZF8][0YPFKGP3dq:KUVh,"M'"s>g5j=L>JM,OD_=JA?;KsrDiG)66ZLnj4n^psPp3pdp',S\0%=:c[(DX,tG'i;h"_?!;%VEs*(s?oeP&@f&=IEseU14?;cJMV7]Z:cE7" SZ?PG$N7fPj8.oNrj^]bc(X5Wi8ucFE.*"MEV.dA=9KPE4&<_6jLo(Qel9Vo;B\;] 15s*\eY_.s9W,i9Em9K;+L(KR&&Vhg3eT@ :48B>W_]5e&P]SXP)gT$9#Gg.$b <\p-O3TuEA*?qVCQL)(c"G92j+e%LWaF^4I,Y:mESL?I\nk*__:9nilCGbSGt!SW5,f6PTMulT:s'sd^]bY[i!Zf*-;!MdKtQW:$Nje= \2^]\#3c2Y>n-n?N!0=G'E^mN39>t!_a*AJc]Rj!jaFUn78R>^Jm_!c723K,nf6VQ 5'Q7>L+Kg`OI=csCsFm0L;WEb![r;L_1ie]fOi,IS/sdH#a`.^C@0OYS-G*Mi4]=J L62NW6dI%CK-P22&MqfecV51c(:qKtS9@CUM!6)hq@-[35,UbooPCZ7Z;!Pl&+>DUX7OitZ3L1kf(ti?a%DeWJ0Skn0,$X>h`4RH$:m`6^ SDS840d%_c@&440q-1\#Iiu:lVj11%<,,Q7.YV]=_*Y8(lGdNB2/98Db.B'Zq-XDl iYb$m8-BMRf;SEH$3RlG%Z8tO.E57))iEKW6+ERb`DRQ]']fXO[UY'?WCao/X-KA* BWF/&3YoR.QiY<&2CAL#)_c?%<88#heND;d0P,s7=`9&a5kSHoo6X8RFO<(K7:0mJ KQ0=76f]+KS:+X.X)2a/(kFJFr6K0+VHCjI@i#KZ?Z*s)Pgd4@a6,lTB67B+<9:Y0 _dISDAc)ao&-~> endstream endobj 233 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 234 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 224 0 R /Resources 236 0 R /Contents 235 0 R >> endobj 235 0 obj << /Length 2463 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdcKHlj,FX&Ca$#\C%P;jc]o1AKqK9T4BVn `oY1MJV;;t(5t_8ELB;m`pq8rc)aV2 Bd>L9prY1C,4&M+9607<0921%E?N>=fNfl,iJY"QK`Xfi0[15.^,QNG9@brClCc-T %\k/i)@Xt8atSca7,JNUd1XD5/+G27'N2,?020i8lpH&\j#Yf9%? #g&&ngcF8cnDTW^c1-F)*;$2@m_tGkq/'^X$r#J7&C,2g#@cZ@-$BsCZ: &4BHp>8u/?=QeFAeguLokLWAm.W"'3AXbh9=O.g40R_("gp>%O2V>osO[a1bm'rUa UJ8S!qZ@,_"Xj.-L_eE1d+sk#Yet>q#:NLA8OQbAOj36XK4`j;9u^G(!_O\a$K6s^ p,^)_g)b43rGX;E7ZINe9a`*qKbGYD*Q+(-H,7$4PSS.g?#hh5X:925Jp`7S,-=Yb XI$Eg@)\GLIU&r_2aTuYYsX*U6AUa;,)(X?d26p6V-*tn[7#l"qh81h[.Q4Og`"!0 <.0HDFZ5'il%&rJ;6`8/+f&58$PJ.f8MN./2C>esTjN^R4?"O,2O[NID19N2Z)[:j (m8&s8'.>]W+=PMQo_`^-%%e]k%FA%!2F,C#Z 8Z2.L*;Do1TpROI]h`X(@_@iTe"Y6k3)=$B"j /9jm'Zk+4&)^FF%3^LU)9!G9K$V3[,+Jo1D)Ij&'9K@qQ"t[cH0PY$Pfp"81Xna&J ?j]H7-s!A?-pa(cK@$r2+VXLsOV/g-9OSY?@*BN%RM,WpV%&Xt]^?a!J8gMl`]4?- jdaiuF:OG_3mZd1Wu'eh$%^qi/=>s++G(P12=r4D1mH?1b6#/6qIZZ.NmZlK)u$dCJ&"I7K>9""V;S;MtHojAsfJ. !+oF`?nj+K;#l58f4tZN,pXa$P!8Ik9>X#S=#F9dRQAMVG?FkD+//eC8)Wp*Y_!N;N,>T.<+@p*cm5J+dnl:.15F-.uIj27^=2Fm`Gth RtZlJQ`(anF2O\\#r+Oi+G:S6JobaF_)sM\Yh$a)r,=JNoHQ6jJ5XY/rf5F^&74oW BhU37#/JT`[Spg7bK*YW@?`fM>55Z_Ae#u7e;Y%$s%4Bk-nIZ?3kum#a[)%I1$(Ds ,s%TF6P>aq@GS$eflYWZ&drk8NEY0@f6FJC,XLD5Tu1!q:GMAgSCc7=#5<;h1/MT& XGF'S'NQ0*)gPFlc;U\dHlJLi

>N'sQW9c/?ZhY;&F(.ThIQ((@s=0J>5FcoXGhB$KJ4,5khpV N8l^Ab#26C1nrWh'2c8L$uCd_^ine)j]]VjX)p?l2:U+oP>l;E75(bCj;R[H@h)qt <$R.W)psag.b4HJO:V~> endstream endobj 236 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F8 40 0 R /F12 71 0 R >> >> endobj 237 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 224 0 R /Resources 239 0 R /Contents 238 0 R >> endobj 238 0 obj << /Length 2592 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdfO:?jN8b=ESBu(/rO'>FEKchJ$mQnO41U6CpkT*T@sY)-&-1A3[4h$t 2N/kBW#%+=.L%l.6+Y4q+Q9P6MM8;*.QIp1BW_mUaV%.%6id:&`s.+!DPeU45T5@a +DM%nYO.Di(obT,AetZ0]SOKq6-O3d1J$dpOJ3%+L_q9!10u5c_kYR6%\jt,U8[DD @_g'%WZ^'*0p$41UMPR5iA^!oN.j7nUV>ljK/hMeNMCa\3WoO5_E!T/?pPJ8@sH9X %3s6BrsZdS'0_'OGg$^rZKeALNT7(,e18?<-ORI\=?noCC8Dh/"gBsP"Lj;X?UdkE KAJ>q""_&Xdr2;Tr/DC5_Z%=>$jILN!_iu$3l=RCTFI/k!Fn0?a3rAp6$;+'0)@_YjYcJ:uRNc:ZE5L=St+)SQ`JDN=l G:SIeL@`C.0\mOC^J/#kaQber(u(iANlh(=Yp_TQk+*lVod=pjK.G+Z%V'b:NOfY\ r9D69obKU'&!Se(glndrj0P.CGVHe=($B&:K&BLVWn+=u0+9";mc=q9rfKPX@c117 "9Z0WJa=3'P5u0]hP_dUPJ&a 5_c"1'HMQU]rIjl@VL!COg`qI9m$/3[Jlp%hs1//QW7OD1,09'Z_I'>C&$ppbe,6: W$V>h^R/bR@mKGlj%H6\%ba1r16.E50aC#[/OPP.cCRCAfn+k(^I!(p44MK$OAUsD 9eHq*[;tT$Q#7NM'5H:4%tJR/ `;3SgRUHX5=d"NX#,`.J\R!Hj3?5scHVHaJDXNJ`07_1_K/([^bAd)&V#'eWb23=, N:O0i[+3:r$[8_7pVBBW=5H9u5L/Zjf%8F[c51('+A6;'kk+XIi^-jUGX7g=f35C'S<-tR".&eaO"I%%&mqE`FUT`l!P0U kUA2ni#@9#EAj[E5t^^TaX;?4SsJ;W%SS;609l2XhZSXeD[l9;W%t8Z*#NeBRN.c` <``352.&tt=s1A=h*[@G?`[Wkd?E:Vi>?AkNqHF5,IjLVES!<%H2''IndAS\4+6Iu (I6%%gR7%"71Q-r1^4s`>Y:eAb^Q,f`L/n<*/K1UjJj[`mcg;N1HS9=6jJIPc@ZbX 3Jn[B1Le;:O[La2C7*UOl7X7/m5uo>q5^hJ'hJm4d;7-k#o_C7)1gKmFNS8Q6s@\H _h`FE.%i=pFS8M'We@A1ad]Sddf:`SjTQ'ld)4CgIYf'd";HEl[M+p->AJ.Ac/+3+ ZaQPH1R2:KiRG,Dp&GC3JQl=6,D;PT#2dD%#(2\/9WesQ6M!W[j')PV5k$UXaqkYXS*DRHX<(NAd&H6n:j3V :g4c^/4rq"_4"gMg,hlHQioG(Zr(2JF/D!5Nb(M>"=i?QF/f=bTpUka /9YrO@"&3g1kb!uPIML/Jn(G(n2c:He7X=6'St%sN#fmaX>.]`a$fq=haLBT*L+VK !>tdr<0lIa%V@4@N3)KBK#O[Gp-h"i^bd*k Ueg3!DPAEhAh<%(J!UhR^e8r7%X\3UD)bN;fd6BpTm/Wj`gFil!B DedYUG]n*D"9DcrVG#aL%WuUA8#:nSjaTlYdjK3rZ3<,dL(Zg$+:3Rs0S5t9$C&Zo $R#p5"?dBsPU0aLb)V2>/1%>SEC&._1cFc%I%^ljm&rLqrNVN:5?Pqo#b`tpZ:0ij GcPH9ls)=iOp40:~> endstream endobj 239 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 240 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 243 0 R /Resources 242 0 R /Contents 241 0 R >> endobj 241 0 obj << /Length 2937 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd1;lVX ;OH6]>AKKnjS8OAc8R?i(.`$])9?&*/"-mDi;qHjAMON$k*)L35]f")7Ai0Y[-DJV #S(JL$&m#Jh&T18isu-c7j2!BS.doTIWXKg%H7JPPiuPU?Xa)aLq5*Z'Qg@P_#]b% f^]o>k91"5aC@/u4a[]F\(d]+'aeJ3)9BZWKF`Ue3%0b'kg_PC6E&MC5leh^X.pni 2iQ*kb.`aa;I1dWU,[HB#GOK31Q;KI7PmBuO?j@Dd/^%#$mQnO41^%Mguejak_aqO .1gC&]>usJ(o!^?IYt_0BW_mUaV%.%81CKLKSG&^DPeU58f/t-W"T$;0Hb%uo!.YA[Zo0U7nOK/f9&BbXpc@A0P%&7<2"hi7jiq7s8>I:lOY54nS1=^\W 1V^iKk+E0@psM'Z/p$7(\s(o3U\;@t[N1Kt*RXed[]F+J'"g#!*;];'\jK5k20Y3Q;YqBlZGs"k#J927%gd l;[OX'jaXeF?*\%aNCmR5t8b8%Dt`pgScV$QZ_,aprK+e3SYL:]V?s)\E&9`4t9kG rGJg!F:Ru.`].7n]Xf>:gQX`Jf[73h3,6F:\U4D42o+H-b2SC*(%jpW[Wp4_2$k;m 3l(3:jOhgrJpn8L%Wbr=VbtTs#/`<&.p#Mt5_:t*,CVC)m\'h#gc+0qf`5VH$Y-=s .^74k_'<]DpHTM7Y6T'(erc6*,8QNkD65Z>nr59#`]@eAbLig'T;m)6"3I/@0$uqK (O#PF!Ncg/!&B=a;X%6!2CcQ(4V^6-Yu"<)%IIO]ZmEM!aa+Y'VXu]#"k_++ils;g bgnsW.ZL"Ym!$^.K,$p.Z!S/a2SlctR[)P.U"g\hO!)Y02B(2?_C'']U)of$Wf53M egO)FLR]SoCJ"sr4Dk;fL6K^:%]iqd_3)pXDYdh.o_QW1=$mVhX_LqVhYf;&I7Pmc%=l?94+:2F!QJ"3prUDXb_& Q73kNc#`&/Y''a_;bhZMK-*90caE],E[V6GTK$/O:4%ZsF@]HWG\cKQ8t +J:!QG,U'0PNZD36!pAme5sp4D^XXq&N%$%'&&.ZC2lBT6t`OX*,:+gjTTLl_n8=% qQ,>MU,9>9%XEo\g-EZ*,;"MGD%X%e3C]/I#=.SUMB_Le/_L>_5k7o^&7\2.hJ+fi i??Rri/!JKg.ph4C'."LLQ2F`fs+\aoW]U;qRT%aVf4/K R2-ptEF55MBJ`$8!sQR4B5h)Z-Rf/3GHuLf@K(C:Udn45;o;]lknYqd#^uD#.WZ9Yb2[n'$RPC"U$PBth=]JT/TP7$0 l.JUi&Iq5gPRdb*jZIdQ50Q%7,Lr7++XD#/uaV5m,Ge7\u9? #4!-W$lTO_XNuR.A/lacerEU:B?1CmA(o,Z7Nn_Z;AsGK%qQuLk760J(Iiu/N30#, XTDE;;SJ%U0KLqk:-6H:FK7Fh\cG>F?o55KJBP2L%*]?tF5;o8f =Uh:2`]WH?c>>_sOEf&>si!5`Y(>][(!;I1Erj%P?,^gQNoW6c+( H)cFiO9_/E6HT@V>)f5E<3Aa<3&%WggW_\,`p6m*m.*NTd6*4R>+I!-08UD*QrN`Z aJk(2VF0`$pDqo@8EdiVWriW9$mI35EG"#gqh8,^(<+>nI*3/-2+l7f:0"ne$%(j( 1&\^)2W3A[U6&K^Z+iT$EI*i\@0NS:G_\BOhTi[(#j^Gm=;Qf^B2jc6OKUaTbH1B? U:C]NpmrUE5d!Ion:.k.(hUjnBcI=RAe\(XMgbr-[ApK/>TE64>Pq1 f\'?#OU;T3BXd)t]#peq$r'>5af5_M'T8;CU!1jCnuUkT@E!n.Oi\VfI0c7"2cs4G W#[d_?L)n?^iVGC^XIC$o`[IP;S;^,.tJ@:9eC?7)G[]?mad"b?%49%k15-uJJQ4C @4E&(@!6qmga\Mh3>of[Z`&]+=qF`U4k_&ll>+\E5u;[+&Ot$$#QbZs%f@+f1#%sh beR&7'&N)@n<433Q$,4V($REs\3q+K$)DEHF"W!d:n`pLHFde`C+%E.9g9_o@%ku[ /EUh:Yj1KE'qjh#Rqu3&u6o$S#Rn[*cqF/,RQjO!e:0jIJf? W?/+`4U^)89,1sijcm(eGN$RJlirO_~> endstream endobj 242 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 243 0 obj << /Type /Pages /Kids [ 240 0 R 244 0 R 247 0 R 250 0 R 253 0 R 256 0 R ] /Count 6 /Parent 125 0 R >> endobj 244 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 243 0 R /Resources 246 0 R /Contents 245 0 R >> endobj 245 0 obj << /Length 3145 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdVs(XimCW-k%tfd 7q;*,KTKD\PmoAHh>F'Y_NrBdB(K`Db&06[`!0^saqZ\#'TAn&+dD&/^-QJ]bu@'T #`&o&;d@tbD&\N*APp!&aCcHY9KN:aDf`SU%YI*"&.WT&a:Jb/)!!B3,FX-@/)s1S 0T>;lkVpg\^T]m:SYfNIrNan`Z1!(YnB*gnW/.+F0DQr7[6mH4Q$&r]p _D_T".*Urd;4K6M6?5o>W\8ss<8okbB3RWeS^,df?m(,*TOL/4QcJ_2D5gXT^$o_#p &BRAW:h5;9Nl*9u&.Hq,i?0O9)!`A,d_mY.$>J4M1+Yd3Qo;"EqL_it22(I`^@U>j W_JSDKVAS_I/OMlWZo1?N:gjh^*40+2'&P*`Dl;+OT!ctX4S)GVd0V'HpkD K:9_5%(D2B0k!FohDMe'(:nVKU(C8`PRb^rO=R")[DZphLG!"g?QAK6@/sI#b%?@M9(J,hk@gU#G !4W^P+`PUsAhY"u&.&&(ZoO-R,RP]TUQpIM51A8L'P2>"5jL]b"O9#dcL?dqN-@D3 dYre9;@j76/U&d%,CF./k6e"@P!b?#bEQ`;/m,AIl7So8cbh`[CdrB2i&G4*8p3$# _bE#F40WgjLd:C\j[$m!r5/Wr!/=p5$Pj+I]0in`t&ttZ]f8rnqV]G(H= GZ[D2`<(^]MA1Rm==8M"S^MWh_fH`%%F+(+fBp-,B34Kt@q$j%11+]mUV.9/"?-0re94ZtaK.A`k iSFXK-GoE(1Y5HDMH%H7r(K\OF?c[F3AY;#q6\q7$AD0 &kusd9d*1GB[S4Y(ht??WIfYrO](Bc#qd@(K`L0Qe:;TMBf3k`+HdEZZWU)QN]AZ> U9K8*PR?W^(S,-h/s,p#TF#;r.A5jS/.'T=uM,sURdPISUi%ZAYdZUMYCfkV_`iFa)c^GM8 Kl7ji47\3''""0<#t;F''Hj3FXPHSU4VU]]h-(^b#$'*;UbmC`(i!e$WY1Q`LG7-q &e]tbnXQL<2TK0i$.,m^&=jKS3n#BUhR;aj(_FW!!. M3SMr8O,u_I;D("73>J)Ztn5]*3pM3Sn3dA@$(ab/uH*I$M\51col%^`SMQ7Bgf'. 8p2>7UotI$@C5+r(>(L]ki8se=S)r#a7]ZJ/``q+mhT3P3GVQj$B?Ltb/KWTLrQ=E BY)dU'IW&=2G!]!J=n0jqP[CN:'[oQ/uYj* h+].]B#(W&bqi'Xf]rSpJJd*+^UEU"SJhS[_6K>&,dhX[:*XKD$`A%3at[i!PFu>GGU:Y?^A1>*a[2k]7._#$$EI`0$>m#M5Ai6,[$hW>h\# [+QsD5eBY[?ZZXrAUXR_a8g`%`nnq/Ys1Dm$JC''U<*1C#0%VFRbB)3eTuZ"LG7La $nK(X;2*'F7IJ6J7!hfJFaui=khYUMIHDpi@2$.C[8I$4afpVDZ^Dl(oAFQ6"X/3H T8WTH4#jfp Mjth*fQaO_*7k+39P@r"?oE>C.MA6%Y]BL^d`'#R0Tct\9rVh9'rt0s0>H-kF$b+cgq9A8#k> _Qh>+AZ9VO&'0a.#lP)KrFkQrU:u0[.\NGp#XhA`$53DU`lgs9pk)=e-mosJ4(#jU_n.814fMKF,)ls- endstream endobj 246 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 247 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 243 0 R /Resources 249 0 R /Contents 248 0 R >> endobj 248 0 obj << /Length 3213 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd9YIi`nk8Fg4@.OV^Yh7ef%^.,k.Qe9@ hD7iCZoBkAMR0Dn].3_;%ZIQhi>W3!Er%6h8U-[_`U:(F13QW*Bl?oGn;\;%QoS0TNF1dhSn3h"psR@Cd0Raq4Ea1X..lf/AV> !6RDn")u?h_1TmdlHV$!M)SHi]`hTej+SNc3Fa2[3fUN!P+no5D"s?WFAtaH4KB3T 's%%&B/E_uYgYK[[EUrua$W6,@aUE^*El`,D4]k12U/^35RDk8C+M^eT,h);%(?FE 't!'O`RL`D>n7U^I8dco?&KUs(n,h1=#MYT<@.CX'^:XDE9U)Qj%/Q(5q3@!(1bsd Dg'VcXr&jOK2kP]%$q\;YLF-u/iUpu+Am2_@taq6N'g.h*=XYM5VlHi9W+6/25RlD ?;Z#tTVurbAK'NBql(FJ_d6_*94JH&rAY9H;/X"`\AL[m1mY#3-qYcXCrgWH2Q5/k G3n`d\<,F=%HA;PkrEXhoc:oE2YI\SD`"OSlk/62^eVG!1HCHAoKa*!Ra+cqE,%lN ,;,ZTNND[1G9b5ah0ko)Vo+c%3Sd&u[no]%-W92>0N>@@%'Er9NBpX-c88n.7K3

*o*p`R1O*F2#L/UX_l1dTM;j1Sc#"Q" AC,F#;bC8q`UK^7%'Q/ReICJo*sX-!N/@UQG=X`.2C(J`+"Cu"J]Z.`,T%O(\7K7_ M(QSt#1!BJ@='M=:%]?t(WM&n,Le74Ho*&QL5-dA;+bJ0%]Q[a KYp+)HJj?PLKE+L>FXDJjLrj6fTQsm=rKFn8a*/gXrCQ.RtU:-Qn?mdF\887ouD9=1`S;$n&3'SQuQnC'#:]2C0HM>Waj$rn.-W""_8 -IVIf$$:ad*.0+dN_/`0e'1l#=o_q`M!q(@a'(47%Wbf 6d+;-13Y@X'<9MbUuHEL`qK:.9Ym*0KZ9bMe;WR2A4r0$/jhlBib5/D8V*1s$A1:bMCN"M7VX\kBub=,+Y'bVa+C.Zk( 'GT'XJeY?OgmoZ=I+"h5RgO"o3Pn_/*0T"e6<:,50ZYVI*@_>=0($eUASic`JkQX? 8e%sL5=@ek'5l)Y6eU+cCJQ'l%[$RK#EW-=b4D*#.SUH)qCO7mg1++(X]m!&[*kOB FK(:c63]oLLM+F1CbnA[/@N:F>oR<8l@B5Unp]+qRoi-Ne2J\a+;k2q,@AiD^87.8 i'VWN6o![a#)(HCOIT("G*1%(C)pe#eh`R(PTY&l)oOj#:B\+ ,i]d\/;(9Jp"\F,"8S'/`KNSVH!Wc"Yjr>FW3*(h)5?13*9]g_Sk5'71s!X]k*U6] 7D0n-;X7X.[IHNWAMgpi!gXS.&'Ek\0a=@qU^@Qu;g)"I,9.C::eh-_!Po-gol?mn _@XEb&M'[24&Tsk6k.iL!Dpo'n'G(tQcOqTT]M&ck)OdR@BJ>jG*I^?@hWL1="9s? &ljQG`=3^d7jo8[LdN'@F3.G?W"k3EV.@u5+0Rb>k8J;0%SR=0'5W:B*\2'Xd31OSj?!8SRIW& dZp`1mr_CW0mbg;]DA!(lcYMQ,heaIKdl*h)Faoi>'>;lFaJ4&)j9l;UJfD8TX*=^ 4F6"a_FND6'uj.)T2P6hX7Gm]hK)]6KpRbFF5Ek!-X21gNqi(d .ar2%C#3EP0VA$7]]FFY4hq">gm4#/'lXYn(@9N>cPYI VBclM:]UYdb-jWY-\0DO0MF"aJ1JZ&];RmU4 endstream endobj 249 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 250 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 243 0 R /Resources 252 0 R /Contents 251 0 R >> endobj 251 0 obj << /Length 1007 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd1;lVX ;OH6]>BQ3#jS8OAb\miT$4eKTW.;a*YFNh6_duZi+?PubL.s@Pae1ZXNclWZeEA#5 Qqnn.'n!k@2]L%HP@/plA_if!-@5U^6(?0_lfUW$QMV4/N(),tJ2\l1)927q%["9b EtB;7_@EDi`,[]&2oal)HU51#F*tu5J^[R0.Y.]%3>+Xh_gu26iY*!=^85 d*g._\AKDK@mImd%HG'FSW33=k.Z_(;I6(,NS938\:\8/X#HeHDDAj:_Q:!P%MVb` 9N(*<2i`d&!8*=3r+=a."*76J6c5,7l6nZs(I0DNF%":=`h gUrN'`&l>`7">gYXV;&t_iR#E-]ZR;m/OjW(rk!a%nm:S`)5Ua<)l4Q3&ie!P'JRV P1Z`dM$gK38ZT]pW:Se"0\H6=cmj^s?&]E>3k2C'i\mj:rKVYgn>O>`>41u)b;0HQ )EC[`73_[pd`i4c4`_R,0Y*1^g=s*A!6\!7QLoZmVtX2j3J84$dipYG?3tER2C4=. 5d6b/$ft47LEaVk`4\BAj-/Es8.i0r3[du!d,tk'PR2NJP'qB*_V'j](bSB8?I0*j .+ endstream endobj 252 0 obj << /ProcSet [ /PDF /Text ] /Font << /F5 5 0 R /F6 6 0 R >> >> endobj 253 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 243 0 R /Resources 255 0 R /Contents 254 0 R >> endobj 254 0 obj << /Length 2737 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdOg0Ai#RF`Te"PI:e*\[_??4S:hi@mekirKEFd!0T>INmPKrV6fQiZ%mkk%L!/\; JW;/jDR-iE3,BG+Tkl?=N^G35.te"F7$8aUb%_bKJY6fuWJHL"#,-X))M'Qc!Yg@n .WQXLXbg3mV]q7,Q5h\Ol9:<(#pZMg/rRf62\(J%hD,S9)a^9tmP5@p\Lq&.k.0FN C)eg=gE=TB%`>=64,hol&.E&ng,?<*U38@&_arcJX6QMF3t;@oo_f3YOM]bMmh/iUS%82UX*(,UFg7dX7N:D0e/055WXpC!r=AM8U[Bo%JIqt s3_S9q`hbe'F$To<=AI\Nn*J&5`O+*j`S5P!8D<"JHOa?%M9)j.TY!30AW_aR)E,:N%8NMD1\;!#S#a>-$l19H(.Tl(m, AqM?NQe*&+kC1,>>i(^WDX(R0We6t^C:5fY'9;4Z/WZT&mj,;g]e9-S2;VmEQ+t?]\WA^$'AU'5#fFXXs5UUG@4g=kE!F51$i>\#O?rrl)Eg d_jg[>PAW6EXhfpQ(4 ?oAr=*`W:ZC_!A>]"_kpp01Fq:+(>3Pt%uS(M^EdXYpi^Qot"I_\HT6M6JBbW8,:1 iacr_OZSn_K3m7C":7fY[".:!TL=/dOW])C*(*n,ZlNKBg[<'2f8i=>:&JD\V4\Y@ORe,:GuK_%#G9BC%Au YX%PTPQHk-maX`Jo"GmUJ1LmiW"fF\&0/Q=]nJ(9;h`bpVPI'O%ZGB.LB;TFMF0X/ Do<>(7lKaG5V**pfsn*94rYOe\3)'oHSB]1f@Yma"Tc[[#&mZ]K&A(fVT&,1KFoG\ $t2>WkcmkDr!OOu]`\??>JN.*.9,BMR:V7m";"Ql-hQ8f_2&j0)8a+`'E<-X$;n,2 I&5CR8S-AtnACLKgOVV9=/!>i`]%Y'6Xp=aZ2jF)An5^Zr65^F">[r.7L1Ej<`n!bPARB<.`sJ=!Ga-H-N.^F_!"mX[36%CM=@Yq:AItS>?8m=Pes8cPnpZM D"Ll=6aC%i?@7Z'M\_,`fY!Nn'SLT$!hfgd'XapO!tAnZ">C"r[U*0p%V4dp#%7#` W<)fW;8SHe+PrfXam"Q=\c&j+oeS)5EgcE]&3XS7J56g1H*#,>LFTi1Ag-Cdan>-R f&WL(K#9^nf\A.6^h=GjY`[5^)$\WkHId%FPjIHk1U(Q=:m)MZaC)QB<&e+/p`N%> OV9"e)N&D\1,!=[(F&tk6kPkF7cXu0US8NE;=S$b,UFM!K:EDa!&R7mPpR,)G`*!0 ;NYq*3LA*%>Dt6g#2n.L(?EUHJXA8T=03jH&mjUgKX4SNeLlq^NgL;jb=mX1XTMLt R]h$TQu0@YKgZ1Ri62l;d_S_VcnP`ta;4u9_JkMY,#mW<$e*,LB(5#0-Wkm= hMb'%!^KNg5uSU?=KJF7nkoksc1-QlH:]2! $jngD.q,A8&7V;/m][*;DGWY=/312D22?'rijOL*GYjBOrMY*1tE"t 8tAm9@rI)XCmS/7kPRB!QA(%D endstream endobj 255 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 256 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 243 0 R /Resources 258 0 R /Contents 257 0 R >> endobj 257 0 obj << /Length 3712 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdVs(XimCW-k%tfd 7q;*,KTKD\PmoAZneo*oD).T@LHFQ4SLi;Z3`(W_bH3YHMh]Whmf)SmPP9e60t\u@ -m5&*B)1Nkb%)&`NWt+AJB:+RRSI;o;:O_h4sP%'3Xl0>j:D`;0]8Zc0EAsF%$,Wu 3Xt$JatSca5X7:<,J_G["isNes"$MkhDm6FJ6EeoMjPFQ#&)c:162MCg,KOo.`=0S @0#FHO%TGWCWm0W,j[$Mo/]N.H(K&4L9^ >o5]'C0i8QCA\@1a3('pYJ0%lo)6>n4=!#/OO0dJLepJS#!#K5XN&*I:tdsiMA1"d _GX%d2EkUV=Y\.!WhC6=&W=l"WhW,OYDa][*hXoR,PO.4"1Z506?&mErC6quPuiVP pk0cC"%_-"N:Me[pqG:Ofip`W^rZqsCZ)-+-1UnN2G$l5Ai\G;b,@,=Q-B=q7e@r, -Ol5hk7EN'7#!eAj'243-Y!P_+[?Y[MDHVgD4P>D$5H'G>GI^*;Eo%aj>5P6mB)$\9_E/+Xk]NFRg:Cq2lUrb)k/VO3*5TYV& BP&3&Ta&"ZMLnYDYbEP$-lE6qY0Ii#.-=9i/mJlf;=061jQZ(uP6MkB)q21^F8]U( 1fO;p5SXD'#ZJfb>c,=n#%<-Ebr^=72"%jSLI/Y5r?D'dU-"HK)#/MspXjE>!`"gG HtZU"nU)u3aRXZR,K^4<>@5$f]^LroH"h^f"*scrW3@^VIa-J_SFa\cS-u\u:>+D+ ^ebG3^bQ*(l/"U%*:Lsgj>2>AV`D6AlsG[GIZ:c!2\T@)%,a*):$(]LTMQF*92[e4 V&,T+R0]!#/8HlVn.P5fS0pO`\L E8KL^G0V,t36)I&h<\Ted?2YmE+Kqpo>uV<2@[62g3$\+EK4sGjMP(&pKbDAGAiW$ 5'MPHO_?V3&7,=W\")2JEMt%#Z-'h&XQH+LqN3.Rfre@+6Rqa,UI6dV(]jgj iPuX[AZRU@2V"D^fh,u_!Bt=q`b9pS !VD4bKaQ$jW.UBS\lY!l*AbRFaNsDW5q@X2LIcHIR;u/HLW)q2`AH/7!^$lB7'Ek- -PBP@)e"U1SHgE,&Xh>FQRMEZ`.g+h,&-p\brdhe"d_C#9-sfJ4^!O$\3`Js\Q1+] $mDU;J`k_p/&s,td?TG'6]bbIJ]aW;>!3LZKf9,HC/khc0OY!E_B\G^Xu=rp(u)rH "if1+"%+Rf,Z2G_'K@teWgbLZ6^B;he;Df@-Jr@9@V4SbVDV9";SgUoL+'H(UBtfF -ZR:s@D7WMAuaIRA.M,Sfq+m_G0pm!]T[5(K#P9$>"MA2YAhc@QBohH_Lf6;AF9"#/-fA&e_>pph$%8hQRV-ef[((f(!ho%d,Dc*5lmWE =b1d+X2m7F)pQ?t[*Kd%>`&W/+Rrh`kA'Xuqk-U@7>?MPXfjZNK[q@F#"RX_3RkT9 eE]5PY>n9U7N3(C)khG L;b8Q+[TXhTX5V(r#Z`UmF.S%h[30K5S6B!FJt%5BnfR[Qc!8$87bX(PGje"=lNAM kT;>[5j([8nehk6+Ut)Pg6LkRHn$mVQ_,Me2A%W`=6cZ!TiL$3VQpgaN559cl3<*_ 3j2'd7&!>lii/jijnq6>=fLq9Ill*USTimIpsae,Si2_--B#T;#T a2-+bU(ED*_mj9"M!ZYWjRJ\F@RFIA0SL/aBVH&oE]KU*:JN2OJ'XpB971n1X:NG8tQe?O!9iF?4826#$efo3F9]VmH8*X"/SdTqCl$[/mU_;V!M;O)b*R.0]JNQi\Bb@k8t% &57]lB>Y]E"!fgu)D\&pDI?qpU(/tH3$@3'1ZLeY@KmsM:26P725=J9,+LRoX=gG( 2e+1X,D:RpP6E>F$DOQr03\0Z;RD)';N1&:+qt2263R^PnVo.UX=-Y*RVCm=]*VC< M&pPEXtD14P)3lV7Sp;HVWLIYAaQFa-CEt-8lY6d:B9ZZ`(iQ#"OD_omr3SW$@k,Z `_fQg:rnsJ.LoW-U^hoY4n,Q_Y7P)of(h]ZcGT5(O(RQc,4r#t?4=32Uk[_K`ogk( ABq&Q@*DshJ3XT<36sKIC?gM.L,TN#"Tl8LnFm,@qZajQ_+@]0B,2rUU3=DPg2@=f %pf^`PZ>NimEN6&"aiUDjBkT9!TGqZ75@R%_K8M,>)&d>MY88)UolDE4DA\PJ@MU% C2kA9#@l3s03o+,M%G3c?4b90A9^[8G!RKHU1E2e`&+b"fITWQ7jJYd`CsKUG^PP= (aV*GKKgD18G5aM(%DJ:E=2ZCLtQ4n9Nr&bPmpLC@n$T?P#5#"6et)V_)X 4pH0nl8[aUBMMsn[:gL#Y3d:mSlg_rBJ*Db+Ptsi>[^5+REm:;AVQc6b4KVG#NPkt V/W#eoIlpC+ endstream endobj 258 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F8 40 0 R >> >> endobj 259 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 263 0 R /Resources 261 0 R /Contents 260 0 R >> endobj 260 0 obj << /Length 687 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd1)Z&q d]#oX3\@b^UN1a8Uh2Q>98*OQWQaLCSkr+X5m:BKBJA);Jch^@^2MSj:-oCU\B/2: SAYV!.Ep4QYA"D,UC"P)DOqdVL(f^15]AR*p#^&V5[#lY7;fY=;!&L DAQYNYVV5qp+l!N`krXl\)&5f)*t*=!P1[^,*.Ur[Y+[SC`B@0Mt.(GJqt=r0?\D< #)4UJ+;q;<=qISUO;]nbUPUHeLa8#=@)XD5Gbd*Rc&g*/2ll3N^`cAb$A^BE!+d1J To,-T0u.U!aiI/3ArWY(fMF/W%iFN`#6CL/&1\@FL,Y*eJDT036ZC<^#`\W-3\%rG P4FS!m&LNt#Z/o"3+2rY3D&7o`%"Cn!9u?K_;*`k%]TNkT+-Ed;D*iY;FKi_-rQf- 1@3($,mf3sa;7RF#_j\(!_A=rMF%TA,Yg^j5Vf]]T[)@#_g'fpNN2b_*%XF!?njQ4 "Y^K~> endstream endobj 261 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 262 0 obj << /Type /Pages /Kids [ 243 0 R 263 0 R 282 0 R 301 0 R 320 0 R 339 0 R ] /Count 36 /Parent 125 0 R >> endobj 263 0 obj << /Type /Pages /Kids [ 259 0 R 264 0 R 267 0 R 270 0 R 273 0 R 276 0 R ] /Count 6 /Parent 262 0 R >> endobj 264 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 263 0 R /Resources 266 0 R /Contents 265 0 R >> endobj 265 0 obj << /Length 1990 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd=6 7!A\@-7Y64MT7LJ0naF?Ql$7On>UNFctPLr%XImg\3OR[SBPUj8W0kse+Sb*;f-p, YN\f[L1AJ)ce$Wf[5tnpd.9:0Id:1$S%&8XD1s-B)`paC(9Hd'!RW0Y;rg\2TAq]^cb2OPAk$dr4=4s/=jhMLgd: &8ne`#WQW;U7lFIMTPp4Xq2&a&kIm]T`AgGU&gp')l\XQMM;H\C';:*%Bp`fWQ9U% (C#f2^r4pWH8NdS8GYZ/,`Y,uFtY>4au0Zl[;*r1+d1Cq>H@pRB*J[HD0@.t5c5*N X`\1-Ls;t(Dmq-IGCcM)TPTUn/gH>6V(auu#)d2G_D?m"&83#;@6f.C#&B=1'F#__ `ZtqC85_'tJl5#[^mP:fjg1@'W;DOa89,\]\Tjts$0%&GS`cY^nY$/#5)L$\fh2Sj #2*rn(kiDf?oa)V@%]?;(VZl@C@F-<"OWnEI=&oMS(+$h0)UkobHX0-3DsuR5Ve9S lno8R&NrNAD3qnI-k54,6npQa(tfb`8jia#\13;3;@K8IqQtt.5(OlmbEfSiLUpD@ 'LXYq9>O%oB,A&Be'&7(A:[&tfS-D==um/RHE\7kY)((Vedj<.l#tNWE>?I#BZ&\A q'uL*GelY.,5dq<"[Z&88#P_W!oM&h$bk]nP-Vh-B!/`PpM]b#D3fFmQh.4BBaq3l oBI5$$=6=U*0)0eO,6pkKl5A[&e?[bFN;6 J^t\pPfU[+F!s>Z%#.*)=2P^k9&U'^2M>38 bT$IR0F#R467GX7-^8JeqI5T,+T b6Cc#1;BX7o#q4b4=G%\]ccME+j:8r!Sm*&1p$gUJ_(>ZRL;RRf*.ZHIn/`,d%j(B U/>ESMsta!#I!B#U#C_A,'n]IqFaGa4?+D465WJ)K/s/B:o6-G),Zbe_QP>.>u? W_#ee=/O3Z81p)4r"(qh8G/aqfPt`i,!rRm$X!jY-/fIdL)EQ!iBT+K;`WUW(NfAJ Wi"( &6ql/*,%hVb<,Ded@!'F"tZmRQ3GGG=eIVVJ@V1+VuH-g`7enL+f.$8>o9Et-:4pX "J+ae#SWr#3\g1i37kP8R_r;Tba#>sOCZu_'4bq+OgKljf%WQ'5]0E77RDuB"qOpd VeKBoPsiV46Ymug0f9fC0V&6>4KENo`9AP88;WAs,7Q(YU8q?ZOJ4ug5AhL[Vru`/ :SNbu'7hC#R%YTIZ7?%h+R#1"!-W*o:L<.=^fEh:)ag\^PB9pP!fH!oTZ_NfP?,<& )Q.4I@:+L?GW&"epHN endstream endobj 266 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 267 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 263 0 R /Resources 269 0 R /Contents 268 0 R >> endobj 268 0 obj << /Length 1239 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd]JTR1&b6;)o]Lt$/l,SpVFrJZZ0 )MM.8j"\1sQS:PI\c\)8=0?X%=6IS%@?<`84t?I!TS*CYauPK5!8=*)Doh)@^EEU> d56aUaInJF,@i8-:(&TjY)dgtkV7Y(D!M6V;K3%#UR7]0N^A9E4?nqS)\,6W99T#q ih4XT_j(R.%NPhFO^7_U)#2;'bK5M5TLo\1X,Aa,+iZV&R55(hX llsf`D((?%N_iso,/;:)fJei[GhZUVj#o[F._u7\k:2*D5jaSAX;VWY0R]j3"9X28 2iM]8kA%Y&l6,])$B%qE1l;'\ToIK&h@L+'kh\Fq8Wme*gOTL12@V>1I+$*%"beNV mkA5C0!AKDQa<]gGm]GW`OIikbj.KO[(+30f"e9@4!rXB:s(JOeu6eRNI_)tr/K9h g(rL;INNaR/k'`G20T0U3Xdp2QN2tqEYSl8:d%*bKEHJD#\h^0df'Z@:r5**p661L %HF#)0jt/AJM^qJ,[l$QZ,9YJ8l-)H!bcm2-XQ,6%+>9;lKe@/?LhXSQ_N73Os9oG]!02's2&a&(uop,^t]j- ( endstream endobj 269 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 270 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 263 0 R /Resources 272 0 R /Contents 271 0 R >> endobj 271 0 obj << /Length 2799 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd1Qmd*#1j1M+iW.8'k\LNN1 )5D)J_D)O;Mn#sLD2W!;2T#gUWXRj,'Jp7PT]XS5DlCf=N,>6,(V@Kd?o/:oYX%gj ;064a7$(Nl&.E&6_Dqe%fVfNFBhP8e-0#9N3Xi-`ocUro1K`6mYD?H>Lo;0MOTt,% OFr;(MCKCcW2Sc4JYKY>2^Cs=.5QVXW,ROsJZs$h+u=H^L'f&<"MZW"n/<.CaFIk= #)g/3`6TUBC5k#_l9:<(#pZ)[+R(Xf,`U'ahD,SHS&X9WXm6-Z\0uua_BP\RRZ0'8 C/j@t:o'`u2@Yms3&[$QW]@.V$huUg,L)>o7`k_dY7=@(4&W)2WkLM'o$rOTNlq3s &.TqbXR$HUJAOAV,=pK5_4^uQj=YT@VTo>V^"mc5"2%Gg>B.X11(HZri:@Do"c3)p _Jn?n^er)dJrEe3gA/Nbm?BS5QV9JL9205:%*;CX-OQ^>)nPBp[n+@q-W3,`&4URo m;+0].mi`a__eAKe=d#(3=LPQN\\fPNTQ5#.>B**jggo=Dq,0Pgb[q>puX9N=StCS Vn)HWTYAaf`u7B,R-PLQ+TR6BLoVA)(q4I++aShD^p5O_OS"Y-8?rNFY16,jT9`mu T]E,dC:%e<6r/Po*Q.aGHOc%1KT?hGRS&\gV;eH:+RT&doIVUk(.-V'!K_)4Dp>># gu/k*eJ?k9f]k@(QlKHMNB+&EfN;@[jMoNIga("`m,5;d.\'LpgScbF9bXT'ID73? &.laX@55^XN(*8BYcDuR--e4T8dNT`^#$8^m^BX`di&W<8@g8W8JU@sNb`ku#`(LQ B<#W41Bh7U+Y:07D!%3H+:[:t[YFk2*Wmg,5)XEi2[8>0>s"X-`\\Zf8!HZ`UCqo> Pp<"O,6ZDq%R.hp2[2Y5)hWFl.$(1N]JI3T&CONC7aO/[+PQ8<=Q*j$t/u;;""5(&QB0Y0^uJng>`:9j+\$d3QDlm kL*kd@'Oif3.nrggk"d2n%&fk7hHVK3-'?2"PKD8N.*!nFDHqL+@O/Hrh&*XF:ULh ,++?&&#;jTVg4Xr`-Wbldc1cfgn?NkPl8rkDMom!_``6B=2SaY%'j=@\ctp5gqt*? U#>ae;SNV_F=W'=\d6:)WdS(Z5A2eq+NlaQ`X_ojP>UBT0Ls:_fqR^=@*gOLAgHK/ Ogn[ESFjU?'(k':<(Lq`YHc'8I2u8Z2JtfYbK71cfIHr1%J>fVVqX!7r6@g6nZanQ ]/:ms7#OQo3Pm-0XpT_5#U.JSL=Ci*k]&h,e3U01C+/@L9A0i>MEs/-K&Rc#B0M+( +1rEI'O/6g"UN?*0aDa=f]iieEufq\aeX,a)b"H+WPG-J3:'AHSS'SJP*5Z,c5^kC NhN[<;-1!?_g?$kOq\I*kou!++g[^ldWPjCDZULK5Zt%KWhD<A5$GV^]j,'BK=mas1,Hp*[UFk^=c#j-VA!@O'#$D!J(I@;?`q]l8T=9,!,^:@>9g e_GFQXXg0qb&FfA*#[Ji$?@glQ8s,CPakW#8MBLFhD"[,rPN/(8'_4)\;KpF-Ugn7J[tqS-t%FZM)2)TCtL u:C*\"H0%>s3E/f0@Q_&VX0O_>DcnP_=.?WdX+3L!r7ST82i_qHHdVVh/O?dlip\doN$\mY?7P7@uM(Z :SBt)2eo28$64nS$4Mt1>CE/cZ?Fo)3,P"9frgo8G#+h;GDsnPge#;kh,Pr$Lpb=S :f1,,-TqmBdT.Op7("GO`GZ/F.N2;XgJ:8gQ7ZTg'hN,+M\&4"@6lAs2RtN/_eYb] pP6aK')#2.,[_>HEp"7-9:gBG%\^9J`Un.V=AJ*QB:?lj,`_b@@6!W?!'0a endstream endobj 272 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F8 40 0 R >> >> endobj 273 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 263 0 R /Resources 275 0 R /Contents 274 0 R >> endobj 274 0 obj << /Length 2318 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd1;lVX ;OH6]>AKKnjS8OAb\miT$4_fC8^'G.3?3u*OTDp\+?(7bRIakE8"MG:bbl,V>/Y#3 S:h)F_1^N]2Ao"O!NPfN6*Uu$%\>J-aNO2L(j"BAs*X&"]PfS%2dE'XCin'\p/N55S,fs[Y`5lX([,L$9T _dj<=XANBH18i$/dMB^(]-nL#B\P;9.#ikV2idEE<;8D5R,&q!b+h-3%V)8%1lUH[ khHYBE9-aM_fECM3m'KOm%[*LUF!4i_Hn]JN:>u#aq7IsrC(3_^>'0MA>lL:i;grj N6`J70oW4+,0bcB#iM7HDMUP'4u#rERCJ)EFR-'sdP-Z\,1o^E#!nns'WqJ8E"kLR B\kg:oI@R9cf#"7pp@q"^CX70OO?&$j-*%XN5H%Us+MZ\W$P2VctXE0PIhtbcW,*r =]U*mD-TJgom7G[@ssLh;PSo>15KG=j^'abo;;GlaT01:%WmNf,:fN0")?Q`b1I\6 J8F_uV4_\E=OqVsDE!Ri$EQ-BMQftqL4MVB'S_0p:cF(OOCf1:p#s:I.[^391PI!> CQU.Ap&T_PK6@Zs`C$<80gLAaha^S>*L->/F=Z`gj5ILE1hO;3s!1e!_mGPql\I!A[smIbB(a7Ss2(?06C?)7Ic=>SR=)lH_76f "n_NY9uoRNLgq!N3@mLKXS]_?AeD.,HVV3f$LHg8,`Z?_a.qP\E.g+X,8f%\FMjE) -p:`kR5$)Z9BJ5uYI]@Z7:XJ^3UHV!doGe&ID`YOR'9C@N0<:MKeure`[P9g1fc'L6@60?(H:^>bYN!L[aMA0@c^8=U-?W>Yto.#sG\6:;,(9*Q!0P;a7B Sq05q=Xu(9RR3mc_#QkoKi$qh#Den$M/G$*\/U'6\6FnX'kF,m)b378fF+BuJu?g@ #4pn_81B'47L6kn!sT#VM-pQC9H+i"inN3,E<^9:LoTC3pD'GG#4/E4Q2kA]K&A&> +VY^qJ@GqodF^NS2>KA=W:6Gk/I*Tg'MHfK&4q[R$,H`k9Tn6@M+j1OPCOMQG2QOi EEOoUJ.r.EHHJO83,2?cD^Rlonll7[K@;jr"u+EjNA;G5`f]jZVu['Q!qJNO\`p3) \/A.($;[n.l-JVpn&aL01lq_N:KNL!@ANmrLp6a&jclT-!eW;LJ.P!=,gV)oRk*^q "QNN]2d\18Gae;Q=,'BM!$e*oN3U6`=iOMu;+Y8C3#*HS J..J9U+1i\'B>hD19c.mK6G.C_Hj5O0Bq1/T\e`2*%P"QaL=oreMA-IZnF9\Ee\6"sHtd2'ekUOCQ?<(P+N66q,=3DkN3 `lVR.$cl.I7[[ll3k\iJbSZH',#I;4(AjpWL6WV>)?WPJG9jmLQ7j^kP6l2O6);,] [3;eK)%mOY/B=O;55dDhSMV(We!H<-C]p062^b]b"qug(c:Q9Hk61'qF1QTl+Abd@ _#kf&~> endstream endobj 275 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 276 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 263 0 R /Resources 278 0 R /Contents 277 0 R >> endobj 277 0 obj << /Length 2625 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd1;lVX ;OH6]>BQ3#jS8OAb\miT(?PgYq2E[@2k+J:o1]M5EtB;7_?6ZS5]A_%FB]bLVcU"( m#394.(DPM1`CKV/S;UXT[?T6LI9.5b\oKI%&P$6Uq0MO8;3fNFB5%Dap\Jo\T">7E@3,T:A6jA8S%-a]>),/%KOd+WUN2!q93\06rX?*EB#i%0u%81[(]hOmB e$#L=0\HR=D=`;,^p#n80Ya:]aO?)bLr;'b K;dq4%.J#k)5/5kOs7K_^`*YK!C6/:\EsYnjgh-/dbTpN#hfhU:h#5=3N^(_.(LoS 9[lOhpu\+pM=c'0$B3p+(r^HhY3a8k$S9LJl3#s!\e"/YU-XF)hZh;fRnF'q%m7ci %0M"08<-Yb<\T1/C5CtcQH.2Hu:iDeAOcp[Nfb;;bNC]r8Ook(57k05GK@0 4:A2)_C^g-.RsEPf,`?sh1KjtFUV3Ngp'k'X:]`3dS$m%4)!()WE=d$nLY4QA2r0H E\O:#5;WAo2S[-UGk_L%K&m9XP[q<'LJ-iC&7?p?U@@29_Hn/cJUh(CV*SL$)7!'Q"K9\"(MIK]-jS/sF_AJat1fd+$Kd,HJ)FM;O=c>m2'LVeHdi&RBX9@u,&5\D/)Hns_ [LOD;k"nq6@Y9M\(kMbC;#tB:-`93fV>rk$6`LJA60]Ai70&=o@8p8sKJKm"HLFPL k!@pU4,+=;S.0H\5u7hI34&VcPfR8C#,+Q'Lbu7:C`P%B\e#'UOK[WQD?[&IY9QNB P6"&;54?-E58_hcT.hZPTq(!BJ6K+.UU<,/nG(#B%]08_2N[+E3ZU#O!YX.01o`r) Wtd<`Q*=g0"UTL?nCXOhS5(@^r:6G#W#4'ae;EA\gb9teWY=;=#pu`$!XF\\+&mQ7 cb]D]%H-onJa(5#ZPr*/dl -FC=!#uYUK98D0pRYVMjj"q]30rbb`A/Z-rMM>T(,bKaWfRF?\AV&6[2[7(>((l4D Br4n.Jr/M\)#A!38CA>$b$PV.M,[GJ-nq@+Y&D05"^pm"@mrsI_u_nb4sl*B-AfkN (CaOon9jq!K"b$FW,sR\5&uDImZ.AK@QC8fAhO/,Iu]b04dIWXf\7I"/-GQ!f[p-2H$MI:Im_nI5HC9bY%);X^mk/QGE+=.D6$8$r;1i $I'R>M'QnqmAG*oKV:ImQtA_$/)a4`4JZ676]n+5[pk1/nfWEsMK=c?V<1:$`#D=B ZCeE[!]Yg*AEe=s#,'H`OF,?IW[\H76/q\d]%%1l]k1*<\J52!<-YG./-GujU>\q\ ?mQQg[]@pli2e;TYM_8AR"G='/4^4h^&LP;eFsk]Z3Vc4$,*`_nP1iJ16:?k5t:F1 ,SQ#\@/9#Soq^(^lQDm`[8K5U^oq,riYug)X2&/A#;igYoU5b.apDo",0F/C-YYi+ M$=pU'!*!=dhfD+>3*<;F))uKLiH6,B0m9j$6p;9aQafMT%`V0.R7pOJn=O;9GT+j;M[^u(L9=saYdY>OR` 5uoNP2iDt,AujC;=oegs6'?!B76LaGl=PdMiQFWoF'3[KDN'n-7]oCQieQj@+P6-> @-Dp0IrnOU/4bAUU,5K!oa1g_6/cT%\nJ5ZZI7E*ic.VX%W;^jEJp4KBj+f`QZkX^ dO7&"CI]="[6_==kDnZme+jfhqRrrleJlCe?C8*jHI endstream endobj 278 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 279 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 282 0 R /Resources 281 0 R /Contents 280 0 R >> endobj 280 0 obj << /Length 2705 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd1;lVX ;OH6]>AKKnjS8OAb\miT$4+ZCOFX$*17\o?o,53oSBi)_k:*JEK0U),-QqG^tJ>%Ust23=97%g,M;M1][DWX,89Y/^1mAh&.FEM=u\oGgcd.m iY0i8%&%W*Kf?gJXu&9D.%iN1@7[sZ#s`;`QcCZK@,[,5aG)M]9l"d@-r?kd3fZs> 3uYj#JAeY/lKi7G%>2g%1P?A$I+#Cm]3[URcDoaf,AN.Hb;0HLK$Q7_'#+$uO(^eO 5-nJ[r'+#a=:blq"@Wqkd6pt1_F#a@&.CqIFM=m&\ho*j+[n^[#GD.-'N(*6-pSTGTr;]DL8!>t(=b5F=UkTL3^HKYUQeM474m"@ )Urk_8?]bp"8:uo[*90[d8YIEcIT#o92it'NN6'kCkKDferg2H=k2I7DAua0J=0tO Q(<".=-`:>!5NP)%fG!GD3,Ic8iRc):\WrSIkp8*YTYiU$Z@c2.Hj#ZNT7,l16']1 qD#GD$GOjC%3),%N:TBR/LiPLUMV[r%2i!`4690_k2WO=6]<^[4+P3R;\7/360,,kC-+b= *":fT"(lR/DlTR)g9[3tf*gjnpkU&KEW$gM:Up"uCG)^;:b7E4&;JJf%9$B\%ZQ"Y ff"hrEDNZ&ONVP(K,D`S%>+r+29>>Z5uC,iX5>,.YaF3%ce>e160gg)6g'E1jGVN1 6^*)WVoe(8M8kU"E&?!>r+kH?Hf&;q5E8gK&i`/^\^g1@IG$[%Wtp_n"-?DK9a0R4 LQWMr=^QLE&3%!"56'>/Si9)p.\+!=i>;\/;lbIFU;U;j?WoC?W>.KTj4]THU.'=( N?t,Q0[3tG-jg8dPpN69Q\lD`%4R/TL<'o3%Bsbj#h0D41s0a#46AF%_@FB51'eP6 "&:MW4$dp1$j"HW0.Gi)#+&_`>mX&DCl+4lF!V]-b5_ekR+a2@S=Ld*?[2uqE@OrQ ma)odGOG^o_R(B/<7=BjD(MEm-:68)Huh\qe\8ku!s%3$r("r[jIA.)Um[,S%5AIf -/tj/,'4T1a\E7"@UT,lN#XcJ#U"l;jr<1)?UA2G"?4BDiZ7XY2@Gc^5:*i<@4[X@ aC8#V2>l$em'E"[ou";-JrgI9R1=7S2C),@-]b@&R_b:p,SZ"@:NDA^$=MVr1np5N 6ZH_9jibO60H:Wi5;sl"(8FcLc!ME:%E3QP7M`#g)St6KV+B*&=M,JS$8?.DJ2^7l C)MX0FUa,&h)iTl*o7RK8IYFg4RbWV?n9?fAJ+-sM%C",W+L/>V:DpK&::75+$IW2 3lKYjO^rN*KqSA+3&"@;,bFf`:Lic,-uI FOZbc>&2B>ZTrE^]/td(8P[IXR.+]e82r[::c^Uf,`N@jQ-t+j!R<_;R5Q\E6!jul YWR^io[9bA5G]SM?i`7;buL$=649W+%#3"??5\Ft%_mrhar#rOD;M2q^Osk]@ >Z5t=ccr%E0f_qebEdWp4G8@=TqR4VfkA[ta9<=J24Y%+=7DC"Bs=JgfQLt"k09T] 0ERdt,(+JCh=eL@o+SJ,LKGom01%'Vktd2mJn*ll/)_Q?];]I@0N^\9NWV)Tl2>O$ \M0ou#;W$aY(^P'ktM\Z8L^+-+H5MoDCC2QTJj[tAHar[E>!+l.1Gj8mA+,(K/HM. o0r2/)G)t1OPArs^]GamU,5R.*0i<:hL4Ujd!^0E/1_bJ,]A,[Hm](_VWV_e3GqSA gaSa$'7l;!es`=`W(P1+XffRT(=oiNZVbN1dZ9qfGW%D*P'U1bP%ZZAb:5qT?jJ/8 #)"+Q&AW4-9IsGkqN0(`?p?s%3V_eXA\r1a?$=*S1.M0 TXGbaF@A%KEEVXYm_KZB\E=?LjKYJa$l1:$gE=UL0Qn'p_-%9tM\hG)d#(*0#Q~> endstream endobj 281 0 obj << /ProcSet [ /PDF /Text ] /Font << /F5 5 0 R /F6 6 0 R /F14 142 0 R >> >> endobj 282 0 obj << /Type /Pages /Kids [ 279 0 R 283 0 R 286 0 R 289 0 R 292 0 R 295 0 R ] /Count 6 /Parent 262 0 R >> endobj 283 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 282 0 R /Resources 285 0 R /Contents 284 0 R >> endobj 284 0 obj << /Length 2681 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdbK21GP-T;UHB+U6=b;Q%#W(0Q@Pt,7TYP %I%k7i&<62NQLqT!M"%5Rjo7KifRomBK$fd#IXZ/<4F=ndm_3,i29_\O`P#64n`,m 2hc86#FX0tE]E:Tb%-*L8?(LuPm'G.36Kmd2p==gK;G5V2CAa'9,\cSiBh;W!O'tp i'7bkcLeog5`.Z6%1pT@KEImoYI#Nq<"KN!1d_kK;(G2d#g2ANTdYJ25_VX`nDlTi JH5&;%]]`La9d1X/RVeRKj-V3feVDQJZoY<:W9cH\b$KFO11<._Z#=fMFMe6R,:) %.Q_c,tDul5rSoKR)1b^npit];3FJu4:Ium5V\8YB],TnUN#H/758"IUV\\=2CK": Gj/1dUS-\?M)B?J3"AsRJt\1O$e[j9iXkYl;8Y*)3fScd`A]%U*<-k8d@HCm=p$7% Y=lcXm*eMpKI%!caa?_pN5LoZ,D6/6W&0L/W7rZO9cSsOX@S%32cV?%X:Pe=4O\)* pRp4oNu,e.IL_WVkC82G\732.D_ocBZXMd:&-u/VjsW('SMiG:K.FME"fr1=JuZ44 mm1Ei.$cfNK+-:8=$q5<+c'P,lSqK08BO&(ZXg[j5BgWlco!!M)"A] BW@6^:cAiLI6f1e69'ZVbM!fO+c]g^L);O711-\UFQIpY=@8@1@r$L=q^[CS)PIJ6 :'t;MN7b=ZEAZ3Z`\Pce5hA9JIK3_Opg,8a<","A6,1kDoii^#6Rp#fJHNRk:L0>N Pd3s0phWJNScCZV5d=XaE*,\W:F(XO_8CGqNrg?+5ifi;=F,/;_,dC4BS;l?s,,3O3J4(& 5u)EN+^d.JO]-""JWM];^sM\;_g#`d\W1r*%HN]1;&Ga(+?F.A(c(]d(Q.u<";5"k Jd+\U_>>o=U`:BdnsFVrkW=%Gm@a*URFlWJC;.YZ3;!]K/.Cg/2d_iGANl0gfoGnXeiW9 bSq1Y@VgUlZR#3!:`ERb:n^`%7VL/6<'n!>LD?eCR+.YXlPGklbcdi+m4gpaGrAOh (J!I0+=%P,#/r-k9j&*I%M>%iUqU-V.5rL%FI!(C?k>o/3>LWIV?RZdho\Ma%:qdR R"Z^M!pNm6.RQq,;VInhM0.4!`D(A/IB&"1d[EMh]oBuR)Ng\)?mT6lT7j!X(^H:! --S\4osV>a]&IpHeR"\u7,c4eJkA>QYpJu=ldfhT3!.k$@^X1B&?>mlU)enq&KOQYNMN`5$SWnk7,u%?9.)a'2>5VF5hV,XS"8i+.Hc0pI$N3UTj&2Srft ?UZdT?IWT,"Hn7Ju[C9Njp[P:/\Q)0Y/*--nRIC1`)hml?k,"frORX'NnY$ Ah;;?Y#kV=i(FGl5u"4WJ@>d8GRIu]"1tMjVP=4nG*?[!)O&C)P,e4P*19^)A[8-Z K!8k9Sr@#G>J1J@HSF9XUC)qm^s^DUM[,f`KQO]au*Fpd<4npILjUC@VjQ9l'],kXUC@l/2\#S,qp9[_m3970uBsS>RD&.W/)Aj XB^Qrk]Jp/L)U^?6ZuW@X@XAV!>=S@jhMYJJRSr%=b,qBfL,CT]k6_t&8o(=N0tG= AF-.Xj02Hp?HoB&;]s&H@h<+."g0!]"moMl<.PFu;LFB;DSD$!L2B)Qco/,_O]doC ZsDscaAhb0e'rD5=j8ti`#=o0N`L_)gT_HHNfoi&5D#WQ4ibA&5S/N@6?l62+YbK' !tG_?[>R86eGmUs":UA8l9P\*o:XST84"a-+ endstream endobj 285 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F14 142 0 R >> >> endobj 286 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 282 0 R /Resources 288 0 R /Contents 287 0 R >> endobj 287 0 obj << /Length 2797 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd1;lVX ;OH6]>AKKnjS8OAc8R?i:1nXs8X/?[0Sg2C'1p81D_hY.##bhijDQuHF9^bJJBb;Z C\AqS8[Q?IYF-e"'=Cs.Se)\A"uCOg4BrTfNV[L#N7!'0$Uu?(Xk+sT)3*TXK7Tif AMsfSL=:hfb&06JTj#.arq4H#f\a'6ORQ=5\g)VSZ%OL'[T.MGgcr8 E"LA-6kL,(2akNq#W`BtPNK'[DK+[:%,jag.LA@V0+>,s$oeqY&3K781\$8d3K,1t ^BR.05i3Hj9f'r`]eX=(Dp[fT,\)qQ3'`*854so8+P8f` <4e5H;WCto$-;^"nM(HI_f@MlYS0)[2)1*Wm5p,(odnkA^r35],F02[=&GIGGZ[s- Ic._EgtEUl\\ia4F%l*Nb7uEt!8/enPZ>6_ALW!u"t5Z@hm/7= HiDdN:"*[PZh4%F2idG4T-;/#7YdeI9k.G\EUIU*VTUf0:b[i59#!sh""gQ!jEJU< %YYh0eCe5Ul@>RI]:2lV3J3Pe3PT\i%l$)^E9E9B![E(T@UTM3WbT#'i%g;hjJqRY DXL;9EGOn5A(Y*mQb Yhm.D)LW*h^^rU`F;W.3G!,lf[[U@FZsA%FJ]QC:(cUs_?kFjn,Ug_Q5n"pO7kDLfH,=c_F;kjs>XD[o fM$#o2ZhW0pPF14%S"u*,3F+k)=VHU#PbKT&tI/e)K[e)?-KPj.iBN*1Ec4\cUOb> LSJ@^@]\rO/-\.8RTX&YP:e!K[V+l\s?Q[[9-BLa+.j[$PQ-e!2"UFQY Q6hdf.tjj^?78O6W(!XV'R,n,`t 90jcB[H(E:]&>tH_8EYs[,7l!jT`qi:Q*#[mXH_)m+NSXrr=qHui2>*hC:C WWRc`>F?Tj#0V;\K*XrU_*BZLZ/uIHg/BL;REu1\#f"_q('@5J&L%rX`">gX%>`5? +\"H46J+idd+pqoLXK%pH0[WVagRguMKI.J><>R_PFa9#OX]Pr;^`qeTbJo2d)pH4 !N7h,L,Op[djATSq3[CgOQB?Mf1`.IFf!,.U.;eS*bed(VM&]RYB&;ab).7=9D,DS .j,^=#UoqMfG"CuNjNM+99R9h^ef?M@;;3c0dUFge\Y6dW4\On;Cu?/@sZDH=t`Q8 B":LQCCO*L'%)G+Xd;V+m'b`jr.=hN,,`9sYIp^$W &1N;oMB2OF@ED6#-2*Lhh/GYO#cK5L_$Uj0"@a*c$"'XH`hhO=8>Alj_\Ab#ai\k6#\^NHNQ8VP^=%a_::8AiigFI8o+[,1?E[X^+r0mN ""JI%YGJr;(YuiYP#uXaQmReaTp Y9=I3):X*V+e't%Wjf-:@*MTh@?(!.Z0\Vif$)` 6_;.(Y_JcMrQ7r7G^.`6LpW>6,mrr*>BHi^(a9jJCZB0YK/4LnlmtpAZN:M&R+)>h K=27UV4an&BJ"i:?49I?;sE5E(s60_&&G2%lMX[%Fag+p6Krn&Gll<,M)O>cZ_',u ^KihYq?qWf:1Tfg/cpR3%!pN%N]lI.Fj@ZOHJ_cl;T#DU%`R*_S$N81&gJS+ODPl2 9fll5[(,PVDEDlq#c%lZJ,~> endstream endobj 288 0 obj << /ProcSet [ /PDF /Text ] /Font << /F5 5 0 R /F6 6 0 R /F14 142 0 R >> >> endobj 289 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 282 0 R /Resources 291 0 R /Contents 290 0 R >> endobj 290 0 obj << /Length 1336 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd1;lVX ;OH6]>BQ3#jS8OAb\miT$4<[e.@BC:&/;IJ;j!="@ks.*L(bH+Y%TY_/*CZPeB2hD )3*f&!=N@X-ts$#'5>88?QR.>K8s&aLT/N9JS"G(u1RV@<*"b_6CQu$:<*igC0t,gX>Qp![B`[Zf@gZgc^45 ==4I'9DB&m'3%mgL*GhH42GbbfII(78h(PZ:$[8dN_UONj1OS@>eZl03s#V@.'!iT #,k67r%TjfQ5Ts`;]Nj_#WbYGZpQW?dkq5u&AOnE;Zst3$lpaRL7t>+9s2[)4qpE4 \r_N;e6$5t;^hL;&;CUV?7J6X#g,+miumVAW(J0t2iQ%$hDPi'X*CWI!eAZ*NT3"Z 0TH6#o!&N(P,P&`_`0n8NPcqS1:ZAKdQ,EB'i)PE5\O[I]@IA?@TL[nV*i92:&l/1 013K_e!;[a.tte>O,Ac$kif8Wf#ZW-$o[4qgQ$%\WG@O]f]Y.C67ppAhB?@f*+0?& Itl")"phk)@?krL=1@U8VrA)lN=`D>_I%DbM;sQLg30EMCU\MT`\]ss^s5("F\3H> =6e5)@3t4QHo;Fs0_3XtpPNpj%YN632_7;*dc6B3CbnPmd6'JY4[Rdt,ie87k+5P) /bBg5Tg5,'No9uDh2QDfEZ@078@g8W;$MdW@!(N+Mp@8C4Yl DSbSajIp<(TKk":,9Y/4LmP`40TS(JE.Zu9r(g^.;Wd;WNIsl\CC^)j`%$ba8F=;Q ,L3'tVUWWcLVsUS8#9[0$V>.DqleL0I>\YlGX8)8/@XRG7mEO-WE2Ql!McLtblt;^ VaAe!j%(rd[:>CEB^;+c5n4%]nLju@`Ik5)?A25B]QI&F_290_XV%jj!/MIufH:I? LbTf;=r9VoFDbK:-&2&R)V2-0DbMJhK'5\ATK48bKY08t"jUdKHWN,fa>k"`*opRn EXcsJ#)#kY5X5~> endstream endobj 291 0 obj << /ProcSet [ /PDF /Text ] /Font << /F5 5 0 R /F6 6 0 R /F8 40 0 R >> >> endobj 292 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 282 0 R /Resources 294 0 R /Contents 293 0 R >> endobj 293 0 obj << /Length 3898 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd-nLJ!-.8J8)DN+!U+m+3b60U; @L!!.A8XFV0jcWe9b^Q;1WA]-\?'4A3A8o[p%oSM'5J'q%49IoY4$^tQ^kQf30"p? M]:-M34s/o3$9K99I=g0cW@4QO^Mf>&DkRbT]*5A(;Iqhj>):\\3Mtu8KoS`-EK#1 )l\XP%L/VYL`1gtX83Nb"tFX9g]iU=&>qU@Y[n/#DZW+aU'W%qPqJ!9OuEl'OPV`& UDlIm\k"<@^btO)BYq!iW,dj+^hdU",AaD))2@?Q>SIHr8Bs&j6dWt9%Uf@o1B[fa dP;6+GX+9Kj/D#n^7bPj1ajLI`ri7ipnT-j5lf!!N$/2=",$ahFuPD\T[")XUO%HD"?2:=:25W$\0 %+$toZcGBfJ/4De=gi.a+!4pH@AQKV^uX..FUShKu<( /`X16psNuN.&Gq]:cjXCps9RTdp2.^cH8l5P?FkQ\\!7@3d9'!2Q=+BPh-+r?%5$3#UJKqQ:#QfU1E?UUp-3V7)TmS#$`4[^u[`nZdUDd;#iHR7,p_:W=F_lsY(I]P' s'PV2LIG?(!X._SRQEHV[:5j4l2g\,HXW'L[c)+sfhuRLS4"k*+)J;MO<@QK3):>4/5nW%cZ5as(85 ZB[^&(K=6K#]p6Q+iP2hNiCG&MM9)B9g]c;PMms9FtFl']YeRRRX6^mVCgX\Q[\YP /&j$2J/SMgG`u;e/"<>#Un*fS!iKkqH=3GHUfO^.>&ZJNe$aIl#KHGm1CQG4%7%?k 6<+Wi=:GX\6q+oP:XQg\F`!QoLHtE*Q!/N1TtiL?L,?&4GskgKFZBZtmb?!C8h&/D \q5_pYM>';GqUdr)e:!")1XLlF-N#tVQDlg&3"0UVKN&2M!o'5o4_gh*#f^.cl'Gp SFc[+*TTRdC3^g_mX:1qkV4WSq[S+ljqfr:S5"mk1Rc;5]Jld-*af@$-cLBuQ1]\P 6bHJJb\<8nW?4#A[+fk+8p\b_Cs2p3,bK2tIlnB53iElL-I]i1SQWUK-YY"bBU6b? VmTS9aYoejZHYLV7emJN6Oi">IhV>#1/G/h#'T>I,F'f"esZ)cTphM".U,ZII>bG] op@Tu8QK-aP^!="XL.8DCFn4/Z`P[R4-lLS/M"N,[nS*;;C^O$AHmmQA=(rKGuW). NZl[/kqlV@;@Y])STgg[:D[SP%CMm\=46:Oqo=.6#u86rMAf\$3oP[[M-c&f'WJ/5MHPM@BoqIp8SKcBXh$kJ7sb\6gNJq&Q<<^n%?RDSGLiA/ ?]ph/V1nlfG'oc34B5QPYAj/Kibk!fnV6&`-Ss1roN#>7:XG6o*-&o alLXD@<)gX3i$tFB((J"1_[`jrg(f $RL]pBC,]\(L*5o-&N.@R[:VWWR0DOE8(I=1.D]a3PMsmF0k0b@eWt[8UJ"PdrSMn YC-!l]17\I4:T85\T9hPDKp*8m[CI+Lr=ZBA+,4X.Q1"H%]^qjX)rHTAN();IgS"fr7qG\U=j_Q46B lL1CgYj9lF/;C]n0#jZjj%[\jXEq2\0!$Z>pl<:acIO[AL7`A1afAAi_iFkV%YVi\ [VKV*Mf;b`D@MFb9aN5BjUC=O!g$s,AH^(V81=A]>"@t98]kM]MWW4-,-J_5l(qQj L&NJGgahVEf56ajjPm\'&:s!S_FGA]VD.-arljJaQS")L#3r(gCfOkpk6g^67ej(E J6jN1i$SS]m;gIIGUJG4c\\^t+,\aT)H_ XQ_LdM)NW@+B6>^RKZM\L.;j;>*7`>%1N56`Pul"L(>Ut%%u=anH-qO^.egU%3X=( O(c("MOfC*"ie%V.[:&&:hSiiDR"/o0#7V5Lb5B@0sP]Yc^:Zr\1Ods+TQEt"+CSn +UZBr"`RWRYe::e0`:3O"lRi6/3m`rcJGWdE3Q?^8?UF@Ll^WX$o&B$-0Pa_iNuX=E;EFbu00dqXj>r@#jqXD+n53is!OS$qRQ7$Y_Dk^/B#;XUR>`O9!VRU0&i] -`nY^0f\=";GLcfIRt5RV0#4(VP`I*>I1.?k<<^ endstream endobj 294 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F8 40 0 R >> >> endobj 295 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 282 0 R /Resources 297 0 R /Contents 296 0 R >> endobj 296 0 obj << /Length 3240 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd1;lVX ;OH6]>BQ3#jS8OAb\miT$4MYf0neh.3X?/6o8nL!S2-UnVW/PYKPW?0c"1r^V!25C SAZ.0$,GJ>2t,!J)ZGdi6eECQ7@P(F5]&QPoHl'f7*9&S`!)nJFB5%Db"Ag7BmGs4 E&LMc`l#m0Z68PSY'pe0_^4rt`q(Gr%/AoJ3NWS@1lUH^691"".,#4BK0k1p%1U,. isi!qL,T5;)MgH`UW]eO$q&*$j:!Y;5V.Qj#i%1%$M^DALeiBWmj]su/BfF"(u@/- K1O]ONeh_H=j-8WA cDZEARj+'F)+G!B6l7kid==L@6DKXj16,d6\ge"^!+Zt$_5/Y"^2^LP7Z=*ul85nC e/GNK1]!5"9Q-#'*!S]JEi0,XV2Is@M1">K:2DKE@Ea:sC)SGJjdHlbe-s9Ocp>1QER`dtM?78]%s+Qfpt;.?KYN_TG&$V9<&bnbsIb>nN4!,h5]%thD2-2"Zc;IL-dH&5')'N5a_K9T=qN6P!`Y/miZ G6tX+ZXS[;*65g>=A#gtU@Dfl%( TOWjI)r+kb%N>U#W*?POX\]odKEH6_Os5qO%N:)c4gC!>k_@lI$6X'd^BS@<'Yu6> Kif6GiJO0uL7uKMH6_Y]+Hei1Krji-+ceP*Lnhk*2\'f?F\[$1c5A77@kI:Mcj2-& _h#7R,AFD+W+t`)l?/T?^A;YcDE0=RG7'<),HV[FrDKU=KE?\N2i`N0a+uh]W!dWt(mQFM=E\.g+MpX?+oOI9\96MYD7+I)eFL43/+^?DjmI?H_(\06Mj\ =(5Ol0ai0*XkXLdPa`F\N-GNaquZq2XOP>c'i, 4U!/T*!:=?$t9o=Ju`S,_^/ng%B6pd$i(O$o8'Q2MA^)>PW=L$@TV2<#Y36X18OuM )G9LCHr#jZ#<0r$,$U(;d(pX<,KCJ65u*;&ZlF]85ScXV0O'YG?nMV9%+>J?9Vo\q Y=..pf6#`]A;RM(OS60M['J>Q1m?ELq0eP3e3OeKh>&jG# @BoBT#i\d?U0B#MhD?AJG4FUf="#5G,VsTTY>YM[Q&!Im&XXd&)#Tt.o%jR"+D,fu X:B;:\l:'AZ9sobAV0K;Q-%]n/YH@BSAd%bcUiG['[nCDdC)!S@Ur1CZDCOcUpRbU qQ(2%.%0E!etZPo^K#;J/];)O'ZPkDk@d?3S$6P:lDG5YbnCQ#=.^T!uP]2'r_od g=dMsHeW>9FVrbe4A'"cn`u_2n@O,^:r.Oqsi\89nbMSj9P-JZ97Js q(F)W-I$i,N`2P6b:>-Qi'JNJ9pD%C(M"6X)pI8)-^DO#'Sg7RWH5O&js@>J[[/Yk N(T#\TU8$T3p3f5+]+)Q&40j5G9Kp:Yn4R-Po+9GjCC="5tE,^&=sPe#Y^.2(*gba 33cq1N/B>a@Du]lWoDdLL/S5Aa)\nU=aCdX2\G@Z`A_e%rS+-J,c5)0@'Nb_I&Iqj `b"8c$kl(V]2gCuh:3!&JX4V[o`")S'.cke#2#hq!bbpdRatuUC"NoZ@5UDn7S1ui @eRD\.am%kc^6+.aF+\p"&r@TFVbLN%J%`l6>Dl##^^BS4B2L6T)euD\/'Y]#6=%+ _$+N^/Jt9PUhq9sI;:^:Lb,snOf&qS]6JZ!K/L$6AlcTGQk^,_"K$PV_0`&[M$ ka=J(I"a2nN4ahZl@hqmfRWD8"Y.Q>@]5>RJGg)6(KDj28UjK%Tc\p&D#FDW>S-3A .n#t%$HjV*"m_6umOUf.8,H[Jl(*U-L(dnhH5BO(M6[L$?Lb1+TI=0%W4nR\iIEF- 0:Cu#i0m,YDPN(]/4#o6YJsZ]\c)\jX\:RF_q?oX(Ffj+Z^2:Lj/5IobcY"hALoC+ ,H3f4YIBdA/5U?6/%4bJIX)]G\k'&V\ol$)Nlc,uEtSuDrK5AX_C;7O(f:mp[/>=_ lpMW9:5W:b*i@B?c?GlIaL8a5ge!UZPK# :]pb`O_N6s%4Y:Oiok4Z endstream endobj 297 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F8 40 0 R >> >> endobj 298 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 301 0 R /Resources 300 0 R /Contents 299 0 R >> endobj 299 0 obj << /Length 4577 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd1;lVX ;OH6]:d_sV@o4(t8f`_H`[7IKZr.X!3F?`N9JD!-1UW2s 98d81#5!3j(6$9YEC"P?.2_L\[<*Q42$A8JDktmXoLL=PtaK0ib -mfs/V@o#Uah>F)E"JsUcs/:Dq-dTYL%JOcL2(+*lNSo/l4aRu&<="(,BM]b":nW* &.D2+`]&mPJEf0Y_#V\>6OmlSUOfMQ8J=#NY],@%_7DDJ,DgZ`d4hMO7b`[VE,MQX _8'U$1[09VUdWEX:`M[8P>rC;kcA7IV*W::Q]=hr8C=ZH\8`LCETDNj8aVBn]Sn)* 4?&7P@,<@so7.aub^6kg7')o;af]X\1`HYB>[WT7!JI84 8s6KB/0ZYak/g,EZg?@7+[B#Jcq'*h),VnnCJrPoI3]$FOg>N:5d\cof28jm?K4*W \gm)2+u@+4!O)i=)Dt;G?M(pLCYr.+84A(+57c6RkQCojRlA-`R[jDne$r4!\"NnQ (;D#^MW;6"CRIE!kUh7;W?HApLKN&C*k^/.#_Y)GWr78>h@`MHVlDVUV^Ma_g0IJW &;./mh<8Buf%uJB?J+-.44Nsk\,DkB5V2%(Mkr8io'Hca"tId$GYIELL6;F4hI7?! "WFbX9EH&C$+2b5=c<8qPUf@KN8hE$SnddI@EajrhU7n@NJQVVatfp#-(=?go&b3Y >IbqX>@@uX=pDHTK_9@]T`ZG*]CIOB63aX$1n)N`e=B'=)N_pcGb(2=5"Vq*)7@mVY/hjlf#om&$";lin?i%CW);3$F6AVjQr4i,B,n'%3q1^pt)Hg>nY#f`Pd "(4Eq9WBA^3Xu&c5SgQ%Kt/QnrbnDbNT6g#&AnEobmM]_\3]A[mqBr<>/4VVX6o9Y YX&.Zq@,UV:b[Vojr7\fDgp"(0a@.6),'0m"[p'(5QKU?K",J'-uoU+n6?5>BHVLO mK9=fDZ8LfbUUg/1C!?n$jun&6(uLY#4U]"aq7$.,mE4^J3Y,BRI5\]D1jU;?MP_" DM$ig3[*QM"9Q_5:b8[Z.PleeMK4)A:$ifC6V*3\=di-DboiqF8DKVA%Q+CBO[^\j )bgZ@B+^eqU:@d'dfCIJDsd+L+HD*WCe0G^<="@VfMJ-3Bg\SN$&09rpIb3mHV]bR `;LB)o9bd[fW!uPg,0TJiK+P7PT 3)aS`DJm6,"re[9j+NLD-+5-6.`m@hLQ>CZ("]ljh/PUeZ^im>e>aHrnR$S#cnL39 fGq_Kl"gj2QpVGP%,lR>Rj_"q]+dCgO*GlBcZhJUOPipWY\ZXC=>L/uG84^P3XsI, +%-G:$DrqKRhSHe\f%n%K:NE:_1Xfiq@&1J;C?%K_*^ZBGobE"1QPD'L`1mk%#Aj[ 26mB/<0[\.JVjV$FZB7JQD1/U6,Yr9L0C<$ODWB1JhOl,V]Kp!SYsMAB=/eN#)ieOPncq;:ul"M#QjL!c3q#Z9KqmI,!Ae`k6j+ hn)rjf3YNR4F4_?$`]\GKJ33_.:$Z6ZL0NNB1+ Dm.-s_)LS^>]m4Uk?SKA>=r%0`i);$j"f;:n$u%UV7ROR@eY7* 2D>@rB-?COKoRg84jP$64l-%*PK4eB4BN[Tg0Cf0Y#24BGW:lK$YF3lX-[0kh[ASJ 1Ri8/*R6)5&E5h>QqC7:C\ei2CQb*aZhpA\QN8BOV@^ta?@bhVVJ##ICNLR;1g<]+9poTg5\gtYhRp5E8iJ*Ncs:M h)g74AD9aZ[m'E@Ru.[1*V4]((B&l4OWA"S4hg\l52I8=rGDNX;NafiiB.\bK3eS) Gi&//fE4gS/N8rs5:Ep4b2F]ojd1*B,pVK@O.2mc&Agt9.gQRWf\kK7/^-Kih!IuP Zi4nEOulplLSbu!aE3@q.Q@\B(^"8n(K0-e0RZLlRWFZp9i9C*@)7!dgs__99uFl: _W5K+kD6IEXNWdGG_8Fn](h%#iL?:epO!==#_sDUr#`[o*SUena+Q^ks,1mAs203P Mt#G-Or*:B"*EK6_#1@d/4OetXc?cN9h6H!NcY^*iB'CHrh8s:L^hjRU*eI%MO.Xa g&XKChE?[[FjFI+J[tRB4\+C0J5AZ?:V?G8Qa>uVeXQ(%Eo!;,CginGa1S#P#5a`; rJpKj\ V^sAgQ<45e[>']K/BV`44?Q+"p97(uV/26,'K^"udm\d^V?UA2K#39seW>[rK5=^Y c38ju2$,Tl:e"'4DONnFK'H=0-Qk"Y`'=AJAjC2D6JTh9B(!SGDh^mV!qM6nqF;lk +UGkJ!\B949ZCPW\K`r2,=L>i9aL7i&nC:I&I,o3g5AfF;R%8cnObEG6Fa9O29OA] 0`)N8&J-.TA[)OV^nqU7`!U8mOsc(nE5%BC%P]057jag$0X+sRr/&=7$@ou%m\[D2 bQk)X\DrPB5[4TtqNPjFCai>"nX?8p0En>E)IdXl!1jN_W9gLbeq@:9ldXcG^]a_. 2rcSOK2!ABQqUAA2UG.+b&X.F`eGj]$p`tlVeUI04I?-09ZW;_WfEJ0G)G:X6)Xp< ,Vgd&YRZR.GH,nQ&:e;g1'J7(NTEKe,>8;Q0J`-(/Wj;<74/pM;Bm_hilE48BZ>NUGF!/mB%%r];^sW`jJ6iau`=g+*A>]f'*:0oB?38PD9.(tr Y"CIk0/CgPECs.H&>UkF?j=Y(lQOJDee3kq+@3Sh'(g%%uSiZcJ"1O%Qe"9*!(,;QuPL apqh9-\?r,8Oc55.h3trT*>!Q?pRIaTKgru>@d#._fd62Ud+oW9o3LWeDX&^(JE8o :c,:N,g6]!X\qL#;:MdB-[AH8@nq.2%5dK9o_o7Ob>>P&8TMhQ'a+rOn29lA8Q%o3 "X+iQCc,3/8sHGU'gkkk4X+Dk9JnGtdPM-7cDZCV!ED3_og)5b[oA3^2Vb*4[C&N23W2j2udLBIh2:':Q; Ec464Fl9=m>UIGq?F3-9oLUpiCfJs^7n[\]duue*WB%p"3[TnY_U0n9.$X888WRj` ,CrI0)bRo5CB2[R6]?>AdL>'\%Y^l]!.OACVlZ's18Q 6m_N*<5^ds't[L(B$DM$~> endstream endobj 300 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F8 40 0 R >> >> endobj 301 0 obj << /Type /Pages /Kids [ 298 0 R 302 0 R 305 0 R 308 0 R 311 0 R 314 0 R ] /Count 6 /Parent 262 0 R >> endobj 302 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 301 0 R /Resources 304 0 R /Contents 303 0 R >> endobj 303 0 obj << /Length 4157 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd98d88W:Wt>*/m?rJk<>dB/5YZVW0\`claqJ%0fa&SBu@1 $:G*i0Si>j*0(?"XhE=foX]HGY?S3eDmj*W$-_OMSdRt5d\/'- 1)C@5_U41N.o;SZ9Vt)VYj9?jo``sj:T)e;UM>]F`A>a@7++(jX'!t3%?kJ[2\"6A hmb9XMYoam@s5$ZUPS1hN^KJ^3BL!#'%K9c_k>\qD_'-5":o"t\Ugi\?l>_RE)>u7 l/Uk$51%4>^*4/R!ueHr]/#_/6[T^oYCW]1LnYNF*#U1kEh"'QBsK.@,G@B@^+`H[ $4mD-X3cePTq`gP!Nb%c2a3CWaR`$Q2"DLgiOieU9@&"&3D>AU+YqB%Jgq^6a\:YR 3Q&/:k_'&*_D\ITR-HD(_e@sd.YB]G3@H?o>S%/cA%o3:Y;-H?0E>cr.8m-MG^ALS2Lh;i+/[= o'BiZ^%K8e`ifo/fK5$:bCVH!j'^$u4$s;/h8Fps4KCEYIJ7,LkXGMS5S^Y?>haLDg6Hmn2GprsKXf4fq[ag##gUZ0i`2:*K^S+ot7@o>!e7+gt l$0(ZH./"klm*&#OO?:!MtUD0J[RV.VlCn-pL#R'W,I5qnseTCZ>r;a*2W(m:b.MM ,T2_.S2:rG]7Z9\0rT_lku'!lSl-,0N^2D42%^tb3fT"3m,5@BD./dP\;"XQk-aWJ DG%4%A/VXr5!i*$O.d1U0V:StXl\iLjd!@GTO\X3c/=TYk&M@OpJQ RiUb"A(sK'[UkqN@q4Y*r[5M$dSa:/KbKs?g7_Qf+6eFjob77!87$iTuJE,!c O]#h/::W;5jhhC(%Y;l'Kco4m_jF7g@84q"OPgBb)jYQ!I??PQ5`"GfUiah*^'-c$ rmfI4"UAVh2Y."Zhp_RVWTi.g=.o9']6]+4rE26!YYB\j:'RM"m_"j*"eZO;#-U1c ^aTou+S+`0VAX<7VfO.--n&nieeG1GS#8*5>d?,'(h2'_#XmH@%e9]sE*YM8ZG<%@EdBl .N3@l$pgWt"T\5s96;(QSp3db0@DpH\(kSPJ9`M3IL)B'GSJ"[:C9BcU.9:OT-%M* .jQ/0RBW2oV][r'n&-'n=Rc2#9YMl"m%/s"PbeJ9,G_.E2_#]2V-uFY5)<>$'h1S. ij,.(L9<,.3MW4lal,E3'Ub)W 32df3.k%PWP1!EAZ5`s;Ga]CO'MPDM)_==>AP,fI)`FumU0P9!o(lB\WL?ni!T$3F k%?T65TC7_3^ebajk@-8F:SHqUkZ/M*PfZ3_lBM#7j/;s#!QeJ'#:HL[a-1kkc%/u V[4o5+kq`:(1qYa%V:O-="?jJe=rCJ[ar-^9);P(V1GcqH-_7-(D>WIn4.@e&53^a =-'"uUo(jF6\Y.Q/_Sma=1Xs&VkoM_A%R"W%m2Ps`&P_b-)& GREH^Z]0S',\U[BY6p"`6l^hpR6H1Zk!OII)2$1W3\f2IhTTpW)bXrpQbOuZac*r* q^=CSRho129NT@*EfF-8#)84E)-&j$ZL2uNJcP'U/s%Yp_3d8r_Hu=+O^8Mej:Gaq )2Cf?+2f^Pqq4))RIQ"\+\-H95!9+m1bt4^3[KI@P$asUCT'[J5W%^Ooj9M8,f`s9 aHcRQ)j592$QC')\FFuY=!:<(J#?S8WA.>[QUB-1E+? 0]IB@-o0otgpX'R,QgBC+:Z8FP"nE@h^F0`,,BF`-))p@@*i^PmeWcfNLX&H&=;GaNQ$KlQg0, 3)0U(ORo2C?0?#'CJR4$,808M6,>*iZ8)LX!ZlZ9$8d?k&M5?I/*K,.0=U&m)5OH# l;*0t,dea+;CbmU.7.`t+JuEI[-e$B-k5D3PHXVn9FH/cAa1:/Wcg9;;nC:Z4S"!1 GE1PJ'C[k6?Y@'$!h&i:l'UgEQl(:/_;;b)f;"tQ["E;Yq)]CD!PT)]YM!=R/n-uh AnR.Tr=:/X%k.:XFQ^^&$Ot+DD8YYAkg#:cUD2sG@eqaT l>W(1n=9JsU1&#u%1^Dd&=uTnp*jsZqG6bm1%J$c#7]\A+HYOk+D2.'oLQ7_+L&T[ D[?ndbnmVm*5WigME%I8%\825!A>u[KZOe"j?9c:`kk^fO*2i>9NVlP#8&@c,$6KG .(,0q=:nCVE=bTo-RUZ\-?17JP&2U++EhHMG5997$ *\X-,OA&\P!r7ulS>;#H@Nci?Y:QOT7P`@7i#L*2TAZ4\o`oE_R)pA=%t4]qV=)aeG0EO=T<'2DHo1j6"mJdU,L*cHAPV_s$.! 8Ar\J-pY_,$;.jg>W(N>%"RV`^uWeAh@7ZW.-lL3`,N`sa@$$L3$gC@DGKr+7LjsG $te!!"nXZCG8Q;J$6\U]i7aObkj:rA,[@1MG@q;`q1[2)I[RbiMJS jZj<><5WCs3,C'&W_-h&lu=/jAOe:kPBaiWnMgW^"9=/^@$1j^5RKi%2P:u%EuN($ N)KlC^'7%2_H'-(U.F3=#U=q\TZRcQ/hBrC('\+C-MUu'BTH#t6LhRh-c44Xa=.\b ,EM4/&mT6M/JIDo0hi(*L0@V+*/5m2J$_g-e5!ejm2PR=0JMd<"s$*%eu!*>Vgr:] LlEtf;L^K?Z]fF8"Xu4f7c8,?[^7[(McQur#+A5s@1ut*"@UOLTSr5B.maF_+9~> endstream endobj 304 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F8 40 0 R >> >> endobj 305 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 301 0 R /Resources 307 0 R /Contents 306 0 R >> endobj 306 0 obj << /Length 6769 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdAG&b/s:JW\@*ds#R_i=BZt9@+c8ph6;TLS)MlUV!>c=WL[&u]FS%q-EJ35BR4,O13:s#g[5ZK*GECRETUN0AdI3c;!7 gc6t8$Je"Hb*q;%D6&]2_+9KQaZlpB,)WtJ6a7!1:#dLg5Od\N%mXg_d`=5jEaJOT DlDIg^FeaI,+rG%)Au*QW,N]0cAKCS[-DU6(l&H;j>):\\3Mtu8L0B8/Vn@\MoKGs ^)%#1'Zfm*OuR!_,-#IH*&pl5OZ3IWOB`[KZZ7hT:K-1;4%U(onf*pRX988D$qo1\aL:NPmK7o2(.$X-RE V$7.PE]@gE;=cAef@Vr2>1:0keTEqXejWpm:f_(%37\k)LJuRml:$hgts,7pNbC42Y#^I 4m*H2bheD_j8$D*RH!o2[H[,UI`8STHCiGOn,%CU"mo]s[VbDG\7T":?0`5TcgBLQ -8+q^HTYk7Xj!Sa8H%F$i.9+(!'`qL9&NqdAe,'p5Lq+_(t8>4%d"#52Eq9#S'-CX "g1(ElC&e6B5N^8!4MTmr#2n?7*l[Z1cRc3;)Al#d'aY&#/q(GN&HE/nZbH*dgOkl 3/A*N"N'qWpRtNXElS>1*S6%H:k1B\Xprn`6baG.CP=+.d'\LM#3h-VD/aK2)B1Zu k2R)PF3Wi.7WKL5<:"CZDPf9[aRp\rCiqe$h7uM%(@2`WEKT[(8B,?Rn(/6AO"ko9 5.M+@(Gj-qdXcPR=WJHQK%#^!anHf)'e1;(fopFCmMm)B(Z,q!I$0pa^3J6MT6-rq P/^3i3GU&)U\9u$%K+!:B)nV&aI3c'@ksD>.FhrTQh!lX^q$'a-IfJcn6Q4<.5&dd *6XlADJdQ&*=/KHgmM*\G>C6<-mBWpRI(a(c=oPt_29S.;(@+B7MOM/Ulq:qZ@,CP V_4Rs%%pMSU;Vp45q!G\M6S[7d!>LTW:]qTXaYW@QTu"L$t)Y!g!rl"(j(>?9H`am 5cKWP,S5Jc?^C$7FYAh79EiWO&ps(p8ZJgK#5KS]e0%[c1Q"9![eX<_<8t+PFF%Rm#8_H- Q#FYC,:9`*?jk#(a?C1oGNEHXf,f%;+U>i.B(;Rqq?'*R8%[$/n>D@gU^J;*LtE;& O[bA7n\[9"j;hUb]R4e(L.!XR^EKM:E0NZ)XJr]K-XU\&J/)4)_Dn/;iI".ZZA'@B lE-i@/2NDeg.oT=:E*cEYE9[Y0?,h).&W/>.[hiDC0B7j7'#sO>\#V9i71RDg'F3j /[Na3LA5GX`LYSt^Wq[o5dXI_S90;sce;kmdCfaV(LqC0>#@NJm/a1pmYfFQ8C'3< C(3IdM()Qlk;4HNHF8L5B$CIdhp#RKE^%-8q%+BAE_ipIo7oqk3eeHX`3 n)>6D;cA_'/.BR-Vk'*D;s#4WgDQ;oc$\H4F)0M:k?-69V71Tl8gO2M."K_K?]5!m +c.o:QK&@f!LX6MasHmEPC_= V)J!8i7kgr9]2u+WUt)=h)fOumX7a5VDK@A$;LXpN[_?8#jS3J)$t#2,iGKQ^#HZE N8TS!I<@aP8f#`Z1.a:GGZVB:qtiSA5hjEQmi1872i.#'AZ^.5"mYrnQh$)6f6P\T D9>b9!LKZf/W:]2Yg)hR'`Z7t+Of<$*XL peKIU^)+``rD2S9s!.2@M+RP,RI/iI3guNsf\e4IfRsHt,b4Vtq#:l;lqf&/cb0>ik%[Ztq'HmXciF79(2iQ[ mmppP6F8=s@Y-5fL55!]/te)WC@);&lgDoNn8(QBR/2nJ:+:W#\VL9 BH$,S<R.0PaEDWIrDbftEC6#Jj$u][&d$"n- G#FF%GA!.rd)b\F[gXt("d%pk0Q@I3]+50ln1SVbnGNhfMY$;g$Lc.D:Un9g^A&5V $debI0`_.`o+7jS%?V#0Dm1s0K*\^q%^A#+YjdM=Rh'lVt5d!l*&3-C<9(Jtb[O8fFm _\Ng&&b1\p&[)ab9+!-AmP's$n;N?@D[$H8Rh"`i.R=BFDPOJe#>)Z;OF!?l;&p8+ "gPlbdFdn?jIjoS2DNG`&_A"AW=;_:%oBYTYjWQ'cO5mGR*ak#dJjb5+JI4/N%-[\ OpIeC,n-S-UFFM9dUNlMO!F3E'QNprEeP\l,bSfs(b-[Dn*)(^2&A!B .[%=fWOWmET)Xq53&2p4dGGef5q$\*(\-B5no$$uTdc/B")u4,&[*i#b:8U!$7],XYD&0pCMYQY\9u_m4qGdGdA1i1BT?F+#).bQB9h-cN=P/ `A1=(\ZX[DZOlbjjY7=s0lpN*d+Jk,+Z3)u1m1!4'5]!F2QBXa$r^:T1Oh9'4V4q! &F37D'28<:QOaI2-Sl>oMX)Y.9_T,+Xg2uGduc^)B/abN-4:DEPH-CdWg8gLCb_h< 1N;q`G]ONd-\BoK8O\<37E5+*V?KF^jOgTG,kTT.,n_BJ Pt'q'+Z+C;@M;JcJPQt_8X.h%2#BhNfY<[,:WKP"[%P--gIFM?8sL1;5T8L]mNAY& +_u0gUUWlDl<"N/+\idu?lDM1$9^c/7ml4rU3$l15Rq2e$r/_@T\)d8h@.1b%Y2nV i5Xl=>iO%<^TLfO!f.8Xg^\kt5;"W!i8n&@j9$mXmD^s;"9 Hs6[<<.["kid;2ckR=k!rEM5#C)%>RE46:QQ,Ub)E /0Z>nE@2>eKH*D_CdA/f+iG0l7:7l/O_;Rb73krJf)"6;XtL.CpA ?m*l+T\'['>VDi";?LK7@Q$J>E'9q!.TT,Qj,ueT<5350)j#)5ET\]j\fMmM^8p.[^6;l-Ql3.7&=ETKL!8I\Wb?D^0 %=n%VU:q$Y`XI!>b@,U`%3Y<*UQ]0h?4MMR%J=LdGPVIC@Kk\#"di\Ir]_7E+CVC "MdBLP?WZVF/41S"8!eHrG4@2%$sn8G"m,1b@\0`G#`_J(N"JbEHnmJG@FKpW6XJ` ?[3HQH'fJ!'qKYpWXUnjJbQQg\$uol]mLRVI!+\$f?b"oeTnSbIA?!8'qo!-a^eK> %CbkJ\4'AKSSk2UIolGrp_/4&T7PQ^IkjIK]`)Qp=IW+Atr.Ld(l;#H:3KUlr:_-]J"TZ`6+E*UW BJUO'3Zq6.)5'Dc3!MBgS*aXRfM@!I6'd%7"W'W3.*[HKnX"c5Q@NoX!,)P(GFr%0 T;][3(gEP=AY3+["lO*L,=XMm7q^KI>LcCSQ2tP^bR?B6%[LO8-K`)G%^f3UOb(+t fPs/c.Dognp8C@('rL$sC8>_n/RqbqSdne$Hs#!$J;75!K32T&@G!go'dt47olA]f 2l)ToUqf*Q*(JRrGW+bKqD"e36!cIiI(0/+QD*r2p4KK5M/,n=2n(P%^ecR^mK=IPM:Wb:MOUoGQa6CeLjId66C2Fj i&(V<1A>]h)!EeD8Q6nr3gQUd`)%p.07u-N%eXac2I6^jf](i+%U-^Y_L)dMlWJBJ -e_:ip(T%O63J)o.[E-Ui5,uAmsr;3LBFfU+<,u$iONEu\\a374/ODeqhu`S%5[Y%[E0-orPZh5Rna&=b&ctc7\/s6k-rRI[]ObFh`TV"fmp08/b'2oj<#& g'neCQ'Op?5i5ktC,(9hW9j&aFP$=u'p=$jr#&*EI"VtU[2BdfY.9jCG 5bLue^d5M8RD%@KI/Dr4QU36)A#res^Fj5-]I?ia8RC-h?b;G.(bk!:F?)B0GPiO^ [teN1bG04?=TIS86M_h!*N8!n@:OsU48iM01^dM\` _V*NYe[=-jZdFid)WPW2mL2@YNC36If$e(h0Iu)1dU:?Ni5d<*\UE1%GrN5[,%lKJ \TmdsHsE^QG1fVAld^UQ@m[lO"pr'A\18\5gc6[3UjmohZ7Irh8MZ3(hb\_\P](21H_dOPuWBA%LYCi@Jp`_ =+&RHjj0@fq!DpcZRKO7;MdWhBTfZhEK5u *Rl>Bk+a6bpUTHKdH>I_3r.\770f=3?F)8DGDUYP`EU)5=rV:5A&e4s0Woo!272:6#=!+/n#j,_Vo . endstream endobj 307 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F8 40 0 R >> >> endobj 308 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 301 0 R /Resources 310 0 R /Contents 309 0 R >> endobj 309 0 obj << /Length 3721 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd1;lVX ;OH6]>BQ3#jS8OAc8R?i&PiW6$+S)V1PdH2"/2I'ANUf(VZULZWMIuK%L44-g4j`1 L^nL;'b\GpU)_E)6Rg)A@1X2FGLKQTq\##8Lq/I]SLQD ?80ARS8-W4c7%p-4jio/Kn67+Au?mE.dY7XZ@&Gc#?&2i,+rG%)T4hU]:/St!K,UI \Z0^a#)_@>glVm#C(`@V#cNuDNC\lnYQP#@5V7nmJB:056))q_]]l^3\>bT*BS4`C Ql7B(W"T%NcAG_=)5-=JKiZZrOD^;r!O-2$WR'!VOJ3%(:b8fY>5(-'2Un'gL3@F* (KrA3N;Tk.;Yrc]Zo&D`d6Vml):@!=@7Z%cSL[@-'[8R3KG=8+:ne[AB2[7P83p8+ Y,rD,L6[qB>/YS!nhPd(eTkLs;!!C?4B=Pk^'@P8J0-3cc7 A/@:gO0R";i7IK4,K`XBA6207/Z+^&*QQRNf+\;4a@"e3Mcgj?WKkP&kHG\D2:NI5):?2LMJPk@=><0]AD L!\eU*E[6FSh5iN]ENYu0Ct]cVIUGtR:Kij0/pp_2O[(VgH!8__XfdjDk-?hA+UH> +cf!70d3sbH/>eIO%EgthO/ldfH4OSp/k&oR#&$FAOk5ZId*U=2A5nk(PrBe($r ]OZuH[=HB=V^J%(X-t]m>g+X3%7,UmSu'G4\!cbMR"WL4:s$A4K=*,k'KL(f.H$oA Re7p\j9Gfuo?C@PF%0YXCaHr%6 N_n=6*SrS1A9&i*a0t(BB#<(DUn!L6J?fFj!NRW"PW&h7^r*#>?pQSpUa#)Z'SCUMPM.*PC#!)V_?)IYF'O]0Q64.UdIbZ8Nk06Y[9BW*FG4D[g-n &4#4^b;W)\Sr8`_SPZ*q07$AuMbt+n$V5EAB8E95,_/_)hY`M3F.)VkC=_\-AT!Jb _DTID#sor/"F&AG?s6'XiT&$f"p\-id:RZ>fk%F/[a&M;p#o[nV7MeF%cGVoLF"Q) -@Jr>[^?HD4GuA^uS0QN94-$\C$3(m$@K#$snFK@K<$S@HN* h6Zs&Nsb>XCG,%IX*C6K6'b*=8K<<^TGn='@1GR3U(cJ1*CGrT-t0Y7OFkDb9):A% B!fqj,D^>l897TT\FUmkDWAm"$U+b/pe,jjfZt$s8BHj_K+*3;:CW.B@+6%LbBB#R 6:tE.@'O38k!W!soZcGY64rn7R'#sD4i`=C&IBn[1c$T%pA7=L_k!>.FglVkj%E7B @@o.LN`/ICnZ;MFV)0aPd-:!s*1]bj[6Y:-,hlR=gNfkTp7'X%"e-4CSZ"EsUk>NaIe2,oF*eK!BRo/=Ggnb>?>o4Z^c&Y'3^CXGQet!T:,%QWcu6t"*(uD*\VQB2%`Dk. &5m$oL+m^0'aT\&0)f8Gp"U2nmGj6_Aj\oGZk=$EU'qO*Zk*B`arEdIBf:ONV?]J/ )7!p)Auf6VF>,rm:hgLNC'm"u^)>bYIje=@kGokeP%Da[8]u[<1)]\1QNNn!2d'fS L1'Rc%=qkPja/P5^d&H`$+gY#:p6&qB#X_(Z4+IqVM?qFEXkoTpVcjXQdR!EB_X%a 1gAsC-/dK9"PuHflmqG<":oG\JCk=hmed()67fDN@-BSOdO7B#btn7<_O+2^"SqL[ "TZ&4TPCMmE,:&$$=q]rT]QR`M@'@3%fjcM`2#GgE!ZN9$:(Nn_A#G3at)bmWt0#U j(AQ,CA\(;jhSf,JB.j'L'>nZs5Z]q!]DMR'*RC0"]/;,5goAQA.DF@b[S:Y5]?mg dN^Gi#I&[bYX$Qg9`S^l!3ip9&A/5S\uNY]%V(/KA>B[WQ372tH[rU0,/aeOhYSs>%YSp4 Dl!OYjU!'nb>A7",F9(h[*gC$8EB208JjPW`n%#qB*+SpB4-0R36pmAU^Gel^l].pm7Fi"Yq?271D=`a4iBficrMUB!=hsq'X1LMnFZChbW)Vi $t.3`9V;f%edC?_$o#sRY?D2U@L:t_#U#KAEAAn.Gl,h(@e&S=JDD9^5RtZ4%^9@B `,bX+8FWoD'LTPU+P-r)V]W-!$B`RXYQ5+bU(#Hg2uqb8@-%pc\.$;\omihDi.OC+ EtCar$macn ;kf_LBYMo7K,X*.PpUNXK;-5Xg?U@HlXCiiKd,/@Ua$0_k^nokj2Qn/QPf-MglB1Z bbtrCbU:8Pp7URob!6!%!W~> endstream endobj 310 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F8 40 0 R >> >> endobj 311 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 301 0 R /Resources 313 0 R /Contents 312 0 R >> endobj 312 0 obj << /Length 2682 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdSIJhBYlR+iu;%M[I&l;f0;VA?4[U!6G>I?K6ZJeX6jlc3fP_1g+pC[QprTsc"Bdt1_j$`U:CRGO&5I3I@6BR,f[)Pd0q;$(KF4PR3@_5tX[Q*J Lh;X`o\BL$e'ZOfr!oKToJ"fJ-k-9+>@EI8JC'pA!\!Q/%R[%6 ,M8;9\.gd%34K[E2b9!7-0%FV-:^seF>o.G;`lmsdfh-S[E8ij0SBO.UAIMKR%>+M @Mk7X$C&p%:08/uPu9#f&DT=+j/dA=X4N$28&MS00+eZ0"j[\gP?g/1`T,&%TFHN* ee^qoHPhaB2a^9g%#NWcTZn'@Je/>^fY`b#oc5eB58%fj%jt=E;&*PBV*)!le=qeN NIs?!4q4LjF"4YTVnMBgh=RjbT0kRbG2*;@Y2%NC#;5A%K>["G72G&OT#s5o/ N0HA@1+n9TA5R2#SL9oVMOodE.p[fXgD#>Um%YPJr(Y(u?Ek"1`l,8ehUmBI+1 O4Y$*;kh]mRXu=96Vd6dlPo38)ok&U8OC\p*OEY^*).\8A'5UZTBeB54eudj&Ar`o :ocJg.h!D$2iWGpU%#2?:Gahc?37i#=:,VZ3maXdRBZ%+"hF,W?PqE8ha1"S%[icX `6Oh`nt%=^S\$3A6s#R5pID5pjbCeeSl*:H7\p^V#=`ZL&u.O*5W)gt_Hmrq_ju9# U!UCV8r:GAK2sAQ8d@WL^YV>T%VC]dRfs;FiTP>pVSVGMpIs!N6jG-"SL[#c\qPue R0\iKJ-K"OC18ns/*)7G\!1,:=\pLPJIF"\\-,mm$pRPoTKK"CBH%XICkGFc1o@M> *CCIRGD=L6_F,aXJO)UnMNsC'PkKu:l\m_FSQ6E`JAN33fLX'UX#In=;%[2V5Ao:Z Z:+>W*o[i[aB_HL7i:6]_-lKr)EYZ36.@\]i6pO. X7d"-)9+sE0.%<'$?]#O\0TRqXdgZKQa6N'L!Zb2J8BW;',;(TjlGG(.!S`HXf'+) /6A]`Qr8kTW!;m_k*"GJ6]MJj6"cX&P`4.*Onb0A\=kKO>aLFYNZ>H@K*(7q3gf8- 3BLma`T0EWS^`*^-8`l,VB",Ym.MR?1j'rR$*njHU*E&RAO-cWYAOem>t.EZl'"If =9X(5PUJ((P<6HS!L0-U1[Q/%&8k>33NQ#UPl^0'3gKo*Zr/o"3/m,":Fuakf8($BQrgbLL%Q3sE; Ef"&GYRNMo5iE^#HuKQ.@lUA+jFG^FDaBIA3^atO>I17G5\\e2":TmQZ5.u9q-Zl8VI36*B#)-R!YBPe)?h;j*5Rb9"q:rK O1PZam6XuP$78L*U(t[XYY*_)hn,IrNEr=5>%FCjJi[47C135T,*jS9JQrUkei!'d ;X>63U$o_cYB@XrQagq8"]7u@5jI8?)hac!#%sRn!j6@/hfR#NqdPXQMSh$#/,1LW=rlNb?'R7G1`q8:@tQ!M6eKqKdfR-_VEc7o$)(5Vb]]#6 $a*1f,gPFA"Q;+GRT(i7GpB3/Wt89nNQt8bP%E Ff:Ze3#1RU",1#NG_t?HCj*oS:gq2%0Sa'NFbJ9t(5'^ZKPt$fpZ+E>F#h)61hK\( =15)WgdJ>"KJ]CV:0qK#*KO,+)dtGYDi\HR53p-?0t5?!BaPq*\A6V7MA(<5%^Ta9 %L's%7iBHsmOPZ0PfZ:21WB+4*8RV"+`05f[3Pk~> endstream endobj 313 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 314 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 301 0 R /Resources 316 0 R /Contents 315 0 R >> endobj 315 0 obj << /Length 6161 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd.WS/ !o&OT?"FQeN1eV/JDDE*Ajt*G>_nCLKUP:@;H-'7&NeVJ"OR6d]+?`*Y^*`P02faPN-1#:o2B0?BY">^tMi:Pldf&_jGDg2-YLu5'flHOY8*mZAU ;$Oh/KOKg?=Ua`<+u'Skb7D3$<^[$eXCi6-,cM+nOtN_3=HfNEE)7a<_k?'LNW[?f +cYM+W&(WY@dJui:ZCN"Y:-?*+bTq'gbK!6$qMiV,7rK_NbdqZ#-]Ws.N>V:(h1F_ b[>J31(EP!bFpU45gQeh(dQND%6>W]L2C-uf7XThAEZ'GW,NF#CCKp/QL5q't)CD/^BVm6*!^?]GaSZU' 5sd9^3=b'TjV/sP0]k/h@qtln5G>a(VD8L1;$/HXG]5c!iFn#Y6t2s&^c3kq%iqqF lPdgmn*gG^!jb8 pn?0slGJ@8FR0qN:5UPj+]N!2P9dA/W])<4kWgPVYqgtq-n6-mUSL`O%oWW5W8J3s K6T[K?^L7eKgi7o@tI0iWsKbHn`7G,f97J8?o-)FM'^g&I?>u3F$-`HLG#nL3&5C. ?#/R?OB\.SZ^&2rcHgpZ4^=qFcTh^+%1)sXaP&^EUfX*.QDNQ"?!'LHK-^m6C(cA+ `g*\e*FmLhJR5X-*;Ic7R4OP2agO=,ABKmfAf7Js]568(:/.Ykmeb1BijemWbp)Wf `S1:''3s9I4)TZ9`O_R^UG?K2UY0U[H5\H2^6A+.]Js)!qcoguIH.9E(TLgH6-t91 +t%?20+>"cg(T*+iBTXW-Y"RQ4%9Yr=*NZ/L;Aj=n]MNoCtT8G,#PX09dNA^"Ip#q &s\_3nN<>j:eT*P_QmgENtq3Uh_a67>J/O>EEnQpqMKB'[(+jeZoXZ@:^a@%cF=Pk OPK@.OE2r;A2bsF3iC?YULjnHW.K_(X&XC9<\H&04QUO%f`FHV.fI9t"bb"SbM.BG d:Kj5L9l]9d!X#2R[aXgio,OO9G#UC^D4KFe%;0"X-q?N#>RXQ@d\aU5lnRH&kh.> #_>Y9I\>'9^`)eH.0\3?U\H+Ni2aX%D[GD.2BKgLF37u"d2OTJaRg5&+u57BlG@bL b$O*YHgTMZT=/5)?VEA=XQDc4"QpRX5G9M@$!!6!Lp'>[%:Kd:g1Su%M#3@mK9?FJCb&kmq8SM!5=i/RV=>KeN5*EcNaY8 .jkgW7EPhC$\\,C'JbGE"7hMoXVGPEij2LnZ>F95I&>%B[@AoS/L_C/$uic$HnN iegY)f1]m)YGS*r_c;R<78N0qS!-HNh2ko%DWTMjVF%!5,GT'ke`KO]_JC=)M*e4R hRjVon(k3Z5[s,_#,G/cd#2H[lRhun7D@QSb1@n)\l#5X:B]H6l#CXu$FpkI;.(S* 95FdXYZP$d?Akk36);7=ehZ[Q)Qe9\fJ.m-Jn/0B<(_.Am0:)5(h4MfIae[bWcii^ i[V+h!Q4f$eJO=!3GTl1*5KY9#_^Q-b;=6G8BJfIG%>ECPil@La>\QZ<:#1$_8O+/ ppUs9Hq,gTmU[VGB]1&gePu+"DT6E3.*J]dNcUWaZD9QEk?GKdnbe/*Bgn^nJ9!tT j`s[ci4(o!\=hhcX-1W&9L*"D*CHWn-fNE5R3b%jk^cN]Tg)4bI52KnhOP(\M2f@6 b8a_SF#*8E.qQrqs7.dHOu1<`B$?B:<'7\B`$VG-XHeqA-8`@Rek#//[?.f0o7kub ,hTcEQP3dFY1_Q?*\Fe)nn-a@qh$=k8sck>mcW;YgG)@!,>eK`)0'p;/s2fH9MZ:+ #'*'jDflYE;ds!%Dt@/)Bd(2`#4Nc]T.$P?EChuO#r0W#qsTppNEmV:OlaTrk]?@1 .DG8nokmn!H3m@:5![m\<;CCV=&a1j_l:8KOjpM*[Q,[!a^itnm9jS?6,rYmi6mif /_'3%010G%msX\i\\Y3l6nZI9SZ$.`8rLFmaS\)^FY9mbE]4X^2=IMdcG@MONuTL[ 8/_&;6aZQ@WM9=fFe":1Q$2Ki3[rgMALq0"3L-,@4*.pbNK;k54P&50O[H7!kYh6e pp[@Q+MZ13dPq4\Bf>^;A\OjRRRmbL^1t3!U=OTBO.kQ)LU$[`^W&6*L[?5H^%tMS 6a6RREi)3/m0SWTp?hG?2o!/1G8)s8i;[UuL@?J6iCAYYlrd138YVkm_f#2'FGTZ_E0DqOr qV3Z[:JPfZD2ePUp=G:Wh9V[jC-;%Rm,&.n99&@O%(,nJE.;V3m-hgLn1%)6G6B*" lU-?kO>d;6k4O`b"=nf_YMeGZBa$1C/keIU64,0]KL]\B#Dh(mCM$Ek<$V]N"f)Jq -0Y]P;TQg"!MHk)!i?Cn<$;Gm"9=)`-km,BW%7#;%HteABZrbm`^6?@%R`oWj^<8) aUFh?$_XF)7LF78H3bWY%)TSkV?ABa5Xj&4%:lIJ+MnQFj:!0b"9@9e`Y]-d^C'J: $Xc4.T\U#e/.KD@%-17pE?mm/5Rmo=$g8:`^sZ]U[T$og-1Jo?TYh.J:b$u]$8A>M J3aT?aUH*2%&O(SMEWG=k7f'&N6r.Hi9Un--PE)p$mRi$a^1dkM=cTVsgM :_&U%=CBq6`J+i,!\<9/@A5#IOS0-Fq@6uQ-U?`kJE.0E2&_(\%Y3sVi*&I_gCb*A .$4(%0anN=*uMf@)A/eVJAr%-`""2q?h#ujZ&WT"W>?]"%)RB>OdZCS\mI2?%=n"# 5irQL`'Me0-rDE*,/57Sr/0`&CD$IFY`u"26u%6L%!!:1i6rkmbX(b,*Kb@P6R3.] Q:18p%'^0TP&1k_b8;AY%]IVp&d8or5p&sm60'79nl#jX=VEPT-IP;>_"AtY>p7(7 -6sLgdZ"o*lr)42N@Bi"!7)RGEt)g,$\2%\^mGC8jpQ];@V9C)KJ6erU*]=s'6),8 &`uL^`'QehJJ(*_OnfR9&LPmK&l?a_dENHj)]I-i3CoqrjX1LFj9'F7ST ;I[B\dfCJ/W`+U$;XtMQ6E(n(i!ggu%%u73`@NP#??h"Q"eaue!dY?>8e,GR)M3Se !:qZC$Kq"f&90*Knn&@$$8I56)o,K;VNVA7`?6m`&I:$'F/_4LYVJZc4ieWa1.i&7 dk+I7*RWh/:mX..?QN)/%j9OEiZ(uAFYf\L+jbBbWG5$]EC:-(51!',-9qd3hK;;` J:NV2_!2)Rm4NoS#G==8UE"kp*kUk@@/saiU*)fg@jPe<#)u_/XnG#X7P.$p/Ll@; %;%W;ZJ\\Y`pAR@A(iQnADSbucHGXfm2&EJoh3Ui9I`l]WE2aq7eW(XE"n^/'[Fi8 hb@p*!Q]XOF?MGp5YCNm7"UpZ,&I;o[mr8UP,0'C/'qUYT*aR+7M=njEns2m;b#\K 5G2\6^s+IHn-+@67ZbVZ0\m3BE26HVV=@/CV:&?d;(]Ro%hnCaJEh^gg/gif8NI+hBG ";,\h6Nk1+.PA,&6hO8]iq$0#o2X^H%b5)coS$>p7hZB`f4eF,;[ 5>S%D0SS48a7rsUni>0H\V_VNZ,/ gc/A*FY/oB(HlobTlpMKFH*,12;(@_^+M8>DD_>NCB&2%n6SOZ2hn1UeJJH6dK^n& GLr*QGFMI&:_'lT;D`)VpSUJ\4:UsL%JAOp+qJ(J$8.SUGf!ffQ!=MN@9-/]EDr;]pM?9NLcAKKG$#0o6O/1m R7%igF;QBTpaeLLf55SG@$c.V"8F=jM#tpkG!J()PgT8!J-%b\=':Bi/?16+AJM7s .U(H$giJR*5;:KE:2Y>EJg:C+1c2u%_G:n%d` 'eNGa%C`tm\2dqATjVM[<\n=nf\1DVL=AOr"d[\EV^>5l5CO>ELZpJq(i1W:KNqO` :Ll[I=C:6Xg\kHr-Z&'X1-)>qtThCCU4hF(fLB4cO&h/$BU.lR`\1*:3 ,>03oZAPPk$0dA#gM%K=VDXpQULn*W;6Fb^4fmDF> endstream endobj 316 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F8 40 0 R /F12 71 0 R >> >> endobj 317 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 320 0 R /Resources 319 0 R /Contents 318 0 R >> endobj 318 0 obj << /Length 5909 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd1;lVX ;OH6]>AKKnjS8OAb\miT$4%urTqL7b1R\FagA.!&@54;U#1G2HhII08NTVGcN8AD2 *DVh;)7VqM1MG#Jnf\F?+>B:n"J.7ddW5s=NV]E]+g;jnK1_fK?K%B`7As1g)KIeaGuKJ_3f K.,k=N9[A+%(L1?`\ELKP>MFpn$(02ZqVf7&d8b\<][oilFu"3%FbY"+]9jm(<,g" h&3m\WdLHf8gC#+^Fhe6lAeadK.4a6FI5/'&53iN%5#LPed]mZgDn-&N$R#8ZU!m? NiK>94GC'8gAF+-"R?i:6^8T6[M0!O4?Po[EeQ.k#gXRrbN]GD0Hb%<[kQc[DAuUa EtF4`a]MfRThKMM(bU`fUI`b8KQ1'HkX$!"0W9U.&6E9?C)dFqrIP7'XHg<:1TbFcM*E_/`il4jA'rpJoml/*`pVSN0h! -\o"/\ltHWY"eA4^qg)`T!i-Gl$GSr.-6Wq!Y0lBBT\%1T4omPhab_le;K$q6Zsu< k78UX'Ffqs@m[j>:s(b'XpE4B^JXt!Z!i\d4?L$pTKoK8nk-V6AF_@"AA0l^dij_c Y*1sO!smgECq%WW1:7Q,=-:k:"/-dWEJD]i%+(T*M8cbA?Zuo-W+Zs*ZlVf= N(&6_#(G!j2?BM=Epj3iGI")nIkT*UI:!=[pAE*9F82WKLYM,>!=.3TJHOt_O+Hgd &/lFK+>\L>fSYof1(8`/gjq`Gn%d:k"5o*uJoF\n&TT]qT[5BEL*XqE5[8RW1^YSJ ;@^QW;$^T8(4q3G'Yq4]"-39sJFFjl")"hI@2oma%KI>uJl_oG!nr8r%&QDeK+c@2 f81'A'Z@dq&)sB.44*gQX(Z8aicIeJDjDQ4a8H2#bX-7USfC$%WcsACD21C#(rlp. %-*jOHNie@7l\Zp8lU%iXo^jXLtqeK6tDC?p`%%9S4?sdCQXc@2o9he$_O5;P/,L8 K&RFX#h53)ngZFea=/OuN2;`A[RA9"@T/sO+,s0-=@(kb#TkUD,-o3m`DY7s-/9Pq o&R3DQs0mY`3,E1\n&f`?SU[/Lkh+DrrWgIYK3f,RF)k)K1W1'-1jp#ZX$s2#c0JW \25D8Jh,,%ffJ.=e=@8e0?K!S2s2`%PqYYu9lOl+AKn%EV)!9c8F8\*r08k`%O'+1 *CD5+Ik1T^CXC^$,3VGaQ(Z3f").`E$XFg*5g!$QqEl7e,bC)$Ai.?(;@I8BhM.9+ NmejB$>ALfUsuE7;ciog.F\QeZ>f9GMLe!Kb^M.+lWSqrF4a9M(+[lW7`+1AF&&QZ*l.+dlTH[C#(JFp08G; $A=6ajO]/S'fK 6jA_sr!Z,rZ't40bEuM2%$a!.,;ZdK=i*-i8I5qRJ=maMZb?mi9%aFLgp\(r:g'?W ih/\Sp(3sm$3?QCE#-*q*SJm;DmQkuC7LJ#o=K:5R0NnrAu4A=mTNB=DnT6'pL(<' GCdZO0ZGi<'buLN8N@kK,U=e)fc>5AI@r3Zgp^`sj#?/tV^(:l;O<=OM$r_ZRho-+ JqDDV0l^m?;ZO:p$QY:I?9g0I"9uLtMXfOsi-Bueql-72oNn*JI%]1`8a_QjYC pn;c/Zhek$Jo"]L"=>8h&>iH6<,Hgc)'\sQQga$Q`IXX,+l(]ho8YuX3TiARKp.A5 Nb;`8UjV>4S"gT\bCI=Sa3S!Sr)&AVAZ4P$BLga _0Vm3Un(c!qf:B@kpL/5UtS`tW'/f\s!$MG+,b&9,.0Gu3qT$`\>qfE-[\nDptc'e _Jc(-]Q_Kahh1!;7=e#m:NuQgYM+A[.G;@p$nGFpDgRKZ\9DRfpX4+,H7h1Hc\UU6 P4R"gRdZKr0F7QX9':(MV@TQg/l)5:guJb9k:^<)807T1]]?%$!3S<`O>?T?f_k4g rD2_?Y"\l_@.%^K"Y]#-YZ<2Kmc'.tpTREmNf*ol!;GgEs3=$(E"<3#2$%TXhCp#T cm7fd9*4OF"=_"dE#Au.`m!M!h)4>Cja*9i@_,_DtjV8@gN3m[OsUKo4%AnHAiQq"H3\"`njld.$@= i;`#(po_.De*@#+(C0OEq&N1IH7]7S[?VZo7X-&N:3#6LXS;E,e29(uVV%6?H'@cNccN.dtjqmBKSZ PVVA7_aY>,//2-sY_XeRf0Y^`I'g(CV1D6\5n"H?$.R')c5_LZF[0m5iQGY"nG,@J ]gWWklnmli0$N;&[##JhU%63O;,]%G;09U?FUL@>D+ui) 7aZhH_NoUA3oT@e/SVgqBB_,q&sUI5<%n`3Z/nq;a&NQKIZd]06O^#1\9"F!lFFJW7S9jndQ>LGB4(!0Uds'`=)>h(!%2AVrcWf`S&4:qXD kX:PuF\T1T]oF06OVjF)*F,b_TlsLH;/$*^M-K[GSG!eU#oQq0b8%3T>AdX-1>n:\ BkTC&Aftl&Q&AX2#8UsO=_2_CSm/*[#Sj; CeHI7ElL3$ieDPsN=qV1(9R H$%DsY[;<-:6H-%5ke>^:SS0h>tDH2En=lL[tRI6X)Sf^HWPGIdeJp0NG+Y*U$j]8 pFbm#hg8=+K=BMFlrsQ7gq=e08=#+5M1&dVdq4Y\3%UD`eGU[!dlN9?o^3D:1Sp5 =E%]Ai'!fdD!*iH!5]5bnk4Mk.#(8ej;^bOI!$]`%D^7V"4AS"HsO[dG%YTA`cO!\ W&GtC?sq,N"#`$S5Y<;ZA"_Iak1TqC-k'TSLZOeW-[J#;4:\%:<$Zcf6k2H%(itFr ;Y[IPUaM781e7^\1ZP,hUTQ[$du6X`8&Bt>`10F;'KnkeMl*j3i6Yr.P&Z6/%lkI9 ju?6?X>iW5%V%YbUS('O'QTj39#iPqVdG=KBJu7oEkpN@QtqYJbYY2TNeE=O@rtNa r%(866bq&dUjJ&'YU;eHOk;]YG_AEehF[M[5/UtAA*e?5oL7oZKa)](!/:beOpXNj M"ustj)d5j`)ToPRT[os"*-`gSiC4f1kloF"+4QeW(a",Qt@6o3'E;?8WO*rS&2m: GfjHi?p.Vj4eTl?ifI1,qCk?PJu*?d-)`OB[!ol^]B)Y:/.+L8Xe4X2"9>*%JppPX R=G_D>%7nCAZC"m&AUllW$T9@-,rVIj-enOFnr79L%9.O-4ZppBg>ih!6d;fC^reE @fq@UcPr",_)e\7NJ?ZK8B+]$b:rrL(YlAX^2Aj4Z$Zgd$J0$sCG3`*Qg3 &%+D3:o#EJ!:uhoYQY,F8"dpS>)TRohkUA:63;6=>3VmZRaR[ZWB(_k`umY-=?._8 6Y7^EV`G9unYEbQ8@nLlq+\,Eh@+]!65`Y',>_>A@WYgDd[RO$qmoF2P"qo16Mnfq "=,C*BKYFo<6]Xk"kU5XW!rN1fE)7"+9~> endstream endobj 319 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F12 71 0 R >> >> endobj 320 0 obj << /Type /Pages /Kids [ 317 0 R 321 0 R 324 0 R 327 0 R 330 0 R 333 0 R ] /Count 6 /Parent 262 0 R >> endobj 321 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 320 0 R /Resources 323 0 R /Contents 322 0 R >> endobj 322 0 obj << /Length 4779 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdr c8lr`"r!8LhJt@hIAT:6;jZe];%02U_7,ppW+r9`^7kXA@M^icMgcP=DBS'sN[(Tp 0rs=?`p5eubEV,t+TemfO%gHp4Z)[*!'.GQEYRqp%f=384Msh=qRoY1iqb%?OQJST 6SR@T"fpjc'd_mJhE-\"TZ$+$+rjq[Vt#tBnN-hoZk*]WN%$$i8u%Xe4n";l1l[,q lQ)!X%$LIH_#gJiNj!35(GDL4"uq18C+:/,Lo=dmV(Bh!(pYW4g'3Gn!8N\^Lk(Ki Td*2I&.(m`e;0.N!'hla!nb)?%$SjR"uoLr>?ml?%s6OV%E30do?HX0hDW@9BSJgqD/.]>Q2,;t \2AbJ=n^[G):/)+_hd3[U^r,]QkgKpfiSFLKjCu.Z5Ve+:,.&odYUacWGIYs&S\j0.;JDgb*imnn^NNVf'0GJ'HkU=!;L(r>/DQl5=PX e8T]Yd;nQcf3Q\]>34\2p754*Z/n-H8`&i=-\:+KrZ2H"pKXnUL2Pk'Qg&Z2B*7Ai HZtUi@_E:&k5/9k^#8cL:CZoBS'Q#=e`!P&\b*PU62;\W-Wec#^W0Zrg*<75n+k\B p6XI)p );eD:M\4A0ZA?=:hb/KT;8)V4(_@.4bFFS&@RJ5(](;XaLjX4%!5BBspLE8]P0uB= \T_0UA@sRQ&T1Fb.+'IhjI)!*Q7>"a0.S9cD3JmHgH&G8.:_F^Q4be=0t7H@j=G_E ntn[XBa67;$T3HBeBH'KLB8f5O(WGVBZ\k!2(!-UPOgDCa[KDKJZRCk;1$XFnlnFgSqNt/Q>XSlC4>*;FV`QZ"ABJ['D.M<:q)b.J/K"_+QlU1i*J;H'V:;?b77i"WS/>X=oZWi?-4X4m5P 9T^'kD%aV7(/Yu9"^-:T"IdnnR93!SENC:n=OuK$>*gsdZF2_0DA3.A* >=;Zr"f2CQ:n!"E%r!5!Issi:5$4'?7EF#=XYrZ'Z@`0hXG*X_TXs%6b<9+2V6dRA B%:).Pd>6BEk0!Jg0Dg2C8d]i.V ftHGfptk="@4oIlG'=Se3,:o_D:RdXQ/uEVK_JuX^&t`E)Z\`qX Cp2JI^?Zur"%CR*8F$sK(^C3!/4<\3-,H[-SDdN8*G=e8*^LPKceRktA$;uX_tON9 >iN%]89Q/_KKA$lTP&j3-4%Rc)^JhH07.DB#D`&VfeGg]CbD"+a6)t7.b`'IB[MUY R0d;5b,o/.?;8Z+:Gf0>M-i!6>-3Nl.-5fNQh.(Tb&t`eAfQAgANaeC:*eGaf?EIT(EJNdeR@kR6:kKll*"8"?TEA4V+1Q]0$ffNNkhhu*eGoJX\8(DX^U3N;hV-[n!aJI7Yk@uC hYI1,C5c[l2ce)YpsWd=9Q.`]!Q*F?X[$e&Y4s^mV7$rUM!b_/_KG:*gtZ+6m&]m" ZpsZ7S2ZR>DFqdR!m8klUq>0J.tR-uERpYchD5OfPI8*7`Rc2,o*XAsH1*TO4(e'C >J%7g(@f2Qq\&Oq2:&OVotVR9C2L+n[PeXfO$&UMOIS\X3q!*k4NCC^5,kRR29+_0 O0e?Z&_>)q")Hg`*'q4>=g0'A3UO!3&c'6@q0r,f,p@a9T@gbZ^Ei&T556C]V:N^)F#&LmF:?pSLO`l+pf(o;8#N%0`m/49`q\MK] @8ETgFoGOY?H;5U^W5PeX7tP%#8j[pO+)L83;\0>q>O1-pUJQ2[[CoYJ#h&#m2$PY5"\$!0+;u@i=th-9;?_/#@i-r9C'H1F,@W\Y i^0XI2E,,m$"?duK8'E"[KQfj:*RCVA5i[66o9Ni2#sL26h1Z(>!)dM#\1kH"3rpb jolm=$@n?o_\=dn+>BQ-0`r+D"S;g*4=88P,ESEO"#Mt&TJQXF#c!tP,GtsFXudj( +JW3P:l$;>+VHL"gI!1@0`3lr\0TH$$5!ONKNLP`_E$Q&%n+u1j2*jq?qMZj4[[jP i64q@\3<9u%A^9(N/jK]6U .2O865"'c.5h\@uHob,U&S.sG&U>>RlofmI$m;Lj5`?g\K1a<#r1WI0nq+hMROe'';QNm n`(["`&b]f%AJXfK;]5egdpM\)1XsY!9u\>gD"Y]%WXH:nSC@nW!n906_NQP+PI5l IQ^)b3htCW0c:g^ZL.35+#'Dm!9O^mh@>Z]7a^!Tj4b@1*_Kha4es8b&jQW*U^Uen 3:Xa1noH0ged]O\8AqVZUTY]cY9)\1.rgqA,-<@N[k"73//!%6JEB9cSh%dh)1mYI &[*/W!?:Sb4[@FY6b"Ni^'pd9(#tc8nj+j\(F%d6%5D]?"8k1?f-d?l'P#W%nAIl5 h@3lX%Kht=&TK!=6pT0K.NhOp@*/r]fM,5t2"Am4diB>LljY4Y0E^2eA>hjo5n%eR 5(o@rjC8q#<>d%g-tEu)Z1L#/Hq^>r6W7Mi+O3\(i!fe<4sWrai5@/9Am8[*-U>[" 5jU#kK,`kK4+j\!7*\MJ"$L-j47a^QnN8dnp]M7k'i<7L6dmnZ$P"k:0k1Y&_@[:\ *#P o:dQ%@_GGJFXtr+8^r/'OOQ]a MF,h17c].YKSLc"NXXEY'S @RUo,E0QbW&1unW:kch?V:ksSE]#D6Ft:k9-$i\9@H7'MbMPT2;Q3ApK:0;[$X !hM\SR1M1E8g$q9JpUaEF\h('$Grl26M^ko4tX,f28ETQY_ZZg.O4XT:c'FCi]]>R +(dEO>p:dVOG>jS:^/gsWbW2ZPYG\MSghQC$>$Lo endstream endobj 323 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 324 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 320 0 R /Resources 326 0 R /Contents 325 0 R >> endobj 325 0 obj << /Length 2529 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdN0lXX,=g;"L\i]#h?':)'7Q/*$DPANu hZi*-*`<`^faP?9@Be2!`R,7ds3p;.A%9UE%]>#Q!EOG"n]L1o:G/VORYe/C;`PWIhY To`s9`pLjY.LAjbC`#Po$o&!n!JF/MXNk9k(l!U9T$'^1HSEbh8TX#48^@5%,Wn?q DXu!PaD2#WZ!GGX6+K(L>`/?Pbm"B3oea)j&rYj[u]bl4+&Ng(X`%>Mlm/ -&Q0_mPAM9QaT$=ag-5"P[,&H!koeXErZE!.ru+8#43!!rN\*%WqcN ;,^ET4%QG?78u&#K9F2On=uZE2he]egHXUG-,WJVb5I8[Au_[54B7Yh@-1aan6m1# 1_q$\=DIVM(>)rIUc)DIPoGQm;Rah4kHo0Jnq8)JWS`EI/O>$9?U3><*fsqD%fA>n oO-Sio*5`@e2E%.FN54(B+;fOT=Uqnqo]Ur$>#'S3)JC-!GHd`TtCps7Yo.'U7d-4 eaERl%$o;'0+J$58Z61rU-kklF:9-LL)5m;SnbH2Wp/^PHHeAJd:)CfJ8F`+l,8l\-2mK`!_.#4_qn\',2G!-k9gE-V%"( (eP3;HZ6n;/@'Gm??2$XiOTT;3(g^9ZY556bcd5gj;d$/rIKh,pq5dEnQ\/sE[=mP "#pk=e/H#V$:W+=i5/J-g4E.`%:cK,r3Kpj6k5)"2CNUW(s3FYFs)=7L2cF`@e+eE &K4f:&Bb)L@^7n(*NklT%N3alh-;^#3gMt_WBH4l3EjCs] Qo#"4\/^q1p1Imr.g;a,1r:Rt[DI#.$W7kb2H@pU$:N#?^s7NP<+t(g!bMQ=@2,Lo MM$he1iB*3(sp/GRcn]"#c%kgJC^`&!Jq:?m9nS!&8lebWlY:8>S%,!6kS-^H(dMP Lk_WF;ZR#3/\s7T"gGR+"!numa*`Ml3_XgT4XMUhOKolYb,9XL6W\dK=]" X*&TeYTA_Ih`B`]roM7A)bdL=2WKKV/G1BVogR?LV:Da]hOOTJ'DaZ1G9e0GS. :d>]1!('^4Yp:>&1.mJ!==#5p^8"N3#Qg)1$&5m2Q:.iGD4RpXi5&N]nnae\W%PTV V!Q=R?ZN#92DsFBp4A3r5YHN1"t5k;Zl"G-R 8>)ZZQdZMp9^/Z<+S=k/;p'Mb.MI(Mk@ls%Wc3u][48E5#+Q^.C^nHJl8gS7[T(H) <<8LuC)%Mh`T!D(J[Hr,==l3=$F`nWogGe06BtPD&RpPJN$3@(f@^3L6nFnl46KEX <&3+X8Eh5&Ul;oeUeV7AYYM`Br/)-1]55iU-sG4TFk7;]BF0(`;)qP:SQ>QK_NA-6 [iX]XddToH@XegNosYb7)Vn(3FY7gS*mVm_p5k2\JEbH"9A-QdPlbP$Tb?,qcrq.H WF$"U:2Pb/j;:m=8RiG,bC3:R$*#%>)kA]^sas6!='*FkaiIPNj^:q;0*=% ;/9HT)(*@SCga/6T"Qp.2O&5^o)_oT<;E endstream endobj 326 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F12 71 0 R >> >> endobj 327 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 320 0 R /Resources 329 0 R /Contents 328 0 R >> endobj 328 0 obj << /Length 6129 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdXS[ ,7*(s'.Fg=jhcnKS3V0Q=3\caBo'oP&0&j1"4hkV+>KA0VJ5\lb&06[`"gu5_4F5%QJWY7UeK`*F+V9?B4W`IFA]d-1-Lj.,K% 69A9C\p[>W>K5<9cNZ0Y6Y%nifXn$J\:Cq8$'#S]"s7R)29Q^ -kIsRZ8/;o@ji6G[j_q\r!$SKAu@>CY\K&j\fs#--#-m=2?C"sBqklg[\=US3i9$\ CJ7Q?Y3M^`MB$C_-H39)g-2n0"ra)VU@$Z0U_%9n1_b`OqQ#bj,,.sZ2'O9V^^pgG !4FMS""*3+C)U-@a+kXpn#pPS#48R3)_S92m49m1cKX(Q_PV1>2^,Z,4-nXEBmf@@ #)6QbC?qPk[!Wg-lY'kaT^_^p3VK#QE3KQqGBfut*WCEEW?K!5)kc/WEi422pOVN& N/DKU+Q1YR'3BMO?4/rDY8R@m(:*X:`(#$5$/u%R./fks 5'_oa+2l35q41b-BWT8d5)?H)@#Q^U:s#W@kV,V5,\2iDfUFo[W>QXsCip523#(R1 >/@RUM\3qm7OM_uN/8$g:EB'k2qACF@81q'fGbBhhHRgXZSLV.cAjI(AkY/61s9.E!OC]XbbhQ7Ua_0*Os31-WG<`Q \P&)_g2fi21E]69pmePYDR?o"0;3H#K&Fdt8sjS@VeiMeo#)MD\FD/-4PZCe32I=p b:d@*$nRJ%4\V"mFf9]5N"fRjd8sC>,rF&E74s\JL>>41Ujn9kZ@^I1%639S8hiTL _B\:e3eQeI6O3QB=KPf,-#S4:ifX]a%7P-ts[8u/3>3>EWT2YcnB_8.OGP=?L]G`D" ]GntsD(qD`D1QZGk/42!mD6>EHFHof4Qlrm*R1\js#2DA naNC+I2B3L5<*mm,]0iuY#\t4.jn)SlSaN :]#uqDEL%&k!f@(k#iA6N=;^?7#+rJT-rRGl=X/P-fh/.2R&K>BX:2RQFrCeDan!J \UQ$RS>hj\<`6>-?9$Zn8V&J;h.hJi(h7446>`R5!P-Z@'"`2%1S/`;Ej"!W$;ET6 X_lBEb9L-/Q9jR\2Pq/ipk*k=onfn2G-AXP:-7"'0"-&L^7fc?EpYuK 1T'@>dZGHZ6g'DokQ-fdX#]W750-&#)C\E+\ar,9\fBM6+?@aE)O0G*qEemb-.t@2 N((3;Yr27*L"fs@eEqf4Bi]>OEYDbj\ReO[(aCm['r>*@TC@JS$*jH^`%*^Sq$:"t`A[oCYBR[3eFf>=4AMT=&o&/!PO @Yhd?p7!;;6Q^O/T:si4]_C?HVWI3j]o1s$):&/@Am4-uM9o$uqU`$7lE!@NbVW*#Up*-S:t![?3u%(a9r2$37f!&+nb%#hr;0YIhe #"TN)WijPU>j?"RS%>kX:RXe8$a3KP5P!cf?:SC;pfUl]?:BW&2Z].O!/_"mV>s cc0o8:A3arK1Qis04<"?$Mc,sb:F_BN#9^=!s&hUQE1/jOLiYklN7]o`i(k5ONPHi rof)?J8]16M9PgWTT!h*`Qs#7:a#P;=TXPM%nn1_n)4##)%[AJ">L2mE2p_A0+_UD ]IQmgd,O(_+VCsW&ofE6LZoB\M##2`d`D@[bJt%Xr/&F&puqRbW'_6>\8$hiH>$FM kjnPJqZP;ErpPm^0CoqkDY?ph'WP.JX>(C7XHnf,'eP%F&Qj:r/07Xt?#TP#i+W^m` \uTddB6l#D3$b4b+p(;*A9]7a:*\dG$"-"'U)l@/P%_iP4!Hj6i(sbY%j<803jKSG ^fkL?W\.af+OUf(&e"qQ&2+HT.>_jcioqXb59`un"s^#hK%au&Os&ej"VNp#!c!0l IP1f'_?>5iW7khr3&*a!-'BDq5^X^V%juBIJ&tD0.H2R]UESW'#\H[Zi1pO>X>OBN @mH>iipa\&k9@]S+gP#j?stFDmKUPh"g*B5dQ&Z&-RU$&.>!ZU"FQR\j";IO<1r:^ P"9cYALqY`Jci>%&reBHY:HJ.A$WopWHQ?L,aP`M.WP\I6jGH!rX"c10($,!P0>6C Zt7?'FE2+(13e#CBP._K188(]dU"(PJ>/1u/sC9!XDGRKFt#a\ 2q#PpBQt[*e@Qpd-F8.Q%qKbdE?e2//i-o[W=XP2AicgQbO]'gK'Xq@DP>J\46@#a +<"g'>)%k2Aqk+GVK-P0#/Dh'@d[a@6LK&^F,I_<'*9uK-/-l!qB2*aLotR]\"^TMCl4;Bh;$9 [fJt_)c:)G<6U3HClXUt9^)E[UHff#q0(>BI4W<=q8#Opde^ce%SLh#oJp#]r-e)tTP^`c@ kfgp#9<``MrZpk$].4\)k/R*q"^`ZD@4.+BMN#kpF'a"F8?6f>cSfKVJ[+]O^Knq_@=9I8VQ."KHO##u-=02#uh4k.//1r1$o$eIKIG 01KTU,?-T8OYUUtG8*i$'2N+]OY9-V4^FeK3_:D\'$GL+>BgrfFO^A9bbkoj-?cWb Gt@.\^guZ\MN'QgH<5egN)=`,I!DkGG;/i<0abe\9h(06Mpr0?F>Wfl2\>^pI9#?? DjLWgOCn"b%qr_+'Omh=d71/(O9g\d0]&_1o6cd>741TUPZm\qn;_H%],//1pZiHrM5:BmQ:f5mZs4qiO3_/AZtJeL6eE[o?p'VqcbY*pZE=Kld3F'`b:Lgii/n ;(Bk.d9./:EnUj&KQhD?@%!u3*`fTD*]5DK[e=r1I f#ZE-0-^.$K)H`W&0_@/#=u+369@apmiV]N@8_q*U0J(6:b#ku%\u)R`:];1^/4kq7C8]t?r@B*W!QG,B//O"ViHnGi4C0`9N1=PLn8n6pkSC3?Qp\= 65biY$@[!MbMjXXXd7/mDMK'aAYMJQG%5*9*@Q`<-)+]P5bJCUNW]?L%HYAljh[/6 5XY^tPm.^@\GdX`X,o[LOGjZ":ZH#=&S=$A@:LQ/j]!c+Z("5QS&"3W"(d[+.SffZ >Qd;M@qlGt8jB!2%K_4k,-1sEQ;^4W-tH'^JBeV^j@pp7$q:"XjpjG;jWfKq8(jsI !9t$qLd.^e;[#!VK?9k,5R;sf%)\Yp)B^rRTFRXI4J4tidkV6,9;e4#V.MOiJ=m0U Rn@6u?Gr->-)a&"uH>@6%I6a@n%qui&p)g %3Y-KUMO(BGZ2PZ%0RMT5X5U*L'iW]Wap77qOoDB#A"dj@0CtfJG1Qotb>L0I+((>mF>C/Ig qj"^]jfG:b%XOs(neWcB?aErr%_Fl*)7,fZICL1+P^r(t`/G,#e%Z$;#ls6JJD77' 23b]a%3($Li'99^Hk$/JYIjl[_ltUO91dh@PIjFPH@)q$M6n0/S>9f8H]OfrPJs3? /4,.%S7Y)-/]/Lqa+Yt]qVXhIcjgso0BIo/,RQ[Wjh=-A-6d&+=[ikYft."a5bi9, =uRs^ho]/P%&XLj7+]"R:.RKgTGe)?gQS]!l,[8W_^"utS,\bH)B^.~> endstream endobj 329 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 330 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 320 0 R /Resources 332 0 R /Contents 331 0 R >> endobj 331 0 obj << /Length 1988 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd=u`@;T_/V9``Hf)&Vb7\<%qg/L^^D)s9[V!LV_ajDonY1F^9- AN*Gc_?(khW$"'4b[sd$9]dHSrT_EZIGu@bL*:Re\i1e$gC$a/nZi;hZ-"gKm\1XCGfHL^0%`F*qataeYCkTU[b[H-OKfB./`=9mUCte5Fm^'.b 7aPs/2TS`7fHJ[Q.*S;2OGiPe4=^6^pV.jOaufj4gj9+4m%&%Js2SB3]L8qU*%:J6,j*cS;fRn=*U-3FQkb$F9POA_+<"0'8;UD3k $pO;OddF0acM8NG'6,XG^k&\-D_G+;ppT!,*=e.Wi=Z,`):lO152j5X'/B 581tSgh.,s/^Ojl"0*M0=r)g>^G3uS4MNu&lO$#Z!7-i^/&q*f"Ymq$>ZVlc5QQ'U KlXj#3SX8`c+4U3*C,7r$U&\UXN.u_60%S9:G:@BJ]CHNpV%Fal/UN?m+>(]]"j1] qum1f,hDWl#)cnVVjK=JX?I1&:<%IOB=\"oU$/u2a0McC"6#5D(-BOh"+h=!@&,E/ 5a4fjBJTh:q9$>b8u8/m!A!H[TW'eXX]AOi/3sQH5G^%N?VD#]&.&1_C)6(_f5L!\Z"A-p;Spp.S/6:Sa5: ^#i4c&1_"5Xr&UYJp,asCJPCpS]&\(>/jUSl=7%6".13Mb+4A.EhL4\0a.&98GQ,I BYFAf(;'nSi$^sMD%\!/E+i<,B+'6e!^L7808stW,804ObL2bfV?nm]-/]Q`M"#pt .Ka^fR=`^nT&i9QjIOb5*)44F%-I2sS`Xg8:&3(5,J6tZ!7H8?$3Uo+=@*1b%Xua' krjU""dfm!P_54=$7JW2" %+@'*bkN`NUFMRBaH@)enpXT4FQt1;.-dN&OX\uB#V8ADr>2t/=4RH!=t<66)PpNUUE+i@)WF?(IZ/*0hgKiB>Zrh)YA$&i.(IOW5:rcBhLLAPQ;E@8k[=n'= ac:X6X7'4bFd=/(8f8BJTiWq!&I*rF:kKT:80OK[#=)Jj@@>(ol<+u/F@ZZ`;c/OZ ]EXH/~> endstream endobj 332 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F14 142 0 R >> >> endobj 333 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 320 0 R /Resources 335 0 R /Contents 334 0 R >> endobj 334 0 obj << /Length 2612 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdWdjCBWmq=sA-Z3]Zh(oJg36sDk",.9pE>/?,5/SX21+Z6HSe+0u)qb)+ c[/pm"jVu4)5.=6r?kaZCsrc1)L4O;]cF&0\\'&%\k`%2SPn) R]6eT!8]M"JH2@o^+()a,*#$SeKHSd.M`5lk^rs_X\#PPlH6?K(roIue$ta%NE!_K 9TP]Ib`1(E=?!SsF#t+qWUd^W]d&_r1X`UCL`t8il?o0Z_k[@ZKEJ18jZ5*&3rIE@ r&kuadDNik_soNDc]"Wh*CeXJ#fJVOoa!i^e9%M7i.M1\n2`AQRo'%+[B(tp:.0$jABs1Jm$* .b]7AYE!KHBY;p45e%L<,HG^U%o,s2'X`3,6onBa,gnD_URH:r%)^?G!=d0uj#].n U;!X%UP:0U2rgbp2e105a;3O[9=LSu)$o.Sj+D,:c9^*S.fNqfqL^ds;#NGT3E[m+ b`9sZ=h[)E=PhKU;dKIfU(3VK\ar_i?6nA:u*'&[OL^g2E*Ol$9)qmc(+l( UMh-UEXNQbjmjE^.FiJIc[tFCb)fY0KH(nV mWSp\$_ZFJ)qMmfE[G*4TpIZm!CLEJbIQLuF6ni/W-!dU!;W7rm/@I;\9%bIe@HiW (u\;)-`X^"Q]R(i_hfQAjco%lE,5qRW%F*A<6g$@Xp>,L+s_h>6C%Ul6:%8&@0@R+ 8CA=h5\U9Q9CY0H![%ofE&8kO+K#fC/5;s^$R=_cL_<1'_NNS$"tV1FK^e>@*N3NZ n;.YYTV)l`3$8L->gGaC@hD:h1J\G-;Q&YpTEqJPPfg;%iW&HUZ@Z+KB<"?.^ 3J,iO=-X[.<)*-A[(7U.:6J"+kBJ5V_"/D/AKdCOQ*g[DTGf#iDiW"u4d H3_Y8OG;p#d%foOB3h*q,"tYHO@KRH6lO42T12_iLoIdGW7;Y bK)9;WFb$cJ/WW`(ekA]/s+K@[p83NGcoRsaB,,u.iCj:#)[ZKEX%-q`c7f^p$"%Etp5sSm?/D37_L3-@2fJK[R !n`j/C1%&Gc_K$uL=,=6Dom6E\8tj5K-Ze(DHk)TG!`d/3iQF:X]Vn%nudU\3"0Wr TX779fENIR&1ooZ[4)1._$^ENW2MY;%han.3^O<*)J5m>N&OE!jYMsf7CEq]#2K)h nB/j29-n?7'pT/GDV`Ymj^b3?.r:Pc[NE&V,7>)&Sdac031"$b%ktFNLh=(?8lk/X 0T1%icL\/4CLHt=-XqT8>G*qD`77$"hTB,68q8ltl'T#g6`ZIguR_jls_AOMl&raL3*5lh0+7S6/FY&#O)TS Bg!j2]\+8?g`$7rs+F+T0o&$bK8Q/I%#V^A3qY\FgrLm3%@(oT\tLh&9%9^T3sYlf d?6!+CGbQJSss7uLQMdm/+39nB)oi>P=D5S\]?^bQX\\!SII+Df)+0/_U^Bj.8EU_ 5*guW60]2(1+FpYdJOMG_KA@_G$kjWo' T3I;<4kDDb2SNq,WebjP\sI8#A*-Y"_7*AT#WmUJ~> endstream endobj 335 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 336 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 339 0 R /Resources 338 0 R /Contents 337 0 R >> endobj 337 0 obj << /Length 2733 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd=u`@;T_/V9`*bC-_$JQTA2\g*o`L1mc\cR.\GK^^*CR=us8TdUg6(I^d,#d)HN YHO+H"B%GLW+@t+bm"i)pd*!_EISoJ2UcKt2bi?!i&[QN5cK^;@*$_(/YG?a%&rg1 1+siEDeOqMMNfTN#/\K,`D+,W R,0RFmAe,kd\E'\9[YPpi&k=36F##__`>hJO"fF2'eMc13:(eVohud?0-gK'@YU (Z"p8:b)B+_b_5DgbI"TUsB\%*XoWIlI2bVSu;0TPti^%1Z<>kj"u!"5qTJ7&jQLc@M* E"oQh_Ta,@;Hpr0s!M4-A>J; .l-H\ML+nM%8<,(GY9D'_q-JN3UER[/4tK8$7,+h![a408a0;)AUK`00+o/H3Jufr :eYZr2Hi(^.H4qf8SL<4Wujn.MpWD6=mM9RcH8n2*PGj8&B 0f.6l(g_9EabsO(mK.T9D0N.B[rp$+F#8cMkZE_ K.?^R7d']BTJB8n_^R>R"/of?`pk'JFuFg"H*K[]pms^m*-gWE&RJFaPeq`[X$R/t V3H"5e%.gk5t+&;<>(SE[ht`Pbt=p&rAIL)ZPMQe?]G_U1)26"kn7)C?9"Z6Id&;@sWch FUW2es'F2WfY#@DGL?8F[f7`!]h*,S63&7XU>&nCn1,g_77j%BZ /SdC=60Lb@`+;99?Ek\GZqbX5RMCN`Cpc)jd3L`:=J3s%e2ko%6+\FpI#jup8I#s& 'NJdkTId8NF;F3W9E0[5h<@E;Tb/a_2M@UB_FFj]NO\<,WL>\W]G.%E1p_T=9SLC" C98L5(mb?pF72WpGi+uea(c`LMEu(I)QM9^M@IIpc&38i[+)^QK7\Qcb\ZX#@rDq<'TK9?6jP_dLGi:U:PMCf:-:JH14]q_&jk#\G@IW.0#Vhh/BeB7)5.10=/&og 3['_D^1_Bi"'U3bkikgnG5-\rKFJF1=geGJ"eYM:WkkUVK5B?9_MYY6h4'\Rn8r3a O76`1R631h5XE9)@!,PnSfP`#1)Y$,,2&J/pEVq"][sJh7?JSrC#e!%$.*QV#?+1+ PoLg%)KFuRpMuo95grU0JA0XM6E;;lhR#@b5&ftV4)jb(Zs#fAS3r_pUP6qaZGGW. (a@kO;#`A/@cD";W>qA`"%W[~> endstream endobj 338 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 339 0 obj << /Type /Pages /Kids [ 336 0 R 340 0 R 343 0 R 346 0 R 349 0 R 352 0 R ] /Count 6 /Parent 262 0 R >> endobj 340 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 339 0 R /Resources 342 0 R /Contents 341 0 R >> endobj 341 0 obj << /Length 2098 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdd11mLU!,c12[Cmg6RePej;G%Z8B+gih5->39.g&2N8s0^*mXB r-0t\&qr"r88[DE'sof6pHI2OlTjWnolZ!Ed*tIbaeeTfjs7EU9o0XAdN",U.Usm[ 4YtCtC@\hl>G*nYYqY5tXAC]DcTW&VF#6Pm/B'AJ24Ggp14s9*gB7t%'KD,T5j:a: o*iYB2G,1.CJk4KW=cOH!/$4[j'a,-AZ-0\.mDe5gq.4'pY[J"V2I7-<+q\ gc:Cu*J[;-cWniC3:EFJ2*m5?",5.OSKhuLOi=u+b"fHY)?u;aXJ]+X7!SJ9j/[4' ^]T\D2pkG)m;ibIRYEafomhpNN[YNWZF^B%C`&n?DWZhbJ7u+8Ue)aZ\qC*_B"8+s Y\p<^46dY2JZ'4R)5.=6r21_>Qs:OUBL85HE4Vi'N[1S2+HegE\eN39JW?e4LnhjZ S*bEq5M5(4&D];#2]c(s#!AV&[>*YL.H#jf(dOo%Tm!Z#Kc&V5g?hKVnq+'8YSt9N $kJ*#H%%E;fkt&`nhLgO#_a_*.j0%U<7mP`dd=^h;6q6TaaZ ccQ#C\DEeTE9LBNdF<1AJ)XX9B!N9Xcce^[)e[1&rPY,aDW(kR:*WWSZUTCnc?;MBE0 "/of?`uNHuo\C;k>>ucr\@MidS>/G_Gcsc+<`dk\O/rM:Y^h>C410K endstream endobj 342 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 343 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 339 0 R /Resources 345 0 R /Contents 344 0 R >> endobj 344 0 obj << /Length 2132 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd=u`@;T_/V9`*bI/_$JQTA2(hcLjOgGf!qIL^nL*k)2pZZm/5js,@?nG-?iLJD`=5n%4%]MLeSB a+aV`g6ST'-pTPH,8ec=3$VhrYI+]-]\#>B,>@^9JTC<*TOWjI)m$3Z%N>U#W*:/a Xl(*JKEH6_Ot^=/Cmp*PU6r.h+hX,]T^hRi."S<0oY)JAnf]TY#_\*],9_[NP,'LX 8IA-^;]NcnaAXE8$nnQL0TDhWnefJ7]Q0pM0VE"tc,k=rdT&G.n(!LJh,i/bRXujp[_"^;XW`dB )$Lc-\ge"OD2V.bQ.7[5f^?7b4hF[\UstEW#is1_F^.:/o(Zl;8\p*A Na_0Mk*,!$79+*QeV.R<%ZAI;/0TEu\A6;3+D?]fkYi.,i`#FpB;Q?%_)qq\Qr"iW (S9O-/P`gd\6)T+Z3$?@o"-JrR%T/QB[9Bu_$bnsC LbslPjrFojFDjZ+hA^Nk##72.B8iXQP7hJS6V5$U5pXa1(e1L_b1)+0]_94;0+/qF 0LLE")7[b(*=EJ[ETVZ>U;L@Y)/7!#YO)VtKJ\2c_YIPNCsI^!BbMJj;[MD/c[l=6 *.qIM$t6fu3YIPN#h&m3LoLilPKFr3=iOK6"Ns/";R^,R,(u!E-,PqT'B*a\L+uNq \VGd@j9.S>$Ba+4Bd"RVe"fVhRFD9@N"+TG#^*!U)3V4/=^H0WG"bK_YLe%,(L=as)!#f6ZdE05^n:3M^f;SKWAFb-=@HZ+$h3D`OUOu),%0K>1GL)ch_ PQWnI,un6_cuVC2*[jUYVastOk:pAl\"ST/0i?"? %58nESZl!#?slG-2,^PBJ5\%7dAC.Ke.`k;;S9h4%gJ;L4'"DpE/_Hj,9Xt+WY/5g @$)lB.["og5hI,Z!KhquD!M0':`6?*3.[9U,8ARrY%FDD1)(aB7Z,N&PaoIN[=6.s .``RorG<"ONio$i'R<&ti3GYkZ.9'F>jCiO"VO)E2qIX^/8Dkj$R-WD#;@*JCl#NM bSpVA;-4EQ!d&K,ijs.T/*2cA9CA=>-8a*9h88km$g9WEjo4J@4/J?57V369pm>sj cr@2@IQ^NQp#aKfRtMgO+])H(V^-em endstream endobj 345 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 346 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 339 0 R /Resources 348 0 R /Contents 347 0 R >> endobj 347 0 obj << /Length 2260 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdfk@??T&cr`dKOkhI[=5X62&Xd)'fa B?eIa.ELmW&04MX>kM$*V[s]ik0o(3kJQ;K9I=g4jrc5mMk[TZDEqScNTEiu'*u/a aQIEL)m/9"JX?ud%(#6mGf-B=)igfE(AD(d$*'ZH2SWZ#L,Zh?n>\DV,G=23MLe^; `OkK*>*k.u5aijRUR8P*KFd;14,g:<5kMMPbG7--D,4MGgPA:lG NU)$O1G-2PiAchD5U?a=.$!B!DJMf,1[^I*dZ*2fRQ$8E9B6WoA#S`\^*RgX`AfZ22F) 3DBl_5du^L1?T(@U`g$J"/Msm%)WA:2Zs`flI%4UYh1mplCcHi%aF5/7#l1aF%/[F "[*Etn!,+AZi_e"2a0sgfib`TghcH"!CWJ0NU@OApnr(!MXe.$11HZs;kW%[$oTC& 347d&LBgJ)79+:9K=Jc\ZSMb%fZLQ)CD>Gf70YHsQ*bl3%?E3#30'b-Ianpo3lOG4 p?_(r%TaR_jYpZVW?@2Jkfu1J&(C=R=XlBM#c7&[(>[Rt"3himmp&p0N_IU/,)sKe ErO52_fm>4QJ-#E2'L_71=gI[U))E\9J"?3$2b`VfC-*GjCPI<"u=aSQ"O:TfS1[h A59bBNNRl'9;0Gt9U6l4K/7NnjTC[GTdYp$VV>44]0-RZ,++r5#b1)j(^D4?E9osN "NQ=XZO4FHEGh))Yf9G^cBmsdaMKZ83h'bN8Z^)s+OXm2o("_R;/cR:fOc&,YZ5W3-aT"m :Z*'A3+f#g'K._GPsMQ'a]SR*1r/1p6Xk@SEU&l9EqLnX`!ss2TB\;'Q2uHh&lu"%g:"aS\\gZd+/!5[t$-Plm\d/qS`o o-0"/1S%K#JsKeK6>325SfnGr\PehJdB+(W;oBR#J8[cR*'MVN' PYn)f8n_Tu"^qZPd^WQk,ZH%0l(o@t.Rl_g`=2uaAIR&'L53Ti151tl(nI+#G:GHg .Ffbs"=f]E&BI/8[6ghoRTO`J'd9H]ABEBDk?)HSA>PLW1fGf<3e@ZI#YLq5"?""D fdrT521=s=`=l`k27e)SeSMod`Q(C1@-EV[4*K>nn#gTGJ? !PoRb$K8gZM/cpS`l^*uD3NGKA5S5r7S,HTUI2FM:$)JW0^`Yto3#oGq>Z3=`b(u- Zjq9'T#Pm@+Oh:7H;tHC/D0))hs3;9aZ\aZQ>3fB$DS)eIS%69_):+A&U^dPKKc=T 0#=8=S1dp4^M#EM"+#o^KesJu^t]pbM@Z_K)[D_oZNC41lRp-094>D?-/lKUK endstream endobj 348 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 349 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 339 0 R /Resources 351 0 R /Contents 350 0 R >> endobj 350 0 obj << /Length 2260 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd=u`@;T_/V9`*bO1_$JQTA2j'h"7 \DnM;k;/O4JXAD:NTNHT!+"*`FlRTaGedpNV85E[f+*F#LtFU[UMTQ3WX=J>2i[CB mL@;]oJ4-V4*&e0mRrj$[OW"dca4htB%j]\bSh83%^WBn.Nq-3U,A#X$kOn@JEk[s N[u#&Z`f7@j"ulTDY6/RF4h%^MP/8X19d/%*Cl(,RQCs',_-F76d"sL"\ErL'a6pS kckWe1V=8mdDJHn`/bU."iX;of\ag`\DJ=LOfXMFb>(I4j#`:jk+FlT,el%Q9e6Z$ :U![@YKNU>g,b4N&E*-$dc8S,bbLr'-Oo+1^B1>ipd%9IeE^dC%s9,@ON8N=-m>B]5).Kq!*C(q($'U0;iU7daa 0:BTo^S2,HG/@pdXrF2`+bL[;iU?gdVi$*4c4mk+TPbX$!U)ceL*=W(Gca9A>U3\1 k.HgFcedU!L2(cW\dp7@n/R3&k56P4ic'stC\>[g\Y\#)S\-Xb^hpf>+c&sb$nbMD -pn&ddQB=6"`H*Rm$8p\1F#OO+MeL+9%10mRo`\!UG1kBGh#XM3ju%"o`msQB!gJU 9(P1SAPi8n(:rqX@Nsn/:UKj`Xg;!Nj+;N0"'EFg`#`iJJ!5^L%N+\MZ/*a#rSQUfYQ36 &pLRn!@:`HCs!l54K!:lS<]8>'&'9Xi.*njF"IGj\M5FDSV)Yp`6"E7iFRbG?^bKX #n$72@;"\VT>r)0JO@`n$P]86GW'u1HQRK%EAo=[IVAnHk"R7Sn3.*m%)RSd=c@o3 PG3:tNWH#/5bK;d!*U4l$dV@!B_TF\?H$$XR=Zp@K!Y0hcj5Ol#42,<1_U-'_5S;, %R%881/-KWK%pY&Q)XpoEn2;aF*C"eY`Q<5fgpsOQf-60bVT+MRV^&_XkgY![een) <'`*(A]+Y:FUJtc%rtF0QoS!cjkS)CF,kP%3[Z'7-:=[_+N`.pa'@sOOhc-kakJn- ffrd'Iu8t]76EA4BO6V<2+dW+]^nOJP]A^ITR\9m=9sOe9S\''&L3mRg3_^&I3nNO PFGZ31g]I_36*2-SAdoj>]DGekV6&:pu)"E4H5USBJpp)mMs'jH"P>ZC4^tF;kL,b ?L#2)(bgUU6D.8XV1i"sdXY/HF=2W*\2oApIW:X!Js7atpK!LcAdD!tWo3;BKi$`! #XN=.0B#,qct!lC#YYT/ij"dnWEG=*BiRi?hjK=!*7(!dOJ[1VTNYh#VBPq_`4Z5D ZG0Y)$F#3k%6mojc@K,k/ZNS`LJ]9C$5U5+/+Cf%XqOkH(Gj]L0_nm?"ld4i*(;L- *Ib3c8J;pd:LO[[Ii/Va'kD+&5#nI3Sa(J:da=VE8YS!U79(7_TftWe^&M$O`>oYd cIqMJQc)i?Q)9Rb>@B"KbrTC;d";;CS.cpS 2eMOf>LT%3"=:o endstream endobj 351 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 352 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 339 0 R /Resources 354 0 R /Contents 353 0 R >> endobj 353 0 obj << /Length 869 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdToW&(mIptRoiUCbT1]^C$cj1u3ZgbXtC6SO2%cj4g-MLeZ3.ZOt=8f9(O":#"D mlV:%$Gm(9._sD_`c-]Q"oVf0n"Od-E+d\67YCsHaC6,N,2cAg]O:fonZ21#2DgDG SFE=D1XuL]9Z_OBe\2-QkW(&D37p?H"-Rik%AP?-&[S`7K!@sG8uX-a.@\4[%'(&i p!%B4Pj/A_oAVci,/T1CV_m#r<%XVP(SF@.)f# _mDWT;)DCKA.3:qd`o+nRWP,KdYqTP"Tfg$.KuR]V(=FTUQ3Pt,BHKiN5Gqd2@c!p Fp endstream endobj 354 0 obj << /ProcSet [ /PDF /Text ] /Font << /F5 5 0 R /F6 6 0 R >> >> endobj 355 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 358 0 R /Resources 357 0 R /Contents 356 0 R >> endobj 356 0 obj << /Length 10810 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd=u`@;T_/V9`*bU3K1N4&N/2C`(L3i4(oXnC'fd-t V/ZOI73mau@,k\>5\N%pdipc5CbVRmJO'RZ3(,550rIDD@c=,c&Pe3X7l9GjQ9u4H $Z0pXUaf0OP`0^Ifm,KSBf;MVU&pIAicR*;9r:j/d,lH,)pV9ZPgJi43Z8,(U4F#= +kuufJX3,ptRZY"+\6EX5fSp%(br$5W6.?9:!N,mYK$M\urR"1B[df\='r29H_!B !GGsBY(8sWTS*EGjtal[d]!]b,B1^8$u.%+W]KSnbRb2?$uIE+DetI^]C-.U"bZpg _]t.uU;$=1iuF.@L!.X4#-Y5a.N8SJW+Hm?_.tYAL!1Z)c7t8u&s?#6P%r17.t%W@ %)GaY)MW>9"["XmEZEA&-u(HOWBncEe>4Tl'1YucOCH^0oXu+`dr^9hca5)K@c"i! 86d$eNU-+",tL,l5R.b^Cok%+")Q#RWB*50-4orD(-9?cA`G5Z:c.R;J WHbMF%_&"1(b[&$D1Ec]>3CAoa`s^0_>sb$%udiXp]f$n>kL94e,9?d0iT`mPh>?D C`G=Rm1ROViHB+j%@KTWc"=N[E.mF8g2!;ARS3INl+.StVBOZ:VjhAY#(o\5RI5NW k?8pPXCrNdgciLXo3os,_!)jE&f5XoQSHq\4Wc!!,:LT4 W:PG'Tt#9ZG?;FT:cdXeDWpP:[Wi`c-XJBaC(np2Pb;aO[GATej9AXD/(8_AQ@n/e qnBO2H/1t8h4q?R!6b4%-`?QjhbM;Ar68m/jneubBA)\p\&YHI?8")/*^f[IO-N>P -f".D$h8WUdJ(Ab%0I:Pq1(-d#s@tcrEu,oFK0IN]QF2`gDQGUAe_Y7F],--,U"<" N7Jb56uX;)Z2pu,)_0K>UL>X51TF"$2c_L)OZR/fK\?72UF*+o9L^^JX\grhQKfEa >pd]L-S0Lu;kEljF,_e&`"uVo+p6SAm\Uo8o6V=1b[`\168V-@CD.\'VfpGd.RgK91qA$s2FS@H"JIb`I:qPG<7r-%V@0i,#&# &qi!@c)8RhHOs@[D`*ZK+[l.`&tu6ZFVE/k(eOH9B5IDL1\,^X\aLhD97*5LotM=X mm?&mMOr;Ec+$[W5cC>7EUk`@:^Qe]W9]bDDXokLg*#m[*@@H/Ai.r8k/DBQ2IJ2CB[)gdJSm.pt3QPF%oAg.&F@=]LA_s?Ai'eNOE0/=DE"HXZI'I!ZDIp#(jYN`K !+V[rn'+lHH'__3<)[Le0d&b@c]W?EXZga&[/.tD9/2uVop"/Kk<3d,*]$nf]Rtek >@M`)_Xr'a@Aes:C^%[R<$[@[)B3)S%5=O/;Cu@j/Vi.>)oK2#$oVU*LU"!-`btdr 7k6l_Cm?`qBE$09r-;kYrYqa1-Q#t`EG4lPRP3k('0V,-M^gO*aECW9gMCSBeHRhO =^)O#qPf`79j4ZE[V 7?t(1,&uS4)5=B%R<:KA5h#(JR%iG'Z^Or>iS>&JBn_o$q*S$?]P"%2`g=Ts)G7d# =.l[U?IP@`39g@OSF)W=c^K.Nl[77n2+D!">0c)q#quso1:_]$FGQ%c6$$Ta*Nr0U <04([8M7BS=uU2,KHn8P_LQ2-5tiu([R5:j`T8NbSmbrnNkErB3(.2r3_\5!/!q(D @'`pTDIem>6]b+R+lG,1afdU(o[(l+@UCl1%Y4US.#G;f#:%a? Do2?V\"s*DmIC_fB.2d&0I)6&m'Arj>6Q"IP_'B!(.&jI#[tdgTHIQ=WH ituq=,+IP2[s@?2=!r3o2/#)> 8CsET0DKpaWk8P3nK'X.jX5Q6@eN_P^4P.icR5ofL:-Od6Ke07cea'+;t4/8:?q9g XZ\hTf"NdsC%#+-I'tp+hZ#J/(A$lK%YO,]ZDcB:BTM$D&Gbfc\:$9ig)LF,e?_1i*9j9 Y>*O27G-jahS"X2Rd#Rc,#k!X#2`q/@1HTI%N)srCgs7o.o%F$DIX;_MN_$Z:B=iLT%cR)U+<8WpH\t:MTY9k`;\H&BQ@# d5\549)t)df5X"TnLB\4Nsj(g$aA4KN&h.f18*q12%"/;-/=<6#fNBJ.:Ne),?iOC "V?WH([oNa8m`M,PfaSsWbQ3FOe!)9+O*^c+q[r&OeSq>>8mp,f*cF7WnP^;i>3*A 1Jqj)ckP-UMN^\q!U2`"cm8aIM\m^gS`=IrLjEFf=BX!1Q7tGX+J;,1ki:Ub%RPUN ,HBNqO?oUo6:lJ-^h!Z9fUE9B@u^%a$'=!dVVZ1Tj=AScV>g`ooU:kVR`jPc:iO1K ]a'-f#KAVJ"\U"J?3UC3+YGJ/5htC[4?@?j,d;kEXtU2X>6W4!+\Ur?mP"oaV!=+\ ^+s7*?pNlbFQ\SaAEIRk&=f:$[Ur@5`3G9,/0QV4+Khu$-Hfg7U273<)]7#qY]BOl EpC%?[5o*DIf(8\unSp_`O$6MJ5,bdgD35p% **7)&+9dB!Bh':T0Jb5T=`P/XA.lL#W?k9]%Kmp0%E.1"#Rukd#"g`u9CjE`)[0r> -"F>/Xk,uq?Rs=^^9VLfifLkeEgr!biTEK3VD@.o9UO7ej"<4uDE?hKRmggY.PdnJ c.aWX@_QrR/1tu+DI4m@[XL2#a,-!M2VtFg< Pt_15JQ4@C; I_J@D=mSRV23u)O3F"@+9m*9k9-uF\`j,3e4fGheFh(`[Ip*H*IeC@]Q%*")R6Sdr 1@3n*2;7h*O]rM%;t]0Qe]C)Ho`:&;6';/aZ3*E'qn?' ;(c?8=/ske-$k#>ol`%0EhbEs-<#&.0QpN>r)\#((LiPm3HgVL4kGO/f5Fq1l8(<^ Eq`0jf2Gb[TjNWcFjSNW[uXXXZIa9I@/!#qC.U]#P*9n`fsW%inc`K1B#=0j+UM9* 1)enh2Hmo&A+i5![RXl$B13;!7Wc%LX4E[s@q9asAM0At1i!;]FXiVW#<%)]$j?s' M+ggtB_9JCC#=&1M$pdu3acYLot,kHm=Q6AK+I-_3'/hh3q'qVKkstI\4%;%@m6`9 KXE%Z1FG76%f=f6K.eO)f7s8>22;o>Lm:>`e_,^jHWM&3G]d(.Y1$E2T_1.nLt<`* (n$&bD2D?)LY&c4ZhYWO_h>FWMUdiJf1V$gF+ceheeHr"Pp^^KeN&lMNcP"Y3:/C' HDC-\MPgAj(jU=miDUp@L8+iI[5Etc4'ai;<-]F=\G9*L#@=oKO7i%%Gn0jBnh"Wc F1EB>R6QY@,C"4WrN- ^s3F_j^qCQ>c!@.P$U[;>e>)_hNhp]$fu"<'aE5f7W`h4V6*85,:Cup#W.DE6nf$W f"7)#%R_EJ+RBN0aao+t.Kusai94]SbFJCWST]VM``7V)c%;qF$l[9(SIsmD;#:W(;dQ7-[<$.UNXtWe)sT^6&.I-U,kl@ e&?O"09DVq5hLR_F4$e+9;Y]VVNoi3&LVU&=)e"5EJ%Pp[hYd,+/[S.W1%k&'CBnQ G?;@qF1RiK)MM<4UT%Ek=IO5Oq>pj:R=gs7D5^JnF^CZERSX_A5FTVl(NN<'R;/0, X&jM,]"=f-gPA0:)5sHq)X7Pga\\mHXRtVOi\LG^8$4j$V7PK(>(4"0Gc2Wkt+)fhC&m`uh`1I+_YN,/CBsUR"_4QBE+Q1*:*2Oad!n$D?0:'M:9/ O_:NLR<2XZPd=udV4)FA7P:#VfrmoQ^;,uNT6?3dWRt!Mom@J(XS8/t>Ffo*^/u$L XS6_9gR#MKPY`0cU8)500mc`$_1e@G]9ebVgTJG5p^ZU*__uIM0Fg=+!P3E.dBL-&&oBa`-Yfo)2F(*kF=4g^3O^+tCPHVu"TF:>-hQYXGA!jcG5fgoo/,pf[]ZsWp8 iIMlC-C>U"gOXq$*@Hjn-giJA+IWe5fI0]]66p4L)i!YTMX+@rT@D(\>`!bgU!u0# `RX/$]T9[19dd#&eN9h]MFbcmO46qm\(t]0ignZildaS:5.u6Tr/&$Y;:euMY.\6@ ?&8&WPK7mUH!:*t4e:6]S&q40E1K;EmmZ[[-q\'S&&bH43r s(":r. i7Qd#ZK^K-mYP9CN[Rp`in9BhGqRtXn-9WbZmj$rj6sUtf&#dj]@$0IGOWjU]mhKE ^G!;#-.ns.nm.CLI?jFDlgm46oUGgOg]@$e"nTJBgF%jJS.K0p6,A%B_7rj`s)5b= eZQ$g["`ARJgGNqn/?p^.J77&b]SNb+GXtHnYGg_>dcJqgpX/ 63&,d"%K%?TX]##&4)&fiL%aj%5S?BV%@>HW!sQ*L*Jta`'@9X"(:V3nA%=<)N0=P !N:NZ16mkE__Hu&>W7?O%A;B?:uskP)IoN'GV$'EP1 %UoLtU)l-D$:Q7.<QthWL'iqh -5g_l6UaC.:-+k<7T5D!hI[<>.Y^lmn/>8`@:c\N2(5P72B?\G!WIV-)*!7\0`eN, +Z:;G"f"AjXCDf%)*t*=!P3tQg;:is3FmG_[22Kpr8EUCcWQS&*t58+V?Og'L4B(,8S2E1bBQ^nUqDe`mG,< %L1n9"Qi.0li/&]ndFB-+N_]<_oV=(P(SM[@R0TKkSae\L.SHo#"K:'.k=#-oRS$s .&=)#5`n*RJu*>VRki)AE,@R&o!iZ2Bn;\N+fKKE0r2)6I6a]'Qer6A.2kpfi"]^h.c26%lZg%'!$:J[2a\P5nA 6l;cSfZ5"Mt5N?a8N"HfLRU/$r+$!LN!VC@rs8@d<_GbV,c6AV84l2$R>WN(Rn .Pme1ni@2R(u@<5b2U(<-;,:C^F`)fgqg-DRp*ld$e@;oA'>;:T.nJUPf9o/HDJY< N3b4_1Q&4sZSX6Q%+?p_930WD9n%E@JZYE-gUA>aG2B^o>D1uqc:/%5dP),4n*<8^ @BRP[r>t4On-EZ&le=j"ZQ\'u`H7i7PO#[CO4kb\=E."L]Z*%q`O3odH53i(S#6Wl 6CY'$MHH/3[q=4h>]CR"q]@7gE><;,c#MKV"4(nn4Gb2!Z5gn0FArC>D=/2kUFGg5 Hj+t<&OaCe9AM7,;qtV_ffWJ>YBabTrZ<:tpg),F5BC-)STAgp7^=#3$)O,qqTut! )$]>-A[#(hm%a(=&s@>,dcnUX\,0EC2dZI=5:cDBn+9O]HdEQ$qL!cZ2-1m_]RfJ_,%"2cK&4!'e]#_u4,G]^K Y7_hff;U5RbN,'VU^!11gAiVf#beQP>0GPYEFD;g&-SZ!&OJHR%RI`^Z%E)dR+jc6 V?"FL6:,-SO`ZCF9JctCj;f+(Iq.iLdOF3O@Z4]-0ucHq`V*:GbsA?eKfu1L!+nSt b#E2YKn>P`5Rr>F64>/R2?o8u>sj9:$tadV(3YkhcRV^5D*6Yoj+,Yi.E=G#Ab1OP 'VaIFRitNQI*)Z8.*+A%PVQuPb*/.kV*es]MOh;l@Ljh-_fp^#.REG(-dYX^tBggQS'P\$) $9Lc8i\IC<.^fABK4s"jP0M&3A+otGp;%b)W&ssN(ge9;^4MJ+gUrs*L ,D]$\]u:?S(h;#IXg+:^)$$NN6YLFO@6i!>!7Dnj-u=m.p;<>''%#ZqY)\Xs)+T9c B$tL,.ck3CQ-e_6bh-@,`d$,T,r[(Ye<=W;":uP50%Jq9Xe\?K>H&!^VOHnkUPW\, K8Y^W6)Dc8XlANOqkHo('XF+nioD2VSO@?_QuA=??arb,F!_JZ]U(q%>q=G\ma6a" mnM0b[:;mKK"l@cMJ?I%nE2Ic"M%T4!+%+ol#3>mFZ5.6:@#!k;H=4A2jnS[41:%hEbeXIBZ1^Z/fip+A 6&R8#bm$,69bc!u4EX_+P>K0O'5C.g(U^]cfZ:99,AjEiOtB!m2*6!3AA_NidFl-A NMUZ8ilB)MX\h!DB=@%Oh0h:_Q8]^3iV'm,]<1+7\*"(/mfr16:$Q/jUSdilg_^-Hc)glWGZOaV;qlon"N6FISP"&CT86mcf>?#A '][K&F`//h^A'LtU"cb(cqXh6QEMmYI(thR^%')XruQI`%Z#-\*K9^<4h3%RbMtot d$tNHkhC'/K8a`dA$/;!)q1_S*h>H$mm&4(@W5CfkaqO44kT$b& G"flB3lDm&a+_=Ark6U'>lsBkJN?q4s=T^ftK9_\,emcL1p-<%) "eCY_'G,%j7qf<\VIe\\?WscpCP/ON[S7t"1sM/"g5MeL@m!\^Tp)oRJQ@F&]?,Qm&5;lG*:)!BicdbgQFm/h.D8Z?;p%uJXW#Xjn \tcFCq^0(J"$Z>O5R[e""9(Q%!k=,.5SK$3bOn(]NY^CPVc4)?@gSL_#;F@!\L\+" fjq_=!^Bk_5pNlf,RlbU&NQWu1P?Gko=@2>S\J(Y,k68r_VHLLO-*%VNqf**$?(r0 +t(6j$#;ORX;!tjl%oE%&=AkP!@&!A_MNTq&!LZ;.%?(2T+nXc1Qmbc'-!Dl0f+2% J[h4;9J44b[?USET6rL_qVf=S2o@>9_iu7\6o`)32Md#MRq+[s6L;Rj0u%6X6`YFL 40]>r5o^R97"b-MS3o[f*jVR_LkGfIdtFe@\(GI_4494<":fq$)du'4),*k1Gm-'(r],Q5s/2bJfU0$.RNc1`N1X# *-WH_AO0:/+G>.a<,'CcCGB(D43VO^oR&rPIRbYja39&iKRjgGLD0;s78 RN%VZ/V/^1JH.3[:c8C_7"J_W!]Uuh81kEL-\JYH"0aV7-p*j4c%A9m]TZ9^co(n+ ao`JA"/t10!K!n&iIdLXK1NY+!$Ho%1SWeaN,U\uE9;^\b`+99+WA#Qm[H0,_@EfJH-lg i/`G>*1D,Zb@pPM+>e^5aUM4dK7(S4<5jK/&-E1s"!m+5,h1#JM3tS8"0%.[Qklf= 6Hm939\eD1:asTO&5=jUN:b8DS@!m814t* endstream endobj 357 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F14 142 0 R >> >> endobj 358 0 obj << /Type /Pages /Kids [ 355 0 R 359 0 R 362 0 R 365 0 R 368 0 R 371 0 R ] /Count 6 /Parent 125 0 R >> endobj 359 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 358 0 R /Resources 361 0 R /Contents 360 0 R >> endobj 360 0 obj << /Length 3919 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdpQ#_:>`Zn34he$U>qI_;>RF1;FWlj=eHd3CCZ6%$$aJh%Ee1m3NPRV UDjUdJDhu=&_)h!%j;!r3CY6DkW5`mJ3^98Yuo]r23Ftl&.G>P@Q"[J.,>fd,D;5Q $q!^O3Y*a^W"gW=1fb.rK.-)YN1k!c:8_!L'cOKtE7-]UULJhKNPPVO1Ih"?W&fpu X8k:0MYu8^%Wh]L1c?i[K-$!s&F);71po%5#%>(Wr3aFJI7@6o/It7sEgY`\";K?g <6dAt^+tY"8q],3pBnJdZX\-1/-HU61qBIKLi0+Xm5.5]^Yo9M2;#9Ack$U?-pSu: UE#ImbgC66(k`")e2.:?R_MQo"+DhF@ks_"1>\A)\@N/+`GD5+1R(j"=d"rLU0XN" nWpqpoTsA&oLS,k5lre^"bnXc@MufP_rMXe8MXW$"<%[_^Hmk+AB6"\NgC0Np`"u3 f%C1iS]T>-U8+nq*iP%!W-5HNl$@)>'K$gjOT_kd?E/nMiC:LWY:E4#N$6k4Pmco/ 7_s6'\CLh*"lh\"7:?jIbqhsq%b86FbWoaKE9qc/QaL"_nqJm'T!?Y=>2Zh'EVLms Wc>Hi5Wk"8kjB7G6X]4iEn2%DE=KOM+^L%"Ltr>8@FSMNZ$D=MuT J9kLPYZq>BBa'g,Adr,l>m17>(PS%CkQl_L#"=WN&Q1<)6*_4OjV@@iAu=_sKIK4K J/B=CO9c1jNJ'Qi/cqCmK8=J&"pES;6ND.O9M$42+M8SYPT'skBJV`h0W.8Sp4\HH Xp#,F)Zl+*%Y:!-"Ug0c]U+pM>hKO4Y_DPRVu$1]S/$eJWZ6YCC^p_m*sL#E`BlNc eKR!cM'1p0`7Xl!gOk<090oGP5bK`.?")@%QKHP*d3kbCNGKWq!7 ^bVKe!UP2fcmf5W6c'6?,CP0*=gUJH,mt$aKK8-H3^b)f#ogN(HZA,&7$@!P+b@gk E7DOQ6P'\I>S;sXQ-KHhB/1b.09pOYLrS*)*'ps=L2u4c8]2JRV2[@L>N9Kk4l&)%2^AV6sd02VPIW3p,c+mT7X()g(K_ FL[N`j&XimGHIEbWV8TTbk%T7/tlFI[:1?69,:V1WTq57/uI;P?%#mBCHL*W#"r\6 g(MoT-$1Dl6eEY)efhqKe.J\rZmBUHI6)3UG#r.r/J'&rS[&sD]$$t-C[(h:4*'HM"B50HC[!ee^anchh1Gb;6%JcF3Wg6SN(2@C_8)"k4Dh!'s:e]Wi.+8t:#KN_%0, !+Z(=pImInHo+$W@]YKUaGqm206X?aP*_B/^%8j5m#:0b``huZ?Gq/`WYjH?dU8?Q g\A1'f&CPBpGH?Ef[atYZ6Kfb_WFUJk#dl)ei[,1ogX6LH?1_96.ibZ@%tIB 2b+"r;]o#Yko0h-;H9+*DcTE6T.@:b5g)j`LUuQX3/Qb';6'_*C:P1X ,,iRbg4":f!K/0Xnmi?Qqc!GU;7d@ea=q%Embng]W`-B:gX8Yr*F7X Z`S?jFk#W(+P9,4XC?Xff6>0"f4\n7cF;NCFZb_85f/e/nl5Xtl$:A)LGYKN>k?m& DSB*e)s&SIJhtBs/6N(%?n0X"4BW6N7&pH9/dJthX?)luj;RicYInf6IXkN%((0>r5TYUpO-@m+`ZLg7'n ,#m*o;=[3[&1W+0_a)5)(a+8-[BZOHC:[K(,rfcGcMH!5cTT!I1>np*:U_BB/O7sK P$DDOdq(KJi5Q8&G#;g=28IL\L(I'J*`_3U%VTTZ6@)[e!`Ph@/&\4nAf;*^SGt0g 7Uu9'UgR[B?=d[,TB-@2JDO5#V!4a1+iPW8Kn:I5csS-M%HTcPU]<>_SH&n8e7E=; .:U\bUjpKCaq"^GY,$qNe4E&qCnPt7"4G)boORF]>#4ku:Xi=iMYpjY"^kX?U(BI9 C0"/kdK\8I:H]"tcnu)N?#7NLC?Sk%$8^C"+1=Bf$o\\L.P;R8fks,le4$dg,@bEPPsT91CK?\iKqBH71%0`Rl^2#n=oHL!^n/:>g#/Rh9A'Krq`U !`&4G(?SH0#a"ogY^U/eg.A&K84AmU%W"A2pdl'DA0rKbJCmnX*"HB)%n9+W5[(ng -N=VO9qKMY8ee5cZ^D$;Oe'X#IJMX0-!O7<%/ 5ZeM*";(SB-)=<`4?s&a70V+M%Z^h5eEM]?=!VQ%@+u^--;;ci=`Po"YR1l%7\*5a %Ut#"XeoLeLD_bu!CE_d,QL79<";`F#qgg%!e_qS?=kpN7]iL(AgAa@Ak=_IAl'cu !*^si[gU+Er[/au+OWd"5m.>?7:^/e-&DLG$:gc37q>A'-<^o@EXd\u"i@iB-*0m2 GY`8+?_DuV;ZoI)I4Bd8$m>,o,nO*B`!o'j7e5,HcAi=O?r<+t/OWYhKP'H2*+Mku /hUN endstream endobj 361 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F8 40 0 R >> >> endobj 362 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 358 0 R /Resources 364 0 R /Contents 363 0 R >> endobj 363 0 obj << /Length 2640 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd=u`@;T_/V9`pICCO^Mf>&DkP^\P5OJ;/Q.ZE\`8> iWF:[8JJs+`l"07ctkY<6u`4Z@72%9Je<`VLf?'<:5B=t_b")WSJ7NQB%mUPQ9u4H $Z0pXUaf0OP`0^)fm-EA?^T'U">WO_QpDl$.V;UfOQUlI6q14n[Z@^Ln4cB?JZMpg ^m/0cidu`ShK1]G9W(7Br/tqY),<[16.1@YW2_pGV^72KLq<% Y1'Mu*-0lPD=jq<6NCpZLJ,I!.1lb-aDRn9Jg`o?Zm^#'#,]T_T/EGgJ9*XQ!ant] KgQDN]HFBK8d;m];n29QE*,@DY[MKehRrGk8IDsb,$YfhURXVhk,BQ1Z *MHJ=X__3BS+J*<,K$Ih"9L"@q@#;GZ8.G`j$kg6osK'2=5g1[*$NE%`C3XgFWj=X Q1>[p[!4HkPtI`:`.X+/7;t95n.H+Z@H!s^6fN>W9UF/ZCIs;9.8$(m6;jmB=cL=, o!)PeYhGf\iGAS%[6^a)hT!l!`#6sGE5j^E:PukA-mK1F!F,A:_hm07E/+/&Qb_ba >Es9j&.CP[B`/&9B^.K(6"n5NKGhkbTT3H"5U?a=.#niIHK,Pslfoh<3*SB!VPHf[ `8^J"K*nV=CL_C41QJ5orhIg9eN/&r;DEFORd\W0%uLSd6gqKS]@ *\WS7%Gjq!_IhW=T*oj\0.]$l@l*a%/#+#:bQJtGOmnC&?>\jt!C25U"+Y%n-S>E( ik-32!EW2]amM\H'#[`Z]2hu?;*=b%O>=!tjWtf7O)agTT@F62NLWocn0jcXG \ASU0``L'i^-=3gI_g5U`uu-5KWlb_63]JK)#Gg?`7Q%3,C7:Z5roKO\bQ:4,LTsunpJEDdEZmgdq"sGFIQ?Pm#&jf;Iu\=FTaPZ!BiRO^g_*=RQOL`oT+J&p9$8kju0;)2$PJuKMO=E12A^r8IB5Z$L3 *7[l%Zl:@EOM`?Vi?\d?,KhECS!fe??P/OH",P=FbG3EhUqR)SLL2dilrZIQQ-m$: 5hEn6??B^<\%e$k`#]j1<5L$(m@!na;U>--D7R]ZW`e,JgNQjA;(WD5&JC\,uj5)m'WRE+@D8X QS,p3#B7j/_#R@=k=0P5Z^PsP&Rp9!%R:4rK,0aK#ZsVGn:MiqC63R4A?.;N,M=7]]UgVdtjFGV(LM;:5dOdG.oRXV1cYflE9jD*&!/eVGAP3Q,''KV"eK J22lkl5En(Tko:C2Erst![>PXK?_NGL(_5G?!)!PZRW+Xkm!e)$A/MXf`odteKiXU `43rt(L2g(`@bAgSLDSu?Bot/U,C&^C,4.>.H;aT'*XuZ.?f@E=7*+K'N9!N72;[09< >S:?WMo-aT7FKN:+C[f+XerPO?PJk/abs*+0.gS^0]`:UPc#EBapZ:lgV`"`2)')Rb]G=+C b,Yg=%/goQCfl932B_KS2@!(4KA+g+!=d8fhQ>IXtqS YtJ]Khes,'E%j24gdiH^212[MPT7VHl_-ZbZb:qoHo;2uhAX4GZ@_sC3efV#[[a\3 Ya4`qeiUcM2V^EEKYA8QOMY!U?uQSC!&VaKl_f@kA_MecR1hjRI8@q)@u\a[1qh^$ W?/q)ToqIQW%0Ua[["GZ$l!RA@MjsoI\G%J"OOBuje+kXFSBKP*0P$@!Ym(*A-;~> endstream endobj 364 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F8 40 0 R >> >> endobj 365 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 358 0 R /Resources 367 0 R /Contents 366 0 R >> endobj 366 0 obj << /Length 3621 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd9]j17X<#iuNN9AMBY"\g)*Ok]7;8"9^JO'UV00 =mo>SAcf+ REb(V>Gs)7H4:^N"iHJ(<^1[QHO/BLoV:],52^4HPqGW`?55"ee(jr.6-*Z95TrD0 rBH*Br"oWqHD:Lq%V-'S$mT`4&4&tU/@C5+Ub`:8:o$d>1/9qU'a+UH6kBcd(u@:^ Yo(DAZ`u%;`We`Q+M"?;b4:/$Z%AA>_bg8`-&BoQ//YXlbbGJ5c[94&8J'qiPD'Q8 FL8#UbV0S3ZGS1%\PVQ*k.*dH2X/.7(l9D#X'2h`.LCK,.S6U-AL'sM'1=jY6Q1V> %&75$Yn#ebkVPD>X2c6\_#UtZ#%;;Mer@S9L8q949?d#aNk!<[1ef1.d1-jp>oq5U =E->qTlf/m:,nS\tRbcQLOI+Gi1n=kBp3!&q7Hm9'3&5Xkat[E5E>n0XM`(BE,BEFfp7,W 4rM>eX/U)>H])L)#Z6RF)DH.r/qXq-OA!okTe5PFc)caQ!PF5DCO(qQE9nDg+UlZE _XFhf(C7XQ,!*.@`pao/]p]-KIg`M*!Lr9hB:@\*RK_/"@`CG9E*J#@GpAY9hEch^ Hb+i&@.U^)CU%!KY;ATbNJ!_CmXtZjnQLmHR-,O!,BLlONNI`f1IRZQ6EP%)FLEM^ %A180TpL'S/^H/M5h<=p>/0=R09B`Rf`fIePefNDE=8>KdnJcKPgb%$'I.EMZFP@A>/9)`3' [?.58@0]U;'L9I,2^>gbS!5sb6\p%%#?R4PM:c6l`I1BGFDCE0#\OX@Yo>lJ:hl-) jX:/@)M^RCG@=,D5Y7g&-KPea_eLYaYH$#DKF.0@88NHI6Lc> $IZ"-,>Z$2X;EDugRMqAETe79#\+k1KI/bBM+9!BoFO:gClE&\;*/1LLgB4V1L[X% 4YT/"iZYO^BT/qr1jBj),/o7)l(T:F)!g!_Eb)HQg;\2X*h2d+/uC1t2T"%sUcS*j $e7>BKHpp-'VBjfS)o/;'^=Q^TWrcNlj_7Gil_S)MVofOfd8Hf ,[=i:cj`^P:0HI@=o/Z$:2mE>1*JafKaItm$@L4\k2OaG+-9)/ a\/:+hF^35S;SL-&EE%Zj5Vt;E+Q;j!*k0?74Dm-[['P,o_[j^.#l^+/&qk`b$3@5 E`DV%(8][8fQAH7cD#ed#&.!T>huEf5qU.`hHfE+3$G\C&-G;ZgS"4GW%J[;;5+VV CU"aM<2$JCV]SrCX`^\h^:_r"(/*H-6_0LlXIhPs;Oo#BKuk3D&spWKUdR!Kk>`J! ]%$Li4YW0m&jOGTY('\UL:(u^PJUu3V98RC%0=O/L=!et*SK-skB-0a(]("i,-,o2 c2@f=OJ`1latn5A1BQ72$YKi<9p'bK*;AZ=eV1kbY8p'3@Yb:5nPfq^AcKmZR]r`Yq._TU.`?lY+_c-0-M7q:4]0iY,#HiR.=TpW$,Q? ()JR]3WYE4VG4=OZRJF'lXqu]3NdNNkRj:EO7M[GTN1f>-Y7FgEEeHD%'m.fEh/dW %?PX5R`W,#(tl2`!-G(!2OME\r#K@T6bo??`Fo/g6YLEpEa1Ol@-Dl1?tG9DKhR^M V!b*q]Ta^o8ubP.WWCT3%^tWnRCdKq:!jN$.O!dU_b_>1nfnRonkNEYM"_flE]ddO 1kuO"WImRQflm&[.U_$Lp+`*DTR]*=BCU%Bf$=Lt""9Y3W!\B/RLW58s1L77EQ\&n %J#t[4L!'C_$8b?YnT!KPoslp_o3*dnBMbCj_p"kk;HB@L[*No[R6YSh@IlbDkq7g K*Z<'1.imWT]BfSLCngPT7s6WK(<9q6\o!I,9%jcN^Jq<%0=-2.1UA(/K*Q]4+E5O (MAnE(/TKCn)1$L`3$Y_!/>rcL' I*66OVC3iB@EX*`?0/)qfQ8ZNo\5$IN:%,3dLMmHrFsoDr5/i;_0^r9WL/LkUI$H5 <%>j4]#+$j9=T,`m!boN%H,M@^sW``R.!F27`7djU;k6*E7BE[!l.&-i5BnKjQFe7 e)a2M:(Z/34Gq%`%HMQi,4aRL?N(EQM$M$f:d$E4\AW,?%Nr5Re4-6P"f-1'KQ>S: IfP0LLu_&1K4&t7`&01f7rW>UOMooCa:,68:9VPDa-]lMi!tS`2IgM#TKql5T*=G= ]`P3m0"_[ehM9$g%&/.O%VV'QB]oUN%1o`EY[ldLlOn'=/)n3'AP50@$?4S- +Q2Eo)!>Bh%KKWBURqB:fJ`t7e?6A^Yj2q,egocYGZ#IA:1Fk+8:Tj'4Dq`S-^a.M LcWr5-g0%j;F#Cu^*Vi]-"Fq*,'6;Z9"]0#H#D>\$_g.,8@acW+CYB~> endstream endobj 367 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F8 40 0 R >> >> endobj 368 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 358 0 R /Resources 370 0 R /Contents 369 0 R >> endobj 369 0 obj << /Length 2629 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd=u`@;T_/V9`*tO/_$Iq<6n0'uLlAHgCZYV.LRO5m ip/-F3\@b^L6/Au6;fP[bA'OG@B`!a2k(9>+TWS"D*"3kf9e-N=au6D@1`o1<,=M' j+4aT<,X:L);B4!JdJsX+JMr^-IW9m^S;?\q6`RLJB2:`:[@1BRF\HUj-`W]1OXB/ AN:aK"q^mjdDn.cbs$1kK=YMPR809)0S&@D%'.Mn(5t&(d1/H`51%4m)MIiI$P:ZDNpag3Ys78!o'!WEE9=fj JPoh[^95)S%R!1Hi@O_f>#q<8Z oq4=dOA$\d`H^t-#M,gnKNDYbE0IDI'M<&IP`+!s@>NsN_m4q.8bkp@3:j5hip4LU ?;G2VdOg0Ed]>ZgnmNpL\je3UQW?F K2%,;CR7/"ICORY0Q18'9cu_XWps]MRq.ZZP7mbi]M[4N#c%lZJ,~> endstream endobj 370 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F8 40 0 R >> >> endobj 371 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 358 0 R /Resources 373 0 R /Contents 372 0 R >> endobj 372 0 obj << /Length 2784 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdYBL*HIcU64U .anssnr1o4ZL+S_ZbA.(@gbWR%Dt;;1.TapNomn:GfhficKto91bBY=TuKp/WDNsg J;PS[cnYsG0>M?5_$/%-%)+G>!,D g/k:AE5&#h'7,H=/c$q8U?P9.aLP`3PA3+@DN 0r/Y28J.WZnDI0R_X4`?;M=L9P>g?(]#,eM;Vr/4;A;a`T]%F))%c/,X71M"0u;!F J2RN41:f3`Z';3g.ZmXs/1CUe]H'o]QaaU^6CBnM&D2(CTqWtZY="W7tJ6VJBB/2KAR kini",U-q_DcREcIE2^d4_d>>Pa?bZ08CnAF,RN/?q+ $.#Zp+Ts#_Rls8)@0?f*m_aMD]fuU7>SK*U1*#+U8ml8/,O;0>dYrVDlEAS(!la60 2@=7?L+'7#Hj0tA9UEPX09bMM0M;fDG?0^k'RF_BX05!ajj8_dTl=fX#]o#0"c:&O 6V`aE!]k(/(`kMoph5HIiN&.:E-tbE=%Y*b.m,+/L!K$mG0iY\ Og:\s_*TB-Y6i`<[u==^L`gRYV843,a;q#'2*9(N0*_,flI<+-2/u>[0\L"S&K#'_ +`o1:U^huaS3UWPhGV!f?O.gnW^Q#J6DRRAfhlcDA&QCY )Bb,VE>PA"ADnhi(%@,?cbOUO:GI$T("g-V!GZg/6$f_XXNArgfTl$H)]i./:Ju;l MK1#Sb+n$A6\'7gXfr@)3DD!",a"86)GEgM%.31EnP9`$1S3YG),s.E%#J:u";Y2] i?A7mYS@CD(d>u9N.mPF_!B2>SrSHF6qFD(3M3$fU;JgG,q>U(ZihqH'r.$VVt$,* aA6,Fl9m0oco2ln@"$Fc7k1V"ecqF@hAPTJ4FsZ/-WGY&9)83J#ru$"nC!5'4?A_R +t'-0M$_R$VGo:Gn=W+\c5D^P] ,3T,,Nq&9sW"`b3=)Cj#g1I\eD@/<1?g3rJE'7Cnh\W /=.p.WF\PsYI_@dWs)s2;_kd%[_s34`OndYi9nIoODAF$g*^G'4N0-TdMObG@Ihea #;Fa2l[fIf5b]^5YiR_O+sI<@j?+mLi:`=*UI_*(4pNGXRF0A(%O'>oL:C$%=A=uN #c'8F32)T\.!0D@-'Y^c%$_Y:B4W]g&7a)=*nXFQ^Z`fdjdVM)DIQU;.>]drT^iI1 #$]9Ke/!d4hMeeJmL8T5=;le(O^q'UMhQQ1h\^Hc_h:Vk'VsE6Q4BV8$3SORq3'>s XQ3/&KfleB.Yg?q3[.R>Qk`(J@5>Kh/4!Xf)-Je#(>f1.Q/Z>[1__V(+_mbCWFbE$ #:@CES/qi[7(S?l=8*JF=T#1;#BpU;)*DQ4>%kTn[0TrmT'B$,-V7pK4[=P9CNDaQ Ue*QrUt%"5Uj\V>]5LS=\0YC]h/pjW?E6VG#)cn?0rl8l!*?+rR[u3OiQQPokr\u" =TYmPWqa9B[=hq%HH%*9+Q+&FC^l\Y3@.XAHl."/B>\hHhfPO<.+i3[&irTqbeZs4 2-T<@m6c<&XPZr#.UsRko6cfa\[$UKhN&nQk9nBuYm`1=cXX4A:#eV4jWCaN7=RNr cEc7P6k$uK1VgG=X:u?g*[7& endstream endobj 373 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F8 40 0 R >> >> endobj 374 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 378 0 R /Resources 376 0 R /Contents 375 0 R >> endobj 375 0 obj << /Length 697 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd=u`@;T_/V9`*tU1_$JQTA2%L)^d94I/0nD!.?2A: [^f@T:Q(Hrj)X*ER,2ip(eeNtWQKj#+d@k9"!OK&E&JP/+q65Vk\./ibU2'>gs`G< &BQ1q@0D:P3J]TJK.5aBAN(J8VEk6el>S%2.S4PWYnGM5MN(MdC$n.#.hD7%6J3Z#um>01HNQLI7kmi\l_E!Tn $jOA%JPoh=2.N$=n'.VKni9Y/F4V*3W#>^~> endstream endobj 376 0 obj << /ProcSet [ /PDF /Text ] /Font << /F5 5 0 R /F6 6 0 R /F8 40 0 R >> >> endobj 377 0 obj << /Type /Pages /Kids [ 358 0 R 378 0 R 397 0 R ] /Count 13 /Parent 125 0 R >> endobj 378 0 obj << /Type /Pages /Kids [ 374 0 R 379 0 R 382 0 R 385 0 R 388 0 R 391 0 R ] /Count 6 /Parent 377 0 R >> endobj 379 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 378 0 R /Resources 381 0 R /Contents 380 0 R >> endobj 380 0 obj << /Length 417 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd&6ol0"LG8g_ae,8(.`)OdR,2jActI%M!RT4[l>mESr/nmQ +?,_#VMb;kAfs+ZVj$BN7,0?+`!)nJFB5'$N'jWkm#N:i`36it!9uSFj.P6?%"ftp 16$p4F?'h>Eq1EIBF"~> endstream endobj 381 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 382 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 378 0 R /Resources 384 0 R /Contents 383 0 R >> endobj 383 0 obj << /Length 2565 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd=u`@;T_/V9`*t[3_$Iq<6n0$/MSp\U#ddQI3>d[U Yc-cof.%\*V"j1F`q_1jQGit?K]q3%1nFgOOdP#a3e^8)KgA8bE%!E.c?"V`aQR1j #Rj4c!BB@]0c<_QP3Gf-%3iU51*O>q?#lBm@\V^q<9.TpQ,Emk'TsokK8kW:\2_iO 2l9flJe#:K(f:LQY?`[F//g7Z7"6%VW7YX?,A?@+OJ3%+.$Y#gP$TUr,&kCWN2t$/ K;7^f:a.8s8D:*t.*EhBZQVSq162H.dMdi@b$[?0:*Y_pL1o#,W5.AD2T`5k.MS3Q %J,,/"9Wnm&.Zjel8"B8apP$mLgXE7\a+;L)_3=9BZJ(G<-g<[igB!c-c9?7.RfGV Nr-\VZbFh]c 9ERu-1.AN3-Q10;W,"=e$`%PW`$q5Ar&*K$O=,o[@uETA.!5b]bk+#l(=?)leJc7Q 10u6D@lVU]N?]S01dY,8*4Z:'jY9QI:cN)mV9IEX(du3'.8n[g9S,8-'AWJ"L5W_O 5Cok/@`[,\I;MbQ(!98c=,i$Y-4HYgcXGJ5^ehQp'0JEhb7t2VQ.i[[+)Hh#h7GjH b>#k&:pjTO)-l`rSr-SCCpY>p,G=0I3's,V3)!Du#.XsS<'_G)[FPPg/L[Ag;cOQ3 ?U3/ob#9kC<.7=lA*so$d#=`2%4G(4<>u;O@sH]e3ocUiTr>?+N=.@)c;gD]=_^`` L3P;PKBBWiEICjq5\?p"1gZMhj&tTG2[k"UTd1+b`$JIO'K4:Gh/^5R:g(u3GThlP 8?%8s+t!%M9ip3M^2:C!fTaEM^ec896[7lU/-,a00Z:?,q*F@)NrN;Mr80T^KFEo3 d]"0K#<)6[Vcg08Q["lL$N,?o7oOi[A\`0VApPa1!\F!)i";`d7a9TLnHR6j//>%NeT/6T Fq;C?-LoSaQ9rDY,D7IKZ/L&[GAm^^!f1M$JHOY#6."B(F=d6S/$!AYEX4\"5Suk- Et/5(laK6+L;TBoLr@!^5'M,^i!^;'!=U:0X&W`Q1Q.0-0LKLIJM9f?-q!u,jLQa.NB`Zh<8!dJ_oLC-R0K>f+u !@hJU80G$6:pq^W2k(G*USruPWN="_LQpEQPE&&pfZf+/ &e\7ik*?[$FM$(HY0i"U`0CL?4^dh8YQVc M(XTo)lH7Y?/[UoL:#1l>.kZ093MQuB6cPa^d_M*S/K]-+%05-'8LXO/HjGX38-42 J##tp;MJGM&&VkQoZ!;nqJ/7PRT^&:`&*5LSf*/:H$g?@A%l-Xi7;X'=qD7W<[hq' 9reY+loB&d\iS2*P4;W(6&O&YE0dC-BqOkZOB4oU7I.UOX6&$%/SG_Ck.I6;Zdi4- HIC,B'2N9MJ2oG?nBkqWemGeM=AjEU8l2&-YOZ]q=ugjkXE=7aiCGn_HP'\eD6YtL AK+j0%;2Sjg9<)QL4^fo\ETheVU=h*c'`q*@ KP#FW`)rf[G&2"o]<(WOPtP#s""72@eGV3r^i;pmCpC^HeGC "XK0KnKf8l\"/_8K$;!79K]ct[Y:1>U_$+THjClO(b5DS-t+G:?/]E<#<[MWEY^!Y 'cSH%Y[\-)3`Lmf5U>GBWG$/6j26.OHqY?a$-K2eBq`&/#&riIXJSA#\JA7Y8Y:=G *:D'R^Gn1-k:`gS=s`WS?Vh^JV=H1]d*u+hd%]Vh.>:R4'h[n./9hj2HW/-H=Z(hf nUbVblF.J0s&/48PX9OlZ'phQ endstream endobj 384 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R /F12 71 0 R >> >> endobj 385 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 378 0 R /Resources 387 0 R /Contents 386 0 R >> endobj 386 0 obj << /Length 2910 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdYJSGl?!"3>gU_,>*AS(QBBNUuf4eaCZm. `h240k[YUI0H:H&_(H[aj1-jilFauP,L#ca CFe`c1;fL92@ct\W])uD$uFCIbu@I"3Iufla@/b>Ere4Zo77'7E,7rKXUq:P$GDU\ SfT_m"igJ`t(pAct8QgdY/SV,BMW;,![h6B%4us:kC-P@Ru\_/M9CnLcos' )`*0L,(_S-K1OaV37"]`(eM7A4N?HM1?',!C2`*lp;QGm`;_m_i@4> 5)Zd>qc,kh38u"<\S9g$)nY6V?6cI_!erHc-f_%=Z6fU#8]R)%\eR8n!!'D!pP *7h_XC>AN2N"7*LcoH&[$@_9"/AF^iAFo7eh0F%jSN_]GX&o.;LUP1,lYAha0V0@\jVkKSP;<+H)(,:F5i`-^Y>\6L9?(C(I/k$AP/- "AEM%G//lVhc]4$$K7d9#=-Z5b03_"0boj)'-=h0b=c-$_Bo7]>AKTsn`tH8c'Wi/AnJ0oiBi4?&Y-p`dLf45i:X #%KQ6U`oejV*tWpS6JD#pHf9KeU[>(u*&PV.N(^Bb9?r F)b1ib#QCNkj>4JH#mrXS5$6='9;,@QqD$0U/r2QB/E,$W3'"/rYX2%g]<4]mrS DG_;BWe-lnK*s@MJqh<>'sKQnV,/t'1;m/p)FSZ[":tg5ZJZUPLj(IkX!Z?QG%B\- L_GK&K/l@bg,UX7ns/ARQ_>&B9=(2165ds[%jhd26k/,%.0^Xkcl>Hk=AtM95?1+N POcj%6"DGJ=W1$Z@\j;;*#TM?hK3-+a]C90^=?sIM%Yd^p*W%^#BAn*"FnQE^@n.GnV1Pq4 3ji0,iiEH7&utS'1BIR"DN(,&E+WVrX*RBmMY3a@AElYi"hY^R.MAaSY1o-:H5T%) [?aaBT[hZU=l4NWug!;P55$p"3+Cl@(0["(%+9_u30G8#)TYfb.FC-#p:2\HrE?FE: K1"AtOYc*'5FO^'Y91+b^S^qr`rI+Hn^hjMKj*7/02S3&pkZEE $0-GZRl0j1C=nd;id9(Qf4M/.RjJhfdpu gXrDSkecSZ_MD/"+c>/m>OS5Gj&]!cHW/(hl]2PD523ULokYLa^\8R5#^d#3:TCpa QE1eVdZLT,CiR@6DCii*?Q\9e_*#hgNsIjdJacp_$5T\NN+]IoVqr;c-q=i&p`#)u WneCU5ZnA$L`qO$";tb9l];bbO/)BK_>2mff-T/%^VH$*?G>*7]Y3e&F.^ml@G23b MVciVrhfA%@a,/TH#)8gP5?SIm2\(BLH!j_4*NWuSEhsuD5_IOY1W1=X,=ei0:#eH QH8Us1!?6B(df-UGHUtTk$82)6&luHr<%hl-J3E,RbOaICAn9gJ.1"Q&m!0^D&Rq$ \!@BHg[$jYDB4IQg^nE8+ilKpE7'(d"fIJHHN?%J(1E:2[Ui1]KDJ[59+HCn[YKM[O>6k~> endstream endobj 387 0 obj << /ProcSet [ /PDF /Text ] /Font << /F5 5 0 R /F6 6 0 R >> >> endobj 388 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 378 0 R /Resources 390 0 R /Contents 389 0 R >> endobj 389 0 obj << /Length 814 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd=u`@;T_/V9`*ta5_$Iq<6n0$/MSp\U#ddQI3>d[U Yc-cof.%\*V"j1F`q_3QOA6IaEKJUn&.jU`!ds4n6)kgc#EtS&b&+f0"h9\L+r*tt 9!32S5n8]l1mnc`H5?MN7=gt>6[L5mjY[mo7D-^@aN=rBL^hc],ZC6=)NJ7D6*T46 &aKO-Kb/&-omE&m"uPOobZlui_CGc%_QU2Z8H/5X!9,mEM4I1:i3/0c:SK5a(3%HETcR3n*, auP+W9L,AK9q.[-`[lIOaX-5i!#Ht+2!k)t.ru>_[hG!=36%oP*%/qV\3S:K'#9qi Vh8sP2i_B55ou#\JEk_r9+^>.";0^C+@oBU2%E!rr6u9Q_3FOS.ke+l,`\'^cnNeP E[nXZ0sVN!%-)Ti+ugeq_@TUaYh1sqma/io9PL$21rFB"mPND^[90Mb;o3*/b;.j" U!)"uO>J@E36FA3oMpk.%>2go.KrCgdnIt;FbhhV%6Psn3,Z*[AD$8 endstream endobj 390 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R /F6 6 0 R >> >> endobj 391 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 378 0 R /Resources 393 0 R /Contents 392 0 R >> endobj 392 0 obj << /Length 3247 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgdqD$&du?4-psBd&&)I; AG\m4CngA*@Mk7Rb^kRZ\q[I6$K826.OsJQX=.c*YpjpNkVVnS<6;_mZ76bNnN?pj ,q3"4(PkEak4C-\qI5l/$RD2oa?pNe1PMSTN%h39:icdE/aUqDK`Vh63Y#VLauKf= #hh71[-23[#!lbY)MS:"bn8LsojTff,8euWNagOV2p?i*kST'J&6b"s:fqX44-C/. /;f$1#m1;4T^nX>&WEcTnq5*8E"N PjQ)!>?3&/^g]2PD.GjGJ;3\*Jn7V3Ne9d$"U8)IIq<,"@+Zuh,:5cmc?V!,g5KN. \i#Nl!6dB^6Y_7/6$#LOPR/N^Qlq2N!7RPKnjTTh:.rQi+UiO%jIW:7$ALK2/_,=u l?H$k$j77b`&NYV31Yh43/AZ=RWki=0a"+lA&Lh!CE o8[7#,/GjHZCO@;+iPQA0aimqK415#inj)k1DD s$8nH=eVs#.n-KZ9tDT_`6^bIMcgm* !2(MZ^o5]=_.6Xi13el8g':%(,,s^G,\;9Z;_k9n-%5je'alF,PW'UfA'i(lV :n!OSiARAB2hke['d#b^:12nA*%=q%%&C%l#Xkm484;u]A.LNFXQla0HD@.T9/]]W ,):^4p!q3s9!*3#2P_3=JNu/Qa5U)lRQs1g'Y[t.Aa4q_E1s7I&Yrqu$WP$4D*g#& JO-Y"X422T,-t2OZZShPe:^V\W$!hafrd3Brf_$\YcRqkFY:hO>T7*MAO/F=(uXJQ C%M75km/L:^n)*HSYDC>6oeR-AeW(7 heSPo=Z4IA*CG?6^)+m:\RuF\W:`Vf2'k`p(s=(]:e7SZNn;_U8?6/BS2R&`Ws2tr !!5GcZd!fhi!!L$%!Z28`:9H(`sD[r"Q),2Ti$Q!FN[Y#l3%X=o[iksLmqdC%I&a< #6='!Z*F[%F2]uc<=MgLQ4>(`qH>PZiG0,[%!cr[k2Cg')1hDhVd_cGe`V$)c6n-h0T6(TJEmV`moRr^e )8'gSids(1bg1nL%GG;8W'sL8duQt1YH+E!LFBKHd--cL50f)\1tC< j'.YibK,h(,e6B-3SAkd/th4c/^lFN!M6X2HIk96<`mP5&ii^)p7Q3mj!hDECb^LD Hr"PS%V)Fr]iq5cm5L(R=LQqj1k,Pf9[+(klMCli/C`DCB(L4,(l,#,n)C!;(*_tJ,qPo#oC(T22cS?r>YF*RF4I\FYJ2J"=-lS3COie)f-Y_] =bdBs`2`;P-j+f`K/nBXWcP:Ce.-SU%8o;Ng7FN`:#i @#,5(cfTe;](q.-Jr+rB$&"NLI!DaDc*,\&LdEjnA5UC)KZ"'[W)0LAl,:jm/qq0KaZZ575Ho$NTE"af^"FU++Hd$)4C+ik5^R[T]Hj "N8^oU';__~> endstream endobj 393 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R >> >> endobj 394 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 397 0 R /Resources 396 0 R /Contents 395 0 R >> endobj 395 0 obj << /Length 2866 /Filter [ /ASCII85Decode /LZWDecode ] >> stream J.RTgd=u`@;T_/V9`jKF`N+*U56p`=@\_2"sYioFJqe9Pm'G0=Z"uG17L;T6+ES" 2,.&"9a@VR*9D$RtLu/ahZsmNaZ]Mf/;tW._7WMRYIQIiQ-!s'^ )MS:!bSWZ4Caf_rNh`]B3:89Of"gR.5*n9>B^.K(6"o11N9^7KR=kn<(cf6;aCb#J cV(M04+Te"((Dfu^,_4+P`$MITqfgOa2fB^)5.=6(qUtD;C]G9U!#M"Y_$obJtV@= Y10YRZ76cIo(toO6f:\A4)>qg+:(rA8/ *j!7-^giA[aSJZnNWViAnAd;*5C?k6A+D=6;6Ci8<`!TR)2B+qTqs66PAiH._`6pA Ae8MMS*IL(E"iWrDHO.NR3V5K:U%'A-u@L^r\>ks2B:_1!O-:=8Pd._4G[K4jP8A/ ie7A5Bi%VG6dJ__O/6Mh;1cnWZ@]:3UHKP$EVFZ22i[ubk,UuZ3#]sE1;jU^aP"aODH .S"hY9,,X9k_?DF?7C.a"]+>k9XAAEnqh!3.0qAYSgOdf?pHIq,7$OT'@QM8LIh%r =0-Gs\9/3m`"4P/hJ`f80eA:M)5GdJEl*oL%*BFD5d5piQLIrb$\e[,V]Q)4P@oLk c3K,,+rc-4DHn]jnq.Km``T:iLBq%_1X]1_8SUOUiRfuiKCnjS95l1JEMNa\NY$aA Bg2mC,m+9n%+sVW"V)M6`=mQkN*p'(d$3b3fX)?FG]sX+5skf4P*&Va[:9VB$U^lV PM+#:##4cr]>UT;T*rJ_A_7rlKq8VQS*=+;XYgpJ$>7l`.!4T"`Yg.'[9ed9H/p*[ (b$G;Sh[&lk+Q<'U9Y/_`1N2**<$nO]PgOW2?jejf:[s3@T"mLC#oN+J@)M+i>U'll!FAYRG**ZNeFV)\KEA+p&P/&d!;U$BOTR^\@l7+"0alG!er& )GCE7OCN<'%LTaK;R@@f_2U'W<6:+u>";>_>)!><:5?cpT$eXBFq#cQ*1\!:0N4uq :FY^UT_`D6Q5l97O#]%L1StPWW$!Odam-eRX)'Ea6%c,-q&8?&%WA#Yb (q`&m@Yj)%f*igbg-[-J3`Z&TF>7GP"r1Mb$D\tM0"%:]A:bBH;We/j7=)4H;aaeX ke##toJ#m2=dBN$VWTT-/pZ7-:kM;'J[sGZ98S"*l9B+DPWUXi#_M'9O!>)X&Yt." *`:*B^K;p-Z;AoAT%4?0OVuKc\Xbl'UucbTi5Pg"VN'tX_(AYPr`-o5Nptir5q+,a #=cI,=TQrk3-#+QLH;r]BHW"[b&pG1>8C"3BeVq9E>!k'>O9n`"9AW\6qimf5lnJ) Y0hn@C8qUq3s5nUrf^61hb'F7jdNW<^P'LL)8i+\7k-V?AILS2X8P.]3(?)E9OT]Y FRmf-&uAROp>9#T1IBd?f[oVq)FGis_`C-3[L(hEcVOci nr.RI-bQ\ejeS+2*9T^1bRM3B_aJb[g#N@>f:,sZ&4&X`Ou5E>$Wr$$F(!NLJ[!^% g$I^7Vkc:PO]t:ED*f4S,sn[?Tf=5!:lH#RE((%m2%*E/r3:F)Kip;Tk82T.Z]$#- gM*5@)2-JJLmId]\!RjmLU:u+ihG9>r_ek]RW/V5fcrRd_?GA]o'GlZQ31:0lVV4<;FE)-ko%jK;q&;C 4QV9YL)>L]9q@DDF_Z=K`f#I?mG5U\#5C#NM;l0hm-N;IY2A#\6K(4A;;<;EJm!I[ Hqo6M@2Q`sVYhuK$U,r7-e8AhRlFR90LG.!6`!Vjb*.nTK:?iJ^2(kijokd$ZZ"R= ANN.6H>gI5E+cV,*p=%=U^Vd(~> endstream endobj 396 0 obj << /ProcSet [ /PDF /Text ] /Font << /F3 4 0 R /F5 5 0 R >> >> endobj 397 0 obj << /Type /Pages /Kids [ 394 0 R ] /Count 1 /Parent 377 0 R >> endobj 398 0 obj << /Type /Catalog /Pages 125 0 R >> endobj 399 0 obj << /CreationDate (Mon, Feb 28, 1994 11:58 PM) /Producer (Acrobat Net Distiller 2.0a1 for Macintosh) /ModDate (D:19940513174841) /SavedBy (Adobe Acrobat 2.0a5) >> endobj xref 0 400 0000000000 65535 f 0000000009 00000 n 0000000122 00000 n 0000001135 00000 n 0000001226 00000 n 0000002274 00000 n 0000003318 00000 n 0000004360 00000 n 0000004458 00000 n 0000004572 00000 n 0000007235 00000 n 0000007327 00000 n 0000007443 00000 n 0000009800 00000 n 0000009882 00000 n 0000009998 00000 n 0000012003 00000 n 0000012095 00000 n 0000012211 00000 n 0000013383 00000 n 0000013475 00000 n 0000013591 00000 n 0000015927 00000 n 0000016019 00000 n 0000016136 00000 n 0000018507 00000 n 0000018599 00000 n 0000018701 00000 n 0000018818 00000 n 0000018935 00000 n 0000021590 00000 n 0000021682 00000 n 0000021799 00000 n 0000024822 00000 n 0000024914 00000 n 0000025031 00000 n 0000026173 00000 n 0000026255 00000 n 0000026372 00000 n 0000027007 00000 n 0000027110 00000 n 0000028152 00000 n 0000028269 00000 n 0000029693 00000 n 0000029796 00000 n 0000029913 00000 n 0000032322 00000 n 0000032425 00000 n 0000032542 00000 n 0000032659 00000 n 0000036968 00000 n 0000037071 00000 n 0000037188 00000 n 0000039969 00000 n 0000040062 00000 n 0000040179 00000 n 0000042943 00000 n 0000043058 00000 n 0000044108 00000 n 0000044225 00000 n 0000046602 00000 n 0000046694 00000 n 0000046811 00000 n 0000048922 00000 n 0000049025 00000 n 0000049142 00000 n 0000051116 00000 n 0000051219 00000 n 0000051336 00000 n 0000051453 00000 n 0000053912 00000 n 0000054018 00000 n 0000055066 00000 n 0000055183 00000 n 0000057813 00000 n 0000057905 00000 n 0000058022 00000 n 0000060739 00000 n 0000060866 00000 n 0000060983 00000 n 0000063705 00000 n 0000063809 00000 n 0000063926 00000 n 0000066358 00000 n 0000066462 00000 n 0000066579 00000 n 0000067220 00000 n 0000067302 00000 n 0000067420 00000 n 0000067537 00000 n 0000070536 00000 n 0000070663 00000 n 0000070780 00000 n 0000073878 00000 n 0000074005 00000 n 0000074122 00000 n 0000076525 00000 n 0000076628 00000 n 0000076745 00000 n 0000078749 00000 n 0000078841 00000 n 0000078961 00000 n 0000081954 00000 n 0000082058 00000 n 0000082179 00000 n 0000084520 00000 n 0000084624 00000 n 0000084748 00000 n 0000084869 00000 n 0000087591 00000 n 0000087695 00000 n 0000087816 00000 n 0000089821 00000 n 0000089914 00000 n 0000090035 00000 n 0000091809 00000 n 0000091902 00000 n 0000092023 00000 n 0000093644 00000 n 0000093737 00000 n 0000093858 00000 n 0000096063 00000 n 0000096156 00000 n 0000096277 00000 n 0000099313 00000 n 0000099417 00000 n 0000099510 00000 n 0000099635 00000 n 0000099756 00000 n 0000102473 00000 n 0000102577 00000 n 0000102698 00000 n 0000105207 00000 n 0000105323 00000 n 0000105444 00000 n 0000107911 00000 n 0000108015 00000 n 0000108136 00000 n 0000110173 00000 n 0000110266 00000 n 0000110387 00000 n 0000112596 00000 n 0000112702 00000 n 0000113740 00000 n 0000113861 00000 n 0000116829 00000 n 0000116933 00000 n 0000117059 00000 n 0000117184 00000 n 0000117305 00000 n 0000119336 00000 n 0000119430 00000 n 0000119551 00000 n 0000121538 00000 n 0000121637 00000 n 0000122680 00000 n 0000122801 00000 n 0000124901 00000 n 0000125000 00000 n 0000125121 00000 n 0000125988 00000 n 0000126087 00000 n 0000126208 00000 n 0000127038 00000 n 0000127131 00000 n 0000127252 00000 n 0000130054 00000 n 0000130147 00000 n 0000130272 00000 n 0000130393 00000 n 0000133627 00000 n 0000133721 00000 n 0000133842 00000 n 0000136965 00000 n 0000137069 00000 n 0000137190 00000 n 0000139987 00000 n 0000140091 00000 n 0000140212 00000 n 0000143502 00000 n 0000143596 00000 n 0000143717 00000 n 0000146709 00000 n 0000146792 00000 n 0000146913 00000 n 0000148556 00000 n 0000148649 00000 n 0000148774 00000 n 0000148895 00000 n 0000149814 00000 n 0000149907 00000 n 0000150028 00000 n 0000152573 00000 n 0000152666 00000 n 0000152787 00000 n 0000155804 00000 n 0000155910 00000 n 0000156031 00000 n 0000158598 00000 n 0000158681 00000 n 0000158802 00000 n 0000161876 00000 n 0000161970 00000 n 0000162091 00000 n 0000164599 00000 n 0000164716 00000 n 0000164841 00000 n 0000164962 00000 n 0000167516 00000 n 0000167609 00000 n 0000167730 00000 n 0000168533 00000 n 0000168626 00000 n 0000168747 00000 n 0000171266 00000 n 0000171372 00000 n 0000171493 00000 n 0000174541 00000 n 0000174634 00000 n 0000174755 00000 n 0000177489 00000 n 0000177605 00000 n 0000177726 00000 n 0000179927 00000 n 0000180020 00000 n 0000180145 00000 n 0000180266 00000 n 0000182045 00000 n 0000182138 00000 n 0000182259 00000 n 0000185032 00000 n 0000185137 00000 n 0000185258 00000 n 0000187213 00000 n 0000187306 00000 n 0000187427 00000 n 0000189982 00000 n 0000190098 00000 n 0000190219 00000 n 0000192903 00000 n 0000192996 00000 n 0000193117 00000 n 0000196146 00000 n 0000196239 00000 n 0000196364 00000 n 0000196485 00000 n 0000199722 00000 n 0000199815 00000 n 0000199936 00000 n 0000203241 00000 n 0000203334 00000 n 0000203455 00000 n 0000204554 00000 n 0000204637 00000 n 0000204758 00000 n 0000207587 00000 n 0000207680 00000 n 0000207801 00000 n 0000211605 00000 n 0000211709 00000 n 0000211830 00000 n 0000212608 00000 n 0000212701 00000 n 0000212827 00000 n 0000212952 00000 n 0000213073 00000 n 0000215155 00000 n 0000215248 00000 n 0000215369 00000 n 0000216700 00000 n 0000216793 00000 n 0000216914 00000 n 0000219805 00000 n 0000219909 00000 n 0000220030 00000 n 0000222440 00000 n 0000222533 00000 n 0000222654 00000 n 0000225371 00000 n 0000225464 00000 n 0000225585 00000 n 0000228382 00000 n 0000228478 00000 n 0000228603 00000 n 0000228724 00000 n 0000231497 00000 n 0000231603 00000 n 0000231724 00000 n 0000234613 00000 n 0000234709 00000 n 0000234830 00000 n 0000236258 00000 n 0000236352 00000 n 0000236473 00000 n 0000240463 00000 n 0000240567 00000 n 0000240688 00000 n 0000244020 00000 n 0000244124 00000 n 0000244245 00000 n 0000248914 00000 n 0000249018 00000 n 0000249143 00000 n 0000249264 00000 n 0000253513 00000 n 0000253617 00000 n 0000253738 00000 n 0000260599 00000 n 0000260703 00000 n 0000260824 00000 n 0000264637 00000 n 0000264741 00000 n 0000264862 00000 n 0000267636 00000 n 0000267729 00000 n 0000267850 00000 n 0000274103 00000 n 0000274219 00000 n 0000274340 00000 n 0000280341 00000 n 0000280446 00000 n 0000280571 00000 n 0000280692 00000 n 0000285563 00000 n 0000285656 00000 n 0000285777 00000 n 0000288398 00000 n 0000288503 00000 n 0000288624 00000 n 0000294845 00000 n 0000294938 00000 n 0000295059 00000 n 0000297139 00000 n 0000297245 00000 n 0000297366 00000 n 0000300070 00000 n 0000300163 00000 n 0000300284 00000 n 0000303109 00000 n 0000303202 00000 n 0000303327 00000 n 0000303448 00000 n 0000305638 00000 n 0000305731 00000 n 0000305852 00000 n 0000308076 00000 n 0000308169 00000 n 0000308290 00000 n 0000310642 00000 n 0000310735 00000 n 0000310856 00000 n 0000313208 00000 n 0000313301 00000 n 0000313422 00000 n 0000314382 00000 n 0000314465 00000 n 0000314586 00000 n 0000325489 00000 n 0000325595 00000 n 0000325720 00000 n 0000325841 00000 n 0000329852 00000 n 0000329956 00000 n 0000330077 00000 n 0000332809 00000 n 0000332913 00000 n 0000333034 00000 n 0000336747 00000 n 0000336851 00000 n 0000336972 00000 n 0000339693 00000 n 0000339797 00000 n 0000339918 00000 n 0000342794 00000 n 0000342898 00000 n 0000343019 00000 n 0000343807 00000 n 0000343901 00000 n 0000344003 00000 n 0000344128 00000 n 0000344249 00000 n 0000344757 00000 n 0000344850 00000 n 0000344971 00000 n 0000347628 00000 n 0000347733 00000 n 0000347854 00000 n 0000350856 00000 n 0000350939 00000 n 0000351060 00000 n 0000351965 00000 n 0000352058 00000 n 0000352179 00000 n 0000355518 00000 n 0000355601 00000 n 0000355722 00000 n 0000358680 00000 n 0000358763 00000 n 0000358848 00000 n 0000358905 00000 n trailer << /Size 400 /Info 399 0 R /Root 398 0 R /ID[] >> startxref 359086 %%EOF \ No newline at end of file From c84ffbae5e2e3f171287697b322a32e94f1d51b5 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 23 Nov 2020 12:59:41 +0100 Subject: [PATCH 084/275] T4 decompression: Clear buffer at start --- src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs | 1 + .../Formats/Tiff/Compression/TiffModifiedHuffmanCompression.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs index 6aeb5af81..6a064962b 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs @@ -32,6 +32,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression using var bitReader = new T4BitReader(stream, byteCount, this.Allocator); + buffer.Clear(); uint bitsWritten = 0; while (bitReader.HasMoreData) { diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffModifiedHuffmanCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffModifiedHuffmanCompression.cs index 1201ab66a..f742c1176 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffModifiedHuffmanCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffModifiedHuffmanCompression.cs @@ -32,6 +32,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression using var bitReader = new T4BitReader(stream, byteCount, this.Allocator, isModifiedHuffman: true); + buffer.Clear(); uint bitsWritten = 0; uint pixelsWritten = 0; while (bitReader.HasMoreData) From 838a1f7fd099501d3493ccc4977de729f3b43493 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 24 Nov 2020 19:58:15 +0100 Subject: [PATCH 085/275] First attempt writing uncompressed tiff --- src/ImageSharp/Formats/Tiff/README.md | 2 +- src/ImageSharp/Formats/Tiff/TiffEncoder.cs | 3 +- .../Formats/Tiff/TiffEncoderCore.cs | 166 +++++++++++++++--- .../Formats/Tiff/Utils/TiffWriter.cs | 70 ++++++-- .../Formats/Tiff/TiffEncoderHeaderTests.cs | 16 +- .../Formats/Tiff/Utils/TiffWriterTests.cs | 87 ++++----- 6 files changed, 248 insertions(+), 96 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/README.md b/src/ImageSharp/Formats/Tiff/README.md index f2fa861a2..636e08a32 100644 --- a/src/ImageSharp/Formats/Tiff/README.md +++ b/src/ImageSharp/Formats/Tiff/README.md @@ -46,7 +46,7 @@ |CcittGroup3Fax | | Y | | |CcittGroup4Fax | | | | |Lzw | | Y | Based on ImageSharp GIF LZW implementation - this code could be modified to be (i) shared, or (ii) optimised for each case | -|Old Jpeg | | | | +|Old Jpeg | | | We should not even try to support this | |Jpeg (Technote 2) | | | | |Deflate (Technote 2) | | Y | | |Old Deflate (Technote 2) | | Y | | diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs index 4c0d5dff8..18c0d12a0 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs @@ -4,6 +4,7 @@ using System.IO; using System.Threading; using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Tiff @@ -17,7 +18,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff public void Encode(Image image, Stream stream) where TPixel : unmanaged, IPixel { - var encode = new TiffEncoderCore(this); + var encode = new TiffEncoderCore(this, image.GetMemoryAllocator()); encode.Encode(image, stream); } diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 8aa0edb97..0350f42a4 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -4,7 +4,8 @@ using System; using System.Collections.Generic; using System.IO; -using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; @@ -15,12 +16,29 @@ namespace SixLabors.ImageSharp.Formats.Tiff ///

internal sealed class TiffEncoderCore { + /// + /// The amount to pad each row by in bytes. + /// + private int padding; + + /// + /// Used for allocating memory during processing operations. + /// + private readonly MemoryAllocator memoryAllocator; + + /// + /// The global configuration. + /// + private Configuration configuration; + /// /// Initializes a new instance of the class. /// /// The options for the encoder. - public TiffEncoderCore(ITiffEncoderOptions options) + /// The memory allocator. + public TiffEncoderCore(ITiffEncoderOptions options, MemoryAllocator memoryAllocator) { + this.memoryAllocator = memoryAllocator; options = options ?? new TiffEncoder(); } @@ -46,10 +64,18 @@ namespace SixLabors.ImageSharp.Formats.Tiff Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); - using (var writer = new TiffWriter(stream)) + this.configuration = image.GetConfiguration(); + + // TODO: bits per pixel hardcoded to 24 for the start. + short bpp = 24; + int bytesPerLine = 4 * (((image.Width * bpp) + 31) / 32); + this.padding = bytesPerLine - (int)(image.Width * (bpp / 8F)); + + using (var writer = new TiffWriter(stream, this.memoryAllocator, this.configuration)) { long firstIfdMarker = this.WriteHeader(writer); - //// todo: multiframing is not support + + // TODO: multiframing is not support long nextIfdMarker = this.WriteImage(writer, image, firstIfdMarker); } } @@ -72,6 +98,31 @@ namespace SixLabors.ImageSharp.Formats.Tiff return firstIfdMarker; } + /// + /// Writes all data required to define an image. + /// + /// The pixel format. + /// The to write data to. + /// The to encode from. + /// The marker to write this IFD offset. + /// The marker to write the next IFD offset (if present). + public long WriteImage(TiffWriter writer, Image image, long ifdOffset) + where TPixel : unmanaged, IPixel + { + var ifdEntries = new List(); + + // Write the image bytes to the steam. + var imageDataStart = (uint)writer.Position; + int imageData = writer.WriteRgbImageData(image, this.padding); + + // Write info's about the image to the stream. + this.AddImageFormat(image, ifdEntries, imageDataStart, imageData); + writer.WriteMarker(ifdOffset, (uint)writer.Position); + long nextIfdMarker = this.WriteIfd(writer, ifdEntries); + + return nextIfdMarker + imageData; + } + /// /// Writes a TIFF IFD block. /// @@ -98,8 +149,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff writer.Write((ushort)entry.DataType); writer.Write(ExifWriter.GetNumberOfComponents(entry)); - uint lenght = ExifWriter.GetLength(entry); - var raw = new byte[lenght]; + uint length = ExifWriter.GetLength(entry); + var raw = new byte[length]; int sz = ExifWriter.WriteValue(entry, raw, 0); DebugGuard.IsTrue(sz == raw.Length, "Incorrect number of bytes written"); if (raw.Length <= 4) @@ -130,36 +181,95 @@ namespace SixLabors.ImageSharp.Formats.Tiff } /// - /// Writes all data required to define an image + /// Adds image format information to the specified IFD. /// /// The pixel format. - /// The to write data to. /// The to encode from. - /// The marker to write this IFD offset. - /// The marker to write the next IFD offset (if present). - public long WriteImage(TiffWriter writer, Image image, long ifdOffset) - where TPixel : unmanaged, IPixel + /// The image format entries to add to the IFD. + /// The start of the image data in the stream. + /// The image data in bytes to write. + public void AddImageFormat(Image image, List ifdEntries, uint imageDataStartOffset, int imageDataBytes) + where TPixel : unmanaged, IPixel { - var ifdEntries = new List(); + var width = new ExifLong(ExifTagValue.ImageWidth) + { + Value = (uint)image.Width + }; - this.AddImageFormat(image, ifdEntries); + var height = new ExifLong(ExifTagValue.ImageLength) + { + Value = (uint)image.Height + }; - writer.WriteMarker(ifdOffset, (uint)writer.Position); - long nextIfdMarker = this.WriteIfd(writer, ifdEntries); + var bitPerSample = new ExifShortArray(ExifTagValue.BitsPerSample) + { + Value = new ushort[] { 8, 8, 8 } + }; - return nextIfdMarker; - } + var compression = new ExifShort(ExifTagValue.Compression) + { + // TODO: for the start, no compression is used. + Value = (ushort)TiffCompression.None + }; - /// - /// Adds image format information to the specified IFD. - /// - /// The pixel format. - /// The to encode from. - /// The image format entries to add to the IFD. - public void AddImageFormat(Image image, List ifdEntries) - where TPixel : unmanaged, IPixel - { - throw new NotImplementedException(); + var photometricInterpretation = new ExifShort(ExifTagValue.PhotometricInterpretation) + { + // TODO: only rgb for now. + Value = (ushort)TiffPhotometricInterpretation.Rgb + }; + + var stripOffsets = new ExifLongArray(ExifTagValue.StripOffsets) + { + // TODO: we only write one image strip for the start. + Value = new uint[] { imageDataStartOffset } + }; + + var samplesPerPixel = new ExifLong(ExifTagValue.SamplesPerPixel) + { + Value = 3 + }; + + var rowsPerStrip = new ExifLong(ExifTagValue.RowsPerStrip) + { + // TODO: all rows in one strip for the start + Value = (uint)image.Height + }; + + var stripByteCounts = new ExifLongArray(ExifTagValue.StripByteCounts) + { + Value = new[] { (uint)(imageDataBytes) } + }; + + var xResolution = new ExifRational(ExifTagValue.XResolution) + { + // TODO: what to use here as a default? + Value = Rational.FromDouble(1.0d) + }; + + var yResolution = new ExifRational(ExifTagValue.YResolution) + { + // TODO: what to use here as a default? + Value = Rational.FromDouble(1.0d) + }; + + var resolutionUnit = new ExifShort(ExifTagValue.ResolutionUnit) + { + // TODO: what to use here as default? + Value = 0 + }; + + ifdEntries.Add(width); + ifdEntries.Add(height); + ifdEntries.Add(bitPerSample); + ifdEntries.Add(compression); + ifdEntries.Add(photometricInterpretation); + ifdEntries.Add(stripOffsets); + ifdEntries.Add(samplesPerPixel); + ifdEntries.Add(rowsPerStrip); + ifdEntries.Add(stripByteCounts); + ifdEntries.Add(xResolution); + ifdEntries.Add(yResolution); + ifdEntries.Add(resolutionUnit); } } } diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs index 7501e314a..1908d38ae 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -4,6 +4,8 @@ using System; using System.Collections.Generic; using System.IO; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Tiff { @@ -14,15 +16,25 @@ namespace SixLabors.ImageSharp.Formats.Tiff { private readonly Stream output; + private readonly MemoryAllocator memoryAllocator; + + private readonly Configuration configuration; + private readonly byte[] paddingBytes = new byte[4]; private readonly List references = new List(); - /// Initializes a new instance of the class. + /// + /// Initializes a new instance of the class. + /// /// The output stream. - public TiffWriter(Stream output) + /// The memory allocator. + /// The configuration. + public TiffWriter(Stream output, MemoryAllocator memoryMemoryAllocator, Configuration configuration) { this.output = output; + this.memoryAllocator = memoryMemoryAllocator; + this.configuration = configuration; } /// @@ -35,7 +47,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// public long Position => this.output.Position; - /// Writes an empty four bytes to the stream, returning the offset to be written later. + /// + /// Writes an empty four bytes to the stream, returning the offset to be written later. + /// /// The offset to be written later public long PlaceMarker() { @@ -44,21 +58,27 @@ namespace SixLabors.ImageSharp.Formats.Tiff return offset; } - /// Writes an array of bytes to the current stream. + /// + /// Writes an array of bytes to the current stream. + /// /// The bytes to write. public void Write(byte[] value) { this.output.Write(value, 0, value.Length); } - /// Writes a byte to the current stream. + /// + /// Writes a byte to the current stream. + /// /// The byte to write. public void Write(byte value) { this.output.Write(new byte[] { value }, 0, 1); } - /// Writes a two-byte unsigned integer to the current stream. + /// + /// Writes a two-byte unsigned integer to the current stream. + /// /// The two-byte unsigned integer to write. public void Write(ushort value) { @@ -66,7 +86,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff this.output.Write(bytes, 0, 2); } - /// Writes a four-byte unsigned integer to the current stream. + /// + /// Writes a four-byte unsigned integer to the current stream. + /// /// The four-byte unsigned integer to write. public void Write(uint value) { @@ -74,7 +96,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff this.output.Write(bytes, 0, 4); } - /// Writes an array of bytes to the current stream, padded to four-bytes. + /// + /// Writes an array of bytes to the current stream, padded to four-bytes. + /// /// The bytes to write. public void WritePadded(byte[] value) { @@ -86,7 +110,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff } } - /// Writes a four-byte unsigned integer to the specified marker in the stream. + /// + /// Writes a four-byte unsigned integer to the specified marker in the stream. + /// /// The offset returned when placing the marker /// The four-byte unsigned integer to write. public void WriteMarker(long offset, uint value) @@ -97,6 +123,30 @@ namespace SixLabors.ImageSharp.Formats.Tiff this.output.Seek(currentOffset, SeekOrigin.Begin); } + /// + /// Writes the image data as RGB to the stream. + /// + /// The pixel data. + /// The image to write to the stream. + /// The padding bytes for each row. + /// The number of bytes written + public int WriteRgbImageData(Image image, int padding) + where TPixel : unmanaged, IPixel + { + using IManagedByteBuffer row = this.AllocateRow(image.Width, 3, padding); + Span rowSpan = row.GetSpan(); + for (int y = 0; y < image.Height; y++) + { + Span pixelRow = image.GetPixelRowSpan(y); + PixelOperations.Instance.ToRgb24Bytes(this.configuration, pixelRow, rowSpan, pixelRow.Length); + this.output.Write(rowSpan); + } + + return image.Width * image.Height * 3; + } + + private IManagedByteBuffer AllocateRow(int width, int bytesPerPixel, int padding) => this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, bytesPerPixel, padding); + /// /// Disposes instance, ensuring any unwritten data is flushed. /// @@ -105,4 +155,4 @@ namespace SixLabors.ImageSharp.Formats.Tiff this.output.Flush(); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs index 2af1b5225..91166bf2d 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs @@ -3,6 +3,7 @@ using System.IO; using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Memory; using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff @@ -10,13 +11,16 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Trait("Category", "Tiff")] public class TiffEncoderHeaderTests { + private static readonly MemoryAllocator MemoryAllocator = new ArrayPoolMemoryAllocator(); + private static readonly Configuration Configuration = Configuration.Default; + [Fact] public void WriteHeader_WritesValidHeader() { - MemoryStream stream = new MemoryStream(); - TiffEncoderCore encoder = new TiffEncoderCore(null); + var stream = new MemoryStream(); + var encoder = new TiffEncoderCore(null, MemoryAllocator); - using (TiffWriter writer = new TiffWriter(stream)) + using (var writer = new TiffWriter(stream, MemoryAllocator, Configuration)) { long firstIfdMarker = encoder.WriteHeader(writer); } @@ -27,10 +31,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Fact] public void WriteHeader_ReturnsFirstIfdMarker() { - MemoryStream stream = new MemoryStream(); - TiffEncoderCore encoder = new TiffEncoderCore(null); + var stream = new MemoryStream(); + var encoder = new TiffEncoderCore(null, MemoryAllocator); - using (TiffWriter writer = new TiffWriter(stream)) + using (var writer = new TiffWriter(stream, MemoryAllocator, Configuration)) { long firstIfdMarker = encoder.WriteHeader(writer); Assert.Equal(4, firstIfdMarker); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs index a3e865519..9023fe3e0 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs @@ -3,6 +3,7 @@ using System.IO; using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Memory; using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff @@ -10,41 +11,35 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Trait("Category", "Tiff")] public class TiffWriterTests { + private static readonly MemoryAllocator MemoryAllocator = new ArrayPoolMemoryAllocator(); + private static readonly Configuration Configuration = Configuration.Default; + [Fact] public void IsLittleEndian_IsTrueOnWindows() { - MemoryStream stream = new MemoryStream(); - - using (TiffWriter writer = new TiffWriter(stream)) - { - Assert.True(writer.IsLittleEndian); - } + using var stream = new MemoryStream(); + using var writer = new TiffWriter(stream, MemoryAllocator, Configuration); + Assert.True(writer.IsLittleEndian); } [Theory] - [InlineData(new byte[] {}, 0)] + [InlineData(new byte[] { }, 0)] [InlineData(new byte[] { 42 }, 1)] [InlineData(new byte[] { 1, 2, 3, 4, 5 }, 5)] public void Position_EqualsTheStreamPosition(byte[] data, long expectedResult) { - MemoryStream stream = new MemoryStream(); - - using (TiffWriter writer = new TiffWriter(stream)) - { - writer.Write(data); - Assert.Equal(writer.Position, expectedResult); - } + using var stream = new MemoryStream(); + using var writer = new TiffWriter(stream, MemoryAllocator, Configuration); + writer.Write(data); + Assert.Equal(writer.Position, expectedResult); } [Fact] public void Write_WritesByte() { - MemoryStream stream = new MemoryStream(); - - using (TiffWriter writer = new TiffWriter(stream)) - { - writer.Write((byte)42); - } + using var stream = new MemoryStream(); + using var writer = new TiffWriter(stream, MemoryAllocator, Configuration); + writer.Write((byte)42); Assert.Equal(new byte[] { 42 }, stream.ToArray()); } @@ -52,12 +47,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Fact] public void Write_WritesByteArray() { - MemoryStream stream = new MemoryStream(); - - using (TiffWriter writer = new TiffWriter(stream)) - { - writer.Write(new byte[] { 2, 4, 6, 8 }); - } + using var stream = new MemoryStream(); + using var writer = new TiffWriter(stream, MemoryAllocator, Configuration); + writer.Write(new byte[] { 2, 4, 6, 8 }); Assert.Equal(new byte[] { 2, 4, 6, 8 }, stream.ToArray()); } @@ -65,12 +57,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Fact] public void Write_WritesUInt16() { - MemoryStream stream = new MemoryStream(); - - using (TiffWriter writer = new TiffWriter(stream)) - { - writer.Write((ushort)1234); - } + using var stream = new MemoryStream(); + using var writer = new TiffWriter(stream, MemoryAllocator, Configuration); + writer.Write((ushort)1234); Assert.Equal(new byte[] { 0xD2, 0x04 }, stream.ToArray()); } @@ -78,12 +67,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Fact] public void Write_WritesUInt32() { - MemoryStream stream = new MemoryStream(); - - using (TiffWriter writer = new TiffWriter(stream)) - { - writer.Write((uint)12345678); - } + using var stream = new MemoryStream(); + using var writer = new TiffWriter(stream, MemoryAllocator, Configuration); + writer.Write((uint)12345678); Assert.Equal(new byte[] { 0x4E, 0x61, 0xBC, 0x00 }, stream.ToArray()); } @@ -97,12 +83,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [InlineData(new byte[] { 2, 4, 6, 8, 10, 12 }, new byte[] { 2, 4, 6, 8, 10, 12 })] public void WritePadded_WritesByteArray(byte[] bytes, byte[] expectedResult) { - MemoryStream stream = new MemoryStream(); - - using (TiffWriter writer = new TiffWriter(stream)) - { - writer.WritePadded(bytes); - } + using var stream = new MemoryStream(); + using var writer = new TiffWriter(stream, MemoryAllocator, Configuration); + writer.WritePadded(bytes); Assert.Equal(expectedResult, stream.ToArray()); } @@ -110,9 +93,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Fact] public void WriteMarker_WritesToPlacedPosition() { - MemoryStream stream = new MemoryStream(); + using var stream = new MemoryStream(); - using (TiffWriter writer = new TiffWriter(stream)) + using (var writer = new TiffWriter(stream, MemoryAllocator, Configuration)) { writer.Write((uint)0x11111111); long marker = writer.PlaceMarker(); @@ -123,10 +106,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff writer.Write((uint)0x44444444); } - Assert.Equal(new byte[] { 0x11, 0x11, 0x11, 0x11, - 0x78, 0x56, 0x34, 0x12, - 0x33, 0x33, 0x33, 0x33, - 0x44, 0x44, 0x44, 0x44 }, stream.ToArray()); + Assert.Equal( + new byte[] + { + 0x11, 0x11, 0x11, 0x11, + 0x78, 0x56, 0x34, 0x12, + 0x33, 0x33, 0x33, 0x33, + 0x44, 0x44, 0x44, 0x44 + }, stream.ToArray()); } } } From ae34c3ceca3987bfc5042c515c84a601b2c26278 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 25 Nov 2020 10:32:40 +0100 Subject: [PATCH 086/275] Add Tiff EncodeAsync --- src/ImageSharp/Formats/Tiff/README.md | 26 +++++++++---------- src/ImageSharp/Formats/Tiff/TiffEncoder.cs | 3 ++- .../Formats/Tiff/TiffEncoderCore.cs | 17 ++++++++---- 3 files changed, 27 insertions(+), 19 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/README.md b/src/ImageSharp/Formats/Tiff/README.md index 636e08a32..0343d0a46 100644 --- a/src/ImageSharp/Formats/Tiff/README.md +++ b/src/ImageSharp/Formats/Tiff/README.md @@ -40,7 +40,7 @@ | |Encoder|Decoder|Comments | |---------------------------|:-----:|:-----:|--------------------------| -|None | | Y | | +|None | Y | Y | encoding only rgb so far | |Ccitt1D | | Y | | |PackBits | | Y | | |CcittGroup3Fax | | Y | | @@ -58,7 +58,7 @@ |WhiteIsZero | | Y | General + 1/4/8-bit optimised implementations | |BlackIsZero | | Y | General + 1/4/8-bit optimised implementations | |Rgb (Chunky) | | Y | General + Rgb888 optimised implementation | -|Rgb (Planar) | | Y | General implementation only | +|Rgb (Planar) | Y | Y | General implementation only | |PaletteColor | | Y | General implementation only | |TransparencyMask | | | | |Separated (TIFF Extension) | | | | @@ -72,34 +72,34 @@ |---------------------------|:-----:|:-----:|--------------------------| |NewSubfileType | | | | |SubfileType | | | | -|ImageWidth | | Y | | -|ImageLength | | Y | | -|BitsPerSample | | Y | | +|ImageWidth | Y | Y | | +|ImageLength | Y | Y | | +|BitsPerSample | Y | Y | | |Compression | | Y | | -|PhotometricInterpretation | | Y | | -|Threshholding | | | | +|PhotometricInterpretation | Y | Y | | +|Thresholding | | | | |CellWidth | | | | |CellLength | | | | |FillOrder | | - | Ignore. In practice is very uncommon, and is not recommended. | |ImageDescription | | Y | | |Make | | Y | | |Model | | Y | | -|StripOffsets | | Y | | +|StripOffsets | Y | Y | | |Orientation | | - | Ignore. Many readers ignore this tag. | -|SamplesPerPixel | | - | Currently ignored, as can be inferred from count of BitsPerSample | +|SamplesPerPixel | Y | - | Currently ignored, as can be inferred from count of BitsPerSample | |RowsPerStrip | | Y | | -|StripByteCounts | | Y | | +|StripByteCounts | Y | Y | | |MinSampleValue | | | | |MaxSampleValue | | | | -|XResolution | | Y | | -|YResolution | | Y | | +|XResolution | Y | Y | | +|YResolution | Y | Y | | |PlanarConfiguration | | Y | | |FreeOffsets | | | | |FreeByteCounts | | | | |GrayResponseUnit | | | | |GrayResponseCurve | | | | |ResolutionUnit | | Y | | -|Software | | Y | | +|Software | Y | Y | | |DateTime | | Y | | |Artist | | Y | | |HostComputer | | Y | | diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs index 18c0d12a0..17ed52182 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs @@ -26,7 +26,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff public Task EncodeAsync(Image image, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { - throw new System.NotImplementedException(); + var encoder = new TiffEncoderCore(this, image.GetMemoryAllocator()); + return encoder.EncodeAsync(image, stream, cancellationToken); } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 0350f42a4..250fb2389 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Threading; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata.Profiles.Exif; @@ -14,7 +15,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Performs the TIFF encoding operation. /// - internal sealed class TiffEncoderCore + internal sealed class TiffEncoderCore : IImageEncoderInternals { /// /// The amount to pad each row by in bytes. @@ -39,7 +40,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff public TiffEncoderCore(ITiffEncoderOptions options, MemoryAllocator memoryAllocator) { this.memoryAllocator = memoryAllocator; - options = options ?? new TiffEncoder(); } /// @@ -58,7 +58,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The pixel format. /// The to encode from. /// The to encode the image data to. - public void Encode(Image image, Stream stream) + /// The token to request cancellation. + public void Encode(Image image, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { Guard.NotNull(image, nameof(image)); @@ -221,7 +222,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff var stripOffsets = new ExifLongArray(ExifTagValue.StripOffsets) { // TODO: we only write one image strip for the start. - Value = new uint[] { imageDataStartOffset } + Value = new[] { imageDataStartOffset } }; var samplesPerPixel = new ExifLong(ExifTagValue.SamplesPerPixel) @@ -237,7 +238,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff var stripByteCounts = new ExifLongArray(ExifTagValue.StripByteCounts) { - Value = new[] { (uint)(imageDataBytes) } + Value = new[] { (uint)imageDataBytes } }; var xResolution = new ExifRational(ExifTagValue.XResolution) @@ -258,6 +259,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff Value = 0 }; + var software = new ExifString(ExifTagValue.Software) + { + Value = "ImageSharp" + }; + ifdEntries.Add(width); ifdEntries.Add(height); ifdEntries.Add(bitPerSample); @@ -270,6 +276,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff ifdEntries.Add(xResolution); ifdEntries.Add(yResolution); ifdEntries.Add(resolutionUnit); + ifdEntries.Add(software); } } } From d5980eafae82b635b46fe1ba8685da00998b59ad Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 25 Nov 2020 13:13:07 +0100 Subject: [PATCH 087/275] Set bits per pixel in tiff metadata --- .../Formats/Tiff/TiffBitsPerPixel.cs | 21 +++++++ .../Formats/Tiff/TiffDecoderCore.cs | 61 +++++++++++++++---- .../Formats/Tiff/TiffEncoderCore.cs | 13 +++- src/ImageSharp/Formats/Tiff/TiffMetadata.cs | 9 ++- 4 files changed, 86 insertions(+), 18 deletions(-) create mode 100644 src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs diff --git a/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs b/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs new file mode 100644 index 000000000..57a6eda5f --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Tiff +{ + /// + /// Enumerates the available bits per pixel the tiff encoder supports. + /// + public enum TiffBitsPerPixel + { + /// + /// 8 bits per pixel. Each pixel consists of 1 byte. + /// + Pixel8 = 8, + + /// + /// 24 bits per pixel. Each pixel consists of 3 bytes. + /// + Pixel24 = 24, + } +} diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index bbf361e0d..08b00d5ba 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -33,6 +33,19 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// private readonly bool ignoreMetadata; + /// + /// The image metadata. + /// + private ImageMetadata metadata; + + /// + /// The tiff specific metadata. + /// + private TiffMetadata tiffMetaData; + + /// + /// The stream to decode from. + /// private BufferedReadStream inputStream; /// @@ -104,7 +117,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff framesMetadata.Add(frameMetadata); } - ImageMetadata metadata = framesMetadata.CreateMetadata(this.ignoreMetadata, tiffStream.ByteOrder); + this.metadata = framesMetadata.CreateMetadata(this.ignoreMetadata, tiffStream.ByteOrder); + this.tiffMetaData = this.metadata.GetTiffMetadata(); + this.SetBitsPerPixel(framesMetadata); // todo: tiff frames can have different sizes { @@ -119,11 +134,31 @@ namespace SixLabors.ImageSharp.Formats.Tiff } } - var image = new Image(this.configuration, metadata, frames); + var image = new Image(this.configuration, this.metadata, frames); return image; } + private void SetBitsPerPixel(List framesMetadata) + { + TiffFrameMetadata firstMetaData = framesMetadata.First(); + ushort[] bitsPerSample = firstMetaData.BitsPerSample; + var bitsPerPixel = 0; + foreach (var bps in bitsPerSample) + { + bitsPerPixel += bps; + } + + if (bitsPerPixel == 24) + { + this.tiffMetaData.BitsPerPixel = TiffBitsPerPixel.Pixel24; + } + else if (bitsPerPixel == 8) + { + this.tiffMetaData.BitsPerPixel = TiffBitsPerPixel.Pixel8; + } + } + /// public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { @@ -168,7 +203,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff private static TiffByteOrder ReadByteOrder(Stream stream) { - byte[] headerBytes = new byte[2]; + var headerBytes = new byte[2]; stream.Read(headerBytes, 0, 2); if (headerBytes[0] == TiffConstants.ByteOrderLittleEndian && headerBytes[1] == TiffConstants.ByteOrderLittleEndian) { @@ -187,26 +222,26 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// The pixel format. /// The IFD tags. - /// The frame metadata. + /// The frame metadata. /// /// The tiff frame. /// - private ImageFrame DecodeFrame(IExifValue[] tags, out TiffFrameMetadata metadata) + private ImageFrame DecodeFrame(IExifValue[] tags, out TiffFrameMetadata frameMetaData) where TPixel : unmanaged, IPixel { var coreMetadata = new ImageFrameMetadata(); - metadata = coreMetadata.GetTiffMetadata(); - metadata.Tags = tags; + frameMetaData = coreMetadata.GetTiffMetadata(); + frameMetaData.Tags = tags; - this.VerifyAndParseOptions(metadata); + this.VerifyAndParseOptions(frameMetaData); - int width = (int)metadata.Width; - int height = (int)metadata.Height; + int width = (int)frameMetaData.Width; + int height = (int)frameMetaData.Height; var frame = new ImageFrame(this.configuration, width, height, coreMetadata); - int rowsPerStrip = (int)metadata.RowsPerStrip; - uint[] stripOffsets = metadata.StripOffsets; - uint[] stripByteCounts = metadata.StripByteCounts; + int rowsPerStrip = (int)frameMetaData.RowsPerStrip; + uint[] stripOffsets = frameMetaData.StripOffsets; + uint[] stripByteCounts = frameMetaData.StripByteCounts; if (this.PlanarConfiguration == TiffPlanarConfiguration.Planar) { diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 250fb2389..10d22b25b 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -5,8 +5,10 @@ using System; using System.Collections.Generic; using System.IO; using System.Threading; + using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; @@ -32,6 +34,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// private Configuration configuration; + /// + /// The color depth, in number of bits per pixel. + /// + private TiffBitsPerPixel? bitsPerPixel; + /// /// Initializes a new instance of the class. /// @@ -66,9 +73,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff Guard.NotNull(stream, nameof(stream)); this.configuration = image.GetConfiguration(); + ImageMetadata metadata = image.Metadata; + TiffMetadata tiffMetadata = metadata.GetTiffMetadata(); + this.bitsPerPixel ??= tiffMetadata.BitsPerPixel; - // TODO: bits per pixel hardcoded to 24 for the start. - short bpp = 24; + short bpp = (short)this.bitsPerPixel; int bytesPerLine = 4 * (((image.Width * bpp) + 31) / 32); this.padding = bytesPerLine - (int)(image.Width * (bpp / 8F)); diff --git a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs index ae25480ed..fd1d84ef3 100644 --- a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs @@ -1,9 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using System.Collections; -using System.Collections.Generic; - namespace SixLabors.ImageSharp.Formats.Tiff { /// @@ -26,6 +23,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff { this.ByteOrder = other.ByteOrder; this.XmpProfile = other.XmpProfile; + this.BitsPerPixel = other.BitsPerPixel; } /// @@ -33,6 +31,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// public TiffByteOrder ByteOrder { get; set; } + /// + /// Gets or sets the number of bits per pixel. + /// + public TiffBitsPerPixel BitsPerPixel { get; set; } = TiffBitsPerPixel.Pixel24; + /// /// Gets or sets the XMP profile. /// From d90c17d0cdba014373aba978fac47a42dd1efe27 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 25 Nov 2020 18:27:40 +0100 Subject: [PATCH 088/275] Add support for writing 8bit gray tiff images --- .../Formats/Tiff/ITiffEncoderOptions.cs | 6 +++- .../Formats/Tiff/TiffBitsPerPixel.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffEncoder.cs | 5 +++ .../Formats/Tiff/TiffEncoderCore.cs | 33 ++++++++++++++----- .../Formats/Tiff/Utils/TiffWriter.cs | 22 +++++++++++++ 5 files changed, 58 insertions(+), 10 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs index 98efa52cd..dcb8a5c44 100644 --- a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs +++ b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Formats.Tiff @@ -8,5 +8,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// public interface ITiffEncoderOptions { + /// + /// Gets the number of bits per pixel. + /// + TiffBitsPerPixel? BitsPerPixel { get; } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs b/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs index 57a6eda5f..502c2e425 100644 --- a/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs +++ b/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff public enum TiffBitsPerPixel { /// - /// 8 bits per pixel. Each pixel consists of 1 byte. + /// 8 bits per pixel, grayscale image. Each pixel consists of 1 byte. /// Pixel8 = 8, diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs index 17ed52182..881b3cc4e 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs @@ -14,6 +14,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff ///
public class TiffEncoder : IImageEncoder, ITiffEncoderOptions { + /// + /// Gets or sets the number of bits per pixel. 8 bit implies a grayscale image. + /// + public TiffBitsPerPixel? BitsPerPixel { get; set; } + /// public void Encode(Image image, Stream stream) where TPixel : unmanaged, IPixel diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 10d22b25b..d81acf6c6 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -47,12 +47,21 @@ namespace SixLabors.ImageSharp.Formats.Tiff public TiffEncoderCore(ITiffEncoderOptions options, MemoryAllocator memoryAllocator) { this.memoryAllocator = memoryAllocator; + + if (options.BitsPerPixel == TiffBitsPerPixel.Pixel8) + { + this.PhotometricInterpretation = TiffPhotometricInterpretation.BlackIsZero; + } + else + { + this.PhotometricInterpretation = TiffPhotometricInterpretation.Rgb; + } } /// - /// Gets or sets the photometric interpretation implementation to use when encoding the image. + /// Gets the photometric interpretation implementation to use when encoding the image. /// - public TiffColorType ColorType { get; set; } + private TiffPhotometricInterpretation PhotometricInterpretation { get; } /// /// Gets or sets the compression implementation to use when encoding the image. @@ -123,14 +132,22 @@ namespace SixLabors.ImageSharp.Formats.Tiff // Write the image bytes to the steam. var imageDataStart = (uint)writer.Position; - int imageData = writer.WriteRgbImageData(image, this.padding); + int imageDataBytes; + if (this.PhotometricInterpretation == TiffPhotometricInterpretation.Rgb) + { + imageDataBytes = writer.WriteRgbImageData(image, this.padding); + } + else + { + imageDataBytes = writer.WriteGrayImageData(image, this.padding); + } // Write info's about the image to the stream. - this.AddImageFormat(image, ifdEntries, imageDataStart, imageData); + this.AddImageFormat(image, ifdEntries, imageDataStart, imageDataBytes); writer.WriteMarker(ifdOffset, (uint)writer.Position); long nextIfdMarker = this.WriteIfd(writer, ifdEntries); - return nextIfdMarker + imageData; + return nextIfdMarker + imageDataBytes; } /// @@ -211,9 +228,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff Value = (uint)image.Height }; + ushort[] bitsPerSampleValue = this.PhotometricInterpretation == TiffPhotometricInterpretation.Rgb ? new ushort[] { 8, 8, 8 } : new ushort[] { 8 }; var bitPerSample = new ExifShortArray(ExifTagValue.BitsPerSample) { - Value = new ushort[] { 8, 8, 8 } + Value = bitsPerSampleValue }; var compression = new ExifShort(ExifTagValue.Compression) @@ -224,8 +242,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff var photometricInterpretation = new ExifShort(ExifTagValue.PhotometricInterpretation) { - // TODO: only rgb for now. - Value = (ushort)TiffPhotometricInterpretation.Rgb + Value = (ushort)this.PhotometricInterpretation }; var stripOffsets = new ExifLongArray(ExifTagValue.StripOffsets) diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs index 1908d38ae..7578c8213 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -145,6 +145,28 @@ namespace SixLabors.ImageSharp.Formats.Tiff return image.Width * image.Height * 3; } + /// + /// Writes the image data as 8 bit gray to the stream. + /// + /// The pixel data. + /// The image to write to the stream. + /// The padding bytes for each row. + /// The number of bytes written + public int WriteGrayImageData(Image image, int padding) + where TPixel : unmanaged, IPixel + { + using IManagedByteBuffer row = this.AllocateRow(image.Width, 1, padding); + Span rowSpan = row.GetSpan(); + for (int y = 0; y < image.Height; y++) + { + Span pixelRow = image.GetPixelRowSpan(y); + PixelOperations.Instance.ToL8Bytes(this.configuration, pixelRow, rowSpan, pixelRow.Length); + this.output.Write(rowSpan); + } + + return image.Width * image.Height; + } + private IManagedByteBuffer AllocateRow(int width, int bytesPerPixel, int padding) => this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, bytesPerPixel, padding); /// From a7deb8b2517f8043c6601991271f14a06ab0686c Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 25 Nov 2020 20:31:23 +0100 Subject: [PATCH 089/275] Add Tiff encoder Tests --- src/ImageSharp/Formats/Tiff/TiffEncoder.cs | 5 + .../Formats/Tiff/TiffEncoderCompression.cs | 16 ++ .../Formats/Tiff/TiffEncoderCore.cs | 22 +-- .../Compression/LzwTiffCompressionTests.cs | 2 +- .../Compression/NoneTiffCompressionTests.cs | 2 +- .../Formats/Tiff/ImageExtensionsTest.cs | 172 ------------------ .../Formats/Tiff/TiffEncoderHeaderTests.cs | 9 +- .../Formats/Tiff/TiffEncoderTests.cs | 70 +++++++ .../Formats/Tiff/TiffTestUtils.cs | 63 +++++++ 9 files changed, 164 insertions(+), 197 deletions(-) create mode 100644 src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs delete mode 100644 tests/ImageSharp.Tests/Formats/Tiff/ImageExtensionsTest.cs create mode 100644 tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs create mode 100644 tests/ImageSharp.Tests/Formats/Tiff/TiffTestUtils.cs diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs index 881b3cc4e..a83e0606c 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs @@ -19,6 +19,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// public TiffBitsPerPixel? BitsPerPixel { get; set; } + /// + /// Gets or sets a value indicating which compression to use. + /// + public TiffEncoderCompression Compression { get; set; } = TiffEncoderCompression.None; + /// public void Encode(Image image, Stream stream) where TPixel : unmanaged, IPixel diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs new file mode 100644 index 000000000..334262dbf --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs @@ -0,0 +1,16 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Tiff +{ + /// + /// Indicates which tiff compression is used. + /// + public enum TiffEncoderCompression + { + /// + /// No compression is used. + /// + None, + } +} diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index d81acf6c6..ffccce520 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -47,21 +47,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff public TiffEncoderCore(ITiffEncoderOptions options, MemoryAllocator memoryAllocator) { this.memoryAllocator = memoryAllocator; - - if (options.BitsPerPixel == TiffBitsPerPixel.Pixel8) - { - this.PhotometricInterpretation = TiffPhotometricInterpretation.BlackIsZero; - } - else - { - this.PhotometricInterpretation = TiffPhotometricInterpretation.Rgb; - } } /// /// Gets the photometric interpretation implementation to use when encoding the image. /// - private TiffPhotometricInterpretation PhotometricInterpretation { get; } + private TiffPhotometricInterpretation PhotometricInterpretation { get; set; } /// /// Gets or sets the compression implementation to use when encoding the image. @@ -85,6 +76,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff ImageMetadata metadata = image.Metadata; TiffMetadata tiffMetadata = metadata.GetTiffMetadata(); this.bitsPerPixel ??= tiffMetadata.BitsPerPixel; + this.PhotometricInterpretation = this.bitsPerPixel == TiffBitsPerPixel.Pixel8 ? TiffPhotometricInterpretation.BlackIsZero : TiffPhotometricInterpretation.Rgb; short bpp = (short)this.bitsPerPixel; int bytesPerLine = 4 * (((image.Width * bpp) + 31) / 32); @@ -132,15 +124,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff // Write the image bytes to the steam. var imageDataStart = (uint)writer.Position; - int imageDataBytes; - if (this.PhotometricInterpretation == TiffPhotometricInterpretation.Rgb) - { - imageDataBytes = writer.WriteRgbImageData(image, this.padding); - } - else - { - imageDataBytes = writer.WriteGrayImageData(image, this.padding); - } + int imageDataBytes = this.PhotometricInterpretation == TiffPhotometricInterpretation.Rgb ? writer.WriteRgbImageData(image, this.padding) : writer.WriteGrayImageData(image, this.padding); // Write info's about the image to the stream. this.AddImageFormat(image, ifdEntries, imageDataStart, imageDataBytes); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs index a26f0b117..3a0ceae74 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs @@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { using (Stream stream = CreateCompressedStream(data)) { - byte[] buffer = new byte[data.Length]; + var buffer = new byte[data.Length]; new LzwTiffCompression(Configuration.Default.MemoryAllocator).Decompress(stream, (int)stream.Length, buffer); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs index 28fbce69f..c1cde9466 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs @@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public void Decompress_ReadsData(byte[] inputData, int byteCount, byte[] expectedResult) { Stream stream = new MemoryStream(inputData); - byte[] buffer = new byte[expectedResult.Length]; + var buffer = new byte[expectedResult.Length]; new NoneTiffCompression(null).Decompress(stream, byteCount, buffer); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/ImageExtensionsTest.cs b/tests/ImageSharp.Tests/Formats/Tiff/ImageExtensionsTest.cs deleted file mode 100644 index af82b4026..000000000 --- a/tests/ImageSharp.Tests/Formats/Tiff/ImageExtensionsTest.cs +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.IO; -using System.Threading.Tasks; -using SixLabors.ImageSharp.Formats; -using SixLabors.ImageSharp.Formats.Tiff; -using SixLabors.ImageSharp.PixelFormats; -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Formats.Tiff -{ - [Trait("Category", "Tiff.BlackBox.Encoder")] - [Trait("Category", "Tiff")] - public class ImageExtensionsTest - { - [Theory] - [WithFile(TestImages.Tiff.RgbUncompressed, PixelTypes.Rgba32)] - public void ThrowsSavingNotImplemented(TestImageProvider provider) - where TPixel : unmanaged, IPixel - { - Assert.Throws(() => - { - string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest)); - string file = Path.Combine(dir, "SaveAsTiff_Path.tiff"); - using var image = provider.GetImage(new TiffDecoder()); - image.SaveAsTiff(file); - }); - } - - [Fact(Skip = "Saving not implemented")] - public void SaveAsTiff_Path() - { - string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest)); - string file = Path.Combine(dir, "SaveAsTiff_Path.tiff"); - - using (var image = new Image(10, 10)) - { - image.SaveAsTiff(file); - } - - using (Image.Load(file, out IImageFormat mime)) - { - Assert.Equal("image/tiff", mime.DefaultMimeType); - } - } - - [Fact(Skip = "Saving not implemented")] - public async Task SaveAsTiffAsync_Path() - { - string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest)); - string file = Path.Combine(dir, "SaveAsTiffAsync_Path.tiff"); - - using (var image = new Image(10, 10)) - { - await image.SaveAsTiffAsync(file); - } - - using (Image.Load(file, out IImageFormat mime)) - { - Assert.Equal("image/tiff", mime.DefaultMimeType); - } - } - - [Fact(Skip = "Saving not implemented")] - public void SaveAsTiff_Path_Encoder() - { - string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions)); - string file = Path.Combine(dir, "SaveAsTiff_Path_Encoder.tiff"); - - using (var image = new Image(10, 10)) - { - image.SaveAsTiff(file, new TiffEncoder()); - } - - using (Image.Load(file, out IImageFormat mime)) - { - Assert.Equal("image/tiff", mime.DefaultMimeType); - } - } - - [Fact(Skip = "Saving not implemented")] - public async Task SaveAsTiffAsync_Path_Encoder() - { - string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions)); - string file = Path.Combine(dir, "SaveAsTiffAsync_Path_Encoder.tiff"); - - using (var image = new Image(10, 10)) - { - await image.SaveAsTiffAsync(file, new TiffEncoder()); - } - - using (Image.Load(file, out IImageFormat mime)) - { - Assert.Equal("image/tiff", mime.DefaultMimeType); - } - } - - [Fact(Skip = "Saving not implemented")] - public void SaveAsTiff_Stream() - { - using var memoryStream = new MemoryStream(); - - using (var image = new Image(10, 10)) - { - image.SaveAsTiff(memoryStream); - } - - memoryStream.Position = 0; - - using (Image.Load(memoryStream, out IImageFormat mime)) - { - Assert.Equal("image/tiff", mime.DefaultMimeType); - } - } - - [Fact(Skip = "Saving not implemented")] - public async Task SaveAsTiffAsync_StreamAsync() - { - using var memoryStream = new MemoryStream(); - - using (var image = new Image(10, 10)) - { - await image.SaveAsTiffAsync(memoryStream); - } - - memoryStream.Position = 0; - - using (Image.Load(memoryStream, out IImageFormat mime)) - { - Assert.Equal("image/tiff", mime.DefaultMimeType); - } - } - - [Fact(Skip = "Saving not implemented")] - public void SaveAsTiff_Stream_Encoder() - { - using var memoryStream = new MemoryStream(); - - using (var image = new Image(10, 10)) - { - image.SaveAsTiff(memoryStream, new TiffEncoder()); - } - - memoryStream.Position = 0; - - using (Image.Load(memoryStream, out IImageFormat mime)) - { - Assert.Equal("image/tiff", mime.DefaultMimeType); - } - } - - [Fact(Skip = "Saving not implemented")] - public async Task SaveAsTiffAsync_Stream_Encoder() - { - using var memoryStream = new MemoryStream(); - - using (var image = new Image(10, 10)) - { - await image.SaveAsTiffAsync(memoryStream, new TiffEncoder()); - } - - memoryStream.Position = 0; - - using (Image.Load(memoryStream, out IImageFormat mime)) - { - Assert.Equal("image/tiff", mime.DefaultMimeType); - } - } - } -} diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs index 91166bf2d..e279ea562 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs @@ -13,12 +13,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { private static readonly MemoryAllocator MemoryAllocator = new ArrayPoolMemoryAllocator(); private static readonly Configuration Configuration = Configuration.Default; + private static readonly ITiffEncoderOptions Options = new TiffEncoder(); [Fact] public void WriteHeader_WritesValidHeader() { - var stream = new MemoryStream(); - var encoder = new TiffEncoderCore(null, MemoryAllocator); + using var stream = new MemoryStream(); + var encoder = new TiffEncoderCore(Options, MemoryAllocator); using (var writer = new TiffWriter(stream, MemoryAllocator, Configuration)) { @@ -31,8 +32,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Fact] public void WriteHeader_ReturnsFirstIfdMarker() { - var stream = new MemoryStream(); - var encoder = new TiffEncoderCore(null, MemoryAllocator); + using var stream = new MemoryStream(); + var encoder = new TiffEncoderCore(Options, MemoryAllocator); using (var writer = new TiffWriter(stream, MemoryAllocator, Configuration)) { diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs new file mode 100644 index 000000000..4d9ea661d --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System.IO; + +using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Formats.Tiff +{ + public class TiffEncoderTests + { + public static readonly TheoryData TiffBitsPerPixelFiles = + new TheoryData + { + { TestImages.Tiff.GrayscaleUncompressed, TiffBitsPerPixel.Pixel8 }, + { TestImages.Tiff.RgbUncompressed, TiffBitsPerPixel.Pixel24 }, + }; + + [Theory] + [MemberData(nameof(TiffBitsPerPixelFiles))] + public void TiffEncoder_PreserveBitsPerPixel(string imagePath, TiffBitsPerPixel expectedBitsPerPixel) + { + // arrange + var tiffEncoder = new TiffEncoder(); + var testFile = TestFile.Create(imagePath); + using Image input = testFile.CreateRgba32Image(); + using var memStream = new MemoryStream(); + + // act + input.Save(memStream, tiffEncoder); + + // assert + memStream.Position = 0; + using var output = Image.Load(memStream); + TiffMetadata meta = output.Metadata.GetTiffMetadata(); + Assert.Equal(expectedBitsPerPixel, meta.BitsPerPixel); + } + + [Theory] + [WithFile(TestImages.Tiff.RgbUncompressed, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeRgb_Works(TestImageProvider provider, TiffBitsPerPixel bitsPerPixel = TiffBitsPerPixel.Pixel24) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, bitsPerPixel); + + [Theory] + [WithFile(TestImages.Tiff.RgbUncompressed, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeGray_Works(TestImageProvider provider, TiffBitsPerPixel bitsPerPixel = TiffBitsPerPixel.Pixel8) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, bitsPerPixel); + + private static void TestTiffEncoderCore( + TestImageProvider provider, + TiffBitsPerPixel bitsPerPixel, + TiffEncoderCompression compression = TiffEncoderCompression.None, + bool useExactComparer = true, + float compareTolerance = 0.01f) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(); + var encoder = new TiffEncoder { BitsPerPixel = bitsPerPixel, Compression = compression }; + + using var memStream = new MemoryStream(); + image.Save(memStream, encoder); + memStream.Position = 0; + using var encodedImage = (Image)Image.Load(memStream); + TiffTestUtils.CompareWithReferenceDecoder(provider, encodedImage, useExactComparer, compareTolerance); + } + } +} diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffTestUtils.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffTestUtils.cs new file mode 100644 index 000000000..edf348330 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffTestUtils.cs @@ -0,0 +1,63 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; +using ImageMagick; + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Formats.Tiff +{ + public class TiffTestUtils + { + public static void CompareWithReferenceDecoder( + TestImageProvider provider, + Image image, + bool useExactComparer = true, + float compareTolerance = 0.01f) + where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel + { + string path = TestImageProvider.GetFilePathOrNull(provider); + if (path == null) + { + throw new InvalidOperationException("CompareToOriginal() works only with file providers!"); + } + + var testFile = TestFile.Create(path); + Image magickImage = DecodeWithMagick(Configuration.Default, new FileInfo(testFile.FullPath)); + if (useExactComparer) + { + ImageComparer.Exact.VerifySimilarity(magickImage, image); + } + else + { + ImageComparer.Tolerant(compareTolerance).VerifySimilarity(magickImage, image); + } + } + + public static Image DecodeWithMagick(Configuration configuration, FileInfo fileInfo) + where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel + { + using var magickImage = new MagickImage(fileInfo); + magickImage.AutoOrient(); + var result = new Image(configuration, magickImage.Width, magickImage.Height); + + Assert.True(result.TryGetSinglePixelSpan(out Span resultPixels)); + + using IUnsafePixelCollection pixels = magickImage.GetPixelsUnsafe(); + byte[] data = pixels.ToByteArray(PixelMapping.RGBA); + + PixelOperations.Instance.FromRgba32Bytes( + configuration, + data, + resultPixels, + resultPixels.Length); + + return result; + } + } +} From c7511c7bd6085bd466f3d1091eb2b864a23f2abe Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 26 Nov 2020 18:18:01 +0100 Subject: [PATCH 090/275] Add support for writing palette color tiff's --- .../Formats/Tiff/ITiffEncoderOptions.cs | 19 ++++- src/ImageSharp/Formats/Tiff/TiffEncoder.cs | 12 +++ .../Formats/Tiff/TiffEncoderCore.cs | 82 +++++++++++++++++-- .../Formats/Tiff/Utils/TiffWriter.cs | 69 +++++++++++++++- .../Formats/Tiff/TiffEncoderTests.cs | 6 ++ 5 files changed, 176 insertions(+), 12 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs index dcb8a5c44..5b849e131 100644 --- a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs +++ b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs @@ -1,16 +1,33 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.Processing.Processors.Quantization; + namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Encapsulates the options for the . /// - public interface ITiffEncoderOptions + internal interface ITiffEncoderOptions { /// /// Gets the number of bits per pixel. /// TiffBitsPerPixel? BitsPerPixel { get; } + + /// + /// Gets the compression type to use. + /// + TiffEncoderCompression Compression { get; } + + /// + /// Gets a value indicating whether to use a color palette. + /// + bool UseColorPalette { get; } + + /// + /// Gets the quantizer for creating a color palette image. + /// + IQuantizer Quantizer { get; } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs index a83e0606c..409d16a68 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors.Quantization; namespace SixLabors.ImageSharp.Formats.Tiff { @@ -24,6 +25,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// public TiffEncoderCompression Compression { get; set; } = TiffEncoderCompression.None; + /// + /// Gets or sets a value indicating whether to use a color palette. + /// + public bool UseColorPalette { get; set; } + + /// + /// Gets or sets the quantizer for color images with a palette. + /// Defaults to OctreeQuantizer. + /// + public IQuantizer Quantizer { get; set; } + /// public void Encode(Image image, Stream stream) where TPixel : unmanaged, IPixel diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index ffccce520..f2aec7a61 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -11,6 +11,8 @@ using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Quantization; namespace SixLabors.ImageSharp.Formats.Tiff { @@ -39,6 +41,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// private TiffBitsPerPixel? bitsPerPixel; + /// + /// The quantizer for creating color palette image. + /// + private readonly IQuantizer quantizer; + /// /// Initializes a new instance of the class. /// @@ -47,17 +54,25 @@ namespace SixLabors.ImageSharp.Formats.Tiff public TiffEncoderCore(ITiffEncoderOptions options, MemoryAllocator memoryAllocator) { this.memoryAllocator = memoryAllocator; + this.CompressionType = options.Compression; + this.UseColorMap = options.UseColorPalette; + this.quantizer = options.Quantizer ?? KnownQuantizers.Octree; } /// - /// Gets the photometric interpretation implementation to use when encoding the image. + /// Gets or sets the photometric interpretation implementation to use when encoding the image. /// private TiffPhotometricInterpretation PhotometricInterpretation { get; set; } /// - /// Gets or sets the compression implementation to use when encoding the image. + /// Gets the compression implementation to use when encoding the image. + /// + private TiffEncoderCompression CompressionType { get; } + + /// + /// Gets a value indicating whether to use a colormap. /// - public TiffCompressionType CompressionType { get; set; } + private bool UseColorMap { get; } /// /// Encodes the image to the specified stream from the . @@ -76,7 +91,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff ImageMetadata metadata = image.Metadata; TiffMetadata tiffMetadata = metadata.GetTiffMetadata(); this.bitsPerPixel ??= tiffMetadata.BitsPerPixel; - this.PhotometricInterpretation = this.bitsPerPixel == TiffBitsPerPixel.Pixel8 ? TiffPhotometricInterpretation.BlackIsZero : TiffPhotometricInterpretation.Rgb; + this.SetPhotometricInterpretation(); short bpp = (short)this.bitsPerPixel; int bytesPerLine = 4 * (((image.Width * bpp) + 31) / 32); @@ -120,14 +135,32 @@ namespace SixLabors.ImageSharp.Formats.Tiff public long WriteImage(TiffWriter writer, Image image, long ifdOffset) where TPixel : unmanaged, IPixel { + IExifValue colorMap = null; var ifdEntries = new List(); // Write the image bytes to the steam. var imageDataStart = (uint)writer.Position; - int imageDataBytes = this.PhotometricInterpretation == TiffPhotometricInterpretation.Rgb ? writer.WriteRgbImageData(image, this.padding) : writer.WriteGrayImageData(image, this.padding); + int imageDataBytes; + if (this.PhotometricInterpretation == TiffPhotometricInterpretation.Rgb) + { + imageDataBytes = writer.WriteRgbImageData(image, this.padding); + } + else if (this.PhotometricInterpretation == TiffPhotometricInterpretation.PaletteColor) + { + imageDataBytes = writer.WritePalettedRgbImageData(image, this.quantizer, this.padding, out colorMap); + } + else + { + imageDataBytes = writer.WriteGrayImageData(image, this.padding); + } // Write info's about the image to the stream. this.AddImageFormat(image, ifdEntries, imageDataStart, imageDataBytes); + if (this.PhotometricInterpretation == TiffPhotometricInterpretation.PaletteColor) + { + ifdEntries.Add(colorMap); + } + writer.WriteMarker(ifdOffset, (uint)writer.Position); long nextIfdMarker = this.WriteIfd(writer, ifdEntries); @@ -200,7 +233,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The start of the image data in the stream. /// The image data in bytes to write. public void AddImageFormat(Image image, List ifdEntries, uint imageDataStartOffset, int imageDataBytes) - where TPixel : unmanaged, IPixel + where TPixel : unmanaged, IPixel { var width = new ExifLong(ExifTagValue.ImageWidth) { @@ -212,7 +245,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff Value = (uint)image.Height }; - ushort[] bitsPerSampleValue = this.PhotometricInterpretation == TiffPhotometricInterpretation.Rgb ? new ushort[] { 8, 8, 8 } : new ushort[] { 8 }; + ushort[] bitsPerSampleValue = this.GetBitsPerSampleValue(); var bitPerSample = new ExifShortArray(ExifTagValue.BitsPerSample) { Value = bitsPerSampleValue @@ -265,8 +298,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff var resolutionUnit = new ExifShort(ExifTagValue.ResolutionUnit) { - // TODO: what to use here as default? - Value = 0 + Value = 3 // 3 is centimeter. }; var software = new ExifString(ExifTagValue.Software) @@ -288,5 +320,37 @@ namespace SixLabors.ImageSharp.Formats.Tiff ifdEntries.Add(resolutionUnit); ifdEntries.Add(software); } + + private void SetPhotometricInterpretation() + { + if (this.UseColorMap) + { + this.PhotometricInterpretation = TiffPhotometricInterpretation.PaletteColor; + return; + } + + if (this.bitsPerPixel == TiffBitsPerPixel.Pixel8) + { + this.PhotometricInterpretation = TiffPhotometricInterpretation.BlackIsZero; + } + else + { + this.PhotometricInterpretation = TiffPhotometricInterpretation.Rgb; + } + } + + private ushort[] GetBitsPerSampleValue() + { + switch (this.PhotometricInterpretation) + { + case TiffPhotometricInterpretation.PaletteColor: + case TiffPhotometricInterpretation.Rgb: + return new ushort[] { 8, 8, 8 }; + case TiffPhotometricInterpretation.BlackIsZero: + return new ushort[] { 8 }; + default: + return new ushort[] { 8, 8, 8 }; + } + } } } diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs index 7578c8213..16c9b87e3 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -2,10 +2,14 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Collections.Generic; using System.IO; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors.Quantization; namespace SixLabors.ImageSharp.Formats.Tiff { @@ -135,14 +139,73 @@ namespace SixLabors.ImageSharp.Formats.Tiff { using IManagedByteBuffer row = this.AllocateRow(image.Width, 3, padding); Span rowSpan = row.GetSpan(); + int bytesWritten = 0; for (int y = 0; y < image.Height; y++) { Span pixelRow = image.GetPixelRowSpan(y); PixelOperations.Instance.ToRgb24Bytes(this.configuration, pixelRow, rowSpan, pixelRow.Length); this.output.Write(rowSpan); + bytesWritten += rowSpan.Length; } - return image.Width * image.Height * 3; + return bytesWritten; + } + + public int WritePalettedRgbImageData(Image image, IQuantizer quantizer, int padding, out IExifValue colorMap) + where TPixel : unmanaged, IPixel + { + using IManagedByteBuffer row = this.AllocateRow(image.Width, 1, padding); + using IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(this.configuration); + using IndexedImageFrame quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(image.Frames.RootFrame, image.Bounds()); + using IMemoryOwner colorPaletteBuffer = this.memoryAllocator.AllocateManagedByteBuffer(256 * 2 * 3); + Span colorPalette = colorPaletteBuffer.GetSpan(); + + ReadOnlySpan quantizedColors = quantized.Palette.Span; + int quantizedColorBytes = quantizedColors.Length * 3 * 2; + + // In the ColorMap, black is represented by 0,0,0 and white is represented by 65535, 65535, 65535. + Span quantizedColorRgb48 = MemoryMarshal.Cast(colorPalette.Slice(0, quantizedColorBytes)); + PixelOperations.Instance.ToRgb48(this.configuration, quantizedColors, quantizedColorRgb48); + + // In a TIFF ColorMap, all the Red values come first, followed by the Green values, + // then the Blue values. Convert the quantized palette to this format. + var palette = new ushort[quantizedColorBytes]; + int paletteIdx = 0; + for (int i = 0; i < quantizedColors.Length; i++) + { + palette[paletteIdx++] = quantizedColorRgb48[i].R; + } + + for (int i = 0; i < quantizedColors.Length; i++) + { + palette[paletteIdx++] = quantizedColorRgb48[i].G; + } + + for (int i = 0; i < quantizedColors.Length; i++) + { + palette[paletteIdx++] = quantizedColorRgb48[i].B; + } + + colorMap = new ExifShortArray(ExifTagValue.ColorMap) + { + Value = palette + }; + + int bytesWritten = 0; + for (int y = 0; y < image.Height; y++) + { + ReadOnlySpan pixelSpan = quantized.GetPixelRowSpan(y); + this.output.Write(pixelSpan); + bytesWritten += pixelSpan.Length; + + for (int i = 0; i < padding; i++) + { + this.output.WriteByte(0); + bytesWritten++; + } + } + + return bytesWritten; } /// @@ -157,14 +220,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff { using IManagedByteBuffer row = this.AllocateRow(image.Width, 1, padding); Span rowSpan = row.GetSpan(); + int bytesWritten = 0; for (int y = 0; y < image.Height; y++) { Span pixelRow = image.GetPixelRowSpan(y); PixelOperations.Instance.ToL8Bytes(this.configuration, pixelRow, rowSpan, pixelRow.Length); this.output.Write(rowSpan); + bytesWritten += rowSpan.Length; } - return image.Width * image.Height; + return bytesWritten; } private IManagedByteBuffer AllocateRow(int width, int bytesPerPixel, int padding) => this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, bytesPerPixel, padding); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 4d9ea661d..16a2ab012 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -49,9 +49,15 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public void TiffEncoder_EncodeGray_Works(TestImageProvider provider, TiffBitsPerPixel bitsPerPixel = TiffBitsPerPixel.Pixel8) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, bitsPerPixel); + [Theory] + [WithFile(TestImages.Tiff.RgbUncompressed, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeColorPalette_Works(TestImageProvider provider, TiffBitsPerPixel bitsPerPixel = TiffBitsPerPixel.Pixel8, bool useColorPalette = true) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, bitsPerPixel, useColorPalette); + private static void TestTiffEncoderCore( TestImageProvider provider, TiffBitsPerPixel bitsPerPixel, + bool useColorPalette = false, TiffEncoderCompression compression = TiffEncoderCompression.None, bool useExactComparer = true, float compareTolerance = 0.01f) From a0e406bec88ab93b71c8e55507bebbebe49a304c Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 26 Nov 2020 20:11:42 +0100 Subject: [PATCH 091/275] Add tiff encoding mode enum --- .../Formats/Tiff/ITiffEncoderOptions.cs | 9 +-- src/ImageSharp/Formats/Tiff/TiffEncoder.cs | 9 +-- .../Formats/Tiff/TiffEncoderCore.cs | 68 ++++++++++++------- .../Formats/Tiff/TiffEncodingMode.cs | 31 +++++++++ .../Formats/Tiff/Utils/TiffWriter.cs | 13 +++- .../Formats/Tiff/TiffEncoderTests.cs | 27 ++++---- .../Formats/Tiff/TiffTestUtils.cs | 63 ----------------- 7 files changed, 105 insertions(+), 115 deletions(-) create mode 100644 src/ImageSharp/Formats/Tiff/TiffEncodingMode.cs delete mode 100644 tests/ImageSharp.Tests/Formats/Tiff/TiffTestUtils.cs diff --git a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs index 5b849e131..97f3d46b0 100644 --- a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs +++ b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs @@ -10,20 +10,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// internal interface ITiffEncoderOptions { - /// - /// Gets the number of bits per pixel. - /// - TiffBitsPerPixel? BitsPerPixel { get; } - /// /// Gets the compression type to use. /// TiffEncoderCompression Compression { get; } /// - /// Gets a value indicating whether to use a color palette. + /// Gets the encoding mode to use. RGB, RGB with color palette or gray. /// - bool UseColorPalette { get; } + TiffEncodingMode Mode { get; } /// /// Gets the quantizer for creating a color palette image. diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs index 409d16a68..3ab17b6c3 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs @@ -15,20 +15,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// public class TiffEncoder : IImageEncoder, ITiffEncoderOptions { - /// - /// Gets or sets the number of bits per pixel. 8 bit implies a grayscale image. - /// - public TiffBitsPerPixel? BitsPerPixel { get; set; } - /// /// Gets or sets a value indicating which compression to use. /// public TiffEncoderCompression Compression { get; set; } = TiffEncoderCompression.None; /// - /// Gets or sets a value indicating whether to use a color palette. + /// Gets or sets the encoding mode to use. RGB, RGB with a color palette or gray. /// - public bool UseColorPalette { get; set; } + public TiffEncodingMode Mode { get; set; } /// /// Gets or sets the quantizer for color images with a palette. diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index f2aec7a61..c493d34a4 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff { this.memoryAllocator = memoryAllocator; this.CompressionType = options.Compression; - this.UseColorMap = options.UseColorPalette; + this.Mode = options.Mode; this.quantizer = options.Quantizer ?? KnownQuantizers.Octree; } @@ -70,9 +70,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff private TiffEncoderCompression CompressionType { get; } /// - /// Gets a value indicating whether to use a colormap. + /// Gets or sets the encoding mode to use. RGB, RGB with color palette or gray. /// - private bool UseColorMap { get; } + private TiffEncodingMode Mode { get; set; } /// /// Encodes the image to the specified stream from the . @@ -91,6 +91,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff ImageMetadata metadata = image.Metadata; TiffMetadata tiffMetadata = metadata.GetTiffMetadata(); this.bitsPerPixel ??= tiffMetadata.BitsPerPixel; + if (this.Mode == TiffEncodingMode.Default) + { + // Preserve input bits per pixel, if no mode was specified. + if (this.bitsPerPixel == TiffBitsPerPixel.Pixel8) + { + this.Mode = TiffEncodingMode.Gray; + } + } + this.SetPhotometricInterpretation(); short bpp = (short)this.bitsPerPixel; @@ -141,17 +150,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff // Write the image bytes to the steam. var imageDataStart = (uint)writer.Position; int imageDataBytes; - if (this.PhotometricInterpretation == TiffPhotometricInterpretation.Rgb) - { - imageDataBytes = writer.WriteRgbImageData(image, this.padding); - } - else if (this.PhotometricInterpretation == TiffPhotometricInterpretation.PaletteColor) - { - imageDataBytes = writer.WritePalettedRgbImageData(image, this.quantizer, this.padding, out colorMap); - } - else + switch (this.PhotometricInterpretation) { - imageDataBytes = writer.WriteGrayImageData(image, this.padding); + case TiffPhotometricInterpretation.PaletteColor: + imageDataBytes = writer.WritePalettedRgbImageData(image, this.quantizer, this.padding, out colorMap); + break; + case TiffPhotometricInterpretation.BlackIsZero: + imageDataBytes = writer.WriteGrayImageData(image, this.padding); + break; + default: + imageDataBytes = writer.WriteRgbImageData(image, this.padding); + break; } // Write info's about the image to the stream. @@ -270,7 +279,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff var samplesPerPixel = new ExifLong(ExifTagValue.SamplesPerPixel) { - Value = 3 + Value = this.GetSamplesPerPixel() }; var rowsPerStrip = new ExifLong(ExifTagValue.RowsPerStrip) @@ -323,19 +332,31 @@ namespace SixLabors.ImageSharp.Formats.Tiff private void SetPhotometricInterpretation() { - if (this.UseColorMap) + switch (this.Mode) { - this.PhotometricInterpretation = TiffPhotometricInterpretation.PaletteColor; - return; + case TiffEncodingMode.ColorPalette: + this.PhotometricInterpretation = TiffPhotometricInterpretation.PaletteColor; + break; + case TiffEncodingMode.Gray: + this.PhotometricInterpretation = TiffPhotometricInterpretation.BlackIsZero; + break; + default: + this.PhotometricInterpretation = TiffPhotometricInterpretation.Rgb; + break; } + } - if (this.bitsPerPixel == TiffBitsPerPixel.Pixel8) - { - this.PhotometricInterpretation = TiffPhotometricInterpretation.BlackIsZero; - } - else + private uint GetSamplesPerPixel() + { + switch (this.PhotometricInterpretation) { - this.PhotometricInterpretation = TiffPhotometricInterpretation.Rgb; + case TiffPhotometricInterpretation.PaletteColor: + case TiffPhotometricInterpretation.Rgb: + return 3; + case TiffPhotometricInterpretation.BlackIsZero: + return 1; + default: + return 3; } } @@ -344,6 +365,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff switch (this.PhotometricInterpretation) { case TiffPhotometricInterpretation.PaletteColor: + return new ushort[] { 8 }; case TiffPhotometricInterpretation.Rgb: return new ushort[] { 8, 8, 8 }; case TiffPhotometricInterpretation.BlackIsZero: diff --git a/src/ImageSharp/Formats/Tiff/TiffEncodingMode.cs b/src/ImageSharp/Formats/Tiff/TiffEncodingMode.cs new file mode 100644 index 000000000..195353ec4 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/TiffEncodingMode.cs @@ -0,0 +1,31 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Tiff +{ + /// + /// Enum for the different tiff encoding options. + /// + public enum TiffEncodingMode + { + /// + /// No mode specified. Will preserve the bits per pixels of the input image. + /// + Default = 0, + + /// + /// The image will be encoded as RGB, 8 bit per channel. + /// + Rgb = 1, + + /// + /// The image will be encoded as RGB with a color palette. + /// + ColorPalette = 2, + + /// + /// The image will be encoded as 8 bit gray. + /// + Gray = 3, + } +} diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs index 16c9b87e3..e99682bc0 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -133,7 +133,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The pixel data. /// The image to write to the stream. /// The padding bytes for each row. - /// The number of bytes written + /// The number of bytes written. public int WriteRgbImageData(Image image, int padding) where TPixel : unmanaged, IPixel { @@ -151,6 +151,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff return bytesWritten; } + /// + /// Writes the image data as indices into a color map to the stream. + /// + /// The pixel data. + /// The image to write to the stream. + /// The quantizer to use. + /// The padding bytes for each row. + /// The color map. + /// The number of bytes written. public int WritePalettedRgbImageData(Image image, IQuantizer quantizer, int padding, out IExifValue colorMap) where TPixel : unmanaged, IPixel { @@ -214,7 +223,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The pixel data. /// The image to write to the stream. /// The padding bytes for each row. - /// The number of bytes written + /// The number of bytes written. public int WriteGrayImageData(Image image, int padding) where TPixel : unmanaged, IPixel { diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 16a2ab012..27ca717e6 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -5,13 +5,17 @@ using System.IO; using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff { + [Trait("Category", "Tiff")] public class TiffEncoderTests { + private static TiffDecoder referenceDecoder = new TiffDecoder(); + public static readonly TheoryData TiffBitsPerPixelFiles = new TheoryData { @@ -41,36 +45,33 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Theory] [WithFile(TestImages.Tiff.RgbUncompressed, PixelTypes.Rgba32)] - public void TiffEncoder_EncodeRgb_Works(TestImageProvider provider, TiffBitsPerPixel bitsPerPixel = TiffBitsPerPixel.Pixel24) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, bitsPerPixel); + public void TiffEncoder_EncodeRgb_Works(TestImageProvider provider, TiffBitsPerPixel bitsPerPixel = TiffBitsPerPixel.Pixel24, TiffEncodingMode mode = TiffEncodingMode.Rgb) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, bitsPerPixel, mode); [Theory] [WithFile(TestImages.Tiff.RgbUncompressed, PixelTypes.Rgba32)] - public void TiffEncoder_EncodeGray_Works(TestImageProvider provider, TiffBitsPerPixel bitsPerPixel = TiffBitsPerPixel.Pixel8) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, bitsPerPixel); + public void TiffEncoder_EncodeGray_Works(TestImageProvider provider, TiffBitsPerPixel bitsPerPixel = TiffBitsPerPixel.Pixel8, TiffEncodingMode mode = TiffEncodingMode.Gray) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, bitsPerPixel, mode); [Theory] [WithFile(TestImages.Tiff.RgbUncompressed, PixelTypes.Rgba32)] - public void TiffEncoder_EncodeColorPalette_Works(TestImageProvider provider, TiffBitsPerPixel bitsPerPixel = TiffBitsPerPixel.Pixel8, bool useColorPalette = true) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, bitsPerPixel, useColorPalette); + public void TiffEncoder_EncodeColorPalette_Works(TestImageProvider provider, TiffBitsPerPixel bitsPerPixel = TiffBitsPerPixel.Pixel24, TiffEncodingMode mode = TiffEncodingMode.ColorPalette) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, bitsPerPixel, mode); private static void TestTiffEncoderCore( TestImageProvider provider, TiffBitsPerPixel bitsPerPixel, - bool useColorPalette = false, + TiffEncodingMode mode, TiffEncoderCompression compression = TiffEncoderCompression.None, bool useExactComparer = true, float compareTolerance = 0.01f) where TPixel : unmanaged, IPixel { using Image image = provider.GetImage(); - var encoder = new TiffEncoder { BitsPerPixel = bitsPerPixel, Compression = compression }; + var encoder = new TiffEncoder { Mode = mode, Compression = compression }; - using var memStream = new MemoryStream(); - image.Save(memStream, encoder); - memStream.Position = 0; - using var encodedImage = (Image)Image.Load(memStream); - TiffTestUtils.CompareWithReferenceDecoder(provider, encodedImage, useExactComparer, compareTolerance); + // Does DebugSave & load reference CompareToReferenceInput(): + image.VerifyEncoder(provider, "tiff", bitsPerPixel, encoder, useExactComparer ? ImageComparer.Exact : ImageComparer.Tolerant(compareTolerance), referenceDecoder: referenceDecoder); } } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffTestUtils.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffTestUtils.cs deleted file mode 100644 index edf348330..000000000 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffTestUtils.cs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.IO; -using ImageMagick; - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; - -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Formats.Tiff -{ - public class TiffTestUtils - { - public static void CompareWithReferenceDecoder( - TestImageProvider provider, - Image image, - bool useExactComparer = true, - float compareTolerance = 0.01f) - where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel - { - string path = TestImageProvider.GetFilePathOrNull(provider); - if (path == null) - { - throw new InvalidOperationException("CompareToOriginal() works only with file providers!"); - } - - var testFile = TestFile.Create(path); - Image magickImage = DecodeWithMagick(Configuration.Default, new FileInfo(testFile.FullPath)); - if (useExactComparer) - { - ImageComparer.Exact.VerifySimilarity(magickImage, image); - } - else - { - ImageComparer.Tolerant(compareTolerance).VerifySimilarity(magickImage, image); - } - } - - public static Image DecodeWithMagick(Configuration configuration, FileInfo fileInfo) - where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel - { - using var magickImage = new MagickImage(fileInfo); - magickImage.AutoOrient(); - var result = new Image(configuration, magickImage.Width, magickImage.Height); - - Assert.True(result.TryGetSinglePixelSpan(out Span resultPixels)); - - using IUnsafePixelCollection pixels = magickImage.GetPixelsUnsafe(); - byte[] data = pixels.ToByteArray(PixelMapping.RGBA); - - PixelOperations.Instance.FromRgba32Bytes( - configuration, - data, - resultPixels, - resultPixels.Length); - - return result; - } - } -} From 4e5be469603ba5d2b5a31f861b4802d9d92d826d Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 27 Nov 2020 12:31:09 +0100 Subject: [PATCH 092/275] Add support for encoding deflate compressed tiff's --- .../Formats/Tiff/TiffEncoderCompression.cs | 5 ++++ .../Formats/Tiff/TiffEncoderCore.cs | 23 +++++++++++---- .../Formats/Tiff/Utils/TiffWriter.cs | 29 ++++++++++++++++++- .../Formats/Tiff/TiffEncoderTests.cs | 24 +++++++++------ 4 files changed, 65 insertions(+), 16 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs index 334262dbf..536cd2c2d 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs @@ -12,5 +12,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// No compression is used. /// None, + + /// + /// Use zlib compression. + /// + Deflate } } diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index c493d34a4..99b299d04 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -150,16 +150,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff // Write the image bytes to the steam. var imageDataStart = (uint)writer.Position; int imageDataBytes; - switch (this.PhotometricInterpretation) + switch (this.Mode) { - case TiffPhotometricInterpretation.PaletteColor: + case TiffEncodingMode.ColorPalette: imageDataBytes = writer.WritePalettedRgbImageData(image, this.quantizer, this.padding, out colorMap); break; - case TiffPhotometricInterpretation.BlackIsZero: + case TiffEncodingMode.Gray: imageDataBytes = writer.WriteGrayImageData(image, this.padding); break; default: - imageDataBytes = writer.WriteRgbImageData(image, this.padding); + imageDataBytes = writer.WriteRgbImageData(image, this.padding, this.CompressionType); break; } @@ -260,10 +260,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff Value = bitsPerSampleValue }; + ushort compressionType = this.GetCompressionType(); var compression = new ExifShort(ExifTagValue.Compression) { - // TODO: for the start, no compression is used. - Value = (ushort)TiffCompression.None + Value = compressionType }; var photometricInterpretation = new ExifShort(ExifTagValue.PhotometricInterpretation) @@ -374,5 +374,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff return new ushort[] { 8, 8, 8 }; } } + + private ushort GetCompressionType() + { + if (this.CompressionType == TiffEncoderCompression.Deflate && + this.PhotometricInterpretation == TiffPhotometricInterpretation.Rgb) + { + return (ushort)TiffCompression.Deflate; + } + + return (ushort)TiffCompression.None; + } } } diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs index e99682bc0..8ef57f4b2 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -6,6 +6,9 @@ using System.Buffers; using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; + +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.Formats.Png.Zlib; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; @@ -133,13 +136,37 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The pixel data. /// The image to write to the stream. /// The padding bytes for each row. + /// The compression to use. /// The number of bytes written. - public int WriteRgbImageData(Image image, int padding) + public int WriteRgbImageData(Image image, int padding, TiffEncoderCompression compression) where TPixel : unmanaged, IPixel { using IManagedByteBuffer row = this.AllocateRow(image.Width, 3, padding); Span rowSpan = row.GetSpan(); int bytesWritten = 0; + if (compression == TiffEncoderCompression.Deflate) + { + using var memoryStream = new MemoryStream(); + + // TODO: move zlib compression from png to a common place? + using var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, PngCompressionLevel.Level6); // TODO: make compression level configurable + + for (int y = 0; y < image.Height; y++) + { + Span pixelRow = image.GetPixelRowSpan(y); + PixelOperations.Instance.ToRgb24Bytes(this.configuration, pixelRow, rowSpan, pixelRow.Length); + deflateStream.Write(rowSpan); + } + + deflateStream.Flush(); + + byte[] buffer = memoryStream.ToArray(); + this.output.Write(buffer); + bytesWritten += buffer.Length; + return bytesWritten; + } + + // No compression. for (int y = 0; y < image.Height; y++) { Span pixelRow = image.GetPixelRowSpan(y); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 27ca717e6..cef8cecc7 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -3,10 +3,11 @@ using System.IO; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; - +using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff @@ -14,7 +15,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Trait("Category", "Tiff")] public class TiffEncoderTests { - private static TiffDecoder referenceDecoder = new TiffDecoder(); + private static readonly IImageDecoder ReferenceDecoder = new MagickReferenceDecoder(); public static readonly TheoryData TiffBitsPerPixelFiles = new TheoryData @@ -45,18 +46,23 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Theory] [WithFile(TestImages.Tiff.RgbUncompressed, PixelTypes.Rgba32)] - public void TiffEncoder_EncodeRgb_Works(TestImageProvider provider, TiffBitsPerPixel bitsPerPixel = TiffBitsPerPixel.Pixel24, TiffEncodingMode mode = TiffEncodingMode.Rgb) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, bitsPerPixel, mode); + public void TiffEncoder_EncodeRgb_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.Rgb); [Theory] [WithFile(TestImages.Tiff.RgbUncompressed, PixelTypes.Rgba32)] - public void TiffEncoder_EncodeGray_Works(TestImageProvider provider, TiffBitsPerPixel bitsPerPixel = TiffBitsPerPixel.Pixel8, TiffEncodingMode mode = TiffEncodingMode.Gray) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, bitsPerPixel, mode); + public void TiffEncoder_EncodeRgb_WithDeflateCompression_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.Rgb, TiffEncoderCompression.Deflate); + + [Theory] + [WithFile(TestImages.Tiff.GrayscaleUncompressed, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeGray_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.Gray); [Theory] [WithFile(TestImages.Tiff.RgbUncompressed, PixelTypes.Rgba32)] - public void TiffEncoder_EncodeColorPalette_Works(TestImageProvider provider, TiffBitsPerPixel bitsPerPixel = TiffBitsPerPixel.Pixel24, TiffEncodingMode mode = TiffEncodingMode.ColorPalette) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, bitsPerPixel, mode); + public void TiffEncoder_EncodeColorPalette_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette); private static void TestTiffEncoderCore( TestImageProvider provider, @@ -71,7 +77,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff var encoder = new TiffEncoder { Mode = mode, Compression = compression }; // Does DebugSave & load reference CompareToReferenceInput(): - image.VerifyEncoder(provider, "tiff", bitsPerPixel, encoder, useExactComparer ? ImageComparer.Exact : ImageComparer.Tolerant(compareTolerance), referenceDecoder: referenceDecoder); + image.VerifyEncoder(provider, "tiff", bitsPerPixel, encoder, useExactComparer ? ImageComparer.Exact : ImageComparer.Tolerant(compareTolerance), referenceDecoder: ReferenceDecoder); } } } From edcdc08efd740cfaa59d8fd78f70c966044b53a9 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 27 Nov 2020 13:56:22 +0100 Subject: [PATCH 093/275] Fix issue with encoding paletted tiff when quantized palette is smaller than 256 --- src/ImageSharp/Formats/Tiff/README.md | 4 +- .../Formats/Tiff/TiffEncoderCore.cs | 6 +- .../Formats/Tiff/Utils/TiffWriter.cs | 68 ++++++++++++------- .../Formats/Tiff/TiffEncoderTests.cs | 2 +- 4 files changed, 51 insertions(+), 29 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/README.md b/src/ImageSharp/Formats/Tiff/README.md index 0343d0a46..2bacf7c51 100644 --- a/src/ImageSharp/Formats/Tiff/README.md +++ b/src/ImageSharp/Formats/Tiff/README.md @@ -48,7 +48,7 @@ |Lzw | | Y | Based on ImageSharp GIF LZW implementation - this code could be modified to be (i) shared, or (ii) optimised for each case | |Old Jpeg | | | We should not even try to support this | |Jpeg (Technote 2) | | | | -|Deflate (Technote 2) | | Y | | +|Deflate (Technote 2) | (Y) | Y | Based on PNG Deflate. Deflate encoding only for RGB now, should we allow this for the gray and palette too? | |Old Deflate (Technote 2) | | Y | | ### Photometric Interpretation Formats @@ -59,7 +59,7 @@ |BlackIsZero | | Y | General + 1/4/8-bit optimised implementations | |Rgb (Chunky) | | Y | General + Rgb888 optimised implementation | |Rgb (Planar) | Y | Y | General implementation only | -|PaletteColor | | Y | General implementation only | +|PaletteColor | Y | Y | General implementation only | |TransparencyMask | | | | |Separated (TIFF Extension) | | | | |YCbCr (TIFF Extension) | | | | diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 99b299d04..6dee09932 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -153,10 +153,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff switch (this.Mode) { case TiffEncodingMode.ColorPalette: - imageDataBytes = writer.WritePalettedRgbImageData(image, this.quantizer, this.padding, out colorMap); + imageDataBytes = writer.WritePalettedRgb(image, this.quantizer, this.padding, out colorMap); break; case TiffEncodingMode.Gray: - imageDataBytes = writer.WriteGrayImageData(image, this.padding); + imageDataBytes = writer.WriteGray(image, this.padding); break; default: imageDataBytes = writer.WriteRgbImageData(image, this.padding, this.CompressionType); @@ -350,9 +350,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff { switch (this.PhotometricInterpretation) { - case TiffPhotometricInterpretation.PaletteColor: case TiffPhotometricInterpretation.Rgb: return 3; + case TiffPhotometricInterpretation.PaletteColor: case TiffPhotometricInterpretation.BlackIsZero: return 1; default: diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs index 8ef57f4b2..c95378356 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -143,30 +143,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff { using IManagedByteBuffer row = this.AllocateRow(image.Width, 3, padding); Span rowSpan = row.GetSpan(); - int bytesWritten = 0; if (compression == TiffEncoderCompression.Deflate) { - using var memoryStream = new MemoryStream(); - - // TODO: move zlib compression from png to a common place? - using var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, PngCompressionLevel.Level6); // TODO: make compression level configurable - - for (int y = 0; y < image.Height; y++) - { - Span pixelRow = image.GetPixelRowSpan(y); - PixelOperations.Instance.ToRgb24Bytes(this.configuration, pixelRow, rowSpan, pixelRow.Length); - deflateStream.Write(rowSpan); - } - - deflateStream.Flush(); - - byte[] buffer = memoryStream.ToArray(); - this.output.Write(buffer); - bytesWritten += buffer.Length; - return bytesWritten; + return this.WriteDeflateCompressedRgb(image, rowSpan); } // No compression. + int bytesWritten = 0; for (int y = 0; y < image.Height; y++) { Span pixelRow = image.GetPixelRowSpan(y); @@ -178,6 +161,37 @@ namespace SixLabors.ImageSharp.Formats.Tiff return bytesWritten; } + /// + /// Writes the image data as RGB compressed with zlib to the stream. + /// + /// The pixel data. + /// The image to write to the stream. + /// A Span for a pixel row. + /// The number of bytes written. + private int WriteDeflateCompressedRgb(Image image, Span rowSpan) + where TPixel : unmanaged, IPixel + { + int bytesWritten = 0; + using var memoryStream = new MemoryStream(); + + // TODO: move zlib compression from png to a common place? + using var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, PngCompressionLevel.Level6); // TODO: make compression level configurable + + for (int y = 0; y < image.Height; y++) + { + Span pixelRow = image.GetPixelRowSpan(y); + PixelOperations.Instance.ToRgb24Bytes(this.configuration, pixelRow, rowSpan, pixelRow.Length); + deflateStream.Write(rowSpan); + } + + deflateStream.Flush(); + + byte[] buffer = memoryStream.ToArray(); + this.output.Write(buffer); + bytesWritten += buffer.Length; + return bytesWritten; + } + /// /// Writes the image data as indices into a color map to the stream. /// @@ -187,13 +201,14 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The padding bytes for each row. /// The color map. /// The number of bytes written. - public int WritePalettedRgbImageData(Image image, IQuantizer quantizer, int padding, out IExifValue colorMap) + public int WritePalettedRgb(Image image, IQuantizer quantizer, int padding, out IExifValue colorMap) where TPixel : unmanaged, IPixel { + int colorPaletteSize = 256 * 3 * 2; using IManagedByteBuffer row = this.AllocateRow(image.Width, 1, padding); using IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(this.configuration); using IndexedImageFrame quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(image.Frames.RootFrame, image.Bounds()); - using IMemoryOwner colorPaletteBuffer = this.memoryAllocator.AllocateManagedByteBuffer(256 * 2 * 3); + using IMemoryOwner colorPaletteBuffer = this.memoryAllocator.AllocateManagedByteBuffer(colorPaletteSize); Span colorPalette = colorPaletteBuffer.GetSpan(); ReadOnlySpan quantizedColors = quantized.Palette.Span; @@ -203,20 +218,27 @@ namespace SixLabors.ImageSharp.Formats.Tiff Span quantizedColorRgb48 = MemoryMarshal.Cast(colorPalette.Slice(0, quantizedColorBytes)); PixelOperations.Instance.ToRgb48(this.configuration, quantizedColors, quantizedColorRgb48); + // It can happen that the quantized colors are less than the expected 256. + var diffToMaxColors = 256 - quantizedColors.Length; + // In a TIFF ColorMap, all the Red values come first, followed by the Green values, // then the Blue values. Convert the quantized palette to this format. - var palette = new ushort[quantizedColorBytes]; + var palette = new ushort[colorPaletteSize]; int paletteIdx = 0; for (int i = 0; i < quantizedColors.Length; i++) { palette[paletteIdx++] = quantizedColorRgb48[i].R; } + paletteIdx += diffToMaxColors; + for (int i = 0; i < quantizedColors.Length; i++) { palette[paletteIdx++] = quantizedColorRgb48[i].G; } + paletteIdx += diffToMaxColors; + for (int i = 0; i < quantizedColors.Length; i++) { palette[paletteIdx++] = quantizedColorRgb48[i].B; @@ -251,7 +273,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The image to write to the stream. /// The padding bytes for each row. /// The number of bytes written. - public int WriteGrayImageData(Image image, int padding) + public int WriteGray(Image image, int padding) where TPixel : unmanaged, IPixel { using IManagedByteBuffer row = this.AllocateRow(image.Width, 1, padding); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index cef8cecc7..9d24132a4 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -60,7 +60,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.Gray); [Theory] - [WithFile(TestImages.Tiff.RgbUncompressed, PixelTypes.Rgba32)] + [WithFile(TestImages.Tiff.Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette); From d0d57ca61a96ef82bbf05ecceb6bf531609d49c3 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 27 Nov 2020 14:30:32 +0100 Subject: [PATCH 094/275] Allow deflate compression for gray tiff --- src/ImageSharp/Formats/Tiff/README.md | 2 +- .../Formats/Tiff/TiffEncoderCore.cs | 10 ++++- .../Formats/Tiff/Utils/TiffWriter.cs | 40 ++++++++++++++++++- .../Formats/Tiff/TiffEncoderTests.cs | 7 ++++ 4 files changed, 55 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/README.md b/src/ImageSharp/Formats/Tiff/README.md index 2bacf7c51..4d7bbbeb7 100644 --- a/src/ImageSharp/Formats/Tiff/README.md +++ b/src/ImageSharp/Formats/Tiff/README.md @@ -48,7 +48,7 @@ |Lzw | | Y | Based on ImageSharp GIF LZW implementation - this code could be modified to be (i) shared, or (ii) optimised for each case | |Old Jpeg | | | We should not even try to support this | |Jpeg (Technote 2) | | | | -|Deflate (Technote 2) | (Y) | Y | Based on PNG Deflate. Deflate encoding only for RGB now, should we allow this for the gray and palette too? | +|Deflate (Technote 2) | (Y) | Y | Based on PNG Deflate. Deflate encoding only for RGB now gray, should we allow this for palette too? | |Old Deflate (Technote 2) | | Y | | ### Photometric Interpretation Formats diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 6dee09932..f070eab31 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -156,7 +156,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff imageDataBytes = writer.WritePalettedRgb(image, this.quantizer, this.padding, out colorMap); break; case TiffEncodingMode.Gray: - imageDataBytes = writer.WriteGray(image, this.padding); + imageDataBytes = writer.WriteGray(image, this.padding, this.CompressionType); break; default: imageDataBytes = writer.WriteRgbImageData(image, this.padding, this.CompressionType); @@ -378,7 +378,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff private ushort GetCompressionType() { if (this.CompressionType == TiffEncoderCompression.Deflate && - this.PhotometricInterpretation == TiffPhotometricInterpretation.Rgb) + this.Mode == TiffEncodingMode.Rgb) + { + return (ushort)TiffCompression.Deflate; + } + + if (this.CompressionType == TiffEncoderCompression.Deflate && + this.Mode == TiffEncodingMode.Gray) { return (ushort)TiffCompression.Deflate; } diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs index c95378356..028d53ab8 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -272,12 +272,19 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The pixel data. /// The image to write to the stream. /// The padding bytes for each row. + /// The compression to use. /// The number of bytes written. - public int WriteGray(Image image, int padding) + public int WriteGray(Image image, int padding, TiffEncoderCompression compression) where TPixel : unmanaged, IPixel { using IManagedByteBuffer row = this.AllocateRow(image.Width, 1, padding); Span rowSpan = row.GetSpan(); + + if (compression == TiffEncoderCompression.Deflate) + { + return this.WriteGrayDeflateCompressed(image, rowSpan); + } + int bytesWritten = 0; for (int y = 0; y < image.Height; y++) { @@ -290,6 +297,37 @@ namespace SixLabors.ImageSharp.Formats.Tiff return bytesWritten; } + /// + /// Writes the image data as 8 bit gray with deflate compression to the stream. + /// + /// The image to write to the stream. + /// A span of a pixel row. + /// The number of bytes written. + private int WriteGrayDeflateCompressed(Image image, Span rowSpan) + where TPixel : unmanaged, IPixel + { + int bytesWritten = 0; + using var memoryStream = new MemoryStream(); + + // TODO: move zlib compression from png to a common place? + using var deflateStream = + new ZlibDeflateStream(this.memoryAllocator, memoryStream, PngCompressionLevel.Level6); // TODO: make compression level configurable + + for (int y = 0; y < image.Height; y++) + { + Span pixelRow = image.GetPixelRowSpan(y); + PixelOperations.Instance.ToL8Bytes(this.configuration, pixelRow, rowSpan, pixelRow.Length); + deflateStream.Write(rowSpan); + } + + deflateStream.Flush(); + + byte[] buffer = memoryStream.ToArray(); + this.output.Write(buffer); + bytesWritten += buffer.Length; + return bytesWritten; + } + private IManagedByteBuffer AllocateRow(int width, int bytesPerPixel, int padding) => this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, bytesPerPixel, padding); /// diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 9d24132a4..458acd34c 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -8,6 +8,7 @@ using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; + using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff @@ -59,6 +60,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public void TiffEncoder_EncodeGray_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.Gray); + [Theory] + [WithFile(TestImages.Tiff.GrayscaleUncompressed, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeGray_WithDeflateCompression_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.Gray, TiffEncoderCompression.Deflate); + + // TODO: this test fails, but the output looks correct. I thinks its due to the fact that a quantizer is used to create the palette. [Theory] [WithFile(TestImages.Tiff.Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_Works(TestImageProvider provider) From add82fc4b34e04781db10ff31ff96c65d9201e99 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 27 Nov 2020 16:33:44 +0100 Subject: [PATCH 095/275] Allow deflate compression for paletted tiff's --- src/ImageSharp/Formats/Tiff/README.md | 6 +-- .../Formats/Tiff/TiffEncoderCore.cs | 8 +++- .../Formats/Tiff/Utils/TiffWriter.cs | 37 ++++++++++++++++--- .../Formats/Tiff/TiffEncoderTests.cs | 6 +++ 4 files changed, 48 insertions(+), 9 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/README.md b/src/ImageSharp/Formats/Tiff/README.md index 4d7bbbeb7..1a81f2286 100644 --- a/src/ImageSharp/Formats/Tiff/README.md +++ b/src/ImageSharp/Formats/Tiff/README.md @@ -40,7 +40,7 @@ | |Encoder|Decoder|Comments | |---------------------------|:-----:|:-----:|--------------------------| -|None | Y | Y | encoding only rgb so far | +|None | Y | Y | | |Ccitt1D | | Y | | |PackBits | | Y | | |CcittGroup3Fax | | Y | | @@ -48,7 +48,7 @@ |Lzw | | Y | Based on ImageSharp GIF LZW implementation - this code could be modified to be (i) shared, or (ii) optimised for each case | |Old Jpeg | | | We should not even try to support this | |Jpeg (Technote 2) | | | | -|Deflate (Technote 2) | (Y) | Y | Based on PNG Deflate. Deflate encoding only for RGB now gray, should we allow this for palette too? | +|Deflate (Technote 2) | (Y) | Y | Based on PNG Deflate. | |Old Deflate (Technote 2) | | Y | | ### Photometric Interpretation Formats @@ -75,7 +75,7 @@ |ImageWidth | Y | Y | | |ImageLength | Y | Y | | |BitsPerSample | Y | Y | | -|Compression | | Y | | +|Compression | Y | Y | | |PhotometricInterpretation | Y | Y | | |Thresholding | | | | |CellWidth | | | | diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index f070eab31..7cf12afb5 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -153,7 +153,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff switch (this.Mode) { case TiffEncodingMode.ColorPalette: - imageDataBytes = writer.WritePalettedRgb(image, this.quantizer, this.padding, out colorMap); + imageDataBytes = writer.WritePalettedRgb(image, this.quantizer, this.padding, this.CompressionType, out colorMap); break; case TiffEncodingMode.Gray: imageDataBytes = writer.WriteGray(image, this.padding, this.CompressionType); @@ -389,6 +389,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff return (ushort)TiffCompression.Deflate; } + if (this.CompressionType == TiffEncoderCompression.Deflate && + this.Mode == TiffEncodingMode.ColorPalette) + { + return (ushort)TiffCompression.Deflate; + } + return (ushort)TiffCompression.None; } } diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs index 028d53ab8..2c6e03d9c 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -201,10 +201,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The padding bytes for each row. /// The color map. /// The number of bytes written. - public int WritePalettedRgb(Image image, IQuantizer quantizer, int padding, out IExifValue colorMap) + public int WritePalettedRgb(Image image, IQuantizer quantizer, int padding, TiffEncoderCompression compression, out IExifValue colorMap) where TPixel : unmanaged, IPixel { int colorPaletteSize = 256 * 3 * 2; + using var memoryStream = new MemoryStream(); + using var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, PngCompressionLevel.Level6); // TODO: make compression level configurable using IManagedByteBuffer row = this.AllocateRow(image.Width, 1, padding); using IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(this.configuration); using IndexedImageFrame quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(image.Frames.RootFrame, image.Bounds()); @@ -253,16 +255,41 @@ namespace SixLabors.ImageSharp.Formats.Tiff for (int y = 0; y < image.Height; y++) { ReadOnlySpan pixelSpan = quantized.GetPixelRowSpan(y); - this.output.Write(pixelSpan); - bytesWritten += pixelSpan.Length; + + if (compression == TiffEncoderCompression.Deflate) + { + deflateStream.Write(pixelSpan); + } + else + { + // No compression. + this.output.Write(pixelSpan); + bytesWritten += pixelSpan.Length; + } for (int i = 0; i < padding; i++) { - this.output.WriteByte(0); - bytesWritten++; + if (compression == TiffEncoderCompression.Deflate) + { + deflateStream.WriteByte(0); + } + else + { + // no compression. + this.output.WriteByte(0); + bytesWritten++; + } } } + if (compression == TiffEncoderCompression.Deflate) + { + deflateStream.Flush(); + byte[] buffer = memoryStream.ToArray(); + this.output.Write(buffer); + bytesWritten += buffer.Length; + } + return bytesWritten; } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 458acd34c..9c043b4ee 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -71,6 +71,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public void TiffEncoder_EncodeColorPalette_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette); + // TODO: this test fails, but the output looks correct. I thinks its due to the fact that a quantizer is used to create the palette. + [Theory] + [WithFile(TestImages.Tiff.Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeColorPalette_WithDeflateCompression_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, TiffEncoderCompression.Deflate); + private static void TestTiffEncoderCore( TestImageProvider provider, TiffBitsPerPixel bitsPerPixel, From 46fa87f0420a4e78c86b83eb0ddc46c5df538635 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 27 Nov 2020 16:38:36 +0100 Subject: [PATCH 096/275] Fix broken tiff spec pdf --- src/ImageSharp/Formats/Tiff/README.md | 2 +- src/ImageSharp/Formats/Tiff/TIFF-v6.pdf | Bin 367248 -> 398722 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Formats/Tiff/README.md b/src/ImageSharp/Formats/Tiff/README.md index 1a81f2286..7202cac46 100644 --- a/src/ImageSharp/Formats/Tiff/README.md +++ b/src/ImageSharp/Formats/Tiff/README.md @@ -48,7 +48,7 @@ |Lzw | | Y | Based on ImageSharp GIF LZW implementation - this code could be modified to be (i) shared, or (ii) optimised for each case | |Old Jpeg | | | We should not even try to support this | |Jpeg (Technote 2) | | | | -|Deflate (Technote 2) | (Y) | Y | Based on PNG Deflate. | +|Deflate (Technote 2) | Y | Y | Based on PNG Deflate. | |Old Deflate (Technote 2) | | Y | | ### Photometric Interpretation Formats diff --git a/src/ImageSharp/Formats/Tiff/TIFF-v6.pdf b/src/ImageSharp/Formats/Tiff/TIFF-v6.pdf index 3e03f2338accfeb5027004f222b1c7e6e6b06679..99117063a05a0ab52595403a733dd536fd998930 100644 GIT binary patch literal 398722 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

%<+cqh>0+|Th&`98 zGpM`97Vq=|S=uA~ibU+8GBbWHioGm2joVLsmIXi`CjrZ&VXH$l3xgzPt&6x1BOtPr zjrax+3>zC|>Yx)*&vVuG@eX|*A3Ra~YT6=-^iiH273XTl4j^23&Mz34!{3;d1t#U) zv@a5=>*)rkxhs7=ZxBb{zO)~%>)zuCp|61HP)2_7Ce3;=V6u^x)*BL>a%Fw=isDz* zT^C`5#1Q#D$W!$7DAQ|AX3kTBwXdVz=F?rA(49ZPyRM5xxSyqYEx7;0R`Dwnj_F=$ zO!&(8RM5txi2^802B|0D@WFbc za`Kz;7h^cL&OW_~i1V;nAu`eVu!(Fu#Xh4z6CeRDheqg74q%#kxc;P8MLv$1gWZY; zg+_Vp%97P_gG_hsJ7?ClrD5uFPs(yZV7;_F5_*A7NB;a4-d+Wb=i7za)Pb^C%I8Rzk^M8&R<438r>%%(hfzGGbB=!c(Au54feVN<%|JE?G8 z1nTD~LszG`B9SkTa?vzDSRDS)llAaV8R6;25NU87|<$A8&~v7Bt@SiuMUV$3&aNZ&f*e z0vSU-j(c^2Z3m)EU>W&sV>r^ho<8{vNTs9wj zP~5Z>cz|Cw4%}G_NhXv|iFbV5;!x-_Ok;QK~<3Ecme!c1cg{qBSjPql7^S8p4 zUhKnxpZ#xZDE>4j^6PK%C+dTl`NK@}UqFtwR`^V#7YhSc8>PkT#Z=Dkz?v$k1mWbW z7cKNR=B>*2Pa{$5I>gF~#>`zXhYC)KwdPmFO0S1s+izZre9boPWpb(1ue)+ZLM{-l za!`+7{RAuTtYd4x>8UIZ^oN+)JOb3m2Ew{OZ)$fo98%%orJSz8Gx*KmF65e8@zEUY zP|I|LX{aA4>etv_n*!V?z|5qC_I~2 zWA*#Qu&P9#f+T8THMnUDCzPa%+=j;ubgmA!hcc$wDZdkYwi!c;nu5GLSF-dZ`0F)- z8`WCE&>6aYwL7>OTU7626#%M58HIA2S@qPN= z&s!v{)x8Cn2*Z`z@E79*;0If=u4TzYTW_K~-Mg+h?VYcSrfmniXe6xMpG+Gkyjybf&Y2h9rlFlR{z zy93-nFDaBOpr?gGTf8guJiaEa#c@d(m^^nm#GtF{j7n079(*~PUK=Jv zs+6W=UiZ$jsqr*k71<3w6#LQHpvq5ax4AYft5C^K3tx>`OU9s*p(`xGum%&|EQ~%qn@p~hL2kQWNriOryWqi5QSfDKXCy5#mkvt1-$Q-=>iN=oKqf< z3b4{N1o_vJTLGl&icR8qIGJb$Q79nZIkxfj1S<&3ZMS$IpuNwSh@3`9I8byZ*WUYz$`N5?! z=`vB=t?8PB*bvm)xrCMnvL{Zv#JEvu4C}Poo&Hv)nhdV`{m{6*@O2LUzx^c5m8*5 z=_VSqeEhyQ`+>L@8@qU);MtjjMRe>ZBttdAh>3@JJ!90TWI`74C4HhGVDsK0`R0`X zr*R_HmD+}KA@73H77Hfy()~8P`n2u! zQ6<+PhY>VzXT^<-h6+tiSO8wDB4`2rf8%(#vE zRTGYO+^7S%qF=DLm#|0mc!>$#!HM&b z#BoRN=sUb9@m(eZdQ~r#u+|>E>5t=s!Ll|4h{iM~=g!LKDk|CZqQy2?BAQI?!5VB| z%mFRFC~&3q<42fMz;g681O~GxC-;)ZLWkbQZoMk-4J~@1J%k|_(Ox&SZAfSdB~Mc8 zF9}k|TxeTg%^}xOr9L%F(WjiNntvjL+=X#ur#kI6BEA8EoeJQB}af%Iovd8EP&F!1r@_uRLH=&&dL7-EwVW#aI5PzGktHNN;_E7K)Jp1Ou%VzUnxaH?@C=lBBrA7mr@xNLHHv)wNt!l*8J4*+o$|w%1U|4L z7KZ!+g2ybbGKWYNzr)1~t5Dcvo+Hjea3m%aUr1;H~!hEcJ2GuMaVWLqWLMMf3=cZb4qsDcBq zdi*(ID=&)YKoF)42IwhS9cJ+>*l_> zBDSJrRf#M{-Ma>~rEhqkvj{4f`|X`y524n1Msn>U1o%+v>(doA$fRi15z%j;T5~fT zCgCCB-ue>USk{}~zRxVED$I~?)B)sv6C}In89RO|${6ITiHz+Cf@uE#nES@?N*8p? z*tTukwpFohClys}+qRulP_b>>X2s6b^y$-m8gu*JdHUX&|9d~*{`Q}>-i7z=eUPJ` z2oXo0YHIz zKk@bn}1 ziTx4Ynq!({g@Bg($^N<$hk~-VQ^eU-*3g=IZo>QqJ5r2?P=SUEBwCzesSPYe2Ir*0 zEVP9X`9_*-7$!~p&K;|8&?+mD^2op!H_uJR5`uFCpB2BGZ13H^J_&2=V|kzpCZ}-i z7bdwkR#EpPz)Newy}kVb;epvp#(#mpV1gg3Ti|aI8vablIvd1VAOl z_Y`UB7hy)lw{5P`MM;WwPouF$BZnq9Du)L5>XeW{%qTuKC%zi0b<60B+%*pbpW5wP zM8tZy1WDWgZpBPfiGX+Sf^?PY=f`5as^5V5QqD3Fh_l5k*e83OL;-Gi3f*d(JU_f5 z-d33!C>6?weH1 z)Ppyj1#a_fx;?}LMYm0Zh66`iyAq%YcWNMlAudxWbxh z-ADv72%h^UDZnvDVa@3KmP3GNqdOn&gn`NZ!%(xAmQ=>d!&Bi5c1nX8HFgR`tV3ca z>26-z-5M>a<*osEs#^!eQyeb*0p@(^dv<=_68&sQ>dMJagL<_C)<-Dl__wi3O_NYyuPb9J$dK9bJ;a4$NTCF!psEKDtqcK5wZI>F8rx z@UzwW+N3p5IL7C{KBdBb`+|2>lqy4?`h!cplZ+)BL80yED&ARl4Q~~G^NK21dK1`B zuOU_@ThEX-IrK*GTQ#2@q={Mz3qgk60Y>Wmlh9A}y)*D??HDl5Q+kFwkLNzZ)`t71jRbG6L);I&?&Pgrxpsc~t`YgJP3%93MFXI)d17925>vEptL0 z16C=4Hm+CvU=B}$N>24d1P|ib{C&v+`Y$)l{6T43~`GPV3dg2z!!#=CwZ6##fF) zgy{JdY8)BzkbJ9Yu{;c?lj-Kc^R>Vg%pFDqA7-2sABpgqS_;idhm^1h7-pZ8xxGPNf48bbV}hcG@8bO5oZZDJ(jrU%fL;mvdmMbzF192nwxD)&C|zo zZUu)MgRF2cpg_MGw6zLk{*-#DqoC5PhY?l_FaD#V z={^!%Th(n&>KkT2Q99{Fkn1W$3Sp?V6Gv%~n9BGS`|U66rNO|Nx;pw6-HJ>4f)ys)>OtSASsWlh!BNuj`M ziQccgMQ{<798Y@roP&mFnKHvnGE4I8YhjF2lG2$S7D2Ux&Cp8HX=v}<6V!WrfO8+( zjqQTpH29BG2v7sW6$Hxlze6q`HTI>)C3@VLj>L@$o_4g`s2Ns$^ssC()_wvqr329{ zuzvxNSBGe>gz%sXLp7~I3=RukeDr%UtT1?dZD#Wdj`@j3c)91D-3w2`WWE8=ot@Jj zcsC5m(eUKokZdZ zyCabbzoMEy(*$8OtTiOk)io+=Bov#kyefDB>vf_LD-6f%)S&>4aw^n=h3)(S)7eNuLMhz*ocMEk>tewo zqux)EhNjp}$@3hqr<;+$De<#l{&g8ld0UV-+YCPa+>*q!zQR-q;K({22nMW3tuR^~ zzynT%xSmi~#BFqkgiPCspf;Hys*x|cLL1R|&gGu4odtCoBIDeCagjlT*ztq*9+`m$ zC3}zn0iL~Kcmi2mrm03m19QO}b)&qxXhFdsf%Gh_=Ocp=6ZUh>#7s@ll3L@XSi1ub z;yAa`Ck*{5tz~7AO_`P4&NL?J?@(N`$6IwhgJj?1MET-R>dW6SVA#1du`xzy)Nj(m zB)YxJc_=ogE$r~pIpme*>mGfQ?1}bml%hu->+mCWQ1vC!WRk2A($MY$L#ZE#iG4uf zGkLGfX&IE$od#e`&~3ZzFIM_xUa_cjv5?8NRx0Ay?T3NEBo7d1%Lfo>?v2-VQ(y=3fw@($84T0FKIpfM-3gkJJm>(R0w6O)+k=A$A`@U1HGcnJZi&P2>zNR7& zPRH3D(bGgWoRBH{MdjYam%%QEHtNx&eHdz2skd&v>!+th`w5^@PzWpPw-4I1<8;5D zC&}G0Umn29qHj}twQg#q>?;$zxinBuEym}bx*%2hwI5=h_XiC-y(OjC)!P>*dpX`H zIN#dDGn2!@4gMIW7ABlresmJ#`ke4hMsN}mO{4FTW7MQ zH{{8cWLqGXa-0V_|L0(nrqv%*WZX3hj;eNxIvN$ns6DR0cRyLqMg4)% zN`I}_s;6VexvxBIJLB=%f|JdaZ8J@6Mpk|-e`jw+Jy!!dK{PFjlkd83pieh!kodMZ zn~&QFkgH|IF3J2d6uGW@xHujk>8^B{!yBAWvsEzMS6o9EJ`J>EH6;)X58RC9t&wfV zHp$i6(ccq{VBU}faS5g|mVD?d422(ZKTtde2g)VKO}5S1*m#?u23|HUq7Bp)Zy5AY z!*cAK1xP1f!0gmT)Qf(mN`_&ymLI*@$qpm*1-&G~R^sz8Ruo&x60&}|tUwC_Tx>a! zLKLIspUH%Ex`a{s@_q&jPpKJVG(En?m;MeJwzgPW#PZ<0u zPw?l1&HrYq^*7On|1PTapU5@-Qu>*HDgD3w2Y;{h{~`47d$@p!;a4Q{Unu>Kop^zL zesPCxD`@b|7k#UsSOBh>WD!ds@N{$|LBh028P3QZ#QS!3*&mh-2L6a1a(8oA3Q&^= z6E_VrSUuwHz>XOYe10ZU)jye80p6<{IGMTb=v$m%*<+ zP#lk9W9uHs@!R8u+h%?>WBdZVk1kdSKa#HBS<^>xXD2Mmku7#F9gaS9L*WyV3)DGt z)+tm{{Z!r3M=uGIZG<9K$3`HCu8qHJ@2X=(JbhT5xnuk1=L12P=M4CpVxs!>LZvsz z4_Qah@}Ue8E|YLc@&!vp|Y%_1;25g&m9 zMN<(LQI?o0jj@d&%JU4tmZL>H6n|kB&J5`4OQ+<{N_o$*)o~=5Z5X7+I1Y0QdL-EoaV) zx3`YD*6q!*hiSZ17Aft-+EH~dGQf;2BrXA_Zn%H-i!h74g5I9SlZ@hW58~m17y!)6KX4^EBd`!LXIzY>A4~@-e;n8gPg-nPgBFo z4@n)$Hvn%|MfH)0aAo@Ftb#L9RXHD%7|+hjB2Q8YXRhcbJit;H9{%kzwu(&iLn0v*NpAnunHS#~49!@?UOLcmP)x!MFB`?_RQCDwYf!mW4ws_@R8+P>_aGm% zfjM_ZjM_t3A2Y1@nwrLC&ZCg<7h)>`mS}!klebgo^;w*_Mi!yI&q)~U;MM$myF`(W zjNFqCknm;Qg7&WZP@+LXclKizy+D^!B-kOMlylg?&|S8wdO?R zNtoAjtjnOGw(Ci^TFxDD%Qq^QLfy4@QxdIBkUHT3h>`qjNck^u4Kyd*ytc|?TcZPC z-pnp+jtpe`4hlEiXe`d|YWx%C%NB`hw+S4pv(S9yZKJ%4#8&H#$5Zb@ygq`j)bR4r z96EH^r6KmdcER`5PX+fVFBJ`-4B}UkS2FUDI<<&ThhV?7b0Fikb^FN(`#2ipzx3%w zC~5LuX`ISYx(f*C$UC0PD`PhOgc~Yq7E8SOq^1$-j6}k^oNG2PT7h6T?oT*P-nyY_ zKj7;So3L%FCz|FhdBX7^fp3F|cRfq!Of4v&n_@wJJ?dqF9Gt6KLo{r(Hrl&6ey-~l zME+#md;&rm&PiVu)HQt2j7%8H>R%`q6mp z;^~?K6|Ha}Dk+9n0wE5KGMf~%&G^1<2^ro4o!sIS^r|y*-kbF`_9b1xDh(@QEoLZ? zd;%1w8Qw6Tz1ID=2-*SU1e-?{FoW!&Lj*z+X5NJZ=*dFT5OwjBavq|kz}ck|=!L`8 zXE8<+(uyui7Hx!Q3boe*eOXY#GH-3CgGTK6@J>=AJ}nmA%+{46yK=V#l#k4sR2**# zco6Sa3w4_GIW2b8%AONLJd{sJe$d2D1h3_UNc!nO&nzABzvgJV5+U2-_dhi~j|} z_M7(ghj9I$PaytT8~f9})_->qh2w7$CI7>O?eBQmpI-Ox#4$$pUrWmW1urYuJPN$? z-mPCn$)BJZ6VKxWSDzNrry0y1Bzq1w=(HJvpTnU929g&`awebuu|1YlitCx`1V+Qm z1UCx^LLcv@QON6-iP>;F`O}>hbdHzh){I5}bU&kj4|UrzhxgKSy$EQdngbZ?MG_*=u5Mxv1g@+kwPS?1>+3+FWwxc}01%ra}Ve#7gRPpfe~O_Nd?cFbBFR8}M3tez zAH`L%ysi>W+JUIJb62P~p7G)<>_H4wD}>pYi2<~wR(c0d67$F93|6) zW9(s{c=U2$3Yq$xRrEdkUSnA{v{a)U4yBI@+Y30Ay*)u71{j;)lq@uPN~92wbd^By ztDG7oe}r?rqxa*;QMO!CznM{SQ?|rz$U;O)>9~q^vs({nzp8SS=4CtNONCNzu2WGz zN50S;AYQ~sR@JX4?XU?Wx-2{DFg!WFou%Rp>c=l1jdeF}Dny#Je=vR@Y@91W{-8qb z1e|U!K7$S zO(Py<4FuN@_29mIC^z`n#^}mA{V2r(04k#Jo>5ZT9gQC^hkgG}LhHVCGC`R+K?CW| z!`Nwp=_1ow6tS$(Y)jPQw6#-}o-q}VXi~QVZFes{1u(ZREq$SLz=74C!I?q0ec72{ z_1XRMH6re{g)y_)t8B%)kTg}r&ZSfJdNr!x0CN9}$9nvikA|ya=hQ=hi>B-tyX8(U zi-;RY?>6U8F{E7h`DWRV(|2({diIngo@mr!5R@3`Oa5b(!kwi|BG+VRcSZPs>_8Lu zgzF-AdRap`kso8BS?_kLqk`D233Lr0sl$WY7PycSQL?)!4u#~GsRb8PjYN$`Es8;e z{%X!2sBK+l6|!a-wc zSlnj1`kuP!V(yOj$;%{u8nnd|YfE6~S{xt>7ngfa# zIlhpDwmcMzgYK7`IRch_u7v14nS24;r@(SD6vUvkbnCavtam14!`7@-@8FG})~u`- z`uWvS0B9{?%Z0~_?AF&1X=viiHxZLMpCKc=Z~ErMWFebcS|J1^z`u+oP~3u-B6&xa z6IXKs-W3a8_)rbRkpuFKnP5m8kDFWAZIu@>bP{Q^E=o4y=Ve}wc-o#&Gca3ub{Glc z3FVyI(K~-Jyu{t3pA!-EM80E1IJ`C~oDi-aMVdeUJO(8maYVLlRD_rmBBbcj4MK8S zatFu$Gb8)WD@BMl=Vp@TuonXrhBA`)VH!{s_W^>ENqls+p`SDWIGZgn(;m*D@q3F* z#gbrMML3Jso;a)w37*tEi0;P%S^~ABT0WVX;D7@1({Au;5X2^i z8UHXvTkuQayRdaQRPf|tE{in3f5khc4LSh-N$d;4y$ot`1BiaIF+kd;0tNtE1N^Bi zJh>p}{WDwTbZwP@P8dG{Y<6B+XdrbISH4YZ!kVm%_T>$vTMHZ;MYah=j()bCG+&=C z7`MP2ou$ytd|wP+8qS^}@uId!yvh!T9<8~7z(|$g>T+DQfJ}^*vweJaL6OTU0%+Rj zPncId!P4@nH$A9IU?&}B=e@nFz3cj!OK|KN*pcAri>)v1(k$u|pDLv*Um5N%I+8Fy z?$s&ZWIo?~!K?KQMy@upE!csZO$mw|BmfY}sMGm@?!U@{cy(`ggT4l6vU(XD?DyN-LW_#1SsG6iP7(-65~BayqjCvBg>i z-y9SPq_|0cc$7(19>F0brh0%z@W$JB2cxf3;%r9{4`RHGBngRo)I~l!k37u{tzDDr ze1ec~g$d1^%K4kgan!|mQ1Ua_!?Q5R()dpmmZuH;%DN-FlK3DJr0KpBNeKt?Dm=JySsi4UHyv^jT zARuK`lT=RL44ye!97wW+Q%(sb;13?GN}0{{IKECh+7JusB9)H-Z@U#e6Ie0Yk` zEG~+UxH+*pEgYOvL+{)OOFi$H8?LV-kU@2R8F*tGb)v)4-|B@e61DVn|ldT#9812xZ@(eMP}c5HaYZ)D6X#2)3tsMwJb zE=@FX#AU=oHX0aGmNJ41x|)w0ngym<#D z4tgv!fIhi+;Y{Y!!eLx_n`6k`q#&#Bg;W$DZ50{P>03xo5--YxC`xQau<=G+-RsRFB72a zsFZCnt*VQa)R1qp+8pM4D7++@Pzro!UIb1UpM`*?&LxaX&+yM0S8piHDNC$-y91>t zXSPgttDWlw4YdgsAzAJwp4^_f_-`Ft<9JfI$o4fh0f8|qYhF<4SaHnYc%ztesi5A8 zYA376`)W7!)lo-AoBT=CW>}TXs{5%iD4KAQy)p)IL>oQ9v|~w=XEC};fsr>s|EuPmS_Uyyu6fLZ;Qrv-U8p5NlG?j6mA;-H?m^GI> zzKVPlmHNnZJW#2PW9F@UtD=Q1Z#HHXJdJCLW^~#)+-qr9HJ3mV`@;3%?%574@*rh6 zrFs_3suzTg7`E}~$zqO!i7V~UWTpA7fo;@Zv=YCfKiAOem{pxQvhK1>+j@Y@RMx4` z!AzGqT9`t;x~S@XJ3iC|sq4GemPQOrO{p#J&c170hKqQU?xwQ6@SBe!4MA9B61IeU z4g{1MYzXfD>3sJw6}mlr9uPEG_f=UXIe5BwwDB3vkPdp6&X_{~4N+_OXFuI-=1Qzi zib>7~O#l6q>KKv>?=8*MEXR%A_YGj(lUrf1t%5*u;AL9*j>ffF{CGoR(6gGMt%mM6 zvpT{b$PnNUK~)w5QNkp7dGEi3IxK*@PO*!T+^ivQw65wdK}B@UgK9SCJS^gwm^`cC z=-Hph#ib8mBHC>0SX?C6g4@o)7u8RNK~$SAEgJ)RjtvHOUN{J#z*eMF#+{eJ^Az(2ic8blOlrp zd@j%}VlaTWrxquEHm}+5P7{_?uF;BeZDYA_y3k5+ECiRpkbYQmHTlkK#hNpv-xDpf zzo-Ul*i5;DVv8V9F1Tl5BFufGk<2g^>m#%S7En<(P*nJqh;8Gbr1<=4*#m{T0IGX} zW}G=QMYO2#C9DkV3BB=FD~1_F8KTv!`9hXYHe$)mcXf~tx!$ZQF2#{G9H<^86V=GfbswC6#NC@{&@oCcjW!6EyMYHVg9dR{JT-}n=SJj zH2-^TnLj=BFF*Bvc*_3|;xL?lu#5fz#w@?~c>D`6ejII3UffoCm2BZ9>6qehw3czL zB4it|bT;LluTe9c!JeK#2tq)Xh*Ox85hTKNUAN!lCTW&lSdQC&i_yy)x_>*@8ypq^ z4gR)k5K_o({(i|-l0gQfi>6cXU35Mx)=8&8GnGGK;CAMhc-oBG+Ph*(hww%%SNCg` zMUikp22RPno|-bCewge?#zako0G83S*vf-K1F2ZvL)LsmiGIq2MCws931 z7Rv{)L8rGlx#%6nIrFM2?U(QwnoUEeAn%`q8t+EJ&Ocnvz*C&5XBIJnqNN_) z#-36e!qgn<;ZHnjvUE=wZ%q{yc&eQoSxQui_4hT~1r;2YdbS%eR-Tk}aZ6A9UZ!v7 zR!Z$OH)|1UmJH;xnYh%Bm+B}-(%89ou}i%Q!orm_|N_nSG8|Yb%`7mPRu=l~GCK$T4X#z6i4fKh9~3 zZ>)9#-UXOvGs1>6U_xL5MiYupMD3l%L5lMV>j9x&)2{h~EmdK2R>u7R4iXHSjkDv{a3380tf~ zFj?l$BD}Ctvs_!FChTKD?gT?B2XhmYwgHU3iiawk?gVzb+&)6z87=~MqT95P!59Oc zdeueIi#JqRSgp)82^qA#K~BDftqvH67Y>BrENjRhNev3#~r}wdJ>fK z81@d6LI@YUnm2prLAE~|XUB6yNq2bWETveJ%`PAWnQ!NIC(54-yZ;$f90USWmPFsf zAJl5siof`%I%$m2#m6H0+|8^5M-->@Q`wFFL{n{JTbJ^vB}Q1BM@?Zi%55u_663Dm zfudrGar;*T^LrEp%v-kY1vDoAss@-}TM>z4hPCQIYKh5s6q$|mVK2qjf^n%)hV?&z z(my!z|0luxpFrvF`w{*MrN2w)zd`A5rN4hOl>Uvj|KA0re+-lUA;|D6)xi2I)$m)6 zo~I^y)JiZ!Y< zFG~f7Y1C;&+t{}NjiX{P4cZAY!-vUJSDQ{*&ir}p($EfuHCNADS9En!K*bTYCsN&? z%jq+a@9Rh`jwoBDjjkThP3sK+9W_ppWuJJ^b`>CdwOF4*9p_6>j3aU@AH|hlfU0bn z+$d+1Ci?UE4hJ|OM-Z6ozOWz*>5ruB7X^y9ozg6=72s5-@>|yIu{H}9d6`_v#aaQ- zkwAMdXB(qYJDL_4(Y#XIpD@U@9q*8O7!8a)F#_rP=N&rn-rF$@yeoP z2tLr#%Fc1mDl>r8*a!LT>DQ-II6!;GFsAic+2ec=(L4jb=!5j_uJb;V<6ZDP4}*ej zeJ_D-_0y`9`a)_wYkK|RT<6Kj>1 zSNJO?7WRQ-hkO*0LG!8r$am|JVZII(B2al=Xc;hYRZBa zK)gBSJ*Rz}!U#Bvbtm%Vt0BTZt8Cr_jxq>ov1=*BMy{Bi%zBs04F#^Qo6?x4yX z^`&f@9L;qE1z9EZ_=ePY3C#XvhgElW+qI2xozeA_v}~nY#*Dih#^J!|^iH+C9@tMl z@7K;&x-((eM?CJOJW%d5b_G~g4HO2GLCR?b(u$al-UGhZtK;r-@o;+q*rc`N)N&{x z2dnFbp*#8tGMpn0M1wXSj4`0*3%kLkCcbM9v;5Ii1jsRSYna>tD>YgyYunRJ z%=9`8z1EB%$xZf|A;?Kw5cZfkL%AIKhbu-=kmQ8?Dq8T=1wA+^mC*Zvc)M1-^aYB- z46?en@*9&hxuKKiXk}BQR$`Yvp6+}&UX*9YH_u0I$rxN>SeZ}B&e&D-k}!dfKCxgR z4H5Pxi|IEZ&%h)fTat12AL{Zn>z0NDjSus2g*6oFlk>LU8T5^VVZr;r5Ni)zE{#NvRx-I__B+EcxV9jO)k+7F!cJMiFR5JzBQ9Ye}dUM zU$%QzK_<|3u*|t!#*Vj@M-%pnTZRuJ;s#|za&C{3D@%v2ZB^f<%=^h>pSUZ+4ON*` z#W(QFd#nUNb7?oZcGcSjB|nem4VF#Jo-SUm5NbBpyyvG0&AxS@7q~E;X#&g``2!)@ zd8}cirB`vh@0dWMntEBZx)lz7o}1&VtP_nfrp)Pc8Kee&;jfmZ0=pfnm@z||CbG4} zZ=V*WYmPQb95=^OI7(BMW^9lG<<1E6UsBQKpot7THfUO2g(};eBBYClPfL{eYG`*yktjGv06&{BiV29AMNhFJHf-%f?=tbkv{F_6jk-<@A|8RH zp5&(zDgTSiqoO(g)ZOXpTFcty=&0DR@Sup7bEjLVVN6fOBBHi6Kw8tTM~0cP8-I{_ zLMQ49v9)9c5)F_0B#}w!H!)ND?=h~F&me8&P=xcx`XwG0nROb0`*T2YhG6F+T@QxY zk?}eS9#k+8-;&5;)|AwsL7o!$DHzN6L}a73b9T(|q-$23^wINc?S&*D(5nu=TcnNA zQwG27Im2am^0<$%r5kVD4;45%FoF7>nuaOiObQZsMFi}Ifed=km}AgY*cC}i<$bYo zrbk#*B#9ojEGSR69#$r<8M7RO{S0k@nnG#3RYB29eF8@6pCPn#pWx`q1Amw_cB%0tguaKw@kr^>~RO$Y1zGmSp zr^T?zOx_|z!qPk1+0VT}SsvoolA7IB0D)MfQh#krZO75$U3CyPostxf9CuRjdli_t z)icWNVHNS=T10$l$_=73LABx@EOL&q&tmE%Dvp)nOzs)i&OGO%oHs2TI0)$4L=kg%W#}z+j&oehM9uqPv&wbk+T-L(=h{4-7+kiv@!E(ce z6-LbfUWoZwgK0DWj57eyzQ^cFf!Errl7Xs@{_q0&R%}WmDDWUpwX5dBr)E$-8kxGZ z%lq2aYGB<{x>xx|8QFH63Dd`&SbPh8DKSt}XIU^3Ur33w%fh>}$d`|3T!@e4Kl!m$Uw_yEq|EntJzlCK7O`j3C$f{azqS zVs;x{<**6B?`+DO)`1C$NLvmQOU7IJ*6PFB2x`U)0{Jq5EV>#EvfhjEXQ}<(1t~}O z?aTtUO7l891@+Bf3qdYa?M{g6-^vLQrKr!~CQ~%!rZ1>FN|V<&b$_VR7dD4Bi!yyxIp^Mu(Lqb{#L+ow}VaAx5hprBc8j$`)C2Xxh^(N6;%;gi zMfOU@DGl@W5*tm0^cP7_HDCH;gdRwCc0pAu>L;ZW_EsC0Q;l4z=7^Qr#J(EE^K&}ZK}znM%IYqyx8 z&WBbhj&^) zr&MtAFjXK|QzW@%R$vp;SLx^NJ$-#;VtE$?sm^;i&L}to+bMS@@t&wY!|R97{8+~~ z#rrAE}OgsxcRRPU45|dQv{q~K@b~(hM(^b48X`R-(VfC;R*$QHX5RTBzW>0 zLx-jcT+R-d8t3>+nGbf?n+o`KoRlBnvalk!zeW-m2H+(Ea0F!=KD`N!m)Q{-0t>N? z0fa$FAuhQ+kq=nn{UqT|9lO_~4_OaXTPYCkelm06B9%6-_whrNd6KY%f7yzgEEDvB zn_Ltm0&d?sHutj%p2Hxc(j_41O(S9hUT>ej7*HsGhAs@iOUoMeqzDzY9qh}xkG+@p8k=cR zGZ?<@c*Brg`s9$2%;Dh&CIj(ZA|0>56xxr|ccKTc2j8}+n~jHE{t?qTM{A$DCC00@ zAseiN=4j$q&J<{P)|kyZ3hWi73n0t{837ENs*fv4lHFnatSb)Gr#p37OLYoWkT$6! z-v0H98p(S!+(Z^B?=yhs)%x8hkeE4c&$Ov=sPp5ee-t_x8UA2D|L2tsMuy*{j6Y-p z85w>z5`JSUznKXCZl>}#7RP^=*zsSjbNojt$ZvnkKkymWe_8Hu_ymPD%h$SvaL*~3 zo1T)pP(etTzmopxk#*tH(=I!uLQst`a>12DeLV(b<`OzTaJ_mBqKY6&^>yiSn z3jHQ%)>XUQY(UQaMVaD&K=9)GT@CS@@-~)j1!Yzyg%rTGIH!Sh3vnc>fh5%_zlTpn zE0SFC*nYsS_vN!{J}UQG3qYhT7qbYWYPreS4gmn3qyxM1_=|Zu39sKAd}}rtGNwd1 zMMOUk0g)lPDfEjMf7acS-iv*#ma)iHH9)yD zOny;&#(fgX^_U_QC% zAd+-v8CCkk-|YJtL+y#VdOz6LNS9X8yg#ub+&&Kv^2shP>XgkE*c9V6q;Tzf9mnW5 zyLHg?qm2{_xJZZj{YjAYmh^Gc>~{MMgftU+aDIw~-#mdZI|rX+8DlR*<7#gY3_)!h z0r%I^f(Cwv*r?<#XuHX$Lbg?C`UnL3#AapxL6EQ5Gd-)V)&~IgKxQCuJV?? z3cx;PPdifbBW>l(wDFaFF9Nvs`lhk88`+65!RwQ*-dZ0E9Ctkrr+ay3FTDa>@6Us7 z<%p1R4o=h6K|1xViGvQq{9vFwR&JVn{^$ole>C_meuq7IvMdz+AuZva6Trcvv8kpv zc8aME9TbPuqaFmS#C%5T=jyI4KXQ5&DR@Yg50z&0S?HYjy4jsMYzpnub{LP3O6K?? zrM5N+lk1G>W6gx~_hS}}TJwoJzU)j0jp>PDL1WlB7)D>5)U*Wd>}mAJ=p4^-cYY}w zQ(~t)D^68CiU?vgoZ;`-%~_rvxC-?tYmWd+7QmI7R5F@U{~IGN5?#boQ(*;owZdq z)m|j^Jz9sO3xgX<;-J;0{!jxfXBkxPC6_6VY+_rfQ+DS;iy?mQg(u)8&>h>8(C%8C zFugsU8}-i8SFSyMNt&i=^WD#bDNxBeAf}Yv_z+*~Wd-+=JnK#`Gh$g5sO)q@C z%R#54h`3qw(4%@LrB6Rb#-_K)bgCJNxT|#XLIh zly!n5JZr=r++T_(d-Mxdx=F(HJ{Vmd(T7Z@`KZ=OcxM|((;}@?N-n0xSId~AhC)?s z3sI=HQWAcJa}@}93*lj>1G^8Mxi3u(uBmD<_Bi%nOV!oPe?o^OpVx zFBGFp1N1x<%rszXH^DZcqey3V%c!Xso|st8W&i}C+oDHMGx~QVd$x|BV01H)?CrN| zL7)6^h;qa;dyS7J<8r+*ZWZwSR-SHLB6A#G!?dEE&vrb4kSX%^r997l?bQmkSl2lh zIWIw4)LSP_+IIK$eByMM8utxw8_pfh(?~% z^}%zK){Qrpv0R5=#ZJY<7fsmZ5p@+tzY##_&+J@A&PG%AhO{9XEhEG>iEjf5sSLaU ze+Afz34mFas$P65u@Cok2G*xhMa>}+p@G>@c3nJt+v^@cX)`^aykj67hdipd!EgN4z**%B@f~~ z^3SgYt9Vx|C`BC9@o+|A3GTCnE3)1nu%_~2^RYUuz=wk-P+sA>M38fC zvk6LjwqH=Y=K)<_om%Vzu2t4)BOyLaZ6o&YsPpE((5q z1|u4kv$)u_h`!3I<8~aL9M+l z5YfTa9x}~G!-EhWn>MJd35#S^CYI4O656-il_Nmm&6trMy;y!=f_J<~PWU6D07N1^uqK*SN!t|h z6m-c$heb18S=TYCy6U_Q+va~!_l{Ar^;x!ep0u5L(zb2ewtdpJZS$mU+qTV*=Svs=Ds2zGKwI`!zF~0D3he{du_p^y#iGo*-yd_3+5|t zi~Q9W_}>}=T&AkYXZ>jA9o!4PQQA7;Stwl}Y>CENe|Ijy6a0awSF9W5!_oudc(s1x zBIIHzfY7EiqQzhkl5Q8Bfc)w9!kF8@ot)(@= zmuqv4vRH(2lODSbnde;XZ1*1L!9)5m0EW1d;fTg?v3u8mcYNDgBu zFnfnn$M#S@JkuQ9xWtTC@g-A*R7(upAjrj_lNgCdYjL~GqsIdoQa2v1vX5HUela{M zndeb{Id^XT@snRhHpk(<+maKG&*i9#mNsYn6z}K#BB0k;7tJ8SfRiTQQ4csC>`5Wa zA-F$gM7z!&hvRH1WGn@5f${I39@CIn?&4hszwd^^jK6Y3!iY7{8JPw->HCWK_vny#CReeHZ;&aSAkhEU_R14Gp)dVkxk`G5C)ef`Qm;n#`*j- z5S7FXqZX?<+@<=$#8C|vTirOu z?+7{Z<=W63{n&Zi-(2ZoZwaC?BH^Ebdssf1oBx&fdr@w-ef`hfG!9#JXqh1c;;_Qk z2Z;85;Bz6z4Fgc{2M6K2%X}bDG__mx)eb_?AO&A5OVGS|neAKf^NK;re(iv&?kdI1 zyXSYRG!gY)rCF>c*COI`NVTsiY`E-nvR_qLhVq!Xn zrGYhM--TH<(Vr<3&Q@zN8_cbbo+<_I2Vzp6oBL!mBi7$>_KgZxbdw|z?|P9 z)PL_g(f?^h{ed}uK+nGzbN+JK|69J(|5UX3%hUfo=CJ(pbsrQL1aZ${U-G=$We2^i z$)qC`pU)tlI)bZXcw8=-$;%%j{D31k4auw6sx`}~nMDC!B6;c2JqtuYBAg6k@8dGV z*LKR;<@Iln5G+_tB7xNvhB{_<-*eD;qG!B$^M8`xHAz%%9m54fuwO(DN|EN-Zrmt1 zn2wXZ_Y+H;D5Cp$>9~8V>eG_*dI|Dyl>TlP?&(w=VBrvp@h_yKu*Zp3@)tPz=`!Br zn&Q8W-Ec|M5ch5R#!J{4o7qr$?N-K7c^VW@rirUE))a5C$rXe94S2Hp`OeRW3N>)h zq871NAApQ(1_kHt01aZNR~s<}XFtMXBqzlU=3KOTcDelRX6qqt3Bz)PdDEJv+H=A} z@*Y&#N;G5Co#AeDNCxIfwvn-}G&ajTdS<_j9taAa@X*ZnjuBeK8L~E3{)PgUk21gf zF?+$9jINDdAAR

Gray = 3, + + /// + /// The image will be written as a white and black image. + /// + BiColor = 4, } } diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs index 2c6e03d9c..d018248ed 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -12,6 +12,8 @@ using SixLabors.ImageSharp.Formats.Png.Zlib; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Dithering; using SixLabors.ImageSharp.Processing.Processors.Quantization; namespace SixLabors.ImageSharp.Formats.Tiff @@ -199,6 +201,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The image to write to the stream. /// The quantizer to use. /// The padding bytes for each row. + /// The compression to use. /// The color map. /// The number of bytes written. public int WritePalettedRgb(Image image, IQuantizer quantizer, int padding, TiffEncoderCompression compression, out IExifValue colorMap) @@ -328,7 +331,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// Writes the image data as 8 bit gray with deflate compression to the stream. ///
/// The image to write to the stream. - /// A span of a pixel row. + /// A span of a row of pixels. /// The number of bytes written. private int WriteGrayDeflateCompressed(Image image, Span rowSpan) where TPixel : unmanaged, IPixel @@ -337,8 +340,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff using var memoryStream = new MemoryStream(); // TODO: move zlib compression from png to a common place? - using var deflateStream = - new ZlibDeflateStream(this.memoryAllocator, memoryStream, PngCompressionLevel.Level6); // TODO: make compression level configurable + using var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, PngCompressionLevel.Level6); // TODO: make compression level configurable for (int y = 0; y < image.Height; y++) { @@ -355,6 +357,52 @@ namespace SixLabors.ImageSharp.Formats.Tiff return bytesWritten; } + public int WriteBiColor(Image image) + where TPixel : unmanaged, IPixel + { + int padding = image.Width % 8 == 0 ? 0 : 1; + int bytesPerRow = (image.Width / 8) + padding; + using IMemoryOwner rowRgb = this.memoryAllocator.Allocate(image.Width); + using IManagedByteBuffer row = this.memoryAllocator.AllocateManagedByteBuffer(bytesPerRow, AllocationOptions.Clean); + Span rowSpan = row.GetSpan(); + Span rowRgbSpan = rowRgb.GetSpan(); + + // Convert image to black and white. + using Image imageClone = image.Clone(); + imageClone.Mutate(img => img.BinaryDither(default(ErrorDither))); + + int bytesWritten = 0; + for (int y = 0; y < image.Height; y++) + { + int bitIndex = 0; + int byteIndex = 0; + Span pixelRow = imageClone.GetPixelRowSpan(y); + PixelOperations.Instance.ToRgb24(this.configuration, pixelRow, rowRgbSpan); + for (int x = 0; x < pixelRow.Length; x++) + { + int shift = 7 - bitIndex; + if (rowRgbSpan[x].R == 255) + { + rowSpan[byteIndex] |= (byte)(1 << shift); + } + + bitIndex++; + if (bitIndex == 8) + { + byteIndex++; + bitIndex = 0; + } + } + + this.output.Write(row); + bytesWritten += row.Length(); + + row.Clear(); + } + + return bytesWritten; + } + private IManagedByteBuffer AllocateRow(int width, int bytesPerPixel, int padding) => this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, bytesPerPixel, padding); /// From 262b63f5eb33d9f2363df2556134885476788e40 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 27 Nov 2020 20:19:49 +0100 Subject: [PATCH 098/275] Add option to use deflate compression for bicolor images --- .../Formats/Tiff/TiffEncoderCore.cs | 16 ++++---- .../Formats/Tiff/Utils/TiffWriter.cs | 38 +++++++++++++++---- .../Formats/Tiff/TiffEncoderTests.cs | 10 +++++ 3 files changed, 50 insertions(+), 14 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index a0e204bd2..75d078ccd 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -159,7 +159,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff imageDataBytes = writer.WriteGray(image, this.padding, this.CompressionType); break; case TiffEncodingMode.BiColor: - imageDataBytes = writer.WriteBiColor(image); + imageDataBytes = writer.WriteBiColor(image, this.CompressionType); break; default: imageDataBytes = writer.WriteRgbImageData(image, this.padding, this.CompressionType); @@ -386,20 +386,22 @@ namespace SixLabors.ImageSharp.Formats.Tiff private ushort GetCompressionType() { - if (this.CompressionType == TiffEncoderCompression.Deflate && - this.Mode == TiffEncodingMode.Rgb) + if (this.CompressionType == TiffEncoderCompression.Deflate && this.Mode == TiffEncodingMode.Rgb) { return (ushort)TiffCompression.Deflate; } - if (this.CompressionType == TiffEncoderCompression.Deflate && - this.Mode == TiffEncodingMode.Gray) + if (this.CompressionType == TiffEncoderCompression.Deflate && this.Mode == TiffEncodingMode.Gray) { return (ushort)TiffCompression.Deflate; } - if (this.CompressionType == TiffEncoderCompression.Deflate && - this.Mode == TiffEncodingMode.ColorPalette) + if (this.CompressionType == TiffEncoderCompression.Deflate && this.Mode == TiffEncodingMode.ColorPalette) + { + return (ushort)TiffCompression.Deflate; + } + + if (this.CompressionType == TiffEncoderCompression.Deflate && this.Mode == TiffEncodingMode.BiColor) { return (ushort)TiffCompression.Deflate; } diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs index d018248ed..3a0fcea75 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -357,15 +357,24 @@ namespace SixLabors.ImageSharp.Formats.Tiff return bytesWritten; } - public int WriteBiColor(Image image) + /// + /// Writes the image data as 1 bit black and white to the stream. + /// + /// The pixel data. + /// The image to write to the stream. + /// The compression to use. + /// The number of bytes written. + public int WriteBiColor(Image image, TiffEncoderCompression compression) where TPixel : unmanaged, IPixel { int padding = image.Width % 8 == 0 ? 0 : 1; int bytesPerRow = (image.Width / 8) + padding; - using IMemoryOwner rowRgb = this.memoryAllocator.Allocate(image.Width); + using IMemoryOwner rowL8 = this.memoryAllocator.Allocate(image.Width); using IManagedByteBuffer row = this.memoryAllocator.AllocateManagedByteBuffer(bytesPerRow, AllocationOptions.Clean); + using var memoryStream = new MemoryStream(); + using var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, PngCompressionLevel.Level6); // TODO: make compression level configurable Span rowSpan = row.GetSpan(); - Span rowRgbSpan = rowRgb.GetSpan(); + Span rowL8Span = rowL8.GetSpan(); // Convert image to black and white. using Image imageClone = image.Clone(); @@ -377,11 +386,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff int bitIndex = 0; int byteIndex = 0; Span pixelRow = imageClone.GetPixelRowSpan(y); - PixelOperations.Instance.ToRgb24(this.configuration, pixelRow, rowRgbSpan); + PixelOperations.Instance.ToL8(this.configuration, pixelRow, rowL8Span); for (int x = 0; x < pixelRow.Length; x++) { int shift = 7 - bitIndex; - if (rowRgbSpan[x].R == 255) + if (rowL8Span[x].PackedValue == 255) { rowSpan[byteIndex] |= (byte)(1 << shift); } @@ -394,12 +403,27 @@ namespace SixLabors.ImageSharp.Formats.Tiff } } - this.output.Write(row); - bytesWritten += row.Length(); + if (compression == TiffEncoderCompression.Deflate) + { + deflateStream.Write(row); + } + else + { + this.output.Write(row); + bytesWritten += row.Length(); + } row.Clear(); } + if (compression == TiffEncoderCompression.Deflate) + { + deflateStream.Flush(); + byte[] buffer = memoryStream.ToArray(); + this.output.Write(buffer); + bytesWritten += buffer.Length; + } + return bytesWritten; } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 9c043b4ee..91a735897 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -77,6 +77,16 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public void TiffEncoder_EncodeColorPalette_WithDeflateCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, TiffEncoderCompression.Deflate); + [Theory] + [WithFile(TestImages.Tiff.Calliphora_HuffmanCompressed, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeBiColor_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.BiColor); + + [Theory] + [WithFile(TestImages.Tiff.Calliphora_HuffmanCompressed, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeBiColor_WithDeflateCompression_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.BiColor, TiffEncoderCompression.Deflate); + private static void TestTiffEncoderCore( TestImageProvider provider, TiffBitsPerPixel bitsPerPixel, From 3f612b736f46d2df4785b67b0946e1496a5a0291 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 28 Nov 2020 16:56:24 +0100 Subject: [PATCH 099/275] Split up WriteBiColor in Deflate and no compression --- .../Formats/Tiff/Utils/TiffWriter.cs | 82 ++++++++++++++----- 1 file changed, 60 insertions(+), 22 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs index 3a0fcea75..586eb0a55 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -369,30 +369,33 @@ namespace SixLabors.ImageSharp.Formats.Tiff { int padding = image.Width % 8 == 0 ? 0 : 1; int bytesPerRow = (image.Width / 8) + padding; - using IMemoryOwner rowL8 = this.memoryAllocator.Allocate(image.Width); + using IMemoryOwner pixelRowAsGray = this.memoryAllocator.Allocate(image.Width); using IManagedByteBuffer row = this.memoryAllocator.AllocateManagedByteBuffer(bytesPerRow, AllocationOptions.Clean); - using var memoryStream = new MemoryStream(); - using var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, PngCompressionLevel.Level6); // TODO: make compression level configurable - Span rowSpan = row.GetSpan(); - Span rowL8Span = rowL8.GetSpan(); + Span outputRow = row.GetSpan(); + Span pixelRowAsGraySpan = pixelRowAsGray.GetSpan(); // Convert image to black and white. using Image imageClone = image.Clone(); imageClone.Mutate(img => img.BinaryDither(default(ErrorDither))); + if (compression == TiffEncoderCompression.Deflate) + { + return this.WriteBiColorDeflate(image, pixelRowAsGraySpan, outputRow); + } + int bytesWritten = 0; for (int y = 0; y < image.Height; y++) { int bitIndex = 0; int byteIndex = 0; Span pixelRow = imageClone.GetPixelRowSpan(y); - PixelOperations.Instance.ToL8(this.configuration, pixelRow, rowL8Span); + PixelOperations.Instance.ToL8(this.configuration, pixelRow, pixelRowAsGraySpan); for (int x = 0; x < pixelRow.Length; x++) { int shift = 7 - bitIndex; - if (rowL8Span[x].PackedValue == 255) + if (pixelRowAsGraySpan[x].PackedValue == 255) { - rowSpan[byteIndex] |= (byte)(1 << shift); + outputRow[byteIndex] |= (byte)(1 << shift); } bitIndex++; @@ -403,27 +406,62 @@ namespace SixLabors.ImageSharp.Formats.Tiff } } - if (compression == TiffEncoderCompression.Deflate) - { - deflateStream.Write(row); - } - else - { - this.output.Write(row); - bytesWritten += row.Length(); - } + this.output.Write(row); + bytesWritten += row.Length(); row.Clear(); } - if (compression == TiffEncoderCompression.Deflate) + return bytesWritten; + } + + /// + /// Writes the image data as 1 bit black and white with deflate compression to the stream. + /// + /// The pixel data. + /// The image to write to the stream. + /// A span for converting a pixel row to gray. + /// A span which will be used to store the output pixels. + /// The number of bytes written. + public int WriteBiColorDeflate(Image image, Span pixelRowAsGraySpan, Span outputRow) + where TPixel : unmanaged, IPixel + { + using var memoryStream = new MemoryStream(); + using var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, PngCompressionLevel.Level6); // TODO: make compression level configurable + + int bytesWritten = 0; + for (int y = 0; y < image.Height; y++) { - deflateStream.Flush(); - byte[] buffer = memoryStream.ToArray(); - this.output.Write(buffer); - bytesWritten += buffer.Length; + int bitIndex = 0; + int byteIndex = 0; + Span pixelRow = image.GetPixelRowSpan(y); + PixelOperations.Instance.ToL8(this.configuration, pixelRow, pixelRowAsGraySpan); + for (int x = 0; x < pixelRow.Length; x++) + { + int shift = 7 - bitIndex; + if (pixelRowAsGraySpan[x].PackedValue == 255) + { + outputRow[byteIndex] |= (byte)(1 << shift); + } + + bitIndex++; + if (bitIndex == 8) + { + byteIndex++; + bitIndex = 0; + } + } + + deflateStream.Write(outputRow); + + outputRow.Clear(); } + deflateStream.Flush(); + byte[] buffer = memoryStream.ToArray(); + this.output.Write(buffer); + bytesWritten += buffer.Length; + return bytesWritten; } From a89c4f5d9b6bee4df1283b55a245151e5d602d12 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 28 Nov 2020 19:23:50 +0100 Subject: [PATCH 100/275] Add T4 BitWriter: So far only works for run length up to 63 --- .../Tiff/Compression/BitWriterUtils.cs | 47 +++ ...n.cs => ModifiedHuffmanTiffCompression.cs} | 18 +- .../Formats/Tiff/Compression/T4BitWriter.cs | 365 ++++++++++++++++++ .../Tiff/Compression/T4TiffCompression.cs | 35 +- .../Compression/TiffCompressionFactory.cs | 2 +- .../Formats/Tiff/TiffEncoderCompression.cs | 7 +- .../Formats/Tiff/TiffEncoderCore.cs | 5 + .../Formats/Tiff/Utils/TiffWriter.cs | 7 + 8 files changed, 445 insertions(+), 41 deletions(-) create mode 100644 src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs rename src/ImageSharp/Formats/Tiff/Compression/{TiffModifiedHuffmanCompression.cs => ModifiedHuffmanTiffCompression.cs} (75%) create mode 100644 src/ImageSharp/Formats/Tiff/Compression/T4BitWriter.cs diff --git a/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs b/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs new file mode 100644 index 000000000..05efb0423 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs @@ -0,0 +1,47 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp.Formats.Tiff.Compression +{ + internal static class BitWriterUtils + { + public static void WriteBits(Span buffer, int pos, uint count, byte value) + { + int bitPos = pos % 8; + int bufferPos = pos / 8; + int startIdx = bufferPos + bitPos; + int endIdx = (int)(startIdx + count); + + for (int i = startIdx; i < endIdx; i++) + { + if (value == 1) + { + WriteBit(buffer, bufferPos, bitPos); + } + else + { + WriteZeroBit(buffer, bufferPos, bitPos); + } + + bitPos++; + if (bitPos >= 8) + { + bitPos = 0; + bufferPos++; + } + } + } + + public static void WriteBit(Span buffer, int bufferPos, int bitPos) + { + buffer[bufferPos] |= (byte)(1 << (7 - bitPos)); + } + + public static void WriteZeroBit(Span buffer, int bufferPos, int bitPos) + { + buffer[bufferPos] = (byte)(buffer[bufferPos] & ~(1 << (7 - bitPos))); + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffModifiedHuffmanCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/ModifiedHuffmanTiffCompression.cs similarity index 75% rename from src/ImageSharp/Formats/Tiff/Compression/TiffModifiedHuffmanCompression.cs rename to src/ImageSharp/Formats/Tiff/Compression/ModifiedHuffmanTiffCompression.cs index f742c1176..63908ff2f 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffModifiedHuffmanCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/ModifiedHuffmanTiffCompression.cs @@ -10,15 +10,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression /// /// Class to handle cases where TIFF image data is compressed using Modified Huffman Compression. /// - internal class TiffModifiedHuffmanCompression : T4TiffCompression + internal class ModifiedHuffmanTiffCompression : T4TiffCompression { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The memory allocator. /// The photometric interpretation. /// The image width. - public TiffModifiedHuffmanCompression(MemoryAllocator allocator, TiffPhotometricInterpretation photometricInterpretation, int width) + public ModifiedHuffmanTiffCompression(MemoryAllocator allocator, TiffPhotometricInterpretation photometricInterpretation, int width) : base(allocator, photometricInterpretation, width) { } @@ -27,8 +27,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression public override void Decompress(Stream stream, int byteCount, Span buffer) { bool isWhiteZero = this.PhotometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero; - int whiteValue = isWhiteZero ? 0 : 1; - int blackValue = isWhiteZero ? 1 : 0; + byte whiteValue = (byte)(isWhiteZero ? 0 : 1); + byte blackValue = (byte)(isWhiteZero ? 1 : 0); using var bitReader = new T4BitReader(stream, byteCount, this.Allocator, isModifiedHuffman: true); @@ -43,13 +43,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression { if (bitReader.IsWhiteRun) { - this.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, whiteValue); + BitWriterUtils.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, whiteValue); bitsWritten += bitReader.RunLength; pixelsWritten += bitReader.RunLength; } else { - this.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, blackValue); + BitWriterUtils.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, blackValue); bitsWritten += bitReader.RunLength; pixelsWritten += bitReader.RunLength; } @@ -59,11 +59,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression { bitReader.StartNewRow(); - // Write padding bytes, if necessary. + // Write padding bits, if necessary. uint pad = 8 - (bitsWritten % 8); if (pad != 8) { - this.WriteBits(buffer, (int)bitsWritten, pad, 0); + BitWriterUtils.WriteBits(buffer, (int)bitsWritten, pad, 0); bitsWritten += pad; } } diff --git a/src/ImageSharp/Formats/Tiff/Compression/T4BitWriter.cs b/src/ImageSharp/Formats/Tiff/Compression/T4BitWriter.cs new file mode 100644 index 000000000..f302a3d4f --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Compression/T4BitWriter.cs @@ -0,0 +1,365 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; +using System.Collections.Generic; +using System.IO; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Tiff.Compression +{ + /// + /// Bitwriter for writing compressed CCITT T4 1D data. + /// + internal class T4BitWriter + { + private static readonly Dictionary WhiteLen4TermCodes = new Dictionary() + { + { 2, 0x7 }, { 3, 0x8 }, { 4, 0xB }, { 5, 0xC }, { 6, 0xE }, { 7, 0xF } + }; + + private static readonly Dictionary WhiteLen5TermCodes = new Dictionary() + { + { 8, 0x13 }, { 9, 0x14 }, { 10, 0x7 }, { 11, 0x8 } + }; + + private static readonly Dictionary WhiteLen6TermCodes = new Dictionary() + { + { 1, 0x7 }, { 12, 0x8 }, { 13, 0x3 }, { 14, 0x34 }, { 15, 0x35 }, { 16, 0x2A }, { 17, 0x2B } + }; + + private static readonly Dictionary WhiteLen7TermCodes = new Dictionary() + { + { 18, 0x27 }, { 19, 0xC }, { 20, 0x8 }, { 21, 0x17 }, { 22, 0x3 }, { 23, 0x4 }, { 24, 0x28 }, { 25, 0x2B }, { 26, 0x13 }, + { 27, 0x24 }, { 28, 0x18 } + }; + + private static readonly Dictionary WhiteLen8TermCodes = new Dictionary() + { + { 0, 0x35 }, { 29, 0x2 }, { 30, 0x3 }, { 31, 0x1A }, { 32, 0x1B }, { 33, 0x12 }, { 34, 0x13 }, { 35, 0x14 }, { 36, 0x15 }, + { 37, 0x16 }, { 38, 0x17 }, { 39, 0x28 }, { 40, 0x29 }, { 41, 0x2A }, { 42, 0x2B }, { 43, 0x2C }, { 44, 0x2D }, { 45, 0x4 }, + { 46, 0x5 }, { 47, 0xA }, { 48, 0xB }, { 49, 0x52 }, { 50, 0x53 }, { 51, 0x54 }, { 52, 0x55 }, { 53, 0x24 }, { 54, 0x25 }, + { 55, 0x58 }, { 56, 0x59 }, { 57, 0x5A }, { 58, 0x5B }, { 59, 0x4A }, { 60, 0x4B }, { 61, 0x32 }, { 62, 0x33 }, { 63, 0x34 } + }; + + private static readonly Dictionary BlackLen2TermCodes = new Dictionary() + { + { 2, 0x3 }, { 3, 0x2 } + }; + + private static readonly Dictionary BlackLen3TermCodes = new Dictionary() + { + { 1, 0x2 }, { 4, 0x3 } + }; + + private static readonly Dictionary BlackLen4TermCodes = new Dictionary() + { + { 5, 0x3 }, { 6, 0x2 } + }; + + private static readonly Dictionary BlackLen5TermCodes = new Dictionary() + { + { 7, 0x3 } + }; + + private static readonly Dictionary BlackLen6TermCodes = new Dictionary() + { + { 8, 0x5 }, { 9, 0x4 } + }; + + private static readonly Dictionary BlackLen7TermCodes = new Dictionary() + { + { 10, 0x4 }, { 11, 0x5 }, { 12, 0x7 } + }; + + private static readonly Dictionary BlackLen8TermCodes = new Dictionary() + { + { 13, 0x4 }, { 14, 0x7 } + }; + + private static readonly Dictionary BlackLen9TermCodes = new Dictionary() + { + { 15, 0x18 } + }; + + private static readonly Dictionary BlackLen10TermCodes = new Dictionary() + { + { 0, 0x37 }, { 16, 0x17 }, { 17, 0x18 }, { 18, 0x8 } + }; + + private static readonly Dictionary BlackLen11TermCodes = new Dictionary() + { + { 19, 0x67 }, { 20, 0x68 }, { 21, 0x6C }, { 22, 0x37 }, { 23, 0x28 }, { 24, 0x17 }, { 25, 0x18 } + }; + + private static readonly Dictionary BlackLen12TermCodes = new Dictionary() + { + { 26, 0xCA }, { 27, 0xCB }, { 28, 0xCC }, { 29, 0xCD }, { 30, 0x68 }, { 31, 0x69 }, { 32, 0x6A }, { 33, 0x6B }, { 34, 0xD2 }, + { 35, 0xD3 }, { 36, 0xD4 }, { 37, 0xD5 }, { 38, 0xD6 }, { 39, 0xD7 }, { 40, 0x6C }, { 41, 0x6D }, { 42, 0xDA }, { 43, 0xDB }, + { 44, 0x54 }, { 45, 0x55 }, { 46, 0x56 }, { 47, 0x57 }, { 48, 0x64 }, { 49, 0x65 }, { 50, 0x52 }, { 51, 0x53 }, { 52, 0x24 }, + { 53, 0x37 }, { 54, 0x38 }, { 55, 0x27 }, { 56, 0x28 }, { 57, 0x58 }, { 58, 0x59 }, { 59, 0x2B }, { 60, 0x2C }, { 61, 0x5A }, + { 62, 0x66 }, { 63, 0x67 } + }; + + private readonly MemoryAllocator memoryAllocator; + + private readonly Configuration configuration; + + private int bytePosition = 0; + + private byte bitPosition = 0; + + /// + /// Initializes a new instance of the class. + /// + /// The memory allocator. + /// The configuration. + public T4BitWriter(MemoryAllocator memoryAllocator, Configuration configuration) + { + this.memoryAllocator = memoryAllocator; + this.configuration = configuration; + this.bytePosition = 0; + this.bitPosition = 0; + } + + /// + /// Writes a image compressed with CCITT T4 to the stream. + /// + /// The pixel data. + /// The image to write to the stream. This has to be a bi-color image. + /// A span for converting a pixel row to gray. + /// The stream to write to. + /// The number of bytes written to the stream. + public int CompressImage(Image image, Span pixelRowAsGray, Stream stream) + where TPixel : unmanaged, IPixel + { + // This is too much memory allocated, but just 1 bit per pixel will not do, if the compression rate is not good. + int maxNeededBytes = image.Width * image.Height; + IMemoryOwner compressedDataBuffer = this.memoryAllocator.Allocate(maxNeededBytes, AllocationOptions.Clean); + Span compressedData = compressedDataBuffer.GetSpan(); + + this.bytePosition = 0; + this.bitPosition = 0; + + // An EOL code is expected at the start of the data. + this.WriteCode(12, 1, compressedData); + + for (int y = 0; y < image.Height; y++) + { + bool isWhiteRun = true; + Span pixelRow = image.GetPixelRowSpan(y); + PixelOperations.Instance.ToL8(this.configuration, pixelRow, pixelRowAsGray); + int x = 0; + while (x < image.Width) + { + uint runLength = 0; + for (int i = x; i < pixelRow.Length; i++) + { + if (isWhiteRun && pixelRowAsGray[i].PackedValue != 255) + { + break; + } + + if (isWhiteRun && pixelRowAsGray[i].PackedValue == 255) + { + runLength++; + continue; + } + + if (!isWhiteRun && pixelRowAsGray[i].PackedValue != 0) + { + break; + } + + if (!isWhiteRun && pixelRowAsGray[i].PackedValue == 0) + { + runLength++; + } + } + + bool gotTermCode = this.GetTermCode(runLength, out var code, out var codeLength, isWhiteRun); + + this.WriteCode(codeLength, code, compressedData); + + x += (int)runLength; + + isWhiteRun = !isWhiteRun; + } + + // Write EOL + this.WriteCode(12, 1, compressedData); + } + + // Write the compressed data to the stream. + stream.Write(compressedData.Slice(0, this.bytePosition)); + + return this.bytePosition; + } + + private void WriteCode(uint codeLength, uint code, Span compressedData) + { + while (codeLength > 0) + { + var bitNumber = (int) codeLength; + var bit = (code & (1 << (bitNumber - 1))) != 0; + if (bit) + { + BitWriterUtils.WriteBit(compressedData, this.bytePosition, this.bitPosition); + } + else + { + BitWriterUtils.WriteZeroBit(compressedData, this.bytePosition, this.bitPosition); + } + + this.bitPosition++; + if (this.bitPosition == 8) + { + this.bytePosition++; + this.bitPosition = 0; + } + + codeLength--; + } + } + + private bool GetTermCode(uint runLength, out uint code, out uint codeLength, bool isWhiteRun) + { + if (isWhiteRun) + { + return this.GetWhiteTermCode(runLength, out code, out codeLength); + } + + return this.GetBlackTermCode(runLength, out code, out codeLength); + } + + private bool GetWhiteTermCode(uint runLength, out uint code, out uint codeLength) + { + code = 0; + codeLength = 0; + + if (WhiteLen4TermCodes.ContainsKey(runLength)) + { + code = WhiteLen4TermCodes[runLength]; + codeLength = 4; + return true; + } + + if (WhiteLen5TermCodes.ContainsKey(runLength)) + { + code = WhiteLen5TermCodes[runLength]; + codeLength = 5; + return true; + } + + if (WhiteLen6TermCodes.ContainsKey(runLength)) + { + code = WhiteLen6TermCodes[runLength]; + codeLength = 6; + return true; + } + + if (WhiteLen7TermCodes.ContainsKey(runLength)) + { + code = WhiteLen7TermCodes[runLength]; + codeLength = 7; + return true; + } + + if (WhiteLen8TermCodes.ContainsKey(runLength)) + { + code = WhiteLen8TermCodes[runLength]; + codeLength = 8; + return true; + } + + return false; + } + + private bool GetBlackTermCode(uint runLength, out uint code, out uint codeLength) + { + code = 0; + codeLength = 0; + + if (BlackLen2TermCodes.ContainsKey(runLength)) + { + code = BlackLen2TermCodes[runLength]; + codeLength = 2; + return true; + } + + if (BlackLen3TermCodes.ContainsKey(runLength)) + { + code = BlackLen3TermCodes[runLength]; + codeLength = 3; + return true; + } + + if (BlackLen4TermCodes.ContainsKey(runLength)) + { + code = BlackLen4TermCodes[runLength]; + codeLength = 4; + return true; + } + + if (BlackLen5TermCodes.ContainsKey(runLength)) + { + code = BlackLen5TermCodes[runLength]; + codeLength = 5; + return true; + } + + if (BlackLen6TermCodes.ContainsKey(runLength)) + { + code = BlackLen6TermCodes[runLength]; + codeLength = 6; + return true; + } + + if (BlackLen7TermCodes.ContainsKey(runLength)) + { + code = BlackLen7TermCodes[runLength]; + codeLength = 7; + return true; + } + + if (BlackLen8TermCodes.ContainsKey(runLength)) + { + code = BlackLen8TermCodes[runLength]; + codeLength = 8; + return true; + } + + if (BlackLen9TermCodes.ContainsKey(runLength)) + { + code = BlackLen9TermCodes[runLength]; + codeLength = 9; + return true; + } + + if (BlackLen10TermCodes.ContainsKey(runLength)) + { + code = BlackLen10TermCodes[runLength]; + codeLength = 10; + return true; + } + + if (BlackLen11TermCodes.ContainsKey(runLength)) + { + code = BlackLen11TermCodes[runLength]; + codeLength = 11; + return true; + } + + if (BlackLen12TermCodes.ContainsKey(runLength)) + { + code = BlackLen12TermCodes[runLength]; + codeLength = 12; + return true; + } + + return false; + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs index 6a064962b..922083603 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs @@ -27,8 +27,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression public override void Decompress(Stream stream, int byteCount, Span buffer) { bool isWhiteZero = this.PhotometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero; - int whiteValue = isWhiteZero ? 0 : 1; - int blackValue = isWhiteZero ? 1 : 0; + byte whiteValue = (byte)(isWhiteZero ? 0 : 1); + byte blackValue = (byte)(isWhiteZero ? 1 : 0); using var bitReader = new T4BitReader(stream, byteCount, this.Allocator); @@ -42,12 +42,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression { if (bitReader.IsWhiteRun) { - this.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, whiteValue); + BitWriterUtils.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, whiteValue); bitsWritten += bitReader.RunLength; } else { - this.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, blackValue); + BitWriterUtils.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, blackValue); bitsWritten += bitReader.RunLength; } } @@ -58,36 +58,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression uint pad = 8 - (bitsWritten % 8); if (pad != 8) { - this.WriteBits(buffer, (int)bitsWritten, pad, 0); + BitWriterUtils.WriteBits(buffer, (int)bitsWritten, pad, 0); bitsWritten += pad; } } } } - - protected void WriteBits(Span buffer, int pos, uint count, int value) - { - int bitPos = pos % 8; - int bufferPos = pos / 8; - int startIdx = bufferPos + bitPos; - int endIdx = (int)(startIdx + count); - - for (int i = startIdx; i < endIdx; i++) - { - this.WriteBit(buffer, bufferPos, bitPos, value); - - bitPos++; - if (bitPos >= 8) - { - bitPos = 0; - bufferPos++; - } - } - } - - protected void WriteBit(Span buffer, int bufferPos, int bitPos, int value) - { - buffer[bufferPos] |= (byte)(value << (7 - bitPos)); - } } } diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs index 3a0e5e6da..11f85faa1 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff case TiffCompressionType.T4: return new T4TiffCompression(allocator, photometricInterpretation, width); case TiffCompressionType.HuffmanRle: - return new TiffModifiedHuffmanCompression(allocator, photometricInterpretation, width); + return new ModifiedHuffmanTiffCompression(allocator, photometricInterpretation, width); default: throw TiffThrowHelper.NotSupportedCompression(nameof(compressionType)); } diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs index 536cd2c2d..30702641a 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs @@ -16,6 +16,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Use zlib compression. /// - Deflate + Deflate, + + /// + /// Use CCITT T4 1D compression. Note: This is only valid for bi-level images. + /// + CcittGroup3Fax, } } diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 75d078ccd..0d5bacc34 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -406,6 +406,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff return (ushort)TiffCompression.Deflate; } + if (this.CompressionType == TiffEncoderCompression.CcittGroup3Fax && this.Mode == TiffEncodingMode.BiColor) + { + return (ushort)TiffCompression.CcittGroup3Fax; + } + return (ushort)TiffCompression.None; } } diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs index 586eb0a55..eaa71c953 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -9,6 +9,7 @@ using System.Runtime.InteropServices; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Png.Zlib; +using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; @@ -383,6 +384,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff return this.WriteBiColorDeflate(image, pixelRowAsGraySpan, outputRow); } + if (compression == TiffEncoderCompression.CcittGroup3Fax) + { + var bitWriter = new T4BitWriter(this.memoryAllocator, this.configuration); + return bitWriter.CompressImage(image, pixelRowAsGraySpan, this.output); + } + int bytesWritten = 0; for (int y = 0; y < image.Height; y++) { From 6ea767533682b4eb91d6ccc07293a4415611501a Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 28 Nov 2020 21:48:02 +0100 Subject: [PATCH 101/275] Add makeup codes for run length above 63 --- .../Formats/Tiff/Compression/T4BitWriter.cs | 301 ++++++++++++++---- .../Formats/Tiff/TiffEncoderCore.cs | 20 ++ 2 files changed, 267 insertions(+), 54 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/T4BitWriter.cs b/src/ImageSharp/Formats/Tiff/Compression/T4BitWriter.cs index f302a3d4f..0dd79410f 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/T4BitWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/T4BitWriter.cs @@ -15,6 +15,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression /// internal class T4BitWriter { + private const uint WhiteZeroRunTermCode = 0x35; + + private const uint BlackZeroRunTermCode = 0x37; + + private static readonly List MakeupRunLength = new List() + { + 64, 128, 192, 256, 320, 384, 448, 512, 576, 640, 704, 768, 832, 896, 960, 1024, 1088, 1152, 1216, 1280, 1344, 1408, 1472, 1536, 1600, 1664, 1728, 1792, 1856, 1920, 1984, 2048, 2112, 2176, 2240, 2304, 2368, 2432, 2496, 2560 + }; + private static readonly Dictionary WhiteLen4TermCodes = new Dictionary() { { 2, 0x7 }, { 3, 0x8 }, { 4, 0xB }, { 5, 0xC }, { 6, 0xE }, { 7, 0xF } @@ -38,10 +47,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression private static readonly Dictionary WhiteLen8TermCodes = new Dictionary() { - { 0, 0x35 }, { 29, 0x2 }, { 30, 0x3 }, { 31, 0x1A }, { 32, 0x1B }, { 33, 0x12 }, { 34, 0x13 }, { 35, 0x14 }, { 36, 0x15 }, - { 37, 0x16 }, { 38, 0x17 }, { 39, 0x28 }, { 40, 0x29 }, { 41, 0x2A }, { 42, 0x2B }, { 43, 0x2C }, { 44, 0x2D }, { 45, 0x4 }, - { 46, 0x5 }, { 47, 0xA }, { 48, 0xB }, { 49, 0x52 }, { 50, 0x53 }, { 51, 0x54 }, { 52, 0x55 }, { 53, 0x24 }, { 54, 0x25 }, - { 55, 0x58 }, { 56, 0x59 }, { 57, 0x5A }, { 58, 0x5B }, { 59, 0x4A }, { 60, 0x4B }, { 61, 0x32 }, { 62, 0x33 }, { 63, 0x34 } + { 0, WhiteZeroRunTermCode }, { 29, 0x2 }, { 30, 0x3 }, { 31, 0x1A }, { 32, 0x1B }, { 33, 0x12 }, { 34, 0x13 }, { 35, 0x14 }, + { 36, 0x15 }, { 37, 0x16 }, { 38, 0x17 }, { 39, 0x28 }, { 40, 0x29 }, { 41, 0x2A }, { 42, 0x2B }, { 43, 0x2C }, { 44, 0x2D }, + { 45, 0x4 }, { 46, 0x5 }, { 47, 0xA }, { 48, 0xB }, { 49, 0x52 }, { 50, 0x53 }, { 51, 0x54 }, { 52, 0x55 }, { 53, 0x24 }, + { 54, 0x25 }, { 55, 0x58 }, { 56, 0x59 }, { 57, 0x5A }, { 58, 0x5B }, { 59, 0x4A }, { 60, 0x4B }, { 61, 0x32 }, { 62, 0x33 }, + { 63, 0x34 } }; private static readonly Dictionary BlackLen2TermCodes = new Dictionary() @@ -86,7 +96,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression private static readonly Dictionary BlackLen10TermCodes = new Dictionary() { - { 0, 0x37 }, { 16, 0x17 }, { 17, 0x18 }, { 18, 0x8 } + { 0, BlackZeroRunTermCode }, { 16, 0x17 }, { 17, 0x18 }, { 18, 0x8 } }; private static readonly Dictionary BlackLen11TermCodes = new Dictionary() @@ -103,13 +113,75 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression { 62, 0x66 }, { 63, 0x67 } }; + private static readonly Dictionary WhiteLen5MakeupCodes = new Dictionary() + { + { 64, 0x1B }, { 128, 0x12 } + }; + + private static readonly Dictionary WhiteLen6MakeupCodes = new Dictionary() + { + { 192, 0x17 }, { 1664, 0x18 } + }; + + private static readonly Dictionary WhiteLen8MakeupCodes = new Dictionary() + { + { 320, 0x36 }, { 384, 0x37 }, { 448, 0x64 }, { 512, 0x65 }, { 576, 0x68 }, { 640, 0x67 } + }; + + private static readonly Dictionary WhiteLen7MakeupCodes = new Dictionary() + { + { 256, 0x37 } + }; + + private static readonly Dictionary WhiteLen9MakeupCodes = new Dictionary() + { + { 704, 0xCC }, { 768, 0xCD }, { 832, 0xD2 }, { 896, 0xD3 }, { 960, 0xD4 }, { 1024, 0xD5 }, { 1088, 0xD6 }, + { 1152, 0xD7 }, { 1216, 0xD8 }, { 1280, 0xD9 }, { 1344, 0xDA }, { 1408, 0xDB }, { 1472, 0x98 }, { 1536, 0x99 }, + { 1600, 0x9A }, { 1728, 0x9B } + }; + + private static readonly Dictionary WhiteLen11MakeupCodes = new Dictionary() + { + { 1792, 0x8 }, { 1856, 0xC }, { 1920, 0xD } + }; + + private static readonly Dictionary WhiteLen12MakeupCodes = new Dictionary() + { + { 1984, 0x12 }, { 2048, 0x13 }, { 2112, 0x14 }, { 2176, 0x15 }, { 2240, 0x16 }, { 2304, 0x17 }, { 2368, 0x1C }, + { 2432, 0x1D }, { 2496, 0x1E }, { 2560, 0x1F } + }; + + private static readonly Dictionary BlackLen10MakeupCodes = new Dictionary() + { + { 64, 0xF } + }; + + private static readonly Dictionary BlackLen11MakeupCodes = new Dictionary() + { + { 1792, 0x8 }, { 1856, 0xC }, { 1920, 0xD } + }; + + private static readonly Dictionary BlackLen12MakeupCodes = new Dictionary() + { + { 128, 0xC8 }, { 192, 0xC9 }, { 256, 0x5B }, { 320, 0x33 }, { 384, 0x34 }, { 448, 0x35 }, + { 1984, 0x12 }, { 2048, 0x13 }, { 2112, 0x14 }, { 2176, 0x15 }, { 2240, 0x16 }, { 2304, 0x17 }, { 2368, 0x1C }, + { 2432, 0x1D }, { 2496, 0x1E }, { 2560, 0x1F } + }; + + private static readonly Dictionary BlackLen13MakeupCodes = new Dictionary() + { + { 512, 0x6C }, { 576, 0x6D }, { 640, 0x4A }, { 704, 0x4B }, { 768, 0x4C }, { 832, 0x4D }, { 896, 0x72 }, + { 960, 0x73 }, { 1024, 0x74 }, { 1088, 0x75 }, { 1152, 0x76 }, { 1216, 0x77 }, { 1280, 0x52 }, { 1344, 0x53 }, + { 1408, 0x54 }, { 1472, 0x55 }, { 1536, 0x5A }, { 1600, 0x5B }, { 1664, 0x64 }, { 1728, 0x65 } + }; + private readonly MemoryAllocator memoryAllocator; private readonly Configuration configuration; - private int bytePosition = 0; + private int bytePosition; - private byte bitPosition = 0; + private byte bitPosition; /// /// Initializes a new instance of the class. @@ -149,13 +221,14 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression for (int y = 0; y < image.Height; y++) { bool isWhiteRun = true; + bool isStartOrRow = true; Span pixelRow = image.GetPixelRowSpan(y); PixelOperations.Instance.ToL8(this.configuration, pixelRow, pixelRowAsGray); int x = 0; while (x < image.Width) { uint runLength = 0; - for (int i = x; i < pixelRow.Length; i++) + for (int i = x; i < image.Width; i++) { if (isWhiteRun && pixelRowAsGray[i].PackedValue != 255) { @@ -179,16 +252,51 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression } } - bool gotTermCode = this.GetTermCode(runLength, out var code, out var codeLength, isWhiteRun); + if (isStartOrRow && runLength == 0) + { + this.WriteCode(8, WhiteZeroRunTermCode, compressedData); - this.WriteCode(codeLength, code, compressedData); + isWhiteRun = false; + isStartOrRow = false; + continue; + } - x += (int)runLength; + uint code; + uint codeLength; + if (runLength <= 63) + { + code = this.GetTermCode(runLength, out codeLength, isWhiteRun); + this.WriteCode(codeLength, code, compressedData); + x += (int)runLength; + } + else + { + runLength = this.GetBestFittingMakeupRunLength(runLength); + code = this.GetMakeupCode(runLength, out codeLength, isWhiteRun); + this.WriteCode(codeLength, code, compressedData); + x += (int)runLength; + // If we are at the end of the line with a makeup code, we need to write a final term code with a length of zero. + if (x == image.Width) + { + if (isWhiteRun) + { + this.WriteCode(8, WhiteZeroRunTermCode, compressedData); + } + else + { + this.WriteCode(10, BlackZeroRunTermCode, compressedData); + } + } + + continue; + } + + isStartOrRow = false; isWhiteRun = !isWhiteRun; } - // Write EOL + // Write EOL. this.WriteCode(12, 1, compressedData); } @@ -202,7 +310,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression { while (codeLength > 0) { - var bitNumber = (int) codeLength; + var bitNumber = (int)codeLength; var bit = (code & (1 << (bitNumber - 1))) != 0; if (bit) { @@ -224,142 +332,227 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression } } - private bool GetTermCode(uint runLength, out uint code, out uint codeLength, bool isWhiteRun) + private uint GetBestFittingMakeupRunLength(uint runLength) + { + for (int i = 0; i < MakeupRunLength.Count - 1; i++) + { + if (MakeupRunLength[i] <= runLength && MakeupRunLength[i + 1] > runLength) + { + return MakeupRunLength[i]; + } + } + + return MakeupRunLength[MakeupRunLength.Count - 1]; + } + + private uint GetTermCode(uint runLength, out uint codeLength, bool isWhiteRun) + { + if (isWhiteRun) + { + return this.GetWhiteTermCode(runLength, out codeLength); + } + + return this.GetBlackTermCode(runLength, out codeLength); + } + + private uint GetMakeupCode(uint runLength, out uint codeLength, bool isWhiteRun) { if (isWhiteRun) { - return this.GetWhiteTermCode(runLength, out code, out codeLength); + return this.GetWhiteMakeupCode(runLength, out codeLength); + } + + return this.GetBlackMakeupCode(runLength, out codeLength); + } + + private uint GetWhiteMakeupCode(uint runLength, out uint codeLength) + { + codeLength = 0; + + if (WhiteLen5MakeupCodes.ContainsKey(runLength)) + { + codeLength = 5; + return WhiteLen5MakeupCodes[runLength]; + } + + if (WhiteLen6MakeupCodes.ContainsKey(runLength)) + { + codeLength = 6; + return WhiteLen6MakeupCodes[runLength]; + } + + if (WhiteLen7MakeupCodes.ContainsKey(runLength)) + { + codeLength = 7; + return WhiteLen7MakeupCodes[runLength]; + } + + if (WhiteLen8MakeupCodes.ContainsKey(runLength)) + { + codeLength = 8; + return WhiteLen8MakeupCodes[runLength]; + } + + if (WhiteLen9MakeupCodes.ContainsKey(runLength)) + { + codeLength = 9; + return WhiteLen9MakeupCodes[runLength]; + } + + if (WhiteLen11MakeupCodes.ContainsKey(runLength)) + { + codeLength = 11; + return WhiteLen11MakeupCodes[runLength]; + } + + if (WhiteLen12MakeupCodes.ContainsKey(runLength)) + { + codeLength = 12; + return WhiteLen12MakeupCodes[runLength]; + } + + return 0; + } + + private uint GetBlackMakeupCode(uint runLength, out uint codeLength) + { + codeLength = 0; + + if (BlackLen10MakeupCodes.ContainsKey(runLength)) + { + codeLength = 10; + return BlackLen10MakeupCodes[runLength]; + } + + if (BlackLen11MakeupCodes.ContainsKey(runLength)) + { + codeLength = 11; + return BlackLen11MakeupCodes[runLength]; + } + + if (BlackLen12MakeupCodes.ContainsKey(runLength)) + { + codeLength = 12; + return BlackLen12MakeupCodes[runLength]; + } + + if (BlackLen13MakeupCodes.ContainsKey(runLength)) + { + codeLength = 13; + return BlackLen13MakeupCodes[runLength]; } - return this.GetBlackTermCode(runLength, out code, out codeLength); + return 0; } - private bool GetWhiteTermCode(uint runLength, out uint code, out uint codeLength) + private uint GetWhiteTermCode(uint runLength, out uint codeLength) { - code = 0; codeLength = 0; if (WhiteLen4TermCodes.ContainsKey(runLength)) { - code = WhiteLen4TermCodes[runLength]; codeLength = 4; - return true; + return WhiteLen4TermCodes[runLength]; } if (WhiteLen5TermCodes.ContainsKey(runLength)) { - code = WhiteLen5TermCodes[runLength]; codeLength = 5; - return true; + return WhiteLen5TermCodes[runLength]; } if (WhiteLen6TermCodes.ContainsKey(runLength)) { - code = WhiteLen6TermCodes[runLength]; codeLength = 6; - return true; + return WhiteLen6TermCodes[runLength]; } if (WhiteLen7TermCodes.ContainsKey(runLength)) { - code = WhiteLen7TermCodes[runLength]; codeLength = 7; - return true; + return WhiteLen7TermCodes[runLength]; } if (WhiteLen8TermCodes.ContainsKey(runLength)) { - code = WhiteLen8TermCodes[runLength]; codeLength = 8; - return true; + return WhiteLen8TermCodes[runLength]; } - return false; + return 0; } - private bool GetBlackTermCode(uint runLength, out uint code, out uint codeLength) + private uint GetBlackTermCode(uint runLength, out uint codeLength) { - code = 0; codeLength = 0; if (BlackLen2TermCodes.ContainsKey(runLength)) { - code = BlackLen2TermCodes[runLength]; codeLength = 2; - return true; + return BlackLen2TermCodes[runLength]; } if (BlackLen3TermCodes.ContainsKey(runLength)) { - code = BlackLen3TermCodes[runLength]; codeLength = 3; - return true; + return BlackLen3TermCodes[runLength]; } if (BlackLen4TermCodes.ContainsKey(runLength)) { - code = BlackLen4TermCodes[runLength]; codeLength = 4; - return true; + return BlackLen4TermCodes[runLength]; } if (BlackLen5TermCodes.ContainsKey(runLength)) { - code = BlackLen5TermCodes[runLength]; codeLength = 5; - return true; + return BlackLen5TermCodes[runLength]; } if (BlackLen6TermCodes.ContainsKey(runLength)) { - code = BlackLen6TermCodes[runLength]; codeLength = 6; - return true; + return BlackLen6TermCodes[runLength]; } if (BlackLen7TermCodes.ContainsKey(runLength)) { - code = BlackLen7TermCodes[runLength]; codeLength = 7; - return true; + return BlackLen7TermCodes[runLength]; } if (BlackLen8TermCodes.ContainsKey(runLength)) { - code = BlackLen8TermCodes[runLength]; codeLength = 8; - return true; + return BlackLen8TermCodes[runLength]; } if (BlackLen9TermCodes.ContainsKey(runLength)) { - code = BlackLen9TermCodes[runLength]; codeLength = 9; - return true; + return BlackLen9TermCodes[runLength]; } if (BlackLen10TermCodes.ContainsKey(runLength)) { - code = BlackLen10TermCodes[runLength]; codeLength = 10; - return true; + return BlackLen10TermCodes[runLength]; } if (BlackLen11TermCodes.ContainsKey(runLength)) { - code = BlackLen11TermCodes[runLength]; codeLength = 11; - return true; + return BlackLen11TermCodes[runLength]; } if (BlackLen12TermCodes.ContainsKey(runLength)) { - code = BlackLen12TermCodes[runLength]; codeLength = 12; - return true; + return BlackLen12TermCodes[runLength]; } - return false; + return 0; } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 0d5bacc34..742c2da42 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -341,6 +341,18 @@ namespace SixLabors.ImageSharp.Formats.Tiff this.PhotometricInterpretation = TiffPhotometricInterpretation.PaletteColor; break; case TiffEncodingMode.BiColor: + if (this.CompressionType == TiffEncoderCompression.CcittGroup3Fax) + { + // The “normal” PhotometricInterpretation for bilevel CCITT compressed data is WhiteIsZero. + this.PhotometricInterpretation = TiffPhotometricInterpretation.WhiteIsZero; + } + else + { + this.PhotometricInterpretation = TiffPhotometricInterpretation.BlackIsZero; + } + + break; + case TiffEncodingMode.Gray: this.PhotometricInterpretation = TiffPhotometricInterpretation.BlackIsZero; break; @@ -358,6 +370,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff return 3; case TiffPhotometricInterpretation.PaletteColor: case TiffPhotometricInterpretation.BlackIsZero: + case TiffPhotometricInterpretation.WhiteIsZero: return 1; default: return 3; @@ -372,6 +385,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff return new ushort[] { 8 }; case TiffPhotometricInterpretation.Rgb: return new ushort[] { 8, 8, 8 }; + case TiffPhotometricInterpretation.WhiteIsZero: + if (this.Mode == TiffEncodingMode.BiColor) + { + return new ushort[] { 1 }; + } + + return new ushort[] { 8 }; case TiffPhotometricInterpretation.BlackIsZero: if (this.Mode == TiffEncodingMode.BiColor) { From 719c7fae2118c633c578a9df162fb0059d8d236a Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 29 Nov 2020 12:43:29 +0100 Subject: [PATCH 102/275] Add ccitt fax3 test images --- .../Formats/Tiff/TiffEncoderTests.cs | 9 +++++++-- tests/ImageSharp.Tests/TestImages.cs | 18 +++++++++++++----- .../Tiff/Calliphora_bicolor_uncompressed.tiff | 3 +++ ...itt_fax3.tif => Calliphora_ccitt_fax3.tiff} | 0 ...man_rle.tif => Calliphora_huffman_rle.tiff} | 0 ...f => ccitt_fax3_all_makeupcodes_codes.tiff} | 0 ...f => ccitt_fax3_all_terminating_codes.tiff} | 0 7 files changed, 23 insertions(+), 7 deletions(-) create mode 100644 tests/Images/Input/Tiff/Calliphora_bicolor_uncompressed.tiff rename tests/Images/Input/Tiff/{Calliphora_ccitt_fax3.tif => Calliphora_ccitt_fax3.tiff} (100%) rename tests/Images/Input/Tiff/{Calliphora_huffman_rle.tif => Calliphora_huffman_rle.tiff} (100%) rename tests/Images/Input/Tiff/{ccitt_fax3_all_makeupcodes_codes.tif => ccitt_fax3_all_makeupcodes_codes.tiff} (100%) rename tests/Images/Input/Tiff/{ccitt_fax3_all_terminating_codes.tif => ccitt_fax3_all_terminating_codes.tiff} (100%) diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 91a735897..03d0b2eef 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -78,15 +78,20 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, TiffEncoderCompression.Deflate); [Theory] - [WithFile(TestImages.Tiff.Calliphora_HuffmanCompressed, PixelTypes.Rgba32)] + [WithFile(TestImages.Tiff.Calliphora_BiColor, PixelTypes.Rgba32)] public void TiffEncoder_EncodeBiColor_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.BiColor); [Theory] - [WithFile(TestImages.Tiff.Calliphora_HuffmanCompressed, PixelTypes.Rgba32)] + [WithFile(TestImages.Tiff.Calliphora_BiColor, PixelTypes.Rgba32)] public void TiffEncoder_EncodeBiColor_WithDeflateCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.BiColor, TiffEncoderCompression.Deflate); + [Theory] + [WithFile(TestImages.Tiff.Calliphora_BiColor, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeBiColor_WithCcittGroup3FaxCompression_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.BiColor, TiffEncoderCompression.CcittGroup3Fax); + private static void TestTiffEncoderCore( TestImageProvider provider, TiffBitsPerPixel bitsPerPixel, diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 7f16f4aa7..099aa2d5a 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -511,11 +511,12 @@ namespace SixLabors.ImageSharp.Tests public const string Calliphora_RgbLzw_Predictor = "Tiff/Calliphora_rgb_lzw.tiff"; public const string Calliphora_RgbPackbits = "Tiff/Calliphora_rgb_packbits.tiff"; public const string Calliphora_RgbUncompressed = "Tiff/Calliphora_rgb_uncompressed.tiff"; - public const string Calliphora_Fax3Compressed = "Tiff/Calliphora_ccitt_fax3.tif"; - public const string Calliphora_HuffmanCompressed = "Tiff/Calliphora_huffman_rle.tif"; + public const string Calliphora_Fax3Compressed = "Tiff/Calliphora_ccitt_fax3.tiff"; + public const string Calliphora_HuffmanCompressed = "Tiff/Calliphora_huffman_rle.tiff"; + public const string Calliphora_BiColor = "Tiff/Calliphora_bicolor_uncompressed.tiff"; - public const string CcittFax3AllTermCodes = "Tiff/ccitt_fax3_all_terminating_codes.tif"; - public const string CcittFax3AllMakupCodes = "Tiff/ccitt_fax3_all_makeupcodes_codes.tif"; + public const string CcittFax3AllTermCodes = "Tiff/ccitt_fax3_all_terminating_codes.tiff"; + public const string CcittFax3AllMakupCodes = "Tiff/ccitt_fax3_all_makeupcodes_codes.tiff"; public const string GrayscaleDeflateMultistrip = "Tiff/grayscale_deflate_multistrip.tiff"; public const string GrayscaleUncompressed = "Tiff/grayscale_uncompressed.tiff"; @@ -546,7 +547,14 @@ namespace SixLabors.ImageSharp.Tests public const string SampleMetadata = "Tiff/metadata_sample.tiff"; - public static readonly string[] All = { Calliphora_GrayscaleUncompressed, Calliphora_PaletteUncompressed, /*Calliphora_RgbDeflate_Predictor, Calliphora_RgbLzwe_Predictor, */ Calliphora_RgbPackbits, Calliphora_RgbUncompressed, Calliphora_HuffmanCompressed, Calliphora_Fax3Compressed, CcittFax3AllTermCodes, CcittFax3AllMakupCodes, GrayscaleDeflateMultistrip, GrayscaleUncompressed, PaletteDeflateMultistrip, PaletteUncompressed, /*RgbDeflate_Predictor,*/ RgbDeflateMultistrip, /*RgbJpeg,*/ /*RgbLzw_Predictor, RgbLzwMultistrip_Predictor,*/ RgbLzw_NoPredictor_Multistrip, RgbLzw_NoPredictor_Multistrip_Motorola, RgbLzw_NoPredictor_Singlestrip_Motorola, RgbPackbits, RgbPackbitsMultistrip, RgbUncompressed, /* MultiframeLzw_Predictor, MultiFrameDifferentVariants, SampleMetadata,*/ SmallRgbDeflate, SmallRgbLzw, }; + public static readonly string[] All = + { + Calliphora_GrayscaleUncompressed, Calliphora_PaletteUncompressed, /*Calliphora_RgbDeflate_Predictor, Calliphora_RgbLzwe_Predictor, */ Calliphora_RgbPackbits, + Calliphora_BiColor, Calliphora_RgbUncompressed, Calliphora_HuffmanCompressed, Calliphora_Fax3Compressed, CcittFax3AllTermCodes, CcittFax3AllMakupCodes, GrayscaleDeflateMultistrip, + GrayscaleUncompressed, PaletteDeflateMultistrip, PaletteUncompressed, /*RgbDeflate_Predictor,*/ RgbDeflateMultistrip, /*RgbJpeg,*/ /*RgbLzw_Predictor, RgbLzwMultistrip_Predictor,*/ + RgbLzw_NoPredictor_Multistrip, RgbLzw_NoPredictor_Multistrip_Motorola, RgbLzw_NoPredictor_Singlestrip_Motorola, RgbPackbits, RgbPackbitsMultistrip, RgbUncompressed, + /* MultiframeLzw_Predictor, MultiFrameDifferentVariants, SampleMetadata,*/ SmallRgbDeflate, SmallRgbLzw, + }; public static readonly string[] Multiframes = { MultiframeDeflateWithPreview /*MultiframeLzw_Predictor, MultiFrameDifferentSize, MultiframeDifferentSizeTiled, MultiFrameDifferentVariants,*/ }; diff --git a/tests/Images/Input/Tiff/Calliphora_bicolor_uncompressed.tiff b/tests/Images/Input/Tiff/Calliphora_bicolor_uncompressed.tiff new file mode 100644 index 000000000..b0dbdde54 --- /dev/null +++ b/tests/Images/Input/Tiff/Calliphora_bicolor_uncompressed.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ed3b08730e5c34eb8d268f58d1e09efe2605398899bfd726cc3b35de21baa6ff +size 121196 diff --git a/tests/Images/Input/Tiff/Calliphora_ccitt_fax3.tif b/tests/Images/Input/Tiff/Calliphora_ccitt_fax3.tiff similarity index 100% rename from tests/Images/Input/Tiff/Calliphora_ccitt_fax3.tif rename to tests/Images/Input/Tiff/Calliphora_ccitt_fax3.tiff diff --git a/tests/Images/Input/Tiff/Calliphora_huffman_rle.tif b/tests/Images/Input/Tiff/Calliphora_huffman_rle.tiff similarity index 100% rename from tests/Images/Input/Tiff/Calliphora_huffman_rle.tif rename to tests/Images/Input/Tiff/Calliphora_huffman_rle.tiff diff --git a/tests/Images/Input/Tiff/ccitt_fax3_all_makeupcodes_codes.tif b/tests/Images/Input/Tiff/ccitt_fax3_all_makeupcodes_codes.tiff similarity index 100% rename from tests/Images/Input/Tiff/ccitt_fax3_all_makeupcodes_codes.tif rename to tests/Images/Input/Tiff/ccitt_fax3_all_makeupcodes_codes.tiff diff --git a/tests/Images/Input/Tiff/ccitt_fax3_all_terminating_codes.tif b/tests/Images/Input/Tiff/ccitt_fax3_all_terminating_codes.tiff similarity index 100% rename from tests/Images/Input/Tiff/ccitt_fax3_all_terminating_codes.tif rename to tests/Images/Input/Tiff/ccitt_fax3_all_terminating_codes.tiff From 3406764a5433482755eb6c83e541054a531c289f Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 29 Nov 2020 12:49:46 +0100 Subject: [PATCH 103/275] Fix compression namespace --- .../Tiff/Compression/DeflateTiffCompression.cs | 2 +- .../Tiff/Compression/LzwTiffCompression.cs | 2 +- .../Tiff/Compression/NoneTiffCompression.cs | 2 +- .../Tiff/Compression/PackBitsTiffCompression.cs | 2 +- .../Tiff/Compression/TiffBaseCompression.cs | 2 +- .../Tiff/Compression/TiffCompressionFactory.cs | 17 ++++++++--------- ...ionType.cs => TiffDecoderCompressionType.cs} | 6 +++--- src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs | 4 +++- .../Formats/Tiff/TiffDecoderHelpers.cs | 13 +++++++------ .../Compression/DeflateTiffCompressionTests.cs | 5 ++--- .../Tiff/Compression/LzwTiffCompressionTests.cs | 1 + .../Compression/NoneTiffCompressionTests.cs | 2 +- .../Compression/PackBitsTiffCompressionTests.cs | 1 + 13 files changed, 31 insertions(+), 28 deletions(-) rename src/ImageSharp/Formats/Tiff/Compression/{TiffCompressionType.cs => TiffDecoderCompressionType.cs} (88%) diff --git a/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs index 3e07851d4..8251f9aab 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs @@ -6,7 +6,7 @@ using System.IO; using System.IO.Compression; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.Compression { /// /// Class to handle cases where TIFF image data is compressed using Deflate compression. diff --git a/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs index f4caeb3c2..0e98d5303 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs @@ -5,7 +5,7 @@ using System; using System.IO; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.Compression { /// /// Class to handle cases where TIFF image data is compressed using LZW compression. diff --git a/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs index ad6801c6a..39b7ca23d 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs @@ -5,7 +5,7 @@ using System; using System.IO; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.Compression { /// /// Class to handle cases where TIFF image data is not compressed. diff --git a/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs index 9862fea74..dc89b650e 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs @@ -6,7 +6,7 @@ using System.Buffers; using System.IO; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.Compression { /// /// Class to handle cases where TIFF image data is compressed using PackBits compression. diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs index fb05a9f25..f24db500b 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs @@ -5,7 +5,7 @@ using System; using System.IO; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.Compression { /// /// Base tiff decompressor class. diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs index 11f85faa1..d32052fcd 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs @@ -1,28 +1,27 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.Compression { internal static class TiffCompressionFactory { - public static TiffBaseCompression Create(TiffCompressionType compressionType, MemoryAllocator allocator, TiffPhotometricInterpretation photometricInterpretation, int width) + public static TiffBaseCompression Create(TiffDecoderCompressionType compressionType, MemoryAllocator allocator, TiffPhotometricInterpretation photometricInterpretation, int width) { switch (compressionType) { - case TiffCompressionType.None: + case TiffDecoderCompressionType.None: return new NoneTiffCompression(allocator); - case TiffCompressionType.PackBits: + case TiffDecoderCompressionType.PackBits: return new PackBitsTiffCompression(allocator); - case TiffCompressionType.Deflate: + case TiffDecoderCompressionType.Deflate: return new DeflateTiffCompression(allocator); - case TiffCompressionType.Lzw: + case TiffDecoderCompressionType.Lzw: return new LzwTiffCompression(allocator); - case TiffCompressionType.T4: + case TiffDecoderCompressionType.T4: return new T4TiffCompression(allocator, photometricInterpretation, width); - case TiffCompressionType.HuffmanRle: + case TiffDecoderCompressionType.HuffmanRle: return new ModifiedHuffmanTiffCompression(allocator, photometricInterpretation, width); default: throw TiffThrowHelper.NotSupportedCompression(nameof(compressionType)); diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffDecoderCompressionType.cs similarity index 88% rename from src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs rename to src/ImageSharp/Formats/Tiff/Compression/TiffDecoderCompressionType.cs index 8a33948f9..80bc0af5a 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffDecoderCompressionType.cs @@ -1,12 +1,12 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.Compression { /// - /// Provides enumeration of the various TIFF compression types. + /// Provides enumeration of the various TIFF compression types the decoder can handle. /// - internal enum TiffCompressionType + internal enum TiffDecoderCompressionType { /// /// Image data is stored uncompressed in the TIFF file. diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 08b00d5ba..16f64e350 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -5,6 +5,8 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; + +using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; @@ -80,7 +82,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Gets or sets the compression implementation to use when decoding the image. /// - public TiffCompressionType CompressionType { get; set; } + public TiffDecoderCompressionType CompressionType { get; set; } /// /// Gets or sets the planar configuration type to use when decoding the image. diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs index 6db776039..751ecf09e 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; +using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Icc; @@ -323,38 +324,38 @@ namespace SixLabors.ImageSharp.Formats.Tiff { case TiffCompression.None: { - options.CompressionType = TiffCompressionType.None; + options.CompressionType = TiffDecoderCompressionType.None; break; } case TiffCompression.PackBits: { - options.CompressionType = TiffCompressionType.PackBits; + options.CompressionType = TiffDecoderCompressionType.PackBits; break; } case TiffCompression.Deflate: case TiffCompression.OldDeflate: { - options.CompressionType = TiffCompressionType.Deflate; + options.CompressionType = TiffDecoderCompressionType.Deflate; break; } case TiffCompression.Lzw: { - options.CompressionType = TiffCompressionType.Lzw; + options.CompressionType = TiffDecoderCompressionType.Lzw; break; } case TiffCompression.CcittGroup3Fax: { - options.CompressionType = TiffCompressionType.T4; + options.CompressionType = TiffDecoderCompressionType.T4; break; } case TiffCompression.Ccitt1D: { - options.CompressionType = TiffCompressionType.HuffmanRle; + options.CompressionType = TiffDecoderCompressionType.HuffmanRle; break; } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs index 10f5819ac..db802d7d7 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs @@ -3,8 +3,7 @@ using System.IO; using SixLabors.ImageSharp.Formats.Png.Zlib; -using SixLabors.ImageSharp.Formats.Tiff; -using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Formats.Tiff.Compression; using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff @@ -22,7 +21,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { using (Stream stream = CreateCompressedStream(data)) { - byte[] buffer = new byte[data.Length]; + var buffer = new byte[data.Length]; new DeflateTiffCompression(null).Decompress(stream, (int)stream.Length, buffer); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs index 3a0ceae74..5c9ef2d31 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs @@ -3,6 +3,7 @@ using System.IO; using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Tiff.Compression; using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs index c1cde9466..24820b906 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System.IO; -using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Tiff.Compression; using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs index b08648465..de8e11f77 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs @@ -3,6 +3,7 @@ using System.IO; using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Memory; using Xunit; From 0d5f255e4e39f6aa2a96a0ba5648c47526b63858 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 29 Nov 2020 13:24:51 +0100 Subject: [PATCH 104/275] Add support for encoding modified huffman RLE --- .../Formats/Tiff/Compression/T4BitReader.cs | 1 + .../Formats/Tiff/Compression/T4BitWriter.cs | 42 ++++++++++++++++--- src/ImageSharp/Formats/Tiff/README.md | 8 ++-- .../Formats/Tiff/TiffBitsPerPixel.cs | 5 +++ .../Formats/Tiff/TiffDecoderCore.cs | 4 ++ .../Formats/Tiff/TiffEncoderCompression.cs | 5 +++ .../Formats/Tiff/TiffEncoderCore.cs | 11 ++++- .../Formats/Tiff/Utils/TiffWriter.cs | 17 +++++--- .../Formats/Tiff/TiffEncoderTests.cs | 10 ++++- 9 files changed, 86 insertions(+), 17 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs index ed2fad7ed..672f4a008 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs @@ -6,6 +6,7 @@ using System.Buffers; using System.Collections.Generic; using System.IO; using System.Linq; + using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Tiff.Compression diff --git a/src/ImageSharp/Formats/Tiff/Compression/T4BitWriter.cs b/src/ImageSharp/Formats/Tiff/Compression/T4BitWriter.cs index 0dd79410f..ee924fc77 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/T4BitWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/T4BitWriter.cs @@ -5,6 +5,7 @@ using System; using System.Buffers; using System.Collections.Generic; using System.IO; + using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -183,17 +184,24 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression private byte bitPosition; + /// + /// The modified huffman is basically the same as CCITT T4, but without EOL markers and padding at the end of the rows. + /// + private bool useModifiedHuffman; + /// /// Initializes a new instance of the class. /// /// The memory allocator. /// The configuration. - public T4BitWriter(MemoryAllocator memoryAllocator, Configuration configuration) + /// Indicates if the modified huffman RLE should be used. + public T4BitWriter(MemoryAllocator memoryAllocator, Configuration configuration, bool useModifiedHuffman = false) { this.memoryAllocator = memoryAllocator; this.configuration = configuration; this.bytePosition = 0; this.bitPosition = 0; + this.useModifiedHuffman = useModifiedHuffman; } /// @@ -215,9 +223,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression this.bytePosition = 0; this.bitPosition = 0; - // An EOL code is expected at the start of the data. - this.WriteCode(12, 1, compressedData); + if (!this.useModifiedHuffman) + { + // An EOL code is expected at the start of the data. + this.WriteCode(12, 1, compressedData); + } + uint pixelsWritten = 0; for (int y = 0; y < image.Height; y++) { bool isWhiteRun = true; @@ -268,6 +280,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression code = this.GetTermCode(runLength, out codeLength, isWhiteRun); this.WriteCode(codeLength, code, compressedData); x += (int)runLength; + pixelsWritten += runLength; } else { @@ -275,6 +288,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression code = this.GetMakeupCode(runLength, out codeLength, isWhiteRun); this.WriteCode(codeLength, code, compressedData); x += (int)runLength; + pixelsWritten += runLength; // If we are at the end of the line with a makeup code, we need to write a final term code with a length of zero. if (x == image.Width) @@ -296,8 +310,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression isWhiteRun = !isWhiteRun; } - // Write EOL. - this.WriteCode(12, 1, compressedData); + this.WriteEndOfLine(compressedData); } // Write the compressed data to the stream. @@ -306,6 +319,25 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression return this.bytePosition; } + private void WriteEndOfLine(Span compressedData) + { + if (this.useModifiedHuffman) + { + // Check if padding is necessary. + if (this.bitPosition % 8 != 0) + { + // Skip padding bits, move to next byte. + this.bytePosition++; + this.bitPosition = 0; + } + } + else + { + // Write EOL. + this.WriteCode(12, 1, compressedData); + } + } + private void WriteCode(uint codeLength, uint code, Span compressedData) { while (codeLength > 0) diff --git a/src/ImageSharp/Formats/Tiff/README.md b/src/ImageSharp/Formats/Tiff/README.md index feefdd55a..6cfda9df9 100644 --- a/src/ImageSharp/Formats/Tiff/README.md +++ b/src/ImageSharp/Formats/Tiff/README.md @@ -41,9 +41,9 @@ | |Encoder|Decoder|Comments | |---------------------------|:-----:|:-----:|--------------------------| |None | Y | Y | | -|Ccitt1D | | Y | | +|Ccitt1D | Y | Y | | |PackBits | | Y | | -|CcittGroup3Fax | | Y | | +|CcittGroup3Fax | Y | Y | | |CcittGroup4Fax | | | | |Lzw | | Y | Based on ImageSharp GIF LZW implementation - this code could be modified to be (i) shared, or (ii) optimised for each case | |Old Jpeg | | | We should not even try to support this | @@ -55,8 +55,8 @@ | |Encoder|Decoder|Comments | |---------------------------|:-----:|:-----:|--------------------------| -|WhiteIsZero | | Y | General + 1/4/8-bit optimised implementations | -|BlackIsZero | | Y | General + 1/4/8-bit optimised implementations | +|WhiteIsZero | Y | Y | General + 1/4/8-bit optimised implementations | +|BlackIsZero | Y | Y | General + 1/4/8-bit optimised implementations | |Rgb (Chunky) | Y | Y | General + Rgb888 optimised implementation | |Rgb (Planar) | | Y | General implementation only | |PaletteColor | Y | Y | General implementation only | diff --git a/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs b/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs index 502c2e425..fe53a1bd3 100644 --- a/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs +++ b/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs @@ -8,6 +8,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// public enum TiffBitsPerPixel { + /// + /// 1 bits per pixel, bi-color image. Each pixel consists of 1 bit. + /// + Pixel1 = 1, + /// /// 8 bits per pixel, grayscale image. Each pixel consists of 1 byte. /// diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 16f64e350..e3806ee54 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -159,6 +159,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff { this.tiffMetaData.BitsPerPixel = TiffBitsPerPixel.Pixel8; } + else if (bitsPerPixel == 1) + { + this.tiffMetaData.BitsPerPixel = TiffBitsPerPixel.Pixel1; + } } /// diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs index 30702641a..c76935b3a 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs @@ -22,5 +22,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// Use CCITT T4 1D compression. Note: This is only valid for bi-level images. /// CcittGroup3Fax, + + /// + /// Use the modified Huffman RLE. Note: This is only valid for bi-level images. + /// + ModifiedHuffman, } } diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 742c2da42..f4e516168 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -98,6 +98,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff { this.Mode = TiffEncodingMode.Gray; } + else if (this.bitsPerPixel == TiffBitsPerPixel.Pixel1) + { + this.Mode = TiffEncodingMode.BiColor; + } } this.SetPhotometricInterpretation(); @@ -341,7 +345,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff this.PhotometricInterpretation = TiffPhotometricInterpretation.PaletteColor; break; case TiffEncodingMode.BiColor: - if (this.CompressionType == TiffEncoderCompression.CcittGroup3Fax) + if (this.CompressionType == TiffEncoderCompression.CcittGroup3Fax || this.CompressionType == TiffEncoderCompression.ModifiedHuffman) { // The “normal” PhotometricInterpretation for bilevel CCITT compressed data is WhiteIsZero. this.PhotometricInterpretation = TiffPhotometricInterpretation.WhiteIsZero; @@ -431,6 +435,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff return (ushort)TiffCompression.CcittGroup3Fax; } + if (this.CompressionType == TiffEncoderCompression.ModifiedHuffman && this.Mode == TiffEncodingMode.BiColor) + { + return (ushort)TiffCompression.Ccitt1D; + } + return (ushort)TiffCompression.None; } } diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs index eaa71c953..245bdb74e 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -376,18 +376,25 @@ namespace SixLabors.ImageSharp.Formats.Tiff Span pixelRowAsGraySpan = pixelRowAsGray.GetSpan(); // Convert image to black and white. - using Image imageClone = image.Clone(); - imageClone.Mutate(img => img.BinaryDither(default(ErrorDither))); + // TODO: Should we allow to skip this by the user, if its known to be black and white already? + using Image imageBlackWhite = image.Clone(); + imageBlackWhite.Mutate(img => img.BinaryDither(default(ErrorDither))); if (compression == TiffEncoderCompression.Deflate) { - return this.WriteBiColorDeflate(image, pixelRowAsGraySpan, outputRow); + return this.WriteBiColorDeflate(imageBlackWhite, pixelRowAsGraySpan, outputRow); } if (compression == TiffEncoderCompression.CcittGroup3Fax) { var bitWriter = new T4BitWriter(this.memoryAllocator, this.configuration); - return bitWriter.CompressImage(image, pixelRowAsGraySpan, this.output); + return bitWriter.CompressImage(imageBlackWhite, pixelRowAsGraySpan, this.output); + } + + if (compression == TiffEncoderCompression.ModifiedHuffman) + { + var bitWriter = new T4BitWriter(this.memoryAllocator, this.configuration, useModifiedHuffman: true); + return bitWriter.CompressImage(imageBlackWhite, pixelRowAsGraySpan, this.output); } int bytesWritten = 0; @@ -395,7 +402,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff { int bitIndex = 0; int byteIndex = 0; - Span pixelRow = imageClone.GetPixelRowSpan(y); + Span pixelRow = imageBlackWhite.GetPixelRowSpan(y); PixelOperations.Instance.ToL8(this.configuration, pixelRow, pixelRowAsGraySpan); for (int x = 0; x < pixelRow.Length; x++) { diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 03d0b2eef..5b09324df 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -21,6 +21,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public static readonly TheoryData TiffBitsPerPixelFiles = new TheoryData { + { TestImages.Tiff.Calliphora_BiColor, TiffBitsPerPixel.Pixel1 }, { TestImages.Tiff.GrayscaleUncompressed, TiffBitsPerPixel.Pixel8 }, { TestImages.Tiff.RgbUncompressed, TiffBitsPerPixel.Pixel24 }, }; @@ -85,12 +86,17 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Theory] [WithFile(TestImages.Tiff.Calliphora_BiColor, PixelTypes.Rgba32)] public void TiffEncoder_EncodeBiColor_WithDeflateCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.BiColor, TiffEncoderCompression.Deflate); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel1, TiffEncodingMode.BiColor, TiffEncoderCompression.Deflate); [Theory] [WithFile(TestImages.Tiff.Calliphora_BiColor, PixelTypes.Rgba32)] public void TiffEncoder_EncodeBiColor_WithCcittGroup3FaxCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.BiColor, TiffEncoderCompression.CcittGroup3Fax); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel1, TiffEncodingMode.BiColor, TiffEncoderCompression.CcittGroup3Fax); + + [Theory] + [WithFile(TestImages.Tiff.Calliphora_BiColor, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeBiColor_WithModifiedHuffmanCompression_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel1, TiffEncodingMode.BiColor, TiffEncoderCompression.ModifiedHuffman); private static void TestTiffEncoderCore( TestImageProvider provider, From d3a8f5ef690370359b659ed10c6ed024cf860c0e Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 30 Nov 2020 18:00:50 +0100 Subject: [PATCH 105/275] Add support for encoding packed bits compressed tiff's --- .../Compression/DeflateTiffCompression.cs | 1 + .../Tiff/Compression/LzwTiffCompression.cs | 1 + .../ModifiedHuffmanTiffCompression.cs | 1 + .../Tiff/Compression/NoneTiffCompression.cs | 2 + .../Compression/PackBitsTiffCompression.cs | 2 + .../Tiff/Compression/PackBitsWriter.cs | 128 +++++++++++++++++ .../BlackIsZeroTiffColor.cs | 1 + .../PaletteTiffColor.cs | 1 + .../RgbPlanarTiffColor.cs | 1 + .../PhotometricInterpretation/RgbTiffColor.cs | 1 + .../WhiteIsZeroTiffColor.cs | 1 + .../Formats/Tiff/TiffEncoderCompression.cs | 5 + .../Formats/Tiff/TiffEncoderCore.cs | 18 ++- .../Formats/Tiff/Utils/BitReader.cs | 2 +- .../Formats/Tiff/Utils/SubStream.cs | 4 +- .../Formats/Tiff/Utils/TiffLzwDecoder.cs | 2 +- .../Formats/Tiff/Utils/TiffLzwEncoder.cs | 6 +- .../Formats/Tiff/Utils/TiffUtils.cs | 2 +- .../Formats/Tiff/Utils/TiffWriter.cs | 131 +++++++++++++++++- .../DeflateTiffCompressionTests.cs | 2 +- .../Compression/LzwTiffCompressionTests.cs | 16 +-- .../Compression/NoneTiffCompressionTests.cs | 2 +- .../PackBitsTiffCompressionTests.cs | 28 +++- .../Formats/Tiff/TiffEncoderHeaderTests.cs | 3 + .../Formats/Tiff/TiffEncoderTests.cs | 23 ++- .../Formats/Tiff/Utils/SubStreamTests.cs | 4 +- .../Formats/Tiff/Utils/TiffWriterTests.cs | 4 +- 27 files changed, 357 insertions(+), 35 deletions(-) create mode 100644 src/ImageSharp/Formats/Tiff/Compression/PackBitsWriter.cs diff --git a/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs index 8251f9aab..772d782ef 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs @@ -4,6 +4,7 @@ using System; using System.IO; using System.IO.Compression; +using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Tiff.Compression diff --git a/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs index 0e98d5303..ffce22145 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs @@ -3,6 +3,7 @@ using System; using System.IO; +using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Tiff.Compression diff --git a/src/ImageSharp/Formats/Tiff/Compression/ModifiedHuffmanTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/ModifiedHuffmanTiffCompression.cs index 63908ff2f..95a41ed54 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/ModifiedHuffmanTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/ModifiedHuffmanTiffCompression.cs @@ -3,6 +3,7 @@ using System; using System.IO; + using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Tiff.Compression diff --git a/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs index 39b7ca23d..a8bfe624d 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs @@ -3,6 +3,8 @@ using System; using System.IO; + +using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Tiff.Compression diff --git a/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs index dc89b650e..a473fcf26 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs @@ -4,6 +4,8 @@ using System; using System.Buffers; using System.IO; + +using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Tiff.Compression diff --git a/src/ImageSharp/Formats/Tiff/Compression/PackBitsWriter.cs b/src/ImageSharp/Formats/Tiff/Compression/PackBitsWriter.cs new file mode 100644 index 000000000..389ad628e --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Compression/PackBitsWriter.cs @@ -0,0 +1,128 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp.Formats.Tiff.Compression +{ + /// + /// Pack Bits compression for tiff images. See Tiff Spec v6, section 9. + /// + internal static class PackBitsWriter + { + public static int PackBits(Span rowSpan, Span compressedRowSpan) + { + int maxRunLength = 127; + int posInRowSpan = 0; + int bytesWritten = 0; + int literalRunLength = 0; + + while (posInRowSpan < rowSpan.Length) + { + var useReplicateRun = IsReplicateRun(rowSpan, posInRowSpan); + if (useReplicateRun) + { + if (literalRunLength > 0) + { + WriteLiteralRun(rowSpan, posInRowSpan, literalRunLength, compressedRowSpan, bytesWritten); + bytesWritten += literalRunLength + 1; + } + + // Write a run with the same bytes. + var runLength = FindRunLength(rowSpan, posInRowSpan, maxRunLength); + WriteRun(rowSpan, posInRowSpan, runLength, compressedRowSpan, bytesWritten); + + bytesWritten += 2; + literalRunLength = 0; + posInRowSpan += runLength; + continue; + } + + literalRunLength++; + posInRowSpan++; + + if (literalRunLength >= maxRunLength) + { + WriteLiteralRun(rowSpan, posInRowSpan, literalRunLength, compressedRowSpan, bytesWritten); + bytesWritten += literalRunLength + 1; + literalRunLength = 0; + } + } + + if (literalRunLength > 0) + { + WriteLiteralRun(rowSpan, posInRowSpan, literalRunLength, compressedRowSpan, bytesWritten); + bytesWritten += literalRunLength + 1; + } + + return bytesWritten; + } + + private static void WriteLiteralRun(Span rowSpan, int end, int literalRunLength, Span compressedRowSpan, int compressedRowPos) + { + DebugGuard.MustBeLessThanOrEqualTo(literalRunLength, 127, nameof(literalRunLength)); + + int literalRunStart = end - literalRunLength; + sbyte runLength = (sbyte)(literalRunLength - 1); + compressedRowSpan[compressedRowPos] = (byte)runLength; + rowSpan.Slice(literalRunStart, literalRunLength).CopyTo(compressedRowSpan.Slice(compressedRowPos + 1)); + } + + private static void WriteRun(Span rowSpan, int start, int runLength, Span compressedRowSpan, int compressedRowPos) + { + DebugGuard.MustBeLessThanOrEqualTo(runLength, 127, nameof(runLength)); + + sbyte headerByte = (sbyte)(-runLength + 1); + compressedRowSpan[compressedRowPos] = (byte)headerByte; + compressedRowSpan[compressedRowPos + 1] = rowSpan[start]; + } + + private static bool IsReplicateRun(Span rowSpan, int startPos) + { + // We consider run which has at least 3 same consecutive bytes a candidate for a run. + var startByte = rowSpan[startPos]; + int count = 0; + for (int i = startPos + 1; i < rowSpan.Length; i++) + { + if (rowSpan[i] == startByte) + { + count++; + if (count >= 2) + { + return true; + } + } + else + { + break; + } + } + + return false; + } + + private static int FindRunLength(Span rowSpan, int startPos, int maxRunLength) + { + var startByte = rowSpan[startPos]; + int count = 1; + for (int i = startPos + 1; i < rowSpan.Length; i++) + { + if (rowSpan[i] == startByte) + { + count++; + } + else + { + break; + } + + if (count == maxRunLength) + { + break; + } + } + + return count; + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs index 91518c662..d2bc2f47e 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs @@ -3,6 +3,7 @@ using System; using System.Numerics; +using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs index 0af9d8698..eb3381b70 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs @@ -3,6 +3,7 @@ using System; using System.Numerics; +using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs index 3bd263ef3..3f96bc220 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System.Numerics; +using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs index bcc303f17..74a4b9496 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs @@ -3,6 +3,7 @@ using System; using System.Numerics; +using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs index dda338d3b..8257dcec2 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs @@ -3,6 +3,7 @@ using System; using System.Numerics; +using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs index c76935b3a..1bd84a60a 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs @@ -18,6 +18,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// Deflate, + /// + /// Use PackBits to compression the image data. + /// + PackBits, + /// /// Use CCITT T4 1D compression. Note: This is only valid for bi-level images. /// diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index f4e516168..f3b138fbf 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -7,6 +7,7 @@ using System.IO; using System.Threading; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; @@ -166,7 +167,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff imageDataBytes = writer.WriteBiColor(image, this.CompressionType); break; default: - imageDataBytes = writer.WriteRgbImageData(image, this.padding, this.CompressionType); + imageDataBytes = writer.WriteRgb(image, this.padding, this.CompressionType); break; } @@ -415,11 +416,21 @@ namespace SixLabors.ImageSharp.Formats.Tiff return (ushort)TiffCompression.Deflate; } + if (this.CompressionType == TiffEncoderCompression.PackBits && this.Mode == TiffEncodingMode.Rgb) + { + return (ushort)TiffCompression.PackBits; + } + if (this.CompressionType == TiffEncoderCompression.Deflate && this.Mode == TiffEncodingMode.Gray) { return (ushort)TiffCompression.Deflate; } + if (this.CompressionType == TiffEncoderCompression.PackBits && this.Mode == TiffEncodingMode.Gray) + { + return (ushort)TiffCompression.PackBits; + } + if (this.CompressionType == TiffEncoderCompression.Deflate && this.Mode == TiffEncodingMode.ColorPalette) { return (ushort)TiffCompression.Deflate; @@ -430,6 +441,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff return (ushort)TiffCompression.Deflate; } + if (this.CompressionType == TiffEncoderCompression.PackBits && this.Mode == TiffEncodingMode.BiColor) + { + return (ushort)TiffCompression.PackBits; + } + if (this.CompressionType == TiffEncoderCompression.CcittGroup3Fax && this.Mode == TiffEncodingMode.BiColor) { return (ushort)TiffCompression.CcittGroup3Fax; diff --git a/src/ImageSharp/Formats/Tiff/Utils/BitReader.cs b/src/ImageSharp/Formats/Tiff/Utils/BitReader.cs index 0093f342a..40e67c1b0 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/BitReader.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.Utils { /// /// Utility class to read a sequence of bits from an array diff --git a/src/ImageSharp/Formats/Tiff/Utils/SubStream.cs b/src/ImageSharp/Formats/Tiff/Utils/SubStream.cs index 1a4da9a31..e83c1f062 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/SubStream.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/SubStream.cs @@ -4,7 +4,7 @@ using System; using System.IO; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.Utils { /// /// Utility class to encapsulate a sub-portion of another . @@ -173,4 +173,4 @@ namespace SixLabors.ImageSharp.Formats.Tiff throw new NotSupportedException(); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs index 2e95d7e5a..4322b04b1 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs @@ -7,7 +7,7 @@ using System.IO; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.Utils { /// /// Decompresses and decodes data using the dynamic LZW algorithms. diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs index a18a820a3..f05f596bf 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System; @@ -6,7 +6,7 @@ using System.Buffers; using System.IO; using SixLabors.ImageSharp.Formats.Gif; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.Utils { /// /// Encodes and compresses the image data using dynamic Lempel-Ziv compression. @@ -492,4 +492,4 @@ namespace SixLabors.ImageSharp.Formats.Tiff this.isDisposed = true; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs index 5c68ca14d..4112ba4ba 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs @@ -4,7 +4,7 @@ using System; using System.IO; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.Utils { /// /// TIFF specific utilities and extension methods. diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs index 245bdb74e..f57f05645 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -3,7 +3,6 @@ using System; using System.Buffers; -using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; @@ -17,7 +16,7 @@ using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Dithering; using SixLabors.ImageSharp.Processing.Processors.Quantization; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.Utils { /// /// Utility class for writing TIFF data to a . @@ -32,8 +31,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff private readonly byte[] paddingBytes = new byte[4]; - private readonly List references = new List(); - /// /// Initializes a new instance of the class. /// @@ -141,7 +138,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The padding bytes for each row. /// The compression to use. /// The number of bytes written. - public int WriteRgbImageData(Image image, int padding, TiffEncoderCompression compression) + public int WriteRgb(Image image, int padding, TiffEncoderCompression compression) where TPixel : unmanaged, IPixel { using IManagedByteBuffer row = this.AllocateRow(image.Width, 3, padding); @@ -151,6 +148,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff return this.WriteDeflateCompressedRgb(image, rowSpan); } + if (compression == TiffEncoderCompression.PackBits) + { + return this.WriteRgbPackBitsCompressed(image, rowSpan); + } + // No compression. int bytesWritten = 0; for (int y = 0; y < image.Height; y++) @@ -195,6 +197,35 @@ namespace SixLabors.ImageSharp.Formats.Tiff return bytesWritten; } + /// + /// Writes the image data as RGB with packed bits compression to the stream. + /// + /// The pixel data. + /// The image to write to the stream. + /// A Span for a pixel row. + /// The number of bytes written. + private int WriteRgbPackBitsCompressed(Image image, Span rowSpan) + where TPixel : unmanaged, IPixel + { + // Worst case is that the actual compressed data is larger then the input data. In this case we need 1 additional byte per 127 bytes. + int additionalBytes = ((image.Width * 3) / 127) + 1; + using IManagedByteBuffer compressedRow = this.memoryAllocator.AllocateManagedByteBuffer((image.Width * 3) + additionalBytes, AllocationOptions.Clean); + Span compressedRowSpan = compressedRow.GetSpan(); + int bytesWritten = 0; + + for (int y = 0; y < image.Height; y++) + { + Span pixelRow = image.GetPixelRowSpan(y); + PixelOperations.Instance.ToRgb24Bytes(this.configuration, pixelRow, rowSpan, pixelRow.Length); + int size = PackBitsWriter.PackBits(rowSpan, compressedRowSpan); + this.output.Write(compressedRow.Slice(0, size)); + bytesWritten += size; + compressedRowSpan.Clear(); + } + + return bytesWritten; + } + /// /// Writes the image data as indices into a color map to the stream. /// @@ -316,6 +347,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff return this.WriteGrayDeflateCompressed(image, rowSpan); } + if (compression == TiffEncoderCompression.PackBits) + { + return this.WriteGrayPackBitsCompressed(image, rowSpan); + } + int bytesWritten = 0; for (int y = 0; y < image.Height; y++) { @@ -358,6 +394,35 @@ namespace SixLabors.ImageSharp.Formats.Tiff return bytesWritten; } + /// + /// Writes the image data as 8 bit gray to the stream. + /// + /// The pixel data. + /// The image to write to the stream. + /// A span of a row of pixels. + /// The number of bytes written. + private int WriteGrayPackBitsCompressed(Image image, Span rowSpan) + where TPixel : unmanaged, IPixel + { + // Worst case is that the actual compressed data is larger then the input data. In this case we need 1 additional byte per 127 bytes. + int additionalBytes = (image.Width / 127) + 1; + using IManagedByteBuffer compressedRow = this.memoryAllocator.AllocateManagedByteBuffer(image.Width + additionalBytes, AllocationOptions.Clean); + Span compressedRowSpan = compressedRow.GetSpan(); + + int bytesWritten = 0; + for (int y = 0; y < image.Height; y++) + { + Span pixelRow = image.GetPixelRowSpan(y); + PixelOperations.Instance.ToL8Bytes(this.configuration, pixelRow, rowSpan, pixelRow.Length); + int size = PackBitsWriter.PackBits(rowSpan, compressedRowSpan); + this.output.Write(compressedRow.Slice(0, size)); + bytesWritten += size; + compressedRowSpan.Clear(); + } + + return bytesWritten; + } + /// /// Writes the image data as 1 bit black and white to the stream. /// @@ -385,6 +450,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff return this.WriteBiColorDeflate(imageBlackWhite, pixelRowAsGraySpan, outputRow); } + if (compression == TiffEncoderCompression.PackBits) + { + return this.WriteBiColorPackBits(imageBlackWhite, pixelRowAsGraySpan, outputRow); + } + if (compression == TiffEncoderCompression.CcittGroup3Fax) { var bitWriter = new T4BitWriter(this.memoryAllocator, this.configuration); @@ -397,6 +467,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff return bitWriter.CompressImage(imageBlackWhite, pixelRowAsGraySpan, this.output); } + // Write image uncompressed. int bytesWritten = 0; for (int y = 0; y < image.Height; y++) { @@ -479,6 +550,56 @@ namespace SixLabors.ImageSharp.Formats.Tiff return bytesWritten; } + /// + /// Writes the image data as 1 bit black and white with pack bits compression to the stream. + /// + /// The pixel data. + /// The image to write to the stream. + /// A span for converting a pixel row to gray. + /// A span which will be used to store the output pixels. + /// The number of bytes written. + public int WriteBiColorPackBits(Image image, Span pixelRowAsGraySpan, Span outputRow) + where TPixel : unmanaged, IPixel + { + // Worst case is that the actual compressed data is larger then the input data. In this case we need 1 additional byte per 127 bits. + int additionalBytes = (image.Width / 127) + 1; + int compressedRowBytes = (image.Width / 8) + additionalBytes; + using IManagedByteBuffer compressedRow = this.memoryAllocator.AllocateManagedByteBuffer(compressedRowBytes, AllocationOptions.Clean); + Span compressedRowSpan = compressedRow.GetSpan(); + + int bytesWritten = 0; + for (int y = 0; y < image.Height; y++) + { + int bitIndex = 0; + int byteIndex = 0; + Span pixelRow = image.GetPixelRowSpan(y); + PixelOperations.Instance.ToL8(this.configuration, pixelRow, pixelRowAsGraySpan); + for (int x = 0; x < pixelRow.Length; x++) + { + int shift = 7 - bitIndex; + if (pixelRowAsGraySpan[x].PackedValue == 255) + { + outputRow[byteIndex] |= (byte)(1 << shift); + } + + bitIndex++; + if (bitIndex == 8) + { + byteIndex++; + bitIndex = 0; + } + } + + var size = PackBitsWriter.PackBits(outputRow, compressedRowSpan); + this.output.Write(compressedRowSpan); + bytesWritten += size; + + outputRow.Clear(); + } + + return bytesWritten; + } + private IManagedByteBuffer AllocateRow(int width, int bytesPerPixel, int padding) => this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, bytesPerPixel, padding); /// diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs index db802d7d7..2f71b0bd9 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs @@ -6,7 +6,7 @@ using SixLabors.ImageSharp.Formats.Png.Zlib; using SixLabors.ImageSharp.Formats.Tiff.Compression; using Xunit; -namespace SixLabors.ImageSharp.Tests.Formats.Tiff +namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression { [Trait("Category", "Tiff")] public class DeflateTiffCompressionTests diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs index 5c9ef2d31..4dc643e52 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs @@ -2,11 +2,13 @@ // Licensed under the Apache License, Version 2.0. using System.IO; -using SixLabors.ImageSharp.Formats.Tiff; + using SixLabors.ImageSharp.Formats.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Tiff.Utils; + using Xunit; -namespace SixLabors.ImageSharp.Tests.Formats.Tiff +namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression { [Trait("Category", "Tiff")] public class LzwTiffCompressionTests @@ -19,14 +21,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [InlineData(new byte[] { 1, 2, 42, 53, 42, 53, 42, 53, 42, 53, 42, 53, 3, 4 })] // Repeated sequence public void Decompress_ReadsData(byte[] data) { - using (Stream stream = CreateCompressedStream(data)) - { - var buffer = new byte[data.Length]; + using Stream stream = CreateCompressedStream(data); + var buffer = new byte[data.Length]; - new LzwTiffCompression(Configuration.Default.MemoryAllocator).Decompress(stream, (int)stream.Length, buffer); + new LzwTiffCompression(Configuration.Default.MemoryAllocator).Decompress(stream, (int)stream.Length, buffer); - Assert.Equal(data, buffer); - } + Assert.Equal(data, buffer); } private static Stream CreateCompressedStream(byte[] data) diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs index 24820b906..8366c4de3 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs @@ -5,7 +5,7 @@ using System.IO; using SixLabors.ImageSharp.Formats.Tiff.Compression; using Xunit; -namespace SixLabors.ImageSharp.Tests.Formats.Tiff +namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression { [Trait("Category", "Tiff")] public class NoneTiffCompressionTests diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs index de8e11f77..923b95e37 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs @@ -1,13 +1,15 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; using System.IO; -using SixLabors.ImageSharp.Formats.Tiff; + using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Memory; + using Xunit; -namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Formats.Tiff +namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression { [Trait("Category", "Tiff")] public class PackBitsTiffCompressionTests @@ -20,16 +22,32 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Formats.Tiff [InlineData(new byte[] { 0xFE, 0x2A }, new byte[] { 0x2A, 0x2A, 0x2A })] // Repeat three bytes [InlineData(new byte[] { 0x80 }, new byte[] { })] // Read a 'No operation' byte [InlineData(new byte[] { 0x01, 0x15, 0x32, 0x80, 0xFF, 0xA2 }, new byte[] { 0x15, 0x32, 0xA2, 0xA2 })] // Read two bytes, nop, repeat two bytes - [InlineData(new byte[] { 0xFE, 0xAA, 0x02, 0x80, 0x00, 0x2A, 0xFD, 0xAA, 0x03, 0x80, 0x00, 0x2A, 0x22, 0xF7, 0xAA }, - new byte[] { 0xAA, 0xAA, 0xAA, 0x80, 0x00, 0x2A, 0xAA, 0xAA, 0xAA, 0xAA, 0x80, 0x00, 0x2A, 0x22, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA })] // Apple PackBits sample + [InlineData(new byte[] { 0xFE, 0xAA, 0x02, 0x80, 0x00, 0x2A, 0xFD, 0xAA, 0x03, 0x80, 0x00, 0x2A, 0x22, 0xF7, 0xAA }, new byte[] { 0xAA, 0xAA, 0xAA, 0x80, 0x00, 0x2A, 0xAA, 0xAA, 0xAA, 0xAA, 0x80, 0x00, 0x2A, 0x22, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA })] // Apple PackBits sample public void Decompress_ReadsData(byte[] inputData, byte[] expectedResult) { Stream stream = new MemoryStream(inputData); - byte[] buffer = new byte[expectedResult.Length]; + var buffer = new byte[expectedResult.Length]; new PackBitsTiffCompression(new ArrayPoolMemoryAllocator()).Decompress(stream, inputData.Length, buffer); Assert.Equal(expectedResult, buffer); } + + [Theory] + [InlineData(new byte[] { 0xAA, 0xAA, 0xAA, 0xCD, 0xBB, 0xBB, 0xBB, 0xBB }, new byte[] { 0xFE, 0xAA, 0x02, 0xCD, 0xFD, 0xBB })] // A run of 3, then one byte, followed by a run of 4. + [InlineData(new byte[] { 0xAB, 0xCD, 0xEF }, new byte[] { 0x04, 0xAB, 0xCD, 0xEF })] // all bytes are different. + [InlineData(new byte[] { 0xAA, 0xAA, 0xAA, 0x80, 0x00, 0x2A, 0xAA, 0xAA, 0xAA, 0xAA, 0x80, 0x00, 0x2A, 0x22, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA }, new byte[] { 0xFE, 0xAA, 0x02, 0x80, 0x00, 0x2A, 0xFD, 0xAA, 0x03, 0x80, 0x00, 0x2A, 0x22, 0xF7, 0xAA })] // Apple PackBits sample + public void Compress_Works(byte[] inputData, byte[] expectedResult) + { + // arrange + Span input = inputData.AsSpan(); + var compressed = new byte[expectedResult.Length]; + + // act + PackBitsWriter.PackBits(input, compressed); + + // assert + Assert.Equal(expectedResult, compressed); + } } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs index e279ea562..37db9a6c7 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs @@ -2,8 +2,11 @@ // Licensed under the Apache License, Version 2.0. using System.IO; + using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; + using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 5b09324df..294a9c0cf 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -47,25 +47,35 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff } [Theory] - [WithFile(TestImages.Tiff.RgbUncompressed, PixelTypes.Rgba32)] + [WithFile(TestImages.Tiff.Calliphora_RgbUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeRgb_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.Rgb); [Theory] - [WithFile(TestImages.Tiff.RgbUncompressed, PixelTypes.Rgba32)] + [WithFile(TestImages.Tiff.Calliphora_RgbUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeRgb_WithDeflateCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.Rgb, TiffEncoderCompression.Deflate); [Theory] - [WithFile(TestImages.Tiff.GrayscaleUncompressed, PixelTypes.Rgba32)] + [WithFile(TestImages.Tiff.Calliphora_RgbUncompressed, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeRgb_WithPackBitsCompression_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.Rgb, TiffEncoderCompression.PackBits); + + [Theory] + [WithFile(TestImages.Tiff.Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeGray_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.Gray); [Theory] - [WithFile(TestImages.Tiff.GrayscaleUncompressed, PixelTypes.Rgba32)] + [WithFile(TestImages.Tiff.Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeGray_WithDeflateCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.Gray, TiffEncoderCompression.Deflate); + [Theory] + [WithFile(TestImages.Tiff.Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeGray_WithPackBitsCompression_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.Gray, TiffEncoderCompression.PackBits); + // TODO: this test fails, but the output looks correct. I thinks its due to the fact that a quantizer is used to create the palette. [Theory] [WithFile(TestImages.Tiff.Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] @@ -88,6 +98,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public void TiffEncoder_EncodeBiColor_WithDeflateCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel1, TiffEncodingMode.BiColor, TiffEncoderCompression.Deflate); + [Theory] + [WithFile(TestImages.Tiff.Calliphora_BiColor, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeBiColor_WithPackBitsCompression_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel1, TiffEncodingMode.BiColor, TiffEncoderCompression.PackBits); + [Theory] [WithFile(TestImages.Tiff.Calliphora_BiColor, PixelTypes.Rgba32)] public void TiffEncoder_EncodeBiColor_WithCcittGroup3FaxCompression_Works(TestImageProvider provider) diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Utils/SubStreamTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Utils/SubStreamTests.cs index dbb053b90..14f46d2a7 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Utils/SubStreamTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Utils/SubStreamTests.cs @@ -3,7 +3,9 @@ using System; using System.IO; -using SixLabors.ImageSharp.Formats.Tiff; + +using SixLabors.ImageSharp.Formats.Tiff.Utils; + using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs index 9023fe3e0..15b495556 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs @@ -2,8 +2,10 @@ // Licensed under the Apache License, Version 2.0. using System.IO; -using SixLabors.ImageSharp.Formats.Tiff; + +using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; + using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff From b04b9f4c7f163df99cb1bd3203e4beb9d1c68487 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 30 Nov 2020 18:32:21 +0100 Subject: [PATCH 106/275] Fix issue with packed bits and bi color tiffs --- src/ImageSharp/Formats/Tiff/README.md | 2 +- src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/README.md b/src/ImageSharp/Formats/Tiff/README.md index 6cfda9df9..a87813a23 100644 --- a/src/ImageSharp/Formats/Tiff/README.md +++ b/src/ImageSharp/Formats/Tiff/README.md @@ -42,7 +42,7 @@ |---------------------------|:-----:|:-----:|--------------------------| |None | Y | Y | | |Ccitt1D | Y | Y | | -|PackBits | | Y | | +|PackBits | Y | Y | | |CcittGroup3Fax | Y | Y | | |CcittGroup4Fax | | | | |Lzw | | Y | Based on ImageSharp GIF LZW implementation - this code could be modified to be (i) shared, or (ii) optimised for each case | diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs index f57f05645..ea8a9d590 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -591,7 +591,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Utils } var size = PackBitsWriter.PackBits(outputRow, compressedRowSpan); - this.output.Write(compressedRowSpan); + this.output.Write(compressedRowSpan.Slice(0, size)); bytesWritten += size; outputRow.Clear(); From b5c68397b7cd0e9883fc0d0f12d6194df08d99ce Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 30 Nov 2020 19:33:33 +0100 Subject: [PATCH 107/275] Untangle writing compressed and none compressed color map tiff --- .../Formats/Tiff/Utils/TiffWriter.cs | 69 +++++++++++-------- 1 file changed, 41 insertions(+), 28 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs index ea8a9d590..3060430ec 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -240,8 +240,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Utils where TPixel : unmanaged, IPixel { int colorPaletteSize = 256 * 3 * 2; - using var memoryStream = new MemoryStream(); - using var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, PngCompressionLevel.Level6); // TODO: make compression level configurable using IManagedByteBuffer row = this.AllocateRow(image.Width, 1, padding); using IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(this.configuration); using IndexedImageFrame quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(image.Frames.RootFrame, image.Bounds()); @@ -286,44 +284,59 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Utils Value = palette }; + if (compression == TiffEncoderCompression.Deflate) + { + return this.WriteDeflateCompressedPalettedRgb(image, quantized, padding); + } + + // No compression. int bytesWritten = 0; for (int y = 0; y < image.Height; y++) { ReadOnlySpan pixelSpan = quantized.GetPixelRowSpan(y); + this.output.Write(pixelSpan); + bytesWritten += pixelSpan.Length; - if (compression == TiffEncoderCompression.Deflate) - { - deflateStream.Write(pixelSpan); - } - else + for (int i = 0; i < padding; i++) { - // No compression. - this.output.Write(pixelSpan); - bytesWritten += pixelSpan.Length; + this.output.WriteByte(0); + bytesWritten++; } + } + + return bytesWritten; + } + + /// + /// Writes the image data as indices into a color map compressed with deflate compression to the stream. + /// + /// The pixel data. + /// The image to write to the stream. + /// The quantized frame. + /// The padding bytes for each row. + /// The number of bytes written. + public int WriteDeflateCompressedPalettedRgb(Image image, IndexedImageFrame quantized, int padding) + where TPixel : unmanaged, IPixel + { + using var memoryStream = new MemoryStream(); + using var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, PngCompressionLevel.Level6); // TODO: make compression level configurable + + int bytesWritten = 0; + for (int y = 0; y < image.Height; y++) + { + ReadOnlySpan pixelSpan = quantized.GetPixelRowSpan(y); + deflateStream.Write(pixelSpan); for (int i = 0; i < padding; i++) { - if (compression == TiffEncoderCompression.Deflate) - { - deflateStream.WriteByte(0); - } - else - { - // no compression. - this.output.WriteByte(0); - bytesWritten++; - } + deflateStream.WriteByte(0); } } - if (compression == TiffEncoderCompression.Deflate) - { - deflateStream.Flush(); - byte[] buffer = memoryStream.ToArray(); - this.output.Write(buffer); - bytesWritten += buffer.Length; - } + deflateStream.Flush(); + byte[] buffer = memoryStream.ToArray(); + this.output.Write(buffer); + bytesWritten += buffer.Length; return bytesWritten; } @@ -337,7 +350,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Utils /// The compression to use. /// The number of bytes written. public int WriteGray(Image image, int padding, TiffEncoderCompression compression) - where TPixel : unmanaged, IPixel + where TPixel : unmanaged, IPixel { using IManagedByteBuffer row = this.AllocateRow(image.Width, 1, padding); Span rowSpan = row.GetSpan(); From 7275b6c2829154107b42edb0d1c62147b1c48b89 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 30 Nov 2020 19:51:02 +0100 Subject: [PATCH 108/275] Add option to use PackBits with paletted tiff's --- .../Tiff/Compression/PackBitsWriter.cs | 10 ++-- .../Formats/Tiff/TiffEncoderCore.cs | 5 ++ .../Formats/Tiff/Utils/TiffWriter.cs | 46 +++++++++++++++++++ .../Formats/Tiff/TiffEncoderTests.cs | 6 +++ 4 files changed, 62 insertions(+), 5 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/PackBitsWriter.cs b/src/ImageSharp/Formats/Tiff/Compression/PackBitsWriter.cs index 389ad628e..1677976e4 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/PackBitsWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/PackBitsWriter.cs @@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression /// internal static class PackBitsWriter { - public static int PackBits(Span rowSpan, Span compressedRowSpan) + public static int PackBits(ReadOnlySpan rowSpan, Span compressedRowSpan) { int maxRunLength = 127; int posInRowSpan = 0; @@ -58,7 +58,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression return bytesWritten; } - private static void WriteLiteralRun(Span rowSpan, int end, int literalRunLength, Span compressedRowSpan, int compressedRowPos) + private static void WriteLiteralRun(ReadOnlySpan rowSpan, int end, int literalRunLength, Span compressedRowSpan, int compressedRowPos) { DebugGuard.MustBeLessThanOrEqualTo(literalRunLength, 127, nameof(literalRunLength)); @@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression rowSpan.Slice(literalRunStart, literalRunLength).CopyTo(compressedRowSpan.Slice(compressedRowPos + 1)); } - private static void WriteRun(Span rowSpan, int start, int runLength, Span compressedRowSpan, int compressedRowPos) + private static void WriteRun(ReadOnlySpan rowSpan, int start, int runLength, Span compressedRowSpan, int compressedRowPos) { DebugGuard.MustBeLessThanOrEqualTo(runLength, 127, nameof(runLength)); @@ -77,7 +77,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression compressedRowSpan[compressedRowPos + 1] = rowSpan[start]; } - private static bool IsReplicateRun(Span rowSpan, int startPos) + private static bool IsReplicateRun(ReadOnlySpan rowSpan, int startPos) { // We consider run which has at least 3 same consecutive bytes a candidate for a run. var startByte = rowSpan[startPos]; @@ -101,7 +101,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression return false; } - private static int FindRunLength(Span rowSpan, int startPos, int maxRunLength) + private static int FindRunLength(ReadOnlySpan rowSpan, int startPos, int maxRunLength) { var startByte = rowSpan[startPos]; int count = 1; diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index f3b138fbf..5e636c097 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -436,6 +436,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff return (ushort)TiffCompression.Deflate; } + if (this.CompressionType == TiffEncoderCompression.PackBits && this.Mode == TiffEncodingMode.ColorPalette) + { + return (ushort)TiffCompression.PackBits; + } + if (this.CompressionType == TiffEncoderCompression.Deflate && this.Mode == TiffEncodingMode.BiColor) { return (ushort)TiffCompression.Deflate; diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs index 3060430ec..1cea6eaab 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -289,6 +289,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Utils return this.WriteDeflateCompressedPalettedRgb(image, quantized, padding); } + if (compression == TiffEncoderCompression.PackBits) + { + return this.WritePackBitsCompressedPalettedRgb(image, quantized, padding); + } + // No compression. int bytesWritten = 0; for (int y = 0; y < image.Height; y++) @@ -341,6 +346,47 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Utils return bytesWritten; } + /// + /// Writes the image data as indices into a color map compressed with deflate compression to the stream. + /// + /// The pixel data. + /// The image to write to the stream. + /// The quantized frame. + /// The padding bytes for each row. + /// The number of bytes written. + public int WritePackBitsCompressedPalettedRgb(Image image, IndexedImageFrame quantized, int padding) + where TPixel : unmanaged, IPixel + { + // Worst case is that the actual compressed data is larger then the input data. In this case we need 1 additional byte per 127 bytes. + int additionalBytes = (image.Width * 3 / 127) + 1; + using IManagedByteBuffer compressedRow = this.memoryAllocator.AllocateManagedByteBuffer((image.Width * 3) + additionalBytes, AllocationOptions.Clean); + using IManagedByteBuffer pixelRowWithPadding = this.memoryAllocator.AllocateManagedByteBuffer((image.Width * 3) + padding, AllocationOptions.Clean); + Span compressedRowSpan = compressedRow.GetSpan(); + Span pixelRowWithPaddingSpan = pixelRowWithPadding.GetSpan(); + + int bytesWritten = 0; + for (int y = 0; y < image.Height; y++) + { + ReadOnlySpan pixelSpan = quantized.GetPixelRowSpan(y); + + int size = 0; + if (padding != 0) + { + pixelSpan.CopyTo(pixelRowWithPaddingSpan); + size = PackBitsWriter.PackBits(pixelRowWithPaddingSpan, compressedRowSpan); + } + else + { + size = PackBitsWriter.PackBits(pixelSpan, compressedRowSpan); + } + + this.output.Write(compressedRowSpan.Slice(0, size)); + bytesWritten += size; + } + + return bytesWritten; + } + /// /// Writes the image data as 8 bit gray to the stream. /// diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 294a9c0cf..34b713d6e 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -88,6 +88,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public void TiffEncoder_EncodeColorPalette_WithDeflateCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, TiffEncoderCompression.Deflate); + // TODO: this test fails, but the output looks correct. I thinks its due to the fact that a quantizer is used to create the palette. + [Theory] + [WithFile(TestImages.Tiff.Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeColorPalette_WithPackBitsCompression_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, TiffEncoderCompression.PackBits); + [Theory] [WithFile(TestImages.Tiff.Calliphora_BiColor, PixelTypes.Rgba32)] public void TiffEncoder_EncodeBiColor_Works(TestImageProvider provider) From 778c5baaa74d535a8490fb1797d69d2bb5209ecd Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 30 Nov 2020 20:26:22 +0100 Subject: [PATCH 109/275] Fix namespaces --- .../Tiff/Compression/ModifiedHuffmanTiffCompression.cs | 1 + .../Formats/Tiff/Compression/T4TiffCompression.cs | 2 ++ .../Formats/Tiff/Compression/TiffBaseCompression.cs | 2 ++ .../Formats/Tiff/Compression/TiffCompressionFactory.cs | 1 + src/ImageSharp/Formats/Tiff/Constants/TiffByteOrder.cs | 2 +- src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs | 2 +- src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs | 4 ++-- src/ImageSharp/Formats/Tiff/Constants/TiffExtraSamples.cs | 4 ++-- src/ImageSharp/Formats/Tiff/Constants/TiffFillOrder.cs | 2 +- .../Formats/Tiff/Constants/TiffNewSubfileType.cs | 2 +- src/ImageSharp/Formats/Tiff/Constants/TiffOrientation.cs | 4 ++-- .../Tiff/Constants/TiffPhotometricInterpretation.cs | 2 +- .../Formats/Tiff/Constants/TiffPlanarConfiguration.cs | 2 +- src/ImageSharp/Formats/Tiff/Constants/TiffPredictor.cs | 2 +- .../Formats/Tiff/Constants/TiffResolutionUnit.cs | 2 +- src/ImageSharp/Formats/Tiff/Constants/TiffSampleFormat.cs | 2 +- src/ImageSharp/Formats/Tiff/Constants/TiffSubfileType.cs | 2 +- .../{TiffThreshholding.cs => TiffThresholding.cs} | 8 ++++---- src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs | 3 +++ src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs | 5 +++-- .../Formats/Tiff/Streams/TiffBigEndianStream.cs | 6 ++++-- .../Formats/Tiff/Streams/TiffLittleEndianStream.cs | 6 ++++-- src/ImageSharp/Formats/Tiff/Streams/TiffStream.cs | 3 ++- src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs | 2 ++ src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs | 2 ++ src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs | 1 + src/ImageSharp/Formats/Tiff/TiffFormat.cs | 2 ++ src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs | 3 ++- src/ImageSharp/Formats/Tiff/TiffMetadata.cs | 2 ++ tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs | 4 +++- tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs | 2 ++ 31 files changed, 58 insertions(+), 29 deletions(-) rename src/ImageSharp/Formats/Tiff/Constants/{TiffThreshholding.cs => TiffThresholding.cs} (70%) diff --git a/src/ImageSharp/Formats/Tiff/Compression/ModifiedHuffmanTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/ModifiedHuffmanTiffCompression.cs index 95a41ed54..b9866c6f2 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/ModifiedHuffmanTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/ModifiedHuffmanTiffCompression.cs @@ -4,6 +4,7 @@ using System; using System.IO; +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Tiff.Compression diff --git a/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs index 922083603..e6d2b4be0 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs @@ -3,6 +3,8 @@ using System; using System.IO; + +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Tiff.Compression diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs index f24db500b..00da17973 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs @@ -3,6 +3,8 @@ using System; using System.IO; + +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Tiff.Compression diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs index d32052fcd..07ca52954 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Tiff.Compression diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffByteOrder.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffByteOrder.cs index b6418d11d..e96824fba 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffByteOrder.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffByteOrder.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.Constants { /// /// The tiff data stream byte order enum. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs index a8a46409f..ff371e617 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.Constants { /// /// Enumeration representing the compression formats defined by the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs index c19072ef0..a26554412 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs @@ -1,9 +1,9 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System.Collections.Generic; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.Constants { /// /// Defines constants defined in the TIFF specification. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffExtraSamples.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffExtraSamples.cs index b306105db..c10167d25 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffExtraSamples.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffExtraSamples.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.Constants { /// /// Enumeration representing the possible uses of extra components in TIFF format files. @@ -23,4 +23,4 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// UnassociatedAlpha = 2 } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffFillOrder.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffFillOrder.cs index 3febf2a96..1bb75f836 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffFillOrder.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffFillOrder.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.Constants { /// /// Enumeration representing the fill orders defined by the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs index 35c4439cb..3b84120a5 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.Constants { /// /// Enumeration representing the sub-file types defined by the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffOrientation.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffOrientation.cs index bb61b51bd..a5305d482 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffOrientation.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffOrientation.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.Constants { /// /// Enumeration representing the image orientations defined by the Tiff file-format. @@ -48,4 +48,4 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// LeftBottom = 8 } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs index dc8225a7a..2e5f3064b 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.Constants { /// /// Enumeration representing the photometric interpretation formats defined by the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffPlanarConfiguration.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffPlanarConfiguration.cs index e04e100e6..6249a935e 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffPlanarConfiguration.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffPlanarConfiguration.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.Constants { /// /// Enumeration representing how the components of each pixel are stored the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffPredictor.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffPredictor.cs index 03965c06f..092bb7aa5 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffPredictor.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffPredictor.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.Constants { /// /// A mathematical operator that is applied to the image data before an encoding scheme is applied. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs index a523f0bc2..bf7a5e9a7 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.Constants { /// /// Enumeration representing the resolution units defined by the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffSampleFormat.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffSampleFormat.cs index cdd618224..81899c5fd 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffSampleFormat.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffSampleFormat.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.Constants { /// /// Specifies how to interpret each data sample in a pixel. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffSubfileType.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffSubfileType.cs index b66b72ce9..280dc76ee 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffSubfileType.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffSubfileType.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.Constants { /// /// Enumeration representing the sub-file types defined by the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffThreshholding.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffThresholding.cs similarity index 70% rename from src/ImageSharp/Formats/Tiff/Constants/TiffThreshholding.cs rename to src/ImageSharp/Formats/Tiff/Constants/TiffThresholding.cs index 9002df978..fce0b175c 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffThreshholding.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffThresholding.cs @@ -1,12 +1,12 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.Constants { /// - /// Enumeration representing the threshholding applied to image data defined by the Tiff file-format. + /// Enumeration representing the thresholding applied to image data defined by the Tiff file-format. /// - internal enum TiffThreshholding + internal enum TiffThresholding { /// /// No dithering or halftoning. @@ -23,4 +23,4 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// Random = 3 } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs index 3aedf422b..b3e3d82ad 100644 --- a/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs +++ b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs @@ -2,6 +2,9 @@ // Licensed under the Apache License, Version 2.0. using System.Collections.Generic; + +using SixLabors.ImageSharp.Formats.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Tiff.Streams; using SixLabors.ImageSharp.Metadata.Profiles.Exif; namespace SixLabors.ImageSharp.Formats.Tiff diff --git a/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs b/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs index 57d69b4a8..d05a6a901 100644 --- a/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs +++ b/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs @@ -3,9 +3,10 @@ using System; using System.Collections.Generic; -using System.IO; -using System.Runtime.InteropServices; using System.Text; + +using SixLabors.ImageSharp.Formats.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Tiff.Streams; using SixLabors.ImageSharp.Metadata.Profiles.Exif; namespace SixLabors.ImageSharp.Formats.Tiff diff --git a/src/ImageSharp/Formats/Tiff/Streams/TiffBigEndianStream.cs b/src/ImageSharp/Formats/Tiff/Streams/TiffBigEndianStream.cs index 157937055..002177a91 100644 --- a/src/ImageSharp/Formats/Tiff/Streams/TiffBigEndianStream.cs +++ b/src/ImageSharp/Formats/Tiff/Streams/TiffBigEndianStream.cs @@ -1,10 +1,12 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System; using System.IO; -namespace SixLabors.ImageSharp.Formats.Tiff +using SixLabors.ImageSharp.Formats.Tiff.Constants; + +namespace SixLabors.ImageSharp.Formats.Tiff.Streams { internal class TiffBigEndianStream : TiffStream { diff --git a/src/ImageSharp/Formats/Tiff/Streams/TiffLittleEndianStream.cs b/src/ImageSharp/Formats/Tiff/Streams/TiffLittleEndianStream.cs index da6b8b8ef..649e71e0a 100644 --- a/src/ImageSharp/Formats/Tiff/Streams/TiffLittleEndianStream.cs +++ b/src/ImageSharp/Formats/Tiff/Streams/TiffLittleEndianStream.cs @@ -1,10 +1,12 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System; using System.IO; -namespace SixLabors.ImageSharp.Formats.Tiff +using SixLabors.ImageSharp.Formats.Tiff.Constants; + +namespace SixLabors.ImageSharp.Formats.Tiff.Streams { internal class TiffLittleEndianStream : TiffStream { diff --git a/src/ImageSharp/Formats/Tiff/Streams/TiffStream.cs b/src/ImageSharp/Formats/Tiff/Streams/TiffStream.cs index 0c62c01c3..af7bc0cd1 100644 --- a/src/ImageSharp/Formats/Tiff/Streams/TiffStream.cs +++ b/src/ImageSharp/Formats/Tiff/Streams/TiffStream.cs @@ -2,8 +2,9 @@ // Licensed under the Apache License, Version 2.0. using System.IO; +using SixLabors.ImageSharp.Formats.Tiff.Constants; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.Streams { /// /// The tiff data stream base class. diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index e3806ee54..4c9b0b48d 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -7,6 +7,8 @@ using System.Linq; using System.Threading; using SixLabors.ImageSharp.Formats.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Tiff.Streams; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs index 751ecf09e..4f45ba5d7 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs @@ -3,7 +3,9 @@ using System.Collections.Generic; using System.Linq; + using SixLabors.ImageSharp.Formats.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Icc; diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 5e636c097..995461c0f 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -7,6 +7,7 @@ using System.IO; using System.Threading; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; diff --git a/src/ImageSharp/Formats/Tiff/TiffFormat.cs b/src/ImageSharp/Formats/Tiff/TiffFormat.cs index 0628ef530..ffae32093 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFormat.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFormat.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; +using SixLabors.ImageSharp.Formats.Tiff.Constants; + namespace SixLabors.ImageSharp.Formats.Tiff { /// diff --git a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs index 466702693..6956fb16a 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs @@ -1,9 +1,10 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using System; using System.Collections.Generic; using System.Linq; + +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Metadata.Profiles.Exif; namespace SixLabors.ImageSharp.Formats.Tiff diff --git a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs index fd1d84ef3..d72a903d8 100644 --- a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.Formats.Tiff.Constants; + namespace SixLabors.ImageSharp.Formats.Tiff { /// diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index 5e04906bb..0db1ad39d 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -2,14 +2,16 @@ // Licensed under the Apache License, Version 2.0. // ReSharper disable InconsistentNaming - using System; using System.IO; + using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; + using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index 0090a8222..3df4b45cb 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -2,8 +2,10 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; + using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff From 6851e4bd74346a3bc05f3d6aca97c3136fd08f6f Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 30 Nov 2020 20:44:01 +0100 Subject: [PATCH 110/275] Fix warnings --- .../BlackIsZeroTiffColorTests.cs | 150 +++++--- .../PaletteTiffColorTests.cs | 67 ++-- .../RgbPlanarTiffColorTests.cs | 351 ++++++++++-------- .../RgbTiffColorTests.cs | 228 +++++++----- .../WhiteIsZeroTiffColorTests.cs | 150 +++++--- .../Formats/Tiff/TiffMetadataTests.cs | 10 +- .../Formats/Tiff/Utils/TiffWriterTests.cs | 10 +- .../TestUtilities/ByteArrayUtility.cs | 8 +- .../TestUtilities/ByteBuffer.cs | 16 +- .../TestUtilities/TestEnvironment.Formats.cs | 3 +- 10 files changed, 566 insertions(+), 427 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs index ba7728b0f..62e17e1fd 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs @@ -2,72 +2,104 @@ // Licensed under the Apache License, Version 2.0. using System.Collections.Generic; + using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.PixelFormats; + using Xunit; -namespace SixLabors.ImageSharp.Tests.Formats.Tiff +namespace SixLabors.ImageSharp.Tests.Formats.Tiff.PhotometricInterpretation { public class BlackIsZeroTiffColorTests : PhotometricInterpretationTestBase { - private static Rgba32 Gray000 = new Rgba32(0, 0, 0, 255); - private static Rgba32 Gray128 = new Rgba32(128, 128, 128, 255); - private static Rgba32 Gray255 = new Rgba32(255, 255, 255, 255); - private static Rgba32 Gray0 = new Rgba32(0, 0, 0, 255); - private static Rgba32 Gray8 = new Rgba32(136, 136, 136, 255); - private static Rgba32 GrayF = new Rgba32(255, 255, 255, 255); - private static Rgba32 Bit0 = new Rgba32(0, 0, 0, 255); - private static Rgba32 Bit1 = new Rgba32(255, 255, 255, 255); - - private static readonly byte[] Bilevel_Bytes4x4 = new byte[] { 0b01010000, - 0b11110000, - 0b01110000, - 0b10010000 }; - - private static readonly Rgba32[][] Bilevel_Result4x4 = new[] { new[] { Bit0, Bit1, Bit0, Bit1 }, - new[] { Bit1, Bit1, Bit1, Bit1 }, - new[] { Bit0, Bit1, Bit1, Bit1 }, - new[] { Bit1, Bit0, Bit0, Bit1 }}; - - private static readonly byte[] Bilevel_Bytes12x4 = new byte[] { 0b01010101, 0b01010000, - 0b11111111, 0b11111111, - 0b01101001, 0b10100000, - 0b10010000, 0b01100000}; - - private static readonly Rgba32[][] Bilevel_Result12x4 = new[] { new[] { Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1 }, - new[] { Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1 }, - new[] { Bit0, Bit1, Bit1, Bit0, Bit1, Bit0, Bit0, Bit1, Bit1, Bit0, Bit1, Bit0 }, - new[] { Bit1, Bit0, Bit0, Bit1, Bit0, Bit0, Bit0, Bit0, Bit0, Bit1, Bit1, Bit0 }}; - - private static readonly byte[] Grayscale4_Bytes4x4 = new byte[] { 0x8F, 0x0F, - 0xFF, 0xFF, - 0x08, 0x8F, - 0xF0, 0xF8 }; - - private static readonly Rgba32[][] Grayscale4_Result4x4 = new[] { new[] { Gray8, GrayF, Gray0, GrayF }, - new[] { GrayF, GrayF, GrayF, GrayF }, - new[] { Gray0, Gray8, Gray8, GrayF }, - new[] { GrayF, Gray0, GrayF, Gray8 }}; - - private static readonly byte[] Grayscale4_Bytes3x4 = new byte[] { 0x8F, 0x00, - 0xFF, 0xF0, - 0x08, 0x80, - 0xF0, 0xF0 }; - - private static readonly Rgba32[][] Grayscale4_Result3x4 = new[] { new[] { Gray8, GrayF, Gray0 }, - new[] { GrayF, GrayF, GrayF }, - new[] { Gray0, Gray8, Gray8 }, - new[] { GrayF, Gray0, GrayF }}; - - private static readonly byte[] Grayscale8_Bytes4x4 = new byte[] { 128, 255, 000, 255, - 255, 255, 255, 255, - 000, 128, 128, 255, - 255, 000, 255, 128 }; - - private static readonly Rgba32[][] Grayscale8_Result4x4 = new[] { new[] { Gray128, Gray255, Gray000, Gray255 }, - new[] { Gray255, Gray255, Gray255, Gray255 }, - new[] { Gray000, Gray128, Gray128, Gray255 }, - new[] { Gray255, Gray000, Gray255, Gray128 }}; + private static Rgba32 gray000 = new Rgba32(0, 0, 0, 255); + private static Rgba32 gray128 = new Rgba32(128, 128, 128, 255); + private static Rgba32 gray255 = new Rgba32(255, 255, 255, 255); + private static Rgba32 gray0 = new Rgba32(0, 0, 0, 255); + private static Rgba32 gray8 = new Rgba32(136, 136, 136, 255); + private static Rgba32 grayF = new Rgba32(255, 255, 255, 255); + private static Rgba32 bit0 = new Rgba32(0, 0, 0, 255); + private static Rgba32 bit1 = new Rgba32(255, 255, 255, 255); + + private static readonly byte[] Bilevel_Bytes4x4 = + { + 0b01010000, + 0b11110000, + 0b01110000, + 0b10010000 + }; + + private static readonly Rgba32[][] Bilevel_Result4x4 = new[] + { + new[] { bit0, bit1, bit0, bit1 }, + new[] { bit1, bit1, bit1, bit1 }, + new[] { bit0, bit1, bit1, bit1 }, + new[] { bit1, bit0, bit0, bit1 } + }; + + private static readonly byte[] Bilevel_Bytes12x4 = + { + 0b01010101, 0b01010000, + 0b11111111, 0b11111111, + 0b01101001, 0b10100000, + 0b10010000, 0b01100000 + }; + + private static readonly Rgba32[][] Bilevel_Result12x4 = + { + new[] { bit0, bit1, bit0, bit1, bit0, bit1, bit0, bit1, bit0, bit1, bit0, bit1 }, + new[] { bit1, bit1, bit1, bit1, bit1, bit1, bit1, bit1, bit1, bit1, bit1, bit1 }, + new[] { bit0, bit1, bit1, bit0, bit1, bit0, bit0, bit1, bit1, bit0, bit1, bit0 }, + new[] { bit1, bit0, bit0, bit1, bit0, bit0, bit0, bit0, bit0, bit1, bit1, bit0 } + }; + + private static readonly byte[] Grayscale4_Bytes4x4 = + { + 0x8F, 0x0F, + 0xFF, 0xFF, + 0x08, 0x8F, + 0xF0, 0xF8 + }; + + private static readonly Rgba32[][] Grayscale4_Result4x4 = + { + new[] { gray8, grayF, gray0, grayF }, + new[] { grayF, grayF, grayF, grayF }, + new[] { gray0, gray8, gray8, grayF }, + new[] { grayF, gray0, grayF, gray8 } + }; + + private static readonly byte[] Grayscale4_Bytes3x4 = + { + 0x8F, 0x00, + 0xFF, 0xF0, + 0x08, 0x80, + 0xF0, 0xF0 + }; + + private static readonly Rgba32[][] Grayscale4_Result3x4 = + { + new[] { gray8, grayF, gray0 }, + new[] { grayF, grayF, grayF }, + new[] { gray0, gray8, gray8 }, + new[] { grayF, gray0, grayF } + }; + + private static readonly byte[] Grayscale8_Bytes4x4 = + { + 128, 255, 000, 255, + 255, 255, 255, 255, + 000, 128, 128, 255, + 255, 000, 255, 128 + }; + + private static readonly Rgba32[][] Grayscale8_Result4x4 = + { + new[] { gray128, gray255, gray000, gray255 }, + new[] { gray255, gray255, gray255, gray255 }, + new[] { gray000, gray128, gray128, gray255 }, + new[] { gray255, gray000, gray255, gray128 } + }; public static IEnumerable Bilevel_Data { diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs index 98c7e6498..5e905e3f0 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs @@ -2,11 +2,13 @@ // Licensed under the Apache License, Version 2.0. using System.Collections.Generic; + using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.PixelFormats; + using Xunit; -namespace SixLabors.ImageSharp.Tests.Formats.Tiff +namespace SixLabors.ImageSharp.Tests.Formats.Tiff.PhotometricInterpretation { public class PaletteTiffColorTests : PhotometricInterpretationTestBase { @@ -14,27 +16,24 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public static ushort[] Palette4_ColorMap { get => GenerateColorMap(Palette4_ColorPalette); } - private static readonly byte[] Palette4_Bytes4x4 = new byte[] { 0x01, 0x23, - 0x4A, 0xD2, - 0x12, 0x34, - 0xAB, 0xEF }; + private static readonly byte[] Palette4_Bytes4x4 = + { + 0x01, 0x23, 0x4A, 0xD2, 0x12, 0x34, 0xAB, 0xEF + }; - private static readonly Rgba32[][] Palette4_Result4x4 = GenerateResult(Palette4_ColorPalette, - new[] { new[] { 0x00, 0x01, 0x02, 0x03 }, - new[] { 0x04, 0x0A, 0x0D, 0x02 }, - new[] { 0x01, 0x02, 0x03, 0x04 }, - new[] { 0x0A, 0x0B, 0x0E, 0x0F }}); + private static readonly Rgba32[][] Palette4_Result4x4 = GenerateResult( + Palette4_ColorPalette, + new[] { new[] { 0x00, 0x01, 0x02, 0x03 }, new[] { 0x04, 0x0A, 0x0D, 0x02 }, new[] { 0x01, 0x02, 0x03, 0x04 }, new[] { 0x0A, 0x0B, 0x0E, 0x0F } }); - private static readonly byte[] Palette4_Bytes3x4 = new byte[] { 0x01, 0x20, - 0x4A, 0xD0, - 0x12, 0x30, - 0xAB, 0xE0 }; + private static readonly byte[] Palette4_Bytes3x4 = + { + 0x01, 0x20, + 0x4A, 0xD0, + 0x12, 0x30, + 0xAB, 0xE0 + }; - private static readonly Rgba32[][] Palette4_Result3x4 = GenerateResult(Palette4_ColorPalette, - new[] { new[] { 0x00, 0x01, 0x02 }, - new[] { 0x04, 0x0A, 0x0D }, - new[] { 0x01, 0x02, 0x03 }, - new[] { 0x0A, 0x0B, 0x0E }}); + private static readonly Rgba32[][] Palette4_Result3x4 = GenerateResult(Palette4_ColorPalette, new[] { new[] { 0x00, 0x01, 0x02 }, new[] { 0x04, 0x0A, 0x0D }, new[] { 0x01, 0x02, 0x03 }, new[] { 0x0A, 0x0B, 0x0E } }); public static IEnumerable Palette4_Data { @@ -51,7 +50,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff yield return new object[] { Palette4_Bytes3x4, 4, Palette4_ColorMap, 1, 0, 3, 4, Offset(Palette4_Result3x4, 1, 0, 6, 6) }; yield return new object[] { Palette4_Bytes3x4, 4, Palette4_ColorMap, 0, 1, 3, 4, Offset(Palette4_Result3x4, 0, 1, 6, 6) }; yield return new object[] { Palette4_Bytes3x4, 4, Palette4_ColorMap, 1, 1, 3, 4, Offset(Palette4_Result3x4, 1, 1, 6, 6) }; - } } @@ -59,16 +57,15 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public static ushort[] Palette8_ColorMap { get => GenerateColorMap(Palette8_ColorPalette); } - private static readonly byte[] Palette8_Bytes4x4 = new byte[] { 000, 001, 002, 003, - 100, 110, 120, 130, - 000, 255, 128, 255, - 050, 100, 150, 200 }; + private static readonly byte[] Palette8_Bytes4x4 = + { + 000, 001, 002, 003, + 100, 110, 120, 130, + 000, 255, 128, 255, + 050, 100, 150, 200 + }; - private static readonly Rgba32[][] Palette8_Result4x4 = GenerateResult(Palette8_ColorPalette, - new[] { new[] { 000, 001, 002, 003 }, - new[] { 100, 110, 120, 130 }, - new[] { 000, 255, 128, 255 }, - new[] { 050, 100, 150, 200 }}); + private static readonly Rgba32[][] Palette8_Result4x4 = GenerateResult(Palette8_ColorPalette, new[] { new[] { 000, 001, 002, 003 }, new[] { 100, 110, 120, 130 }, new[] { 000, 255, 128, 255 }, new[] { 050, 100, 150, 200 } }); public static IEnumerable Palette8_Data { @@ -95,11 +92,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff private static uint[][] GeneratePalette(int count) { - uint[][] palette = new uint[count][]; + var palette = new uint[count][]; for (uint i = 0; i < count; i++) { - palette[i] = new uint[] { (i * 2u) % 65536u, (i * 2625u) % 65536u, (i * 29401u) % 65536u }; + palette[i] = new[] { (i * 2u) % 65536u, (i * 2625u) % 65536u, (i * 29401u) % 65536u }; } return palette; @@ -108,13 +105,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff private static ushort[] GenerateColorMap(uint[][] colorPalette) { int colorCount = colorPalette.Length; - ushort[] colorMap = new ushort[colorCount * 3]; + var colorMap = new ushort[colorCount * 3]; for (int i = 0; i < colorCount; i++) { - colorMap[colorCount * 0 + i] = (ushort)colorPalette[i][0]; - colorMap[colorCount * 1 + i] = (ushort)colorPalette[i][1]; - colorMap[colorCount * 2 + i] = (ushort)colorPalette[i][2]; + colorMap[(colorCount * 0) + i] = (ushort)colorPalette[i][0]; + colorMap[(colorCount * 1) + i] = (ushort)colorPalette[i][1]; + colorMap[(colorCount * 2) + i] = (ushort)colorPalette[i][2]; } return colorMap; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs index 1982a8ebe..2aab2f3ec 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs @@ -3,184 +3,237 @@ using System; using System.Collections.Generic; + using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; + using Xunit; -namespace SixLabors.ImageSharp.Tests.Formats.Tiff +namespace SixLabors.ImageSharp.Tests.Formats.Tiff.PhotometricInterpretation { public class RgbPlanarTiffColorTests : PhotometricInterpretationTestBase { - private static Rgba32 Rgb4_000 = new Rgba32(0, 0, 0, 255); - private static Rgba32 Rgb4_444 = new Rgba32(68, 68, 68, 255); - private static Rgba32 Rgb4_888 = new Rgba32(136, 136, 136, 255); - private static Rgba32 Rgb4_CCC = new Rgba32(204, 204, 204, 255); - private static Rgba32 Rgb4_FFF = new Rgba32(255, 255, 255, 255); - private static Rgba32 Rgb4_F00 = new Rgba32(255, 0, 0, 255); - private static Rgba32 Rgb4_0F0 = new Rgba32(0, 255, 0, 255); - private static Rgba32 Rgb4_00F = new Rgba32(0, 0, 255, 255); - private static Rgba32 Rgb4_F0F = new Rgba32(255, 0, 255, 255); - private static Rgba32 Rgb4_400 = new Rgba32(68, 0, 0, 255); - private static Rgba32 Rgb4_800 = new Rgba32(136, 0, 0, 255); - private static Rgba32 Rgb4_C00 = new Rgba32(204, 0, 0, 255); - private static Rgba32 Rgb4_48C = new Rgba32(68, 136, 204, 255); - - private static byte[] Rgb4_Bytes4x4_R = new byte[] { 0x0F, 0x0F, - 0xF0, 0x0F, - 0x48, 0xC4, - 0x04, 0x8C }; - - private static byte[] Rgb4_Bytes4x4_G = new byte[] { 0x0F, 0x0F, - 0x0F, 0x00, - 0x00, 0x08, - 0x04, 0x8C }; - - private static byte[] Rgb4_Bytes4x4_B = new byte[] { 0x0F, 0x0F, - 0x00, 0xFF, - 0x00, 0x0C, - 0x04, 0x8C }; - - private static byte[][] Rgb4_Bytes4x4 = new[] { Rgb4_Bytes4x4_R, Rgb4_Bytes4x4_G, Rgb4_Bytes4x4_B }; - - private static Rgba32[][] Rgb4_Result4x4 = new[] { new[] { Rgb4_000, Rgb4_FFF, Rgb4_000, Rgb4_FFF }, - new[] { Rgb4_F00, Rgb4_0F0, Rgb4_00F, Rgb4_F0F }, - new[] { Rgb4_400, Rgb4_800, Rgb4_C00, Rgb4_48C }, - new[] { Rgb4_000, Rgb4_444, Rgb4_888, Rgb4_CCC }}; - - private static byte[] Rgb4_Bytes3x4_R = new byte[] { 0x0F, 0x00, - 0xF0, 0x00, - 0x48, 0xC0, - 0x04, 0x80 }; - - private static byte[] Rgb4_Bytes3x4_G = new byte[] { 0x0F, 0x00, - 0x0F, 0x00, - 0x00, 0x00, - 0x04, 0x80 }; - - private static byte[] Rgb4_Bytes3x4_B = new byte[] { 0x0F, 0x00, - 0x00, 0xF0, - 0x00, 0x00, - 0x04, 0x80 }; - - private static byte[][] Rgb4_Bytes3x4 = new[] { Rgb4_Bytes3x4_R, Rgb4_Bytes3x4_G, Rgb4_Bytes3x4_B }; - - private static Rgba32[][] Rgb4_Result3x4 = new[] { new[] { Rgb4_000, Rgb4_FFF, Rgb4_000 }, - new[] { Rgb4_F00, Rgb4_0F0, Rgb4_00F }, - new[] { Rgb4_400, Rgb4_800, Rgb4_C00 }, - new[] { Rgb4_000, Rgb4_444, Rgb4_888 }}; + private static Rgba32 rgb4_000 = new Rgba32(0, 0, 0, 255); + private static Rgba32 rgb4_444 = new Rgba32(68, 68, 68, 255); + private static Rgba32 rgb4_888 = new Rgba32(136, 136, 136, 255); + private static Rgba32 rgb4_CCC = new Rgba32(204, 204, 204, 255); + private static Rgba32 rgb4_FFF = new Rgba32(255, 255, 255, 255); + private static Rgba32 rgb4_F00 = new Rgba32(255, 0, 0, 255); + private static Rgba32 rgb4_0F0 = new Rgba32(0, 255, 0, 255); + private static Rgba32 rgb4_00F = new Rgba32(0, 0, 255, 255); + private static Rgba32 rgb4_F0F = new Rgba32(255, 0, 255, 255); + private static Rgba32 rgb4_400 = new Rgba32(68, 0, 0, 255); + private static Rgba32 rgb4_800 = new Rgba32(136, 0, 0, 255); + private static Rgba32 rgb4_C00 = new Rgba32(204, 0, 0, 255); + private static Rgba32 rgb4_48C = new Rgba32(68, 136, 204, 255); + + private static byte[] rgb4_Bytes4x4_R = + { + 0x0F, 0x0F, + 0xF0, 0x0F, + 0x48, 0xC4, + 0x04, 0x8C + }; + + private static byte[] rgb4_Bytes4x4_G = + { + 0x0F, 0x0F, + 0x0F, 0x00, + 0x00, 0x08, + 0x04, 0x8C + }; + + private static byte[] rgb4_Bytes4x4_B = + { + 0x0F, 0x0F, + 0x00, 0xFF, + 0x00, 0x0C, + 0x04, 0x8C + }; + + private static byte[][] rgb4_Bytes4x4 = { rgb4_Bytes4x4_R, rgb4_Bytes4x4_G, rgb4_Bytes4x4_B }; + + private static Rgba32[][] rgb4_Result4x4 = + { + new[] { rgb4_000, rgb4_FFF, rgb4_000, rgb4_FFF }, + new[] { rgb4_F00, rgb4_0F0, rgb4_00F, rgb4_F0F }, + new[] { rgb4_400, rgb4_800, rgb4_C00, rgb4_48C }, + new[] { rgb4_000, rgb4_444, rgb4_888, rgb4_CCC } + }; + + private static byte[] rgb4_Bytes3x4_R = + { + 0x0F, 0x00, + 0xF0, 0x00, + 0x48, 0xC0, + 0x04, 0x80 + }; + + private static byte[] rgb4_Bytes3x4_G = + { + 0x0F, 0x00, + 0x0F, 0x00, + 0x00, 0x00, + 0x04, 0x80 + }; + + private static byte[] rgb4_Bytes3x4_B = + { + 0x0F, 0x00, + 0x00, 0xF0, + 0x00, 0x00, + 0x04, 0x80 + }; + + private static byte[][] rgb4_Bytes3x4 = { rgb4_Bytes3x4_R, rgb4_Bytes3x4_G, rgb4_Bytes3x4_B }; + + private static Rgba32[][] rgb4_Result3x4 = + { + new[] { rgb4_000, rgb4_FFF, rgb4_000 }, + new[] { rgb4_F00, rgb4_0F0, rgb4_00F }, + new[] { rgb4_400, rgb4_800, rgb4_C00 }, + new[] { rgb4_000, rgb4_444, rgb4_888 } + }; public static IEnumerable Rgb4_Data { get { - yield return new object[] { Rgb4_Bytes4x4, new ushort[] { 4, 4, 4 }, 0, 0, 4, 4, Rgb4_Result4x4 }; - yield return new object[] { Rgb4_Bytes4x4, new ushort[] { 4, 4, 4 }, 0, 0, 4, 4, Offset(Rgb4_Result4x4, 0, 0, 6, 6) }; - yield return new object[] { Rgb4_Bytes4x4, new ushort[] { 4, 4, 4 }, 1, 0, 4, 4, Offset(Rgb4_Result4x4, 1, 0, 6, 6) }; - yield return new object[] { Rgb4_Bytes4x4, new ushort[] { 4, 4, 4 }, 0, 1, 4, 4, Offset(Rgb4_Result4x4, 0, 1, 6, 6) }; - yield return new object[] { Rgb4_Bytes4x4, new ushort[] { 4, 4, 4 }, 1, 1, 4, 4, Offset(Rgb4_Result4x4, 1, 1, 6, 6) }; - - yield return new object[] { Rgb4_Bytes3x4, new ushort[] { 4, 4, 4 }, 0, 0, 3, 4, Rgb4_Result3x4 }; - yield return new object[] { Rgb4_Bytes3x4, new ushort[] { 4, 4, 4 }, 0, 0, 3, 4, Offset(Rgb4_Result3x4, 0, 0, 6, 6) }; - yield return new object[] { Rgb4_Bytes3x4, new ushort[] { 4, 4, 4 }, 1, 0, 3, 4, Offset(Rgb4_Result3x4, 1, 0, 6, 6) }; - yield return new object[] { Rgb4_Bytes3x4, new ushort[] { 4, 4, 4 }, 0, 1, 3, 4, Offset(Rgb4_Result3x4, 0, 1, 6, 6) }; - yield return new object[] { Rgb4_Bytes3x4, new ushort[] { 4, 4, 4 }, 1, 1, 3, 4, Offset(Rgb4_Result3x4, 1, 1, 6, 6) }; + yield return new object[] { rgb4_Bytes4x4, new ushort[] { 4, 4, 4 }, 0, 0, 4, 4, rgb4_Result4x4 }; + yield return new object[] { rgb4_Bytes4x4, new ushort[] { 4, 4, 4 }, 0, 0, 4, 4, Offset(rgb4_Result4x4, 0, 0, 6, 6) }; + yield return new object[] { rgb4_Bytes4x4, new ushort[] { 4, 4, 4 }, 1, 0, 4, 4, Offset(rgb4_Result4x4, 1, 0, 6, 6) }; + yield return new object[] { rgb4_Bytes4x4, new ushort[] { 4, 4, 4 }, 0, 1, 4, 4, Offset(rgb4_Result4x4, 0, 1, 6, 6) }; + yield return new object[] { rgb4_Bytes4x4, new ushort[] { 4, 4, 4 }, 1, 1, 4, 4, Offset(rgb4_Result4x4, 1, 1, 6, 6) }; + + yield return new object[] { rgb4_Bytes3x4, new ushort[] { 4, 4, 4 }, 0, 0, 3, 4, rgb4_Result3x4 }; + yield return new object[] { rgb4_Bytes3x4, new ushort[] { 4, 4, 4 }, 0, 0, 3, 4, Offset(rgb4_Result3x4, 0, 0, 6, 6) }; + yield return new object[] { rgb4_Bytes3x4, new ushort[] { 4, 4, 4 }, 1, 0, 3, 4, Offset(rgb4_Result3x4, 1, 0, 6, 6) }; + yield return new object[] { rgb4_Bytes3x4, new ushort[] { 4, 4, 4 }, 0, 1, 3, 4, Offset(rgb4_Result3x4, 0, 1, 6, 6) }; + yield return new object[] { rgb4_Bytes3x4, new ushort[] { 4, 4, 4 }, 1, 1, 3, 4, Offset(rgb4_Result3x4, 1, 1, 6, 6) }; } } - private static Rgba32 Rgb8_000 = new Rgba32(0, 0, 0, 255); - private static Rgba32 Rgb8_444 = new Rgba32(64, 64, 64, 255); - private static Rgba32 Rgb8_888 = new Rgba32(128, 128, 128, 255); - private static Rgba32 Rgb8_CCC = new Rgba32(192, 192, 192, 255); - private static Rgba32 Rgb8_FFF = new Rgba32(255, 255, 255, 255); - private static Rgba32 Rgb8_F00 = new Rgba32(255, 0, 0, 255); - private static Rgba32 Rgb8_0F0 = new Rgba32(0, 255, 0, 255); - private static Rgba32 Rgb8_00F = new Rgba32(0, 0, 255, 255); - private static Rgba32 Rgb8_F0F = new Rgba32(255, 0, 255, 255); - private static Rgba32 Rgb8_400 = new Rgba32(64, 0, 0, 255); - private static Rgba32 Rgb8_800 = new Rgba32(128, 0, 0, 255); - private static Rgba32 Rgb8_C00 = new Rgba32(192, 0, 0, 255); - private static Rgba32 Rgb8_48C = new Rgba32(64, 128, 192, 255); - - private static byte[] Rgb8_Bytes4x4_R = new byte[] { 000, 255, 000, 255, - 255, 000, 000, 255, - 064, 128, 192, 064, - 000, 064, 128, 192 }; - - private static byte[] Rgb8_Bytes4x4_G = new byte[] { 000, 255, 000, 255, - 000, 255, 000, 000, - 000, 000, 000, 128, - 000, 064, 128, 192 }; - - private static byte[] Rgb8_Bytes4x4_B = new byte[] { 000, 255, 000, 255, - 000, 000, 255, 255, - 000, 000, 000, 192, - 000, 064, 128, 192 }; - - private static byte[][] Rgb8_Bytes4x4 = new[] { Rgb8_Bytes4x4_R, Rgb8_Bytes4x4_G, Rgb8_Bytes4x4_B }; - - private static Rgba32[][] Rgb8_Result4x4 = new[] { new[] { Rgb8_000, Rgb8_FFF, Rgb8_000, Rgb8_FFF }, - new[] { Rgb8_F00, Rgb8_0F0, Rgb8_00F, Rgb8_F0F }, - new[] { Rgb8_400, Rgb8_800, Rgb8_C00, Rgb8_48C }, - new[] { Rgb8_000, Rgb8_444, Rgb8_888, Rgb8_CCC }}; + private static Rgba32 rgb8_000 = new Rgba32(0, 0, 0, 255); + private static Rgba32 rgb8_444 = new Rgba32(64, 64, 64, 255); + private static Rgba32 rgb8_888 = new Rgba32(128, 128, 128, 255); + private static Rgba32 rgb8_CCC = new Rgba32(192, 192, 192, 255); + private static Rgba32 rgb8_FFF = new Rgba32(255, 255, 255, 255); + private static Rgba32 rgb8_F00 = new Rgba32(255, 0, 0, 255); + private static Rgba32 rgb8_0F0 = new Rgba32(0, 255, 0, 255); + private static Rgba32 rgb8_00F = new Rgba32(0, 0, 255, 255); + private static Rgba32 rgb8_F0F = new Rgba32(255, 0, 255, 255); + private static Rgba32 rgb8_400 = new Rgba32(64, 0, 0, 255); + private static Rgba32 rgb8_800 = new Rgba32(128, 0, 0, 255); + private static Rgba32 rgb8_C00 = new Rgba32(192, 0, 0, 255); + private static Rgba32 rgb8_48C = new Rgba32(64, 128, 192, 255); + + private static byte[] rgb8_Bytes4x4_R = + { + 000, 255, 000, 255, + 255, 000, 000, 255, + 064, 128, 192, 064, + 000, 064, 128, 192 + }; + + private static byte[] rgb8_Bytes4x4_G = + { + 000, 255, 000, 255, + 000, 255, 000, 000, + 000, 000, 000, 128, + 000, 064, 128, 192 + }; + + private static byte[] rgb8_Bytes4x4_B = + { + 000, 255, 000, 255, + 000, 000, 255, 255, + 000, 000, 000, 192, + 000, 064, 128, 192 + }; + + private static byte[][] rgb8_Bytes4x4 = + { + rgb8_Bytes4x4_R, rgb8_Bytes4x4_G, rgb8_Bytes4x4_B + }; + + private static Rgba32[][] rgb8_Result4x4 = + { + new[] { rgb8_000, rgb8_FFF, rgb8_000, rgb8_FFF }, + new[] { rgb8_F00, rgb8_0F0, rgb8_00F, rgb8_F0F }, + new[] { rgb8_400, rgb8_800, rgb8_C00, rgb8_48C }, + new[] { rgb8_000, rgb8_444, rgb8_888, rgb8_CCC } + }; public static IEnumerable Rgb8_Data { get { - yield return new object[] { Rgb8_Bytes4x4, new ushort[] { 8, 8, 8 }, 0, 0, 4, 4, Rgb8_Result4x4 }; - yield return new object[] { Rgb8_Bytes4x4, new ushort[] { 8, 8, 8 }, 0, 0, 4, 4, Offset(Rgb8_Result4x4, 0, 0, 6, 6) }; - yield return new object[] { Rgb8_Bytes4x4, new ushort[] { 8, 8, 8 }, 1, 0, 4, 4, Offset(Rgb8_Result4x4, 1, 0, 6, 6) }; - yield return new object[] { Rgb8_Bytes4x4, new ushort[] { 8, 8, 8 }, 0, 1, 4, 4, Offset(Rgb8_Result4x4, 0, 1, 6, 6) }; - yield return new object[] { Rgb8_Bytes4x4, new ushort[] { 8, 8, 8 }, 1, 1, 4, 4, Offset(Rgb8_Result4x4, 1, 1, 6, 6) }; + yield return new object[] { rgb8_Bytes4x4, new ushort[] { 8, 8, 8 }, 0, 0, 4, 4, rgb8_Result4x4 }; + yield return new object[] { rgb8_Bytes4x4, new ushort[] { 8, 8, 8 }, 0, 0, 4, 4, Offset(rgb8_Result4x4, 0, 0, 6, 6) }; + yield return new object[] { rgb8_Bytes4x4, new ushort[] { 8, 8, 8 }, 1, 0, 4, 4, Offset(rgb8_Result4x4, 1, 0, 6, 6) }; + yield return new object[] { rgb8_Bytes4x4, new ushort[] { 8, 8, 8 }, 0, 1, 4, 4, Offset(rgb8_Result4x4, 0, 1, 6, 6) }; + yield return new object[] { rgb8_Bytes4x4, new ushort[] { 8, 8, 8 }, 1, 1, 4, 4, Offset(rgb8_Result4x4, 1, 1, 6, 6) }; } } - private static Rgba32 Rgb484_000 = new Rgba32(0, 0, 0, 255); - private static Rgba32 Rgb484_444 = new Rgba32(68, 64, 68, 255); - private static Rgba32 Rgb484_888 = new Rgba32(136, 128, 136, 255); - private static Rgba32 Rgb484_CCC = new Rgba32(204, 192, 204, 255); - private static Rgba32 Rgb484_FFF = new Rgba32(255, 255, 255, 255); - private static Rgba32 Rgb484_F00 = new Rgba32(255, 0, 0, 255); - private static Rgba32 Rgb484_0F0 = new Rgba32(0, 255, 0, 255); - private static Rgba32 Rgb484_00F = new Rgba32(0, 0, 255, 255); - private static Rgba32 Rgb484_F0F = new Rgba32(255, 0, 255, 255); - private static Rgba32 Rgb484_400 = new Rgba32(68, 0, 0, 255); - private static Rgba32 Rgb484_800 = new Rgba32(136, 0, 0, 255); - private static Rgba32 Rgb484_C00 = new Rgba32(204, 0, 0, 255); - private static Rgba32 Rgb484_48C = new Rgba32(68, 128, 204, 255); - - private static byte[] Rgb484_Bytes4x4_R = new byte[] { 0x0F, 0x0F, - 0xF0, 0x0F, - 0x48, 0xC4, - 0x04, 0x8C }; - - private static byte[] Rgb484_Bytes4x4_G = new byte[] { 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x80, - 0x00, 0x40, 0x80, 0xC0 }; - - private static byte[] Rgb484_Bytes4x4_B = new byte[] { 0x0F, 0x0F, - 0x00, 0xFF, - 0x00, 0x0C, - 0x04, 0x8C }; - - private static Rgba32[][] Rgb484_Result4x4 = new[] { new[] { Rgb484_000, Rgb484_FFF, Rgb484_000, Rgb484_FFF }, - new[] { Rgb484_F00, Rgb484_0F0, Rgb484_00F, Rgb484_F0F }, - new[] { Rgb484_400, Rgb484_800, Rgb484_C00, Rgb484_48C }, - new[] { Rgb484_000, Rgb484_444, Rgb484_888, Rgb484_CCC }}; - - private static byte[][] Rgb484_Bytes4x4 = new[] { Rgb484_Bytes4x4_R, Rgb484_Bytes4x4_G, Rgb484_Bytes4x4_B }; + private static Rgba32 rgb484_000 = new Rgba32(0, 0, 0, 255); + private static Rgba32 rgb484_444 = new Rgba32(68, 64, 68, 255); + private static Rgba32 rgb484_888 = new Rgba32(136, 128, 136, 255); + private static Rgba32 rgb484_CCC = new Rgba32(204, 192, 204, 255); + private static Rgba32 rgb484_FFF = new Rgba32(255, 255, 255, 255); + private static Rgba32 rgb484_F00 = new Rgba32(255, 0, 0, 255); + private static Rgba32 rgb484_0F0 = new Rgba32(0, 255, 0, 255); + private static Rgba32 rgb484_00F = new Rgba32(0, 0, 255, 255); + private static Rgba32 rgb484_F0F = new Rgba32(255, 0, 255, 255); + private static Rgba32 rgb484_400 = new Rgba32(68, 0, 0, 255); + private static Rgba32 rgb484_800 = new Rgba32(136, 0, 0, 255); + private static Rgba32 rgb484_C00 = new Rgba32(204, 0, 0, 255); + private static Rgba32 rgb484_48C = new Rgba32(68, 128, 204, 255); + + private static byte[] rgb484_Bytes4x4_R = + { + 0x0F, 0x0F, + 0xF0, 0x0F, + 0x48, 0xC4, + 0x04, 0x8C + }; + + private static byte[] rgb484_Bytes4x4_G = + { + 0x00, 0xFF, 0x00, 0xFF, + 0x00, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, + 0x00, 0x40, 0x80, 0xC0 + }; + + private static byte[] rgb484_Bytes4x4_B = + { + 0x0F, 0x0F, + 0x00, 0xFF, + 0x00, 0x0C, + 0x04, 0x8C + }; + + private static Rgba32[][] rgb484_Result4x4 = + { + new[] { rgb484_000, rgb484_FFF, rgb484_000, rgb484_FFF }, + new[] { rgb484_F00, rgb484_0F0, rgb484_00F, rgb484_F0F }, + new[] { rgb484_400, rgb484_800, rgb484_C00, rgb484_48C }, + new[] { rgb484_000, rgb484_444, rgb484_888, rgb484_CCC } + }; + + private static byte[][] rgb484_Bytes4x4 = { rgb484_Bytes4x4_R, rgb484_Bytes4x4_G, rgb484_Bytes4x4_B }; public static IEnumerable Rgb484_Data { get { - yield return new object[] { Rgb484_Bytes4x4, new ushort[] { 4, 8, 4 }, 0, 0, 4, 4, Rgb484_Result4x4 }; - yield return new object[] { Rgb484_Bytes4x4, new ushort[] { 4, 8, 4 }, 0, 0, 4, 4, Offset(Rgb484_Result4x4, 0, 0, 6, 6) }; - yield return new object[] { Rgb484_Bytes4x4, new ushort[] { 4, 8, 4 }, 1, 0, 4, 4, Offset(Rgb484_Result4x4, 1, 0, 6, 6) }; - yield return new object[] { Rgb484_Bytes4x4, new ushort[] { 4, 8, 4 }, 0, 1, 4, 4, Offset(Rgb484_Result4x4, 0, 1, 6, 6) }; - yield return new object[] { Rgb484_Bytes4x4, new ushort[] { 4, 8, 4 }, 1, 1, 4, 4, Offset(Rgb484_Result4x4, 1, 1, 6, 6) }; + yield return new object[] { rgb484_Bytes4x4, new ushort[] { 4, 8, 4 }, 0, 0, 4, 4, rgb484_Result4x4 }; + yield return new object[] { rgb484_Bytes4x4, new ushort[] { 4, 8, 4 }, 0, 0, 4, 4, Offset(rgb484_Result4x4, 0, 0, 6, 6) }; + yield return new object[] { rgb484_Bytes4x4, new ushort[] { 4, 8, 4 }, 1, 0, 4, 4, Offset(rgb484_Result4x4, 1, 0, 6, 6) }; + yield return new object[] { rgb484_Bytes4x4, new ushort[] { 4, 8, 4 }, 0, 1, 4, 4, Offset(rgb484_Result4x4, 0, 1, 6, 6) }; + yield return new object[] { rgb484_Bytes4x4, new ushort[] { 4, 8, 4 }, 1, 1, 4, 4, Offset(rgb484_Result4x4, 1, 1, 6, 6) }; } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs index f1ba32c5d..f1fa118f3 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs @@ -2,135 +2,161 @@ // Licensed under the Apache License, Version 2.0. using System.Collections.Generic; + using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.PixelFormats; + using Xunit; -namespace SixLabors.ImageSharp.Tests.Formats.Tiff +namespace SixLabors.ImageSharp.Tests.Formats.Tiff.PhotometricInterpretation { public class RgbTiffColorTests : PhotometricInterpretationTestBase { - private static Rgba32 Rgb4_000 = new Rgba32(0, 0, 0, 255); - private static Rgba32 Rgb4_444 = new Rgba32(68, 68, 68, 255); - private static Rgba32 Rgb4_888 = new Rgba32(136, 136, 136, 255); - private static Rgba32 Rgb4_CCC = new Rgba32(204, 204, 204, 255); - private static Rgba32 Rgb4_FFF = new Rgba32(255, 255, 255, 255); - private static Rgba32 Rgb4_F00 = new Rgba32(255, 0, 0, 255); - private static Rgba32 Rgb4_0F0 = new Rgba32(0, 255, 0, 255); - private static Rgba32 Rgb4_00F = new Rgba32(0, 0, 255, 255); - private static Rgba32 Rgb4_F0F = new Rgba32(255, 0, 255, 255); - private static Rgba32 Rgb4_400 = new Rgba32(68, 0, 0, 255); - private static Rgba32 Rgb4_800 = new Rgba32(136, 0, 0, 255); - private static Rgba32 Rgb4_C00 = new Rgba32(204, 0, 0, 255); - private static Rgba32 Rgb4_48C = new Rgba32(68, 136, 204, 255); - - private static byte[] Rgb4_Bytes4x4 = new byte[] { 0x00, 0x0F, 0xFF, 0x00, 0x0F, 0xFF, - 0xF0, 0x00, 0xF0, 0x00, 0xFF, 0x0F, - 0x40, 0x08, 0x00, 0xC0, 0x04, 0x8C, - 0x00, 0x04, 0x44, 0x88, 0x8C, 0xCC }; - - private static Rgba32[][] Rgb4_Result4x4 = new[] { new[] { Rgb4_000, Rgb4_FFF, Rgb4_000, Rgb4_FFF }, - new[] { Rgb4_F00, Rgb4_0F0, Rgb4_00F, Rgb4_F0F }, - new[] { Rgb4_400, Rgb4_800, Rgb4_C00, Rgb4_48C }, - new[] { Rgb4_000, Rgb4_444, Rgb4_888, Rgb4_CCC }}; - - private static byte[] Rgb4_Bytes3x4 = new byte[] { 0x00, 0x0F, 0xFF, 0x00, 0x00, - 0xF0, 0x00, 0xF0, 0x00, 0xF0, - 0x40, 0x08, 0x00, 0xC0, 0x00, - 0x00, 0x04, 0x44, 0x88, 0x80 }; - - private static Rgba32[][] Rgb4_Result3x4 = new[] { new[] { Rgb4_000, Rgb4_FFF, Rgb4_000 }, - new[] { Rgb4_F00, Rgb4_0F0, Rgb4_00F }, - new[] { Rgb4_400, Rgb4_800, Rgb4_C00 }, - new[] { Rgb4_000, Rgb4_444, Rgb4_888 }}; + private static Rgba32 rgb4_000 = new Rgba32(0, 0, 0, 255); + private static Rgba32 rgb4_444 = new Rgba32(68, 68, 68, 255); + private static Rgba32 rgb4_888 = new Rgba32(136, 136, 136, 255); + private static Rgba32 rgb4_CCC = new Rgba32(204, 204, 204, 255); + private static Rgba32 rgb4_FFF = new Rgba32(255, 255, 255, 255); + private static Rgba32 rgb4_F00 = new Rgba32(255, 0, 0, 255); + private static Rgba32 rgb4_0F0 = new Rgba32(0, 255, 0, 255); + private static Rgba32 rgb4_00F = new Rgba32(0, 0, 255, 255); + private static Rgba32 rgb4_F0F = new Rgba32(255, 0, 255, 255); + private static Rgba32 rgb4_400 = new Rgba32(68, 0, 0, 255); + private static Rgba32 rgb4_800 = new Rgba32(136, 0, 0, 255); + private static Rgba32 rgb4_C00 = new Rgba32(204, 0, 0, 255); + private static Rgba32 rgb4_48C = new Rgba32(68, 136, 204, 255); + + private static byte[] rgb4_Bytes4x4 = + { + 0x00, 0x0F, 0xFF, 0x00, 0x0F, 0xFF, + 0xF0, 0x00, 0xF0, 0x00, 0xFF, 0x0F, + 0x40, 0x08, 0x00, 0xC0, 0x04, 0x8C, + 0x00, 0x04, 0x44, 0x88, 0x8C, 0xCC + }; + + private static Rgba32[][] rgb4_Result4x4 = + { + new[] { rgb4_000, rgb4_FFF, rgb4_000, rgb4_FFF }, + new[] { rgb4_F00, rgb4_0F0, rgb4_00F, rgb4_F0F }, + new[] { rgb4_400, rgb4_800, rgb4_C00, rgb4_48C }, + new[] { rgb4_000, rgb4_444, rgb4_888, rgb4_CCC } + }; + + private static byte[] rgb4_Bytes3x4 = + { + 0x00, 0x0F, 0xFF, 0x00, 0x00, + 0xF0, 0x00, 0xF0, 0x00, 0xF0, + 0x40, 0x08, 0x00, 0xC0, 0x00, + 0x00, 0x04, 0x44, 0x88, 0x80 + }; + + private static Rgba32[][] rgb4_Result3x4 = + { + new[] { rgb4_000, rgb4_FFF, rgb4_000 }, + new[] { rgb4_F00, rgb4_0F0, rgb4_00F }, + new[] { rgb4_400, rgb4_800, rgb4_C00 }, + new[] { rgb4_000, rgb4_444, rgb4_888 } + }; public static IEnumerable Rgb4_Data { get { - yield return new object[] { Rgb4_Bytes4x4, new ushort[] { 4, 4, 4 }, 0, 0, 4, 4, Rgb4_Result4x4 }; - yield return new object[] { Rgb4_Bytes4x4, new ushort[] { 4, 4, 4 }, 0, 0, 4, 4, Offset(Rgb4_Result4x4, 0, 0, 6, 6) }; - yield return new object[] { Rgb4_Bytes4x4, new ushort[] { 4, 4, 4 }, 1, 0, 4, 4, Offset(Rgb4_Result4x4, 1, 0, 6, 6) }; - yield return new object[] { Rgb4_Bytes4x4, new ushort[] { 4, 4, 4 }, 0, 1, 4, 4, Offset(Rgb4_Result4x4, 0, 1, 6, 6) }; - yield return new object[] { Rgb4_Bytes4x4, new ushort[] { 4, 4, 4 }, 1, 1, 4, 4, Offset(Rgb4_Result4x4, 1, 1, 6, 6) }; - - yield return new object[] { Rgb4_Bytes3x4, new ushort[] { 4, 4, 4 }, 0, 0, 3, 4, Rgb4_Result3x4 }; - yield return new object[] { Rgb4_Bytes3x4, new ushort[] { 4, 4, 4 }, 0, 0, 3, 4, Offset(Rgb4_Result3x4, 0, 0, 6, 6) }; - yield return new object[] { Rgb4_Bytes3x4, new ushort[] { 4, 4, 4 }, 1, 0, 3, 4, Offset(Rgb4_Result3x4, 1, 0, 6, 6) }; - yield return new object[] { Rgb4_Bytes3x4, new ushort[] { 4, 4, 4 }, 0, 1, 3, 4, Offset(Rgb4_Result3x4, 0, 1, 6, 6) }; - yield return new object[] { Rgb4_Bytes3x4, new ushort[] { 4, 4, 4 }, 1, 1, 3, 4, Offset(Rgb4_Result3x4, 1, 1, 6, 6) }; + yield return new object[] { rgb4_Bytes4x4, new ushort[] { 4, 4, 4 }, 0, 0, 4, 4, rgb4_Result4x4 }; + yield return new object[] { rgb4_Bytes4x4, new ushort[] { 4, 4, 4 }, 0, 0, 4, 4, Offset(rgb4_Result4x4, 0, 0, 6, 6) }; + yield return new object[] { rgb4_Bytes4x4, new ushort[] { 4, 4, 4 }, 1, 0, 4, 4, Offset(rgb4_Result4x4, 1, 0, 6, 6) }; + yield return new object[] { rgb4_Bytes4x4, new ushort[] { 4, 4, 4 }, 0, 1, 4, 4, Offset(rgb4_Result4x4, 0, 1, 6, 6) }; + yield return new object[] { rgb4_Bytes4x4, new ushort[] { 4, 4, 4 }, 1, 1, 4, 4, Offset(rgb4_Result4x4, 1, 1, 6, 6) }; + + yield return new object[] { rgb4_Bytes3x4, new ushort[] { 4, 4, 4 }, 0, 0, 3, 4, rgb4_Result3x4 }; + yield return new object[] { rgb4_Bytes3x4, new ushort[] { 4, 4, 4 }, 0, 0, 3, 4, Offset(rgb4_Result3x4, 0, 0, 6, 6) }; + yield return new object[] { rgb4_Bytes3x4, new ushort[] { 4, 4, 4 }, 1, 0, 3, 4, Offset(rgb4_Result3x4, 1, 0, 6, 6) }; + yield return new object[] { rgb4_Bytes3x4, new ushort[] { 4, 4, 4 }, 0, 1, 3, 4, Offset(rgb4_Result3x4, 0, 1, 6, 6) }; + yield return new object[] { rgb4_Bytes3x4, new ushort[] { 4, 4, 4 }, 1, 1, 3, 4, Offset(rgb4_Result3x4, 1, 1, 6, 6) }; } } - private static Rgba32 Rgb8_000 = new Rgba32(0, 0, 0, 255); - private static Rgba32 Rgb8_444 = new Rgba32(64, 64, 64, 255); - private static Rgba32 Rgb8_888 = new Rgba32(128, 128, 128, 255); - private static Rgba32 Rgb8_CCC = new Rgba32(192, 192, 192, 255); - private static Rgba32 Rgb8_FFF = new Rgba32(255, 255, 255, 255); - private static Rgba32 Rgb8_F00 = new Rgba32(255, 0, 0, 255); - private static Rgba32 Rgb8_0F0 = new Rgba32(0, 255, 0, 255); - private static Rgba32 Rgb8_00F = new Rgba32(0, 0, 255, 255); - private static Rgba32 Rgb8_F0F = new Rgba32(255, 0, 255, 255); - private static Rgba32 Rgb8_400 = new Rgba32(64, 0, 0, 255); - private static Rgba32 Rgb8_800 = new Rgba32(128, 0, 0, 255); - private static Rgba32 Rgb8_C00 = new Rgba32(192, 0, 0, 255); - private static Rgba32 Rgb8_48C = new Rgba32(64, 128, 192, 255); - - private static byte[] Rgb8_Bytes4x4 = new byte[] { 000, 000, 000, 255, 255, 255, 000, 000, 000, 255, 255, 255, - 255, 000, 000, 000, 255, 000, 000, 000, 255, 255, 000, 255, - 064, 000, 000, 128, 000, 000, 192, 000, 000, 064, 128, 192, - 000, 000, 000, 064, 064, 064, 128, 128, 128, 192, 192, 192 }; - - private static Rgba32[][] Rgb8_Result4x4 = new[] { new[] { Rgb8_000, Rgb8_FFF, Rgb8_000, Rgb8_FFF }, - new[] { Rgb8_F00, Rgb8_0F0, Rgb8_00F, Rgb8_F0F }, - new[] { Rgb8_400, Rgb8_800, Rgb8_C00, Rgb8_48C }, - new[] { Rgb8_000, Rgb8_444, Rgb8_888, Rgb8_CCC }}; + private static Rgba32 rgb8_000 = new Rgba32(0, 0, 0, 255); + private static Rgba32 rgb8_444 = new Rgba32(64, 64, 64, 255); + private static Rgba32 rgb8_888 = new Rgba32(128, 128, 128, 255); + private static Rgba32 rgb8_CCC = new Rgba32(192, 192, 192, 255); + private static Rgba32 rgb8_FFF = new Rgba32(255, 255, 255, 255); + private static Rgba32 rgb8_F00 = new Rgba32(255, 0, 0, 255); + private static Rgba32 rgb8_0F0 = new Rgba32(0, 255, 0, 255); + private static Rgba32 rgb8_00F = new Rgba32(0, 0, 255, 255); + private static Rgba32 rgb8_F0F = new Rgba32(255, 0, 255, 255); + private static Rgba32 rgb8_400 = new Rgba32(64, 0, 0, 255); + private static Rgba32 rgb8_800 = new Rgba32(128, 0, 0, 255); + private static Rgba32 rgb8_C00 = new Rgba32(192, 0, 0, 255); + private static Rgba32 rgb8_48C = new Rgba32(64, 128, 192, 255); + + private static byte[] rgb8_Bytes4x4 = + { + 000, 000, 000, 255, 255, 255, 000, 000, 000, 255, 255, 255, + 255, 000, 000, 000, 255, 000, 000, 000, 255, 255, 000, 255, + 064, 000, 000, 128, 000, 000, 192, 000, 000, 064, 128, 192, + 000, 000, 000, 064, 064, 064, 128, 128, 128, 192, 192, 192 + }; + + private static Rgba32[][] rgb8_Result4x4 = + { + new[] { rgb8_000, rgb8_FFF, rgb8_000, rgb8_FFF }, + new[] { rgb8_F00, rgb8_0F0, rgb8_00F, rgb8_F0F }, + new[] { rgb8_400, rgb8_800, rgb8_C00, rgb8_48C }, + new[] { rgb8_000, rgb8_444, rgb8_888, rgb8_CCC } + }; public static IEnumerable Rgb8_Data { get { - yield return new object[] { Rgb8_Bytes4x4, new ushort[] { 8, 8, 8 }, 0, 0, 4, 4, Rgb8_Result4x4 }; - yield return new object[] { Rgb8_Bytes4x4, new ushort[] { 8, 8, 8 }, 0, 0, 4, 4, Offset(Rgb8_Result4x4, 0, 0, 6, 6) }; - yield return new object[] { Rgb8_Bytes4x4, new ushort[] { 8, 8, 8 }, 1, 0, 4, 4, Offset(Rgb8_Result4x4, 1, 0, 6, 6) }; - yield return new object[] { Rgb8_Bytes4x4, new ushort[] { 8, 8, 8 }, 0, 1, 4, 4, Offset(Rgb8_Result4x4, 0, 1, 6, 6) }; - yield return new object[] { Rgb8_Bytes4x4, new ushort[] { 8, 8, 8 }, 1, 1, 4, 4, Offset(Rgb8_Result4x4, 1, 1, 6, 6) }; + yield return new object[] { rgb8_Bytes4x4, new ushort[] { 8, 8, 8 }, 0, 0, 4, 4, rgb8_Result4x4 }; + yield return new object[] { rgb8_Bytes4x4, new ushort[] { 8, 8, 8 }, 0, 0, 4, 4, Offset(rgb8_Result4x4, 0, 0, 6, 6) }; + yield return new object[] { rgb8_Bytes4x4, new ushort[] { 8, 8, 8 }, 1, 0, 4, 4, Offset(rgb8_Result4x4, 1, 0, 6, 6) }; + yield return new object[] { rgb8_Bytes4x4, new ushort[] { 8, 8, 8 }, 0, 1, 4, 4, Offset(rgb8_Result4x4, 0, 1, 6, 6) }; + yield return new object[] { rgb8_Bytes4x4, new ushort[] { 8, 8, 8 }, 1, 1, 4, 4, Offset(rgb8_Result4x4, 1, 1, 6, 6) }; } } - private static Rgba32 Rgb484_000 = new Rgba32(0, 0, 0, 255); - private static Rgba32 Rgb484_444 = new Rgba32(68, 64, 68, 255); - private static Rgba32 Rgb484_888 = new Rgba32(136, 128, 136, 255); - private static Rgba32 Rgb484_CCC = new Rgba32(204, 192, 204, 255); - private static Rgba32 Rgb484_FFF = new Rgba32(255, 255, 255, 255); - private static Rgba32 Rgb484_F00 = new Rgba32(255, 0, 0, 255); - private static Rgba32 Rgb484_0F0 = new Rgba32(0, 255, 0, 255); - private static Rgba32 Rgb484_00F = new Rgba32(0, 0, 255, 255); - private static Rgba32 Rgb484_F0F = new Rgba32(255, 0, 255, 255); - private static Rgba32 Rgb484_400 = new Rgba32(68, 0, 0, 255); - private static Rgba32 Rgb484_800 = new Rgba32(136, 0, 0, 255); - private static Rgba32 Rgb484_C00 = new Rgba32(204, 0, 0, 255); - private static Rgba32 Rgb484_48C = new Rgba32(68, 128, 204, 255); - - private static byte[] Rgb484_Bytes4x4 = new byte[] { 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, - 0xF0, 0x00, 0x0F, 0xF0, 0x00, 0x0F, 0xF0, 0x0F, - 0x40, 0x00, 0x80, 0x00, 0xC0, 0x00, 0x48, 0x0C, - 0x00, 0x00, 0x44, 0x04, 0x88, 0x08, 0xCC, 0x0C }; - - private static Rgba32[][] Rgb484_Result4x4 = new[] { new[] { Rgb484_000, Rgb484_FFF, Rgb484_000, Rgb484_FFF }, - new[] { Rgb484_F00, Rgb484_0F0, Rgb484_00F, Rgb484_F0F }, - new[] { Rgb484_400, Rgb484_800, Rgb484_C00, Rgb484_48C }, - new[] { Rgb484_000, Rgb484_444, Rgb484_888, Rgb484_CCC }}; + private static Rgba32 rgb484_000 = new Rgba32(0, 0, 0, 255); + private static Rgba32 rgb484_444 = new Rgba32(68, 64, 68, 255); + private static Rgba32 rgb484_888 = new Rgba32(136, 128, 136, 255); + private static Rgba32 rgb484_CCC = new Rgba32(204, 192, 204, 255); + private static Rgba32 rgb484_FFF = new Rgba32(255, 255, 255, 255); + private static Rgba32 rgb484_F00 = new Rgba32(255, 0, 0, 255); + private static Rgba32 rgb484_0F0 = new Rgba32(0, 255, 0, 255); + private static Rgba32 rgb484_00F = new Rgba32(0, 0, 255, 255); + private static Rgba32 rgb484_F0F = new Rgba32(255, 0, 255, 255); + private static Rgba32 rgb484_400 = new Rgba32(68, 0, 0, 255); + private static Rgba32 rgb484_800 = new Rgba32(136, 0, 0, 255); + private static Rgba32 rgb484_C00 = new Rgba32(204, 0, 0, 255); + private static Rgba32 rgb484_48C = new Rgba32(68, 128, 204, 255); + + private static byte[] rgb484_Bytes4x4 = + { + 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, + 0xF0, 0x00, 0x0F, 0xF0, 0x00, 0x0F, 0xF0, 0x0F, + 0x40, 0x00, 0x80, 0x00, 0xC0, 0x00, 0x48, 0x0C, + 0x00, 0x00, 0x44, 0x04, 0x88, 0x08, 0xCC, 0x0C + }; + + private static Rgba32[][] rgb484_Result4x4 = + { + new[] { rgb484_000, rgb484_FFF, rgb484_000, rgb484_FFF }, + new[] { rgb484_F00, rgb484_0F0, rgb484_00F, rgb484_F0F }, + new[] { rgb484_400, rgb484_800, rgb484_C00, rgb484_48C }, + new[] { rgb484_000, rgb484_444, rgb484_888, rgb484_CCC } + }; public static IEnumerable Rgb484_Data { get { - yield return new object[] { Rgb484_Bytes4x4, new ushort[] { 4, 8, 4 }, 0, 0, 4, 4, Rgb484_Result4x4 }; - yield return new object[] { Rgb484_Bytes4x4, new ushort[] { 4, 8, 4 }, 0, 0, 4, 4, Offset(Rgb484_Result4x4, 0, 0, 6, 6) }; - yield return new object[] { Rgb484_Bytes4x4, new ushort[] { 4, 8, 4 }, 1, 0, 4, 4, Offset(Rgb484_Result4x4, 1, 0, 6, 6) }; - yield return new object[] { Rgb484_Bytes4x4, new ushort[] { 4, 8, 4 }, 0, 1, 4, 4, Offset(Rgb484_Result4x4, 0, 1, 6, 6) }; - yield return new object[] { Rgb484_Bytes4x4, new ushort[] { 4, 8, 4 }, 1, 1, 4, 4, Offset(Rgb484_Result4x4, 1, 1, 6, 6) }; + yield return new object[] { rgb484_Bytes4x4, new ushort[] { 4, 8, 4 }, 0, 0, 4, 4, rgb484_Result4x4 }; + yield return new object[] { rgb484_Bytes4x4, new ushort[] { 4, 8, 4 }, 0, 0, 4, 4, Offset(rgb484_Result4x4, 0, 0, 6, 6) }; + yield return new object[] { rgb484_Bytes4x4, new ushort[] { 4, 8, 4 }, 1, 0, 4, 4, Offset(rgb484_Result4x4, 1, 0, 6, 6) }; + yield return new object[] { rgb484_Bytes4x4, new ushort[] { 4, 8, 4 }, 0, 1, 4, 4, Offset(rgb484_Result4x4, 0, 1, 6, 6) }; + yield return new object[] { rgb484_Bytes4x4, new ushort[] { 4, 8, 4 }, 1, 1, 4, 4, Offset(rgb484_Result4x4, 1, 1, 6, 6) }; } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs index faea296d0..64e1aa077 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs @@ -2,72 +2,104 @@ // Licensed under the Apache License, Version 2.0. using System.Collections.Generic; + using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.PixelFormats; + using Xunit; -namespace SixLabors.ImageSharp.Tests.Formats.Tiff +namespace SixLabors.ImageSharp.Tests.Formats.Tiff.PhotometricInterpretation { public class WhiteIsZeroTiffColorTests : PhotometricInterpretationTestBase { - private static Rgba32 Gray000 = new Rgba32(255, 255, 255, 255); - private static Rgba32 Gray128 = new Rgba32(127, 127, 127, 255); - private static Rgba32 Gray255 = new Rgba32(0, 0, 0, 255); - private static Rgba32 Gray0 = new Rgba32(255, 255, 255, 255); - private static Rgba32 Gray8 = new Rgba32(119, 119, 119, 255); - private static Rgba32 GrayF = new Rgba32(0, 0, 0, 255); - private static Rgba32 Bit0 = new Rgba32(255, 255, 255, 255); - private static Rgba32 Bit1 = new Rgba32(0, 0, 0, 255); - - private static readonly byte[] Bilevel_Bytes4x4 = new byte[] { 0b01010000, - 0b11110000, - 0b01110000, - 0b10010000 }; - - private static readonly Rgba32[][] Bilevel_Result4x4 = new[] { new[] { Bit0, Bit1, Bit0, Bit1 }, - new[] { Bit1, Bit1, Bit1, Bit1 }, - new[] { Bit0, Bit1, Bit1, Bit1 }, - new[] { Bit1, Bit0, Bit0, Bit1 }}; - - private static readonly byte[] Bilevel_Bytes12x4 = new byte[] { 0b01010101, 0b01010000, - 0b11111111, 0b11111111, - 0b01101001, 0b10100000, - 0b10010000, 0b01100000}; - - private static readonly Rgba32[][] Bilevel_Result12x4 = new[] { new[] { Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1 }, - new[] { Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1 }, - new[] { Bit0, Bit1, Bit1, Bit0, Bit1, Bit0, Bit0, Bit1, Bit1, Bit0, Bit1, Bit0 }, - new[] { Bit1, Bit0, Bit0, Bit1, Bit0, Bit0, Bit0, Bit0, Bit0, Bit1, Bit1, Bit0 }}; - - private static readonly byte[] Grayscale4_Bytes4x4 = new byte[] { 0x8F, 0x0F, - 0xFF, 0xFF, - 0x08, 0x8F, - 0xF0, 0xF8 }; - - private static readonly Rgba32[][] Grayscale4_Result4x4 = new[] { new[] { Gray8, GrayF, Gray0, GrayF }, - new[] { GrayF, GrayF, GrayF, GrayF }, - new[] { Gray0, Gray8, Gray8, GrayF }, - new[] { GrayF, Gray0, GrayF, Gray8 }}; - - private static readonly byte[] Grayscale4_Bytes3x4 = new byte[] { 0x8F, 0x00, - 0xFF, 0xF0, - 0x08, 0x80, - 0xF0, 0xF0 }; - - private static readonly Rgba32[][] Grayscale4_Result3x4 = new[] { new[] { Gray8, GrayF, Gray0 }, - new[] { GrayF, GrayF, GrayF }, - new[] { Gray0, Gray8, Gray8 }, - new[] { GrayF, Gray0, GrayF }}; - - private static readonly byte[] Grayscale8_Bytes4x4 = new byte[] { 128, 255, 000, 255, - 255, 255, 255, 255, - 000, 128, 128, 255, - 255, 000, 255, 128 }; - - private static readonly Rgba32[][] Grayscale8_Result4x4 = new[] { new[] { Gray128, Gray255, Gray000, Gray255 }, - new[] { Gray255, Gray255, Gray255, Gray255 }, - new[] { Gray000, Gray128, Gray128, Gray255 }, - new[] { Gray255, Gray000, Gray255, Gray128 }}; + private static Rgba32 gray000 = new Rgba32(255, 255, 255, 255); + private static Rgba32 gray128 = new Rgba32(127, 127, 127, 255); + private static Rgba32 gray255 = new Rgba32(0, 0, 0, 255); + private static Rgba32 gray0 = new Rgba32(255, 255, 255, 255); + private static Rgba32 gray8 = new Rgba32(119, 119, 119, 255); + private static Rgba32 grayF = new Rgba32(0, 0, 0, 255); + private static Rgba32 bit0 = new Rgba32(255, 255, 255, 255); + private static Rgba32 bit1 = new Rgba32(0, 0, 0, 255); + + private static readonly byte[] Bilevel_Bytes4x4 = + { + 0b01010000, + 0b11110000, + 0b01110000, + 0b10010000 + }; + + private static readonly Rgba32[][] Bilevel_Result4x4 = + { + new[] { bit0, bit1, bit0, bit1 }, + new[] { bit1, bit1, bit1, bit1 }, + new[] { bit0, bit1, bit1, bit1 }, + new[] { bit1, bit0, bit0, bit1 } + }; + + private static readonly byte[] Bilevel_Bytes12x4 = + { + 0b01010101, 0b01010000, + 0b11111111, 0b11111111, + 0b01101001, 0b10100000, + 0b10010000, 0b01100000 + }; + + private static readonly Rgba32[][] Bilevel_Result12x4 = + { + new[] { bit0, bit1, bit0, bit1, bit0, bit1, bit0, bit1, bit0, bit1, bit0, bit1 }, + new[] { bit1, bit1, bit1, bit1, bit1, bit1, bit1, bit1, bit1, bit1, bit1, bit1 }, + new[] { bit0, bit1, bit1, bit0, bit1, bit0, bit0, bit1, bit1, bit0, bit1, bit0 }, + new[] { bit1, bit0, bit0, bit1, bit0, bit0, bit0, bit0, bit0, bit1, bit1, bit0 } + }; + + private static readonly byte[] Grayscale4_Bytes4x4 = + { + 0x8F, 0x0F, + 0xFF, 0xFF, + 0x08, 0x8F, + 0xF0, 0xF8 + }; + + private static readonly Rgba32[][] Grayscale4_Result4x4 = + { + new[] { gray8, grayF, gray0, grayF }, + new[] { grayF, grayF, grayF, grayF }, + new[] { gray0, gray8, gray8, grayF }, + new[] { grayF, gray0, grayF, gray8 } + }; + + private static readonly byte[] Grayscale4_Bytes3x4 = + { + 0x8F, 0x00, + 0xFF, 0xF0, + 0x08, 0x80, + 0xF0, 0xF0 + }; + + private static readonly Rgba32[][] Grayscale4_Result3x4 = + { + new[] { gray8, grayF, gray0 }, + new[] { grayF, grayF, grayF }, + new[] { gray0, gray8, gray8 }, + new[] { grayF, gray0, grayF } + }; + + private static readonly byte[] Grayscale8_Bytes4x4 = + { + 128, 255, 000, 255, + 255, 255, 255, 255, + 000, 128, 128, 255, + 255, 000, 255, 128 + }; + + private static readonly Rgba32[][] Grayscale8_Result4x4 = + { + new[] { gray128, gray255, gray000, gray255 }, + new[] { gray255, gray255, gray255, gray255 }, + new[] { gray000, gray128, gray128, gray255 }, + new[] { gray255, gray000, gray255, gray128 } + }; public static IEnumerable Bilevel_Data { diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index 3df4b45cb..3affbce4c 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -70,18 +70,18 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal(TiffPlanarConfiguration.Chunky, frame.PlanarConfiguration); Assert.Equal(TiffResolutionUnit.Inch, frame.ResolutionUnit); Assert.Equal("IrfanView", frame.Software); - Assert.Equal(null, frame.DateTime); + Assert.Null(frame.DateTime); Assert.Equal("This is author1;Author2", frame.Artist); - Assert.Equal(null, frame.HostComputer); + Assert.Null(frame.HostComputer); Assert.Equal(48, frame.ColorMap.Length); Assert.Equal(10537, frame.ColorMap[0]); Assert.Equal(14392, frame.ColorMap[1]); Assert.Equal(58596, frame.ColorMap[46]); Assert.Equal(3855, frame.ColorMap[47]); - Assert.Equal(null, frame.ExtraSamples); + Assert.Null(frame.ExtraSamples); Assert.Equal(TiffPredictor.None, frame.Predictor); - Assert.Equal(null, frame.SampleFormat); + Assert.Null(frame.SampleFormat); Assert.Equal("This is Авторские права", frame.Copyright); } } @@ -100,7 +100,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff TiffFrameMetadata frame0 = image.Frames[0].Metadata.GetTiffMetadata(); Assert.Equal(TiffNewSubfileType.FullImage, frame0.NewSubfileType); - Assert.Equal(null, frame0.SubfileType); + Assert.Null(frame0.SubfileType); Assert.Equal(255u, frame0.Width); Assert.Equal(255u, frame0.Height); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs index 15b495556..7336d0b3f 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs @@ -8,7 +8,7 @@ using SixLabors.ImageSharp.Memory; using Xunit; -namespace SixLabors.ImageSharp.Tests.Formats.Tiff +namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Utils { [Trait("Category", "Tiff")] public class TiffWriterTests @@ -71,7 +71,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { using var stream = new MemoryStream(); using var writer = new TiffWriter(stream, MemoryAllocator, Configuration); - writer.Write((uint)12345678); + writer.Write(12345678U); Assert.Equal(new byte[] { 0x4E, 0x61, 0xBC, 0x00 }, stream.ToArray()); } @@ -99,13 +99,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff using (var writer = new TiffWriter(stream, MemoryAllocator, Configuration)) { - writer.Write((uint)0x11111111); + writer.Write(0x11111111); long marker = writer.PlaceMarker(); - writer.Write((uint)0x33333333); + writer.Write(0x33333333); writer.WriteMarker(marker, 0x12345678); - writer.Write((uint)0x44444444); + writer.Write(0x44444444); } Assert.Equal( diff --git a/tests/ImageSharp.Tests/TestUtilities/ByteArrayUtility.cs b/tests/ImageSharp.Tests/TestUtilities/ByteArrayUtility.cs index 27dfdb8a2..501651285 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ByteArrayUtility.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ByteArrayUtility.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.TestUtilities { using System; @@ -9,9 +9,9 @@ namespace SixLabors.ImageSharp.Tests { public static byte[] WithByteOrder(this byte[] bytes, bool isLittleEndian) { - if (BitConverter.IsLittleEndian != isLittleEndian) + if (isLittleEndian != BitConverter.IsLittleEndian) { - byte[] reversedBytes = new byte[bytes.Length]; + var reversedBytes = new byte[bytes.Length]; Array.Copy(bytes, reversedBytes, bytes.Length); Array.Reverse(reversedBytes); return reversedBytes; @@ -22,4 +22,4 @@ namespace SixLabors.ImageSharp.Tests } } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/TestUtilities/ByteBuffer.cs b/tests/ImageSharp.Tests/TestUtilities/ByteBuffer.cs index 92b59271f..bbb75a9cf 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ByteBuffer.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ByteBuffer.cs @@ -1,15 +1,15 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.TestUtilities { using System; using System.Collections.Generic; public class ByteBuffer { - List bytes = new List(); - bool isLittleEndian; + private readonly List bytes = new List(); + private readonly bool isLittleEndian; public ByteBuffer(bool isLittleEndian) { @@ -18,22 +18,22 @@ namespace SixLabors.ImageSharp.Tests public void AddByte(byte value) { - bytes.Add(value); + this.bytes.Add(value); } public void AddUInt16(ushort value) { - bytes.AddRange(BitConverter.GetBytes(value).WithByteOrder(isLittleEndian)); + this.bytes.AddRange(BitConverter.GetBytes(value).WithByteOrder(this.isLittleEndian)); } public void AddUInt32(uint value) { - bytes.AddRange(BitConverter.GetBytes(value).WithByteOrder(isLittleEndian)); + this.bytes.AddRange(BitConverter.GetBytes(value).WithByteOrder(this.isLittleEndian)); } public byte[] ToArray() { - return bytes.ToArray(); + return this.bytes.ToArray(); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs index 1bcacc4de..36f869844 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs @@ -57,8 +57,7 @@ namespace SixLabors.ImageSharp.Tests new JpegConfigurationModule(), new GifConfigurationModule(), new TgaConfigurationModule(), - new TiffConfigurationModule() - ); + new TiffConfigurationModule()); // Magick codecs should work on all platforms IImageEncoder pngEncoder = IsWindows ? (IImageEncoder)SystemDrawingReferenceEncoder.Png : new PngEncoder(); From 22f7ec2a1c529d00c34e4d78f38b3f3f68cbf29b Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 1 Dec 2020 09:35:16 +0100 Subject: [PATCH 111/275] Change paletted tiff encoder tests Because a quantizer is used to create the palette (and therefore changes to the original are expected), we do not compare the encoded image against the original: Instead we load the encoded image with a reference decoder and compare against that image. --- .../Formats/Tiff/TiffDecoderHelpers.cs | 2 +- .../Formats/Tiff/TiffEncoderTests.cs | 48 ++++++++++++-- .../Formats/Tiff/TiffTestUtils.cs | 63 +++++++++++++++++++ tests/ImageSharp.Tests/TestImages.cs | 17 +++-- .../Input/Tiff/Calliphora_ccitt_fax3.tiff | 2 +- .../Input/Tiff/Calliphora_gray_deflate.tiff | 3 + .../Calliphora_gray_deflate_predictor.tiff | 3 + .../Input/Tiff/Calliphora_rgb_deflate.tiff | 3 - .../Calliphora_rgb_deflate_predictor.tiff | 3 + .../Images/Input/Tiff/Calliphora_rgb_lzw.tiff | 3 - .../Tiff/Calliphora_rgb_lzw_predictor.tiff | 3 + ....tiff => ccitt_fax3_all_makeup_codes.tiff} | 0 .../Tiff/huffman_rle_all_makeup_codes.tiff | 3 + .../huffman_rle_all_terminating_codes.tiff | 3 + 14 files changed, 136 insertions(+), 20 deletions(-) create mode 100644 tests/ImageSharp.Tests/Formats/Tiff/TiffTestUtils.cs create mode 100644 tests/Images/Input/Tiff/Calliphora_gray_deflate.tiff create mode 100644 tests/Images/Input/Tiff/Calliphora_gray_deflate_predictor.tiff delete mode 100644 tests/Images/Input/Tiff/Calliphora_rgb_deflate.tiff create mode 100644 tests/Images/Input/Tiff/Calliphora_rgb_deflate_predictor.tiff delete mode 100644 tests/Images/Input/Tiff/Calliphora_rgb_lzw.tiff create mode 100644 tests/Images/Input/Tiff/Calliphora_rgb_lzw_predictor.tiff rename tests/Images/Input/Tiff/{ccitt_fax3_all_makeupcodes_codes.tiff => ccitt_fax3_all_makeup_codes.tiff} (100%) create mode 100644 tests/Images/Input/Tiff/huffman_rle_all_makeup_codes.tiff create mode 100644 tests/Images/Input/Tiff/huffman_rle_all_terminating_codes.tiff diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs index 4f45ba5d7..d127fd870 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs @@ -108,7 +108,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff if (entries.Predictor != TiffPredictor.None) { - TiffThrowHelper.ThrowNotSupported("At the moment support only None Predictor."); + TiffThrowHelper.ThrowNotSupported("At the moment we support only None Predictor images."); } if (entries.SampleFormat != null) diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 34b713d6e..09938582a 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -76,23 +76,59 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public void TiffEncoder_EncodeGray_WithPackBitsCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.Gray, TiffEncoderCompression.PackBits); - // TODO: this test fails, but the output looks correct. I thinks its due to the fact that a quantizer is used to create the palette. [Theory] [WithFile(TestImages.Tiff.Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette); + where TPixel : unmanaged, IPixel + { + // Because a quantizer is used to create the palette (and therefore changes to the original are expected), + // we do not compare the encoded image against the original: + // Instead we load the encoded image with a reference decoder and compare against that image. + // TODO: There is a difference of 0,0043% + using Image image = provider.GetImage(); + using var memStream = new MemoryStream(); + var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.None }; + + image.Save(memStream, encoder); + memStream.Position = 0; + + using var encodedImage = (Image)Image.Load(memStream); + TiffTestUtils.CompareWithReferenceDecoder(provider, encodedImage); + } - // TODO: this test fails, but the output looks correct. I thinks its due to the fact that a quantizer is used to create the palette. [Theory] [WithFile(TestImages.Tiff.Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_WithDeflateCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, TiffEncoderCompression.Deflate); + where TPixel : unmanaged, IPixel + { + // TODO: There is a difference of 0,0043% + using Image image = provider.GetImage(); + using var memStream = new MemoryStream(); + var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Deflate }; + + image.Save(memStream, encoder); + memStream.Position = 0; + + using var encodedImage = (Image)Image.Load(memStream); + TiffTestUtils.CompareWithReferenceDecoder(provider, encodedImage); + } - // TODO: this test fails, but the output looks correct. I thinks its due to the fact that a quantizer is used to create the palette. [Theory] [WithFile(TestImages.Tiff.Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_WithPackBitsCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, TiffEncoderCompression.PackBits); + where TPixel : unmanaged, IPixel + { + // TODO: There is a difference of 0,0043% + using Image image = provider.GetImage(); + using var memStream = new MemoryStream(); + var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.PackBits }; + + image.Save(memStream, encoder); + memStream.Position = 0; + + using var encodedImage = (Image)Image.Load(memStream); + TiffTestUtils.CompareWithReferenceDecoder(provider, encodedImage); + } [Theory] [WithFile(TestImages.Tiff.Calliphora_BiColor, PixelTypes.Rgba32)] diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffTestUtils.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffTestUtils.cs new file mode 100644 index 000000000..b4ebd088e --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffTestUtils.cs @@ -0,0 +1,63 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; + +using ImageMagick; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Formats.Tiff +{ + public static class TiffTestUtils + { + public static void CompareWithReferenceDecoder( + TestImageProvider provider, + Image image, + bool useExactComparer = true, + float compareTolerance = 0.01f) + where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel + { + string path = TestImageProvider.GetFilePathOrNull(provider); + if (path == null) + { + throw new InvalidOperationException("CompareToOriginal() works only with file providers!"); + } + + var testFile = TestFile.Create(path); + Image magickImage = DecodeWithMagick(Configuration.Default, new FileInfo(testFile.FullPath)); + if (useExactComparer) + { + ImageComparer.Exact.VerifySimilarity(magickImage, image); + } + else + { + ImageComparer.Tolerant(compareTolerance).VerifySimilarity(magickImage, image); + } + } + + public static Image DecodeWithMagick(Configuration configuration, FileInfo fileInfo) + where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel + { + using var magickImage = new MagickImage(fileInfo); + magickImage.AutoOrient(); + var result = new Image(configuration, magickImage.Width, magickImage.Height); + + Assert.True(result.TryGetSinglePixelSpan(out Span resultPixels)); + + using IUnsafePixelCollection pixels = magickImage.GetPixelsUnsafe(); + byte[] data = pixels.ToByteArray(PixelMapping.RGBA); + + PixelOperations.Instance.FromRgba32Bytes( + configuration, + data, + resultPixels, + resultPixels.Length); + + return result; + } + } +} diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 099aa2d5a..6bce48eb2 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -505,10 +505,12 @@ namespace SixLabors.ImageSharp.Tests public const string Benchmark_RgbUncompressed = "Tiff/Benchmarks/jpeg444_big_rgb_uncompressed.tiff"; public const string Calliphora_GrayscaleUncompressed = "Tiff/Calliphora_grayscale_uncompressed.tiff"; + public const string Calliphora_GrayscaleDeflate_Predictor = "Tiff/Calliphora_gray_deflate_predictor.tiff"; + public const string Calliphora_GrayscaleDeflate = "Tiff/Calliphora_gray_deflate.tiff"; public const string Calliphora_PaletteUncompressed = "Tiff/Calliphora_palette_uncompressed.tiff"; - public const string Calliphora_RgbDeflate_Predictor = "Tiff/Calliphora_rgb_deflate.tiff"; + public const string Calliphora_RgbDeflate_Predictor = "Tiff/Calliphora_rgb_deflate_predictor.tiff"; public const string Calliphora_RgbJpeg = "Tiff/Calliphora_rgb_jpeg.tiff"; - public const string Calliphora_RgbLzw_Predictor = "Tiff/Calliphora_rgb_lzw.tiff"; + public const string Calliphora_RgbLzw_Predictor = "Tiff/Calliphora_rgb_lzw_predictor.tiff"; public const string Calliphora_RgbPackbits = "Tiff/Calliphora_rgb_packbits.tiff"; public const string Calliphora_RgbUncompressed = "Tiff/Calliphora_rgb_uncompressed.tiff"; public const string Calliphora_Fax3Compressed = "Tiff/Calliphora_ccitt_fax3.tiff"; @@ -516,7 +518,9 @@ namespace SixLabors.ImageSharp.Tests public const string Calliphora_BiColor = "Tiff/Calliphora_bicolor_uncompressed.tiff"; public const string CcittFax3AllTermCodes = "Tiff/ccitt_fax3_all_terminating_codes.tiff"; - public const string CcittFax3AllMakupCodes = "Tiff/ccitt_fax3_all_makeupcodes_codes.tiff"; + public const string CcittFax3AllMakeupCodes = "Tiff/ccitt_fax3_all_makeup_codes.tiff"; + public const string HuffmanRleAllTermCodes = "Tiff/huffman_rle_all_terminating_codes.tiff"; + public const string HuffmanRleAllMakeupCodes = "Tiff/huffman_rle_all_makeup_codes.tiff"; public const string GrayscaleDeflateMultistrip = "Tiff/grayscale_deflate_multistrip.tiff"; public const string GrayscaleUncompressed = "Tiff/grayscale_uncompressed.tiff"; @@ -549,8 +553,9 @@ namespace SixLabors.ImageSharp.Tests public static readonly string[] All = { - Calliphora_GrayscaleUncompressed, Calliphora_PaletteUncompressed, /*Calliphora_RgbDeflate_Predictor, Calliphora_RgbLzwe_Predictor, */ Calliphora_RgbPackbits, - Calliphora_BiColor, Calliphora_RgbUncompressed, Calliphora_HuffmanCompressed, Calliphora_Fax3Compressed, CcittFax3AllTermCodes, CcittFax3AllMakupCodes, GrayscaleDeflateMultistrip, + Calliphora_PaletteUncompressed, /*Calliphora_RgbDeflate_Predictor, Calliphora_RgbLzwe_Predictor, */ Calliphora_RgbPackbits, + Calliphora_BiColor, Calliphora_RgbUncompressed, Calliphora_HuffmanCompressed, Calliphora_Fax3Compressed, CcittFax3AllTermCodes, CcittFax3AllMakeupCodes, + HuffmanRleAllTermCodes, HuffmanRleAllMakeupCodes, GrayscaleDeflateMultistrip, Calliphora_GrayscaleDeflate, Calliphora_GrayscaleUncompressed, GrayscaleUncompressed, PaletteDeflateMultistrip, PaletteUncompressed, /*RgbDeflate_Predictor,*/ RgbDeflateMultistrip, /*RgbJpeg,*/ /*RgbLzw_Predictor, RgbLzwMultistrip_Predictor,*/ RgbLzw_NoPredictor_Multistrip, RgbLzw_NoPredictor_Multistrip_Motorola, RgbLzw_NoPredictor_Singlestrip_Motorola, RgbPackbits, RgbPackbitsMultistrip, RgbUncompressed, /* MultiframeLzw_Predictor, MultiFrameDifferentVariants, SampleMetadata,*/ SmallRgbDeflate, SmallRgbLzw, @@ -560,7 +565,7 @@ namespace SixLabors.ImageSharp.Tests public static readonly string[] Metadata = { SampleMetadata }; - public static readonly string[] NotSupported = { Calliphora_RgbJpeg, Calliphora_RgbDeflate_Predictor, Calliphora_RgbLzw_Predictor, RgbDeflate_Predictor, RgbLzw_Predictor, RgbLzwMultistrip_Predictor, RgbJpeg, RgbUncompressedTiled, MultiframeLzw_Predictor, MultiframeDifferentSize, MultiframeDifferentVariants }; + public static readonly string[] NotSupported = { Calliphora_GrayscaleDeflate_Predictor, Calliphora_RgbJpeg, Calliphora_RgbDeflate_Predictor, Calliphora_RgbLzw_Predictor, RgbDeflate_Predictor, RgbLzw_Predictor, RgbLzwMultistrip_Predictor, RgbJpeg, RgbUncompressedTiled, MultiframeLzw_Predictor, MultiframeDifferentSize, MultiframeDifferentVariants }; } } } diff --git a/tests/Images/Input/Tiff/Calliphora_ccitt_fax3.tiff b/tests/Images/Input/Tiff/Calliphora_ccitt_fax3.tiff index e50ee6f2a..df6f89b42 100644 --- a/tests/Images/Input/Tiff/Calliphora_ccitt_fax3.tiff +++ b/tests/Images/Input/Tiff/Calliphora_ccitt_fax3.tiff @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a45c92b187e7a59247ccc50f418379e91fd169b39fa7fc8a6dcda9b092fc3013 +oid sha256:c35abf4ea204b130c9c70590581c0bb88566630fbc0fe5bd2dabaa90379dc4f1 size 125776 diff --git a/tests/Images/Input/Tiff/Calliphora_gray_deflate.tiff b/tests/Images/Input/Tiff/Calliphora_gray_deflate.tiff new file mode 100644 index 000000000..0cf2c2136 --- /dev/null +++ b/tests/Images/Input/Tiff/Calliphora_gray_deflate.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5a019baef7da23cb937a65131ee34b59988ca5ace5d26fe36139d6e6b12e8d59 +size 557710 diff --git a/tests/Images/Input/Tiff/Calliphora_gray_deflate_predictor.tiff b/tests/Images/Input/Tiff/Calliphora_gray_deflate_predictor.tiff new file mode 100644 index 000000000..1e316caa4 --- /dev/null +++ b/tests/Images/Input/Tiff/Calliphora_gray_deflate_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7f57e87714dca75ce414b01e9a47e4fd0f0ecfd50bc408eb80f3a53cf758e148 +size 630942 diff --git a/tests/Images/Input/Tiff/Calliphora_rgb_deflate.tiff b/tests/Images/Input/Tiff/Calliphora_rgb_deflate.tiff deleted file mode 100644 index c2ebed364..000000000 --- a/tests/Images/Input/Tiff/Calliphora_rgb_deflate.tiff +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:da6e6a35a0bb0f5f2d49e3c5f0eb2deb7118718dd08844f66a6cb72f48b5c489 -size 1476294 diff --git a/tests/Images/Input/Tiff/Calliphora_rgb_deflate_predictor.tiff b/tests/Images/Input/Tiff/Calliphora_rgb_deflate_predictor.tiff new file mode 100644 index 000000000..0784d8875 --- /dev/null +++ b/tests/Images/Input/Tiff/Calliphora_rgb_deflate_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f614a127d6741b0ed335c5fc5e5a2917dd737a4db6afb40ff71b0346e691097a +size 1476268 diff --git a/tests/Images/Input/Tiff/Calliphora_rgb_lzw.tiff b/tests/Images/Input/Tiff/Calliphora_rgb_lzw.tiff deleted file mode 100644 index 3a37054cc..000000000 --- a/tests/Images/Input/Tiff/Calliphora_rgb_lzw.tiff +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:36b828df14ffda9b64f8eed99714e7af9d6324efe2349a972003af7166fc4629 -size 1792988 diff --git a/tests/Images/Input/Tiff/Calliphora_rgb_lzw_predictor.tiff b/tests/Images/Input/Tiff/Calliphora_rgb_lzw_predictor.tiff new file mode 100644 index 000000000..c0ab53c1d --- /dev/null +++ b/tests/Images/Input/Tiff/Calliphora_rgb_lzw_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9bbfce6af3942b2dc3edaaaac7de64956b1a532c48b43a7b0ba887b2dd98fcc8 +size 1792960 diff --git a/tests/Images/Input/Tiff/ccitt_fax3_all_makeupcodes_codes.tiff b/tests/Images/Input/Tiff/ccitt_fax3_all_makeup_codes.tiff similarity index 100% rename from tests/Images/Input/Tiff/ccitt_fax3_all_makeupcodes_codes.tiff rename to tests/Images/Input/Tiff/ccitt_fax3_all_makeup_codes.tiff diff --git a/tests/Images/Input/Tiff/huffman_rle_all_makeup_codes.tiff b/tests/Images/Input/Tiff/huffman_rle_all_makeup_codes.tiff new file mode 100644 index 000000000..643805ca7 --- /dev/null +++ b/tests/Images/Input/Tiff/huffman_rle_all_makeup_codes.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aa8dfeb96763b2b35b5f06f37021d7e33551485105ad4a3a704d76b3aecf039d +size 518 diff --git a/tests/Images/Input/Tiff/huffman_rle_all_terminating_codes.tiff b/tests/Images/Input/Tiff/huffman_rle_all_terminating_codes.tiff new file mode 100644 index 000000000..2ab78c71e --- /dev/null +++ b/tests/Images/Input/Tiff/huffman_rle_all_terminating_codes.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:99a488957403e3c35f7dfbbcbe7f187a74e6cab61b233c91f4892079c04984fd +size 550 From 00331086fae80a0501da43220d9c7767637c9985 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 1 Dec 2020 12:33:51 +0100 Subject: [PATCH 112/275] Add support for undoing horizontal prediction: Works with deflate, still some issue with lzw --- .../Tiff/Constants/TiffPlanarConfiguration.cs | 10 +++++++ src/ImageSharp/Formats/Tiff/README.md | 9 ++++++ .../Formats/Tiff/TiffDecoderCore.cs | 30 +++++++++++++++++++ .../Formats/Tiff/TiffDecoderHelpers.cs | 6 ++-- tests/ImageSharp.Tests/TestImages.cs | 10 ++++--- .../{rgb_lzw.tiff => rgb_lzw_predictor.tiff} | 0 6 files changed, 58 insertions(+), 7 deletions(-) rename tests/Images/Input/Tiff/{rgb_lzw.tiff => rgb_lzw_predictor.tiff} (100%) diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffPlanarConfiguration.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffPlanarConfiguration.cs index 6249a935e..ea526ede5 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffPlanarConfiguration.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffPlanarConfiguration.cs @@ -10,11 +10,21 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Constants { /// /// Chunky format. + /// The component values for each pixel are stored contiguously. + /// The order of the components within the pixel is specified by + /// PhotometricInterpretation. For example, for RGB data, the data is stored as RGBRGBRGB. /// Chunky = 1, /// /// Planar format. + /// The components are stored in separate “component planes.” The + /// values in StripOffsets and StripByteCounts are then arranged as a 2-dimensional + /// array, with SamplesPerPixel rows and StripsPerImage columns. (All of the columns + /// for row 0 are stored first, followed by the columns of row 1, and so on.) + /// PhotometricInterpretation describes the type of data stored in each component + /// plane. For example, RGB data is stored with the Red components in one component + /// plane, the Green in another, and the Blue in another. /// Planar = 2 } diff --git a/src/ImageSharp/Formats/Tiff/README.md b/src/ImageSharp/Formats/Tiff/README.md index a87813a23..bb5e93c72 100644 --- a/src/ImageSharp/Formats/Tiff/README.md +++ b/src/ImageSharp/Formats/Tiff/README.md @@ -25,6 +25,15 @@ ## Implementation Status +### Know issue which need to be fixed + +Decoder: +- Decoding HUffman RLE for `Calliphora_huffman_rle.tiff` has 4 pixels difference to the reference decoder. Al those are at the very edge of the image (reason unknown so far). +- Decoding compressed images with HorizontalPrediction: Works for deflate, but not for lzw. + +Encoder: +- Encoding image with a palette have a difference of 0.0043% to the ReferenceDecoder (ImageMagick) + ### Deviations from the TIFF spec (to be fixed) - Decoder diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 4c9b0b48d..d29cf7438 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -240,6 +240,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff var coreMetadata = new ImageFrameMetadata(); frameMetaData = coreMetadata.GetTiffMetadata(); frameMetaData.Tags = tags; + TiffFrameMetadata tiffFormatMetaData = coreMetadata.GetFormatMetadata(TiffFormat.Instance); this.VerifyAndParseOptions(frameMetaData); @@ -260,9 +261,38 @@ namespace SixLabors.ImageSharp.Formats.Tiff this.DecodeStripsChunky(frame, rowsPerStrip, stripOffsets, stripByteCounts, width); } + if (tiffFormatMetaData.Predictor == TiffPredictor.Horizontal) + { + this.UndoHorizontalPredictor(width, height, frame); + } + return frame; } + private void UndoHorizontalPredictor(int width, int height, ImageFrame frame) + where TPixel : unmanaged, IPixel + { + using System.Buffers.IMemoryOwner rowRgbBuffer = this.memoryAllocator.Allocate(width); + System.Span rowRgb = rowRgbBuffer.GetSpan(); + for (int y = 0; y < height; y++) + { + System.Span pixelRow = frame.GetPixelRowSpan(y); + PixelOperations.Instance.ToRgb24(this.configuration, pixelRow, rowRgb); + byte r = rowRgb[0].R; + byte g = rowRgb[0].G; + byte b = rowRgb[0].B; + for (int x = 1; x < width; x++) + { + ref TPixel pixel = ref pixelRow[x]; + r += rowRgb[x].R; + g += rowRgb[x].G; + b += rowRgb[x].B; + var rgb = new Rgb24(r, g, b); + pixel.FromRgb24(rgb); + } + } + } + /// /// Calculates the size (in bytes) for a pixel buffer using the determined color format. /// diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs index d127fd870..5ae58c6a6 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs @@ -106,9 +106,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff TiffThrowHelper.ThrowNotSupported("The Tile images is not supported."); } - if (entries.Predictor != TiffPredictor.None) + if (entries.Predictor == TiffPredictor.FloatingPoint) { - TiffThrowHelper.ThrowNotSupported("At the moment we support only None Predictor images."); + TiffThrowHelper.ThrowNotSupported("ImageSharp does not support FloatingPoint Predictor images."); } if (entries.SampleFormat != null) @@ -117,7 +117,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff { if (format != TiffSampleFormat.UnsignedInteger) { - TiffThrowHelper.ThrowNotSupported("At the moment support only UnsignedInteger SampleFormat."); + TiffThrowHelper.ThrowNotSupported("ImageSharp only supports the UnsignedInteger SampleFormat."); } } } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 6bce48eb2..9f7516fe2 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -529,7 +529,7 @@ namespace SixLabors.ImageSharp.Tests public const string RgbDeflate_Predictor = "Tiff/rgb_deflate.tiff"; public const string RgbDeflateMultistrip = "Tiff/rgb_deflate_multistrip.tiff"; public const string RgbJpeg = "Tiff/rgb_jpeg.tiff"; - public const string RgbLzw_Predictor = "Tiff/rgb_lzw.tiff"; + public const string RgbLzw_Predictor = "Tiff/rgb_lzw_predictor.tiff"; public const string RgbLzw_NoPredictor_Multistrip = "Tiff/rgb_lzw_noPredictor_multistrip.tiff"; public const string RgbLzw_NoPredictor_Multistrip_Motorola = "Tiff/rgb_lzw_noPredictor_multistrip_Motorola.tiff"; public const string RgbLzw_NoPredictor_Singlestrip_Motorola = "Tiff/rgb_lzw_noPredictor_singlestrip_Motorola.tiff"; @@ -553,10 +553,12 @@ namespace SixLabors.ImageSharp.Tests public static readonly string[] All = { - Calliphora_PaletteUncompressed, /*Calliphora_RgbDeflate_Predictor, Calliphora_RgbLzwe_Predictor, */ Calliphora_RgbPackbits, + Calliphora_PaletteUncompressed, Calliphora_RgbPackbits, + Calliphora_GrayscaleDeflate_Predictor, Calliphora_RgbDeflate_Predictor, RgbDeflate_Predictor, + Calliphora_RgbLzw_Predictor, RgbLzw_Predictor, // TODO: Undoing the horizontal prediction seems to fail for lzw. Do we need to do something different for lzw? Calliphora_BiColor, Calliphora_RgbUncompressed, Calliphora_HuffmanCompressed, Calliphora_Fax3Compressed, CcittFax3AllTermCodes, CcittFax3AllMakeupCodes, HuffmanRleAllTermCodes, HuffmanRleAllMakeupCodes, GrayscaleDeflateMultistrip, Calliphora_GrayscaleDeflate, Calliphora_GrayscaleUncompressed, - GrayscaleUncompressed, PaletteDeflateMultistrip, PaletteUncompressed, /*RgbDeflate_Predictor,*/ RgbDeflateMultistrip, /*RgbJpeg,*/ /*RgbLzw_Predictor, RgbLzwMultistrip_Predictor,*/ + GrayscaleUncompressed, PaletteDeflateMultistrip, PaletteUncompressed, RgbDeflateMultistrip, /*RgbJpeg,*/ /* RgbLzwMultistrip_Predictor,*/ RgbLzw_NoPredictor_Multistrip, RgbLzw_NoPredictor_Multistrip_Motorola, RgbLzw_NoPredictor_Singlestrip_Motorola, RgbPackbits, RgbPackbitsMultistrip, RgbUncompressed, /* MultiframeLzw_Predictor, MultiFrameDifferentVariants, SampleMetadata,*/ SmallRgbDeflate, SmallRgbLzw, }; @@ -565,7 +567,7 @@ namespace SixLabors.ImageSharp.Tests public static readonly string[] Metadata = { SampleMetadata }; - public static readonly string[] NotSupported = { Calliphora_GrayscaleDeflate_Predictor, Calliphora_RgbJpeg, Calliphora_RgbDeflate_Predictor, Calliphora_RgbLzw_Predictor, RgbDeflate_Predictor, RgbLzw_Predictor, RgbLzwMultistrip_Predictor, RgbJpeg, RgbUncompressedTiled, MultiframeLzw_Predictor, MultiframeDifferentSize, MultiframeDifferentVariants }; + public static readonly string[] NotSupported = { Calliphora_RgbJpeg, RgbLzwMultistrip_Predictor, RgbJpeg, RgbUncompressedTiled, MultiframeLzw_Predictor, MultiframeDifferentSize, MultiframeDifferentVariants }; } } } diff --git a/tests/Images/Input/Tiff/rgb_lzw.tiff b/tests/Images/Input/Tiff/rgb_lzw_predictor.tiff similarity index 100% rename from tests/Images/Input/Tiff/rgb_lzw.tiff rename to tests/Images/Input/Tiff/rgb_lzw_predictor.tiff From 57fe8ea703c8bffa1134603cac064d3b80514249 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 1 Dec 2020 12:46:13 +0100 Subject: [PATCH 113/275] Change tiff namespace to SixLabors.ImageSharp.Formats.Experimental.Tiff --- src/ImageSharp/Advanced/AotCompilerTools.cs | 3 +-- src/ImageSharp/Configuration.cs | 2 +- src/ImageSharp/Formats/ImageExtensions.Save.cs | 2 +- src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs | 2 +- .../Formats/Tiff/Compression/DeflateTiffCompression.cs | 4 ++-- .../Formats/Tiff/Compression/LzwTiffCompression.cs | 4 ++-- .../Tiff/Compression/ModifiedHuffmanTiffCompression.cs | 4 ++-- .../Formats/Tiff/Compression/NoneTiffCompression.cs | 4 ++-- .../Formats/Tiff/Compression/PackBitsTiffCompression.cs | 4 ++-- src/ImageSharp/Formats/Tiff/Compression/PackBitsWriter.cs | 2 +- src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs | 2 +- src/ImageSharp/Formats/Tiff/Compression/T4BitWriter.cs | 2 +- .../Formats/Tiff/Compression/T4TiffCompression.cs | 4 ++-- .../Formats/Tiff/Compression/TiffBaseCompression.cs | 4 ++-- .../Formats/Tiff/Compression/TiffCompressionFactory.cs | 4 ++-- .../Tiff/Compression/TiffDecoderCompressionType.cs | 2 +- src/ImageSharp/Formats/Tiff/Constants/TiffByteOrder.cs | 2 +- src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs | 2 +- src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs | 2 +- src/ImageSharp/Formats/Tiff/Constants/TiffExtraSamples.cs | 2 +- src/ImageSharp/Formats/Tiff/Constants/TiffFillOrder.cs | 2 +- .../Formats/Tiff/Constants/TiffNewSubfileType.cs | 2 +- src/ImageSharp/Formats/Tiff/Constants/TiffOrientation.cs | 2 +- .../Tiff/Constants/TiffPhotometricInterpretation.cs | 2 +- .../Formats/Tiff/Constants/TiffPlanarConfiguration.cs | 2 +- src/ImageSharp/Formats/Tiff/Constants/TiffPredictor.cs | 2 +- .../Formats/Tiff/Constants/TiffResolutionUnit.cs | 2 +- src/ImageSharp/Formats/Tiff/Constants/TiffSampleFormat.cs | 2 +- src/ImageSharp/Formats/Tiff/Constants/TiffSubfileType.cs | 2 +- src/ImageSharp/Formats/Tiff/Constants/TiffThresholding.cs | 2 +- src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs | 2 +- src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs | 2 +- src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs | 6 +++--- src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs | 6 +++--- src/ImageSharp/Formats/Tiff/ImageExtensions.cs | 2 +- src/ImageSharp/Formats/Tiff/MetadataExtensions.cs | 2 +- .../PhotometricInterpretation/BlackIsZero1TiffColor.cs | 2 +- .../PhotometricInterpretation/BlackIsZero4TiffColor.cs | 2 +- .../PhotometricInterpretation/BlackIsZero8TiffColor.cs | 2 +- .../PhotometricInterpretation/BlackIsZeroTiffColor.cs | 4 ++-- .../Tiff/PhotometricInterpretation/PaletteTiffColor.cs | 4 ++-- .../Tiff/PhotometricInterpretation/Rgb888TiffColor.cs | 2 +- .../Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs | 4 ++-- .../Tiff/PhotometricInterpretation/RgbTiffColor.cs | 4 ++-- .../PhotometricInterpretation/TiffBaseColorDecoder.cs | 2 +- .../PhotometricInterpretation/TiffColorDecoderFactory.cs | 2 +- .../Tiff/PhotometricInterpretation/TiffColorType.cs | 2 +- .../PhotometricInterpretation/WhiteIsZero1TiffColor.cs | 2 +- .../PhotometricInterpretation/WhiteIsZero4TiffColor.cs | 2 +- .../PhotometricInterpretation/WhiteIsZero8TiffColor.cs | 2 +- .../PhotometricInterpretation/WhiteIsZeroTiffColor.cs | 4 ++-- .../Formats/Tiff/Streams/TiffBigEndianStream.cs | 4 ++-- .../Formats/Tiff/Streams/TiffLittleEndianStream.cs | 4 ++-- src/ImageSharp/Formats/Tiff/Streams/TiffStream.cs | 4 ++-- src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffDecoder.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs | 8 ++++---- src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs | 6 +++--- src/ImageSharp/Formats/Tiff/TiffEncoder.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs | 6 +++--- src/ImageSharp/Formats/Tiff/TiffEncodingMode.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffFormat.cs | 4 ++-- src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs | 4 ++-- src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffMetadata.cs | 4 ++-- src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs | 2 +- src/ImageSharp/Formats/Tiff/Utils/BitReader.cs | 2 +- src/ImageSharp/Formats/Tiff/Utils/SubStream.cs | 2 +- src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs | 2 +- src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs | 2 +- src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs | 2 +- src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs | 4 ++-- tests/ImageSharp.Tests/ConfigurationTests.cs | 2 +- .../Tiff/Compression/DeflateTiffCompressionTests.cs | 2 +- .../Formats/Tiff/Compression/LzwTiffCompressionTests.cs | 4 ++-- .../Formats/Tiff/Compression/NoneTiffCompressionTests.cs | 2 +- .../Tiff/Compression/PackBitsTiffCompressionTests.cs | 4 +--- .../BlackIsZeroTiffColorTests.cs | 2 +- .../PhotometricInterpretation/PaletteTiffColorTests.cs | 2 +- .../PhotometricInterpretation/RgbPlanarTiffColorTests.cs | 2 +- .../Tiff/PhotometricInterpretation/RgbTiffColorTests.cs | 2 +- .../WhiteIsZeroTiffColorTests.cs | 2 +- tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs | 4 ++-- .../Formats/Tiff/TiffEncoderHeaderTests.cs | 4 ++-- tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs | 2 +- tests/ImageSharp.Tests/Formats/Tiff/TiffFormatTests.cs | 2 +- tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs | 4 ++-- .../ImageSharp.Tests/Formats/Tiff/Utils/SubStreamTests.cs | 2 +- .../Formats/Tiff/Utils/TiffWriterTests.cs | 2 +- .../TestUtilities/TestEnvironment.Formats.cs | 2 +- 92 files changed, 127 insertions(+), 130 deletions(-) diff --git a/src/ImageSharp/Advanced/AotCompilerTools.cs b/src/ImageSharp/Advanced/AotCompilerTools.cs index 7cd6dac09..335724088 100644 --- a/src/ImageSharp/Advanced/AotCompilerTools.cs +++ b/src/ImageSharp/Advanced/AotCompilerTools.cs @@ -8,7 +8,6 @@ using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Dithering; using SixLabors.ImageSharp.Processing.Processors.Quantization; @@ -95,7 +94,7 @@ namespace SixLabors.ImageSharp.Advanced AotCodec(new Formats.Gif.GifDecoder(), new Formats.Gif.GifEncoder()); AotCodec(new Formats.Jpeg.JpegDecoder(), new Formats.Jpeg.JpegEncoder()); AotCodec(new Formats.Tga.TgaDecoder(), new Formats.Tga.TgaEncoder()); - AotCodec(new Formats.Tiff.TiffDecoder(), new Formats.Tiff.TiffEncoder()); + AotCodec(new Formats.Experimental.Tiff.TiffDecoder(), new Formats.Experimental.Tiff.TiffEncoder()); // TODO: Do the discovery work to figure out what works and what doesn't. } diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index 854a5d69c..80aab1cc4 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -10,7 +10,7 @@ using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Tga; -using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Experimental.Tiff; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Processing; diff --git a/src/ImageSharp/Formats/ImageExtensions.Save.cs b/src/ImageSharp/Formats/ImageExtensions.Save.cs index 0f8b1e16d..7084f1542 100644 --- a/src/ImageSharp/Formats/ImageExtensions.Save.cs +++ b/src/ImageSharp/Formats/ImageExtensions.Save.cs @@ -12,7 +12,7 @@ using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Tga; -using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Experimental.Tiff; namespace SixLabors.ImageSharp { diff --git a/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs b/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs index 05efb0423..9c857eccd 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.Formats.Tiff.Compression +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression { internal static class BitWriterUtils { diff --git a/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs index 772d782ef..6e206f0ed 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs @@ -4,10 +4,10 @@ using System; using System.IO; using System.IO.Compression; -using SixLabors.ImageSharp.Formats.Tiff.Utils; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Tiff.Compression +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression { /// /// Class to handle cases where TIFF image data is compressed using Deflate compression. diff --git a/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs index ffce22145..14f45fc9d 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs @@ -3,10 +3,10 @@ using System; using System.IO; -using SixLabors.ImageSharp.Formats.Tiff.Utils; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Tiff.Compression +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression { /// /// Class to handle cases where TIFF image data is compressed using LZW compression. diff --git a/src/ImageSharp/Formats/Tiff/Compression/ModifiedHuffmanTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/ModifiedHuffmanTiffCompression.cs index b9866c6f2..6176d7565 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/ModifiedHuffmanTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/ModifiedHuffmanTiffCompression.cs @@ -4,10 +4,10 @@ using System; using System.IO; -using SixLabors.ImageSharp.Formats.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Tiff.Compression +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression { /// /// Class to handle cases where TIFF image data is compressed using Modified Huffman Compression. diff --git a/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs index a8bfe624d..94cf5a9ca 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs @@ -4,10 +4,10 @@ using System; using System.IO; -using SixLabors.ImageSharp.Formats.Tiff.Utils; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Tiff.Compression +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression { /// /// Class to handle cases where TIFF image data is not compressed. diff --git a/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs index a473fcf26..d49aced44 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs @@ -5,10 +5,10 @@ using System; using System.Buffers; using System.IO; -using SixLabors.ImageSharp.Formats.Tiff.Utils; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Tiff.Compression +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression { /// /// Class to handle cases where TIFF image data is compressed using PackBits compression. diff --git a/src/ImageSharp/Formats/Tiff/Compression/PackBitsWriter.cs b/src/ImageSharp/Formats/Tiff/Compression/PackBitsWriter.cs index 1677976e4..d70c9a370 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/PackBitsWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/PackBitsWriter.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.Formats.Tiff.Compression +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression { /// /// Pack Bits compression for tiff images. See Tiff Spec v6, section 9. diff --git a/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs index 672f4a008..3a4f8bd51 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs @@ -9,7 +9,7 @@ using System.Linq; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Tiff.Compression +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression { /// /// Bitreader for reading compressed CCITT T4 1D data. diff --git a/src/ImageSharp/Formats/Tiff/Compression/T4BitWriter.cs b/src/ImageSharp/Formats/Tiff/Compression/T4BitWriter.cs index ee924fc77..72605b74c 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/T4BitWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/T4BitWriter.cs @@ -9,7 +9,7 @@ using System.IO; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Tiff.Compression +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression { /// /// Bitwriter for writing compressed CCITT T4 1D data. diff --git a/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs index e6d2b4be0..b8649d210 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs @@ -4,10 +4,10 @@ using System; using System.IO; -using SixLabors.ImageSharp.Formats.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Tiff.Compression +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression { /// /// Class to handle cases where TIFF image data is compressed using CCITT T4 compression. diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs index 00da17973..45571d503 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs @@ -4,10 +4,10 @@ using System; using System.IO; -using SixLabors.ImageSharp.Formats.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Tiff.Compression +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression { /// /// Base tiff decompressor class. diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs index 07ca52954..6f785bb31 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs @@ -1,10 +1,10 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Formats.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Tiff.Compression +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression { internal static class TiffCompressionFactory { diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffDecoderCompressionType.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffDecoderCompressionType.cs index 80bc0af5a..247d91e63 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffDecoderCompressionType.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffDecoderCompressionType.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Tiff.Compression +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression { /// /// Provides enumeration of the various TIFF compression types the decoder can handle. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffByteOrder.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffByteOrder.cs index e96824fba..e83fc6bec 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffByteOrder.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffByteOrder.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Tiff.Constants +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants { /// /// The tiff data stream byte order enum. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs index ff371e617..d5717dbfb 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Tiff.Constants +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants { /// /// Enumeration representing the compression formats defined by the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs index a26554412..68f121fc3 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; -namespace SixLabors.ImageSharp.Formats.Tiff.Constants +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants { /// /// Defines constants defined in the TIFF specification. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffExtraSamples.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffExtraSamples.cs index c10167d25..d5b69bdab 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffExtraSamples.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffExtraSamples.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Tiff.Constants +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants { /// /// Enumeration representing the possible uses of extra components in TIFF format files. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffFillOrder.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffFillOrder.cs index 1bb75f836..3d6c1cea5 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffFillOrder.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffFillOrder.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Tiff.Constants +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants { /// /// Enumeration representing the fill orders defined by the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs index 3b84120a5..fea32e412 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.Formats.Tiff.Constants +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants { /// /// Enumeration representing the sub-file types defined by the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffOrientation.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffOrientation.cs index a5305d482..d022a7e77 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffOrientation.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffOrientation.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Tiff.Constants +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants { /// /// Enumeration representing the image orientations defined by the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs index 2e5f3064b..d5f234c3f 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Tiff.Constants +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants { /// /// Enumeration representing the photometric interpretation formats defined by the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffPlanarConfiguration.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffPlanarConfiguration.cs index ea526ede5..835df35e3 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffPlanarConfiguration.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffPlanarConfiguration.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Tiff.Constants +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants { /// /// Enumeration representing how the components of each pixel are stored the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffPredictor.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffPredictor.cs index 092bb7aa5..67e456517 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffPredictor.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffPredictor.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Tiff.Constants +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants { /// /// A mathematical operator that is applied to the image data before an encoding scheme is applied. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs index bf7a5e9a7..0fab224de 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Tiff.Constants +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants { /// /// Enumeration representing the resolution units defined by the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffSampleFormat.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffSampleFormat.cs index 81899c5fd..072172ba7 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffSampleFormat.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffSampleFormat.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Tiff.Constants +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants { /// /// Specifies how to interpret each data sample in a pixel. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffSubfileType.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffSubfileType.cs index 280dc76ee..c8218b77e 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffSubfileType.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffSubfileType.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Tiff.Constants +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants { /// /// Enumeration representing the sub-file types defined by the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffThresholding.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffThresholding.cs index fce0b175c..05391b233 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffThresholding.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffThresholding.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Tiff.Constants +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants { /// /// Enumeration representing the thresholding applied to image data defined by the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs b/src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs index cee66694b..7a3ab6cd8 100644 --- a/src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs +++ b/src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { /// /// Encapsulates the options for the . diff --git a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs index 97f3d46b0..b24d7ff3d 100644 --- a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs +++ b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs @@ -3,7 +3,7 @@ using SixLabors.ImageSharp.Processing.Processors.Quantization; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { /// /// Encapsulates the options for the . diff --git a/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs index b3e3d82ad..d5387166c 100644 --- a/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs +++ b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs @@ -3,11 +3,11 @@ using System.Collections.Generic; -using SixLabors.ImageSharp.Formats.Tiff.Constants; -using SixLabors.ImageSharp.Formats.Tiff.Streams; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Streams; using SixLabors.ImageSharp.Metadata.Profiles.Exif; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { /// /// The TIFF IFD reader class. diff --git a/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs b/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs index d05a6a901..b8532e0c5 100644 --- a/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs +++ b/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs @@ -5,11 +5,11 @@ using System; using System.Collections.Generic; using System.Text; -using SixLabors.ImageSharp.Formats.Tiff.Constants; -using SixLabors.ImageSharp.Formats.Tiff.Streams; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Streams; using SixLabors.ImageSharp.Metadata.Profiles.Exif; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { internal class EntryReader { diff --git a/src/ImageSharp/Formats/Tiff/ImageExtensions.cs b/src/ImageSharp/Formats/Tiff/ImageExtensions.cs index 611f99538..2583dbd01 100644 --- a/src/ImageSharp/Formats/Tiff/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Tiff/ImageExtensions.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System.IO; -using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Experimental.Tiff; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp diff --git a/src/ImageSharp/Formats/Tiff/MetadataExtensions.cs b/src/ImageSharp/Formats/Tiff/MetadataExtensions.cs index b9da86fc4..1946221cb 100644 --- a/src/ImageSharp/Formats/Tiff/MetadataExtensions.cs +++ b/src/ImageSharp/Formats/Tiff/MetadataExtensions.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Experimental.Tiff; using SixLabors.ImageSharp.Metadata; namespace SixLabors.ImageSharp diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs index 902882c56..14c31dbd0 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs @@ -5,7 +5,7 @@ using System; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { /// /// Implements the 'BlackIsZero' photometric interpretation (optimised for bilevel images). diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs index 46e0e82bc..39216dff7 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs @@ -5,7 +5,7 @@ using System; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { /// /// Implements the 'BlackIsZero' photometric interpretation (optimised for 4-bit grayscale images). diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs index 013dae688..8f281fd04 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs @@ -5,7 +5,7 @@ using System; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { /// /// Implements the 'BlackIsZero' photometric interpretation (optimised for 8-bit grayscale images). diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs index d2bc2f47e..7fef9ddf6 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs @@ -3,11 +3,11 @@ using System; using System.Numerics; -using SixLabors.ImageSharp.Formats.Tiff.Utils; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { /// /// Implements the 'BlackIsZero' photometric interpretation (for all bit depths). diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs index eb3381b70..719610a9f 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs @@ -3,11 +3,11 @@ using System; using System.Numerics; -using SixLabors.ImageSharp.Formats.Tiff.Utils; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { /// /// Implements the 'PaletteTiffColor' photometric interpretation (for all bit depths). diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs index b19028a97..cc3236b89 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs @@ -5,7 +5,7 @@ using System; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { /// /// Implements the 'RGB' photometric interpretation (optimised for 8-bit full color images). diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs index 3f96bc220..20ce17d99 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs @@ -2,11 +2,11 @@ // Licensed under the Apache License, Version 2.0. using System.Numerics; -using SixLabors.ImageSharp.Formats.Tiff.Utils; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { /// /// Implements the 'RGB' photometric interpretation with 'Planar' layout (for all bit depths). diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs index 74a4b9496..6b812ed07 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs @@ -3,11 +3,11 @@ using System; using System.Numerics; -using SixLabors.ImageSharp.Formats.Tiff.Utils; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { /// /// Implements the 'RGB' photometric interpretation (for all bit depths). diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffBaseColorDecoder.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffBaseColorDecoder.cs index ad67c463f..7c0e831b5 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffBaseColorDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffBaseColorDecoder.cs @@ -5,7 +5,7 @@ using System; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { /// /// The base class for photometric interpretation decoders. diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory.cs index 20129da99..abc502dea 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory.cs @@ -4,7 +4,7 @@ using System; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { internal static class TiffColorDecoderFactory where TPixel : unmanaged, IPixel diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs index c86a5e76c..7d17ff0b7 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { /// /// Provides enumeration of the various TIFF photometric interpretation implementation types. diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs index 95ff7c6a7..51f84d3c8 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs @@ -5,7 +5,7 @@ using System; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { /// /// Implements the 'WhiteIsZero' photometric interpretation (optimised for bilevel images). diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs index 2720a1aa5..bbee41dcd 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs @@ -5,7 +5,7 @@ using System; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { /// /// Implements the 'WhiteIsZero' photometric interpretation (optimised for 4-bit grayscale images). diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs index 30d8ea1db..7e192f1af 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs @@ -5,7 +5,7 @@ using System; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { /// /// Implements the 'WhiteIsZero' photometric interpretation (optimised for 8-bit grayscale images). diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs index 8257dcec2..a34927c8d 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs @@ -3,11 +3,11 @@ using System; using System.Numerics; -using SixLabors.ImageSharp.Formats.Tiff.Utils; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { /// /// Implements the 'WhiteIsZero' photometric interpretation (for all bit depths). diff --git a/src/ImageSharp/Formats/Tiff/Streams/TiffBigEndianStream.cs b/src/ImageSharp/Formats/Tiff/Streams/TiffBigEndianStream.cs index 002177a91..ba0364b88 100644 --- a/src/ImageSharp/Formats/Tiff/Streams/TiffBigEndianStream.cs +++ b/src/ImageSharp/Formats/Tiff/Streams/TiffBigEndianStream.cs @@ -4,9 +4,9 @@ using System; using System.IO; -using SixLabors.ImageSharp.Formats.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; -namespace SixLabors.ImageSharp.Formats.Tiff.Streams +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Streams { internal class TiffBigEndianStream : TiffStream { diff --git a/src/ImageSharp/Formats/Tiff/Streams/TiffLittleEndianStream.cs b/src/ImageSharp/Formats/Tiff/Streams/TiffLittleEndianStream.cs index 649e71e0a..f15b465ba 100644 --- a/src/ImageSharp/Formats/Tiff/Streams/TiffLittleEndianStream.cs +++ b/src/ImageSharp/Formats/Tiff/Streams/TiffLittleEndianStream.cs @@ -4,9 +4,9 @@ using System; using System.IO; -using SixLabors.ImageSharp.Formats.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; -namespace SixLabors.ImageSharp.Formats.Tiff.Streams +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Streams { internal class TiffLittleEndianStream : TiffStream { diff --git a/src/ImageSharp/Formats/Tiff/Streams/TiffStream.cs b/src/ImageSharp/Formats/Tiff/Streams/TiffStream.cs index af7bc0cd1..cc469db59 100644 --- a/src/ImageSharp/Formats/Tiff/Streams/TiffStream.cs +++ b/src/ImageSharp/Formats/Tiff/Streams/TiffStream.cs @@ -2,9 +2,9 @@ // Licensed under the Apache License, Version 2.0. using System.IO; -using SixLabors.ImageSharp.Formats.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; -namespace SixLabors.ImageSharp.Formats.Tiff.Streams +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Streams { /// /// The tiff data stream base class. diff --git a/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs b/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs index fe53a1bd3..163876a3f 100644 --- a/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs +++ b/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { /// /// Enumerates the available bits per pixel the tiff encoder supports. diff --git a/src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs b/src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs index e96dba207..07345608e 100644 --- a/src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs +++ b/src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { /// /// Registers the image encoders, decoders and mime type detectors for the TIFF format. diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs index fadcb7550..ec67c815e 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { /// /// Image decoder for generating an image out of a TIFF stream. diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index d29cf7438..e19630f26 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -6,16 +6,16 @@ using System.IO; using System.Linq; using System.Threading; -using SixLabors.ImageSharp.Formats.Tiff.Compression; -using SixLabors.ImageSharp.Formats.Tiff.Constants; -using SixLabors.ImageSharp.Formats.Tiff.Streams; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Streams; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { /// /// Performs the tiff decoding operation. diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs index 5ae58c6a6..b69c57093 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs @@ -4,14 +4,14 @@ using System.Collections.Generic; using System.Linq; -using SixLabors.ImageSharp.Formats.Tiff.Compression; -using SixLabors.ImageSharp.Formats.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Icc; using SixLabors.ImageSharp.Metadata.Profiles.Iptc; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { /// /// The decoder helper methods. diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs index 3ab17b6c3..84e9fb979 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs @@ -8,7 +8,7 @@ using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Quantization; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { /// /// Encoder for writing the data image to a stream in TIFF format. diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs index 1bd84a60a..145849d4f 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { /// /// Indicates which tiff compression is used. diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 995461c0f..c6fec546f 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -7,8 +7,8 @@ using System.IO; using System.Threading; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Formats.Tiff.Constants; -using SixLabors.ImageSharp.Formats.Tiff.Utils; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; @@ -16,7 +16,7 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Quantization; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { /// /// Performs the TIFF encoding operation. diff --git a/src/ImageSharp/Formats/Tiff/TiffEncodingMode.cs b/src/ImageSharp/Formats/Tiff/TiffEncodingMode.cs index 374505195..934cd6826 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncodingMode.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncodingMode.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { /// /// Enum for the different tiff encoding options. diff --git a/src/ImageSharp/Formats/Tiff/TiffFormat.cs b/src/ImageSharp/Formats/Tiff/TiffFormat.cs index ffae32093..ea57f67cc 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFormat.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFormat.cs @@ -3,9 +3,9 @@ using System.Collections.Generic; -using SixLabors.ImageSharp.Formats.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { /// /// Encapsulates the means to encode and decode Tiff images. diff --git a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs index 6956fb16a..d4577159f 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs @@ -4,10 +4,10 @@ using System.Collections.Generic; using System.Linq; -using SixLabors.ImageSharp.Formats.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Metadata.Profiles.Exif; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { /// /// Provides Tiff specific metadata information for the frame. diff --git a/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs b/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs index f7e6f7a99..624e0858c 100644 --- a/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs +++ b/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { /// /// Detects tiff file headers diff --git a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs index d72a903d8..a3ee80fc3 100644 --- a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs @@ -1,9 +1,9 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Formats.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { /// /// Provides Tiff specific metadata information for the image. diff --git a/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs b/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs index 96a3e8dbc..ff44e0d6e 100644 --- a/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs +++ b/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs @@ -5,7 +5,7 @@ using System; using System.IO; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { /// /// Cold path optimizations for throwing tiff format based exceptions. diff --git a/src/ImageSharp/Formats/Tiff/Utils/BitReader.cs b/src/ImageSharp/Formats/Tiff/Utils/BitReader.cs index 40e67c1b0..067d119dd 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/BitReader.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.Formats.Tiff.Utils +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils { /// /// Utility class to read a sequence of bits from an array diff --git a/src/ImageSharp/Formats/Tiff/Utils/SubStream.cs b/src/ImageSharp/Formats/Tiff/Utils/SubStream.cs index e83c1f062..22cdaf5e8 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/SubStream.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/SubStream.cs @@ -4,7 +4,7 @@ using System; using System.IO; -namespace SixLabors.ImageSharp.Formats.Tiff.Utils +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils { /// /// Utility class to encapsulate a sub-portion of another . diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs index 4322b04b1..ddd63b910 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs @@ -7,7 +7,7 @@ using System.IO; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Tiff.Utils +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils { /// /// Decompresses and decodes data using the dynamic LZW algorithms. diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs index f05f596bf..d22f14ce5 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs @@ -6,7 +6,7 @@ using System.Buffers; using System.IO; using SixLabors.ImageSharp.Formats.Gif; -namespace SixLabors.ImageSharp.Formats.Tiff.Utils +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils { /// /// Encodes and compresses the image data using dynamic Lempel-Ziv compression. diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs index 4112ba4ba..ec4a87664 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs @@ -4,7 +4,7 @@ using System; using System.IO; -namespace SixLabors.ImageSharp.Formats.Tiff.Utils +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils { /// /// TIFF specific utilities and extension methods. diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs index 1cea6eaab..dbaeac7e6 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -8,7 +8,7 @@ using System.Runtime.InteropServices; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Png.Zlib; -using SixLabors.ImageSharp.Formats.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; @@ -16,7 +16,7 @@ using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Dithering; using SixLabors.ImageSharp.Processing.Processors.Quantization; -namespace SixLabors.ImageSharp.Formats.Tiff.Utils +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils { /// /// Utility class for writing TIFF data to a . diff --git a/tests/ImageSharp.Tests/ConfigurationTests.cs b/tests/ImageSharp.Tests/ConfigurationTests.cs index 655e98c7f..f6111da5a 100644 --- a/tests/ImageSharp.Tests/ConfigurationTests.cs +++ b/tests/ImageSharp.Tests/ConfigurationTests.cs @@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Tests public Configuration DefaultConfiguration { get; } - private readonly int expectedDefaultConfigurationCount = 5; + private readonly int expectedDefaultConfigurationCount = 6; public ConfigurationTests() { diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs index 2f71b0bd9..65d31dbcc 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs @@ -3,7 +3,7 @@ using System.IO; using SixLabors.ImageSharp.Formats.Png.Zlib; -using SixLabors.ImageSharp.Formats.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs index 4dc643e52..789b7d441 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs @@ -3,8 +3,8 @@ using System.IO; -using SixLabors.ImageSharp.Formats.Tiff.Compression; -using SixLabors.ImageSharp.Formats.Tiff.Utils; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; using Xunit; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs index 8366c4de3..979af22e3 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System.IO; -using SixLabors.ImageSharp.Formats.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs index 923b95e37..add5c8dbd 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs @@ -4,7 +4,7 @@ using System; using System.IO; -using SixLabors.ImageSharp.Formats.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; using SixLabors.ImageSharp.Memory; using Xunit; @@ -34,8 +34,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression } [Theory] - [InlineData(new byte[] { 0xAA, 0xAA, 0xAA, 0xCD, 0xBB, 0xBB, 0xBB, 0xBB }, new byte[] { 0xFE, 0xAA, 0x02, 0xCD, 0xFD, 0xBB })] // A run of 3, then one byte, followed by a run of 4. - [InlineData(new byte[] { 0xAB, 0xCD, 0xEF }, new byte[] { 0x04, 0xAB, 0xCD, 0xEF })] // all bytes are different. [InlineData(new byte[] { 0xAA, 0xAA, 0xAA, 0x80, 0x00, 0x2A, 0xAA, 0xAA, 0xAA, 0xAA, 0x80, 0x00, 0x2A, 0x22, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA }, new byte[] { 0xFE, 0xAA, 0x02, 0x80, 0x00, 0x2A, 0xFD, 0xAA, 0x03, 0x80, 0x00, 0x2A, 0x22, 0xF7, 0xAA })] // Apple PackBits sample public void Compress_Works(byte[] inputData, byte[] expectedResult) { diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs index 62e17e1fd..79dd65968 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; -using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Experimental.Tiff; using SixLabors.ImageSharp.PixelFormats; using Xunit; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs index 5e905e3f0..79a9900f6 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; -using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Experimental.Tiff; using SixLabors.ImageSharp.PixelFormats; using Xunit; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs index 2aab2f3ec..f1372e712 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; -using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Experimental.Tiff; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs index f1fa118f3..aa3da7d41 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; -using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Experimental.Tiff; using SixLabors.ImageSharp.PixelFormats; using Xunit; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs index 64e1aa077..e714996ae 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; -using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Experimental.Tiff; using SixLabors.ImageSharp.PixelFormats; using Xunit; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index 0db1ad39d..11a01d50f 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -5,8 +5,8 @@ using System; using System.IO; -using SixLabors.ImageSharp.Formats.Tiff; -using SixLabors.ImageSharp.Formats.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Experimental.Tiff; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs index 37db9a6c7..c3c196ea9 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs @@ -3,8 +3,8 @@ using System.IO; -using SixLabors.ImageSharp.Formats.Tiff; -using SixLabors.ImageSharp.Formats.Tiff.Utils; +using SixLabors.ImageSharp.Formats.Experimental.Tiff; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; using SixLabors.ImageSharp.Memory; using Xunit; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 09938582a..d7d0f4b0f 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -4,7 +4,7 @@ using System.IO; using SixLabors.ImageSharp.Formats; -using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Experimental.Tiff; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffFormatTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffFormatTests.cs index 312c84308..d35e48b5b 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffFormatTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffFormatTests.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Experimental.Tiff; using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index 3affbce4c..2f32ad38f 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -1,8 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Formats.Tiff; -using SixLabors.ImageSharp.Formats.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Experimental.Tiff; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Utils/SubStreamTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Utils/SubStreamTests.cs index 14f46d2a7..9bc6643a6 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Utils/SubStreamTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Utils/SubStreamTests.cs @@ -4,7 +4,7 @@ using System; using System.IO; -using SixLabors.ImageSharp.Formats.Tiff.Utils; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; using Xunit; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs index 7336d0b3f..e5e36184f 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs @@ -3,7 +3,7 @@ using System.IO; -using SixLabors.ImageSharp.Formats.Tiff.Utils; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; using SixLabors.ImageSharp.Memory; using Xunit; diff --git a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs index 36f869844..70ae84190 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs @@ -9,7 +9,7 @@ using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Tga; -using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Experimental.Tiff; using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; namespace SixLabors.ImageSharp.Tests From 8e85b1dd69e57349e28f08beca1bd4a3972d8d02 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 1 Dec 2020 19:23:49 +0100 Subject: [PATCH 114/275] Fix issue writing too large color palette --- src/ImageSharp/Configuration.cs | 2 +- .../Formats/Tiff/Utils/TiffWriter.cs | 12 +++++---- .../DeflateTiffCompressionTests.cs | 2 +- .../Formats/Tiff/TiffDecoderTests.cs | 4 +-- .../Formats/Tiff/TiffEncoderTests.cs | 3 +++ tests/ImageSharp.Tests/TestImages.cs | 25 ++++++++++--------- .../Input/Tiff/rgb_lzw_no_predictor.tiff | 3 +++ 7 files changed, 30 insertions(+), 21 deletions(-) create mode 100644 tests/Images/Input/Tiff/rgb_lzw_no_predictor.tiff diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index 80aab1cc4..192bf5a73 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -6,11 +6,11 @@ using System.Collections.Concurrent; using System.Collections.Generic; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Experimental.Tiff; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Tga; -using SixLabors.ImageSharp.Formats.Experimental.Tiff; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Processing; diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs index dbaeac7e6..774273b07 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -6,9 +6,9 @@ using System.Buffers; using System.IO; using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Png.Zlib; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; @@ -239,11 +239,13 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils public int WritePalettedRgb(Image image, IQuantizer quantizer, int padding, TiffEncoderCompression compression, out IExifValue colorMap) where TPixel : unmanaged, IPixel { - int colorPaletteSize = 256 * 3 * 2; + int colorsPerChannel = 256; + int colorPaletteSize = colorsPerChannel * 3; + int colorPaletteBytes = colorPaletteSize * 2; using IManagedByteBuffer row = this.AllocateRow(image.Width, 1, padding); using IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(this.configuration); using IndexedImageFrame quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(image.Frames.RootFrame, image.Bounds()); - using IMemoryOwner colorPaletteBuffer = this.memoryAllocator.AllocateManagedByteBuffer(colorPaletteSize); + using IMemoryOwner colorPaletteBuffer = this.memoryAllocator.AllocateManagedByteBuffer(colorPaletteBytes); Span colorPalette = colorPaletteBuffer.GetSpan(); ReadOnlySpan quantizedColors = quantized.Palette.Span; @@ -253,8 +255,8 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils Span quantizedColorRgb48 = MemoryMarshal.Cast(colorPalette.Slice(0, quantizedColorBytes)); PixelOperations.Instance.ToRgb48(this.configuration, quantizedColors, quantizedColorRgb48); - // It can happen that the quantized colors are less than the expected 256. - var diffToMaxColors = 256 - quantizedColors.Length; + // It can happen that the quantized colors are less than the expected 256 per channel. + var diffToMaxColors = colorsPerChannel - quantizedColors.Length; // In a TIFF ColorMap, all the Red values come first, followed by the Green values, // then the Blue values. Convert the quantized palette to this format. diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs index 65d31dbcc..0f0b867f7 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs @@ -2,8 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System.IO; -using SixLabors.ImageSharp.Formats.Png.Zlib; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Png.Zlib; using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index 11a01d50f..09f1c8d0c 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -60,8 +60,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff } [Theory] - [InlineData(TestImages.Tiff.RgbLzw_NoPredictor_Multistrip, TiffByteOrder.LittleEndian)] - [InlineData(TestImages.Tiff.RgbLzw_NoPredictor_Multistrip_Motorola, TiffByteOrder.BigEndian)] + [InlineData(TestImages.Tiff.RgbLzwNoPredictorMultistrip, TiffByteOrder.LittleEndian)] + [InlineData(TestImages.Tiff.RgbLzwNoPredictorMultistripMotorola, TiffByteOrder.BigEndian)] public void ByteOrder(string imagePath, TiffByteOrder expectedByteOrder) { var testFile = TestFile.Create(imagePath); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index d7d0f4b0f..99d9a10d5 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -93,6 +93,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff memStream.Position = 0; using var encodedImage = (Image)Image.Load(memStream); + provider.Utility.SaveTestOutputFile(encodedImage, "tiff", encoder); TiffTestUtils.CompareWithReferenceDecoder(provider, encodedImage); } @@ -110,6 +111,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff memStream.Position = 0; using var encodedImage = (Image)Image.Load(memStream); + provider.Utility.SaveTestOutputFile(encodedImage, "tiff", encoder); TiffTestUtils.CompareWithReferenceDecoder(provider, encodedImage); } @@ -127,6 +129,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff memStream.Position = 0; using var encodedImage = (Image)Image.Load(memStream); + provider.Utility.SaveTestOutputFile(encodedImage, "tiff", encoder); TiffTestUtils.CompareWithReferenceDecoder(provider, encodedImage); } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 9f7516fe2..8913fee1e 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -526,14 +526,15 @@ namespace SixLabors.ImageSharp.Tests public const string GrayscaleUncompressed = "Tiff/grayscale_uncompressed.tiff"; public const string PaletteDeflateMultistrip = "Tiff/palette_grayscale_deflate_multistrip.tiff"; public const string PaletteUncompressed = "Tiff/palette_uncompressed.tiff"; - public const string RgbDeflate_Predictor = "Tiff/rgb_deflate.tiff"; + public const string RgbDeflatePredictor = "Tiff/rgb_deflate.tiff"; public const string RgbDeflateMultistrip = "Tiff/rgb_deflate_multistrip.tiff"; public const string RgbJpeg = "Tiff/rgb_jpeg.tiff"; - public const string RgbLzw_Predictor = "Tiff/rgb_lzw_predictor.tiff"; - public const string RgbLzw_NoPredictor_Multistrip = "Tiff/rgb_lzw_noPredictor_multistrip.tiff"; - public const string RgbLzw_NoPredictor_Multistrip_Motorola = "Tiff/rgb_lzw_noPredictor_multistrip_Motorola.tiff"; - public const string RgbLzw_NoPredictor_Singlestrip_Motorola = "Tiff/rgb_lzw_noPredictor_singlestrip_Motorola.tiff"; - public const string RgbLzwMultistrip_Predictor = "Tiff/rgb_lzw_multistrip.tiff"; + public const string RgbLzwPredictor = "Tiff/rgb_lzw_predictor.tiff"; + public const string RgbLzwNoPredictor = "Tiff/rgb_lzw_no_predictor.tiff"; + public const string RgbLzwNoPredictorMultistrip = "Tiff/rgb_lzw_noPredictor_multistrip.tiff"; + public const string RgbLzwNoPredictorMultistripMotorola = "Tiff/rgb_lzw_noPredictor_multistrip_Motorola.tiff"; + public const string RgbLzwNoPredictorSinglestripMotorola = "Tiff/rgb_lzw_noPredictor_singlestrip_Motorola.tiff"; + public const string RgbLzwMultistripPredictor = "Tiff/rgb_lzw_multistrip.tiff"; public const string RgbPackbits = "Tiff/rgb_packbits.tiff"; public const string RgbPackbitsMultistrip = "Tiff/rgb_packbits_multistrip.tiff"; public const string RgbUncompressed = "Tiff/rgb_uncompressed.tiff"; @@ -544,7 +545,7 @@ namespace SixLabors.ImageSharp.Tests public const string RgbUncompressedTiled = "Tiff/rgb_uncompressed_tiled.tiff"; public const string MultiframeDifferentSizeTiled = "Tiff/multipage_ withPreview_differentSize_tiled.tiff"; - public const string MultiframeLzw_Predictor = "Tiff/multipage_lzw.tiff"; + public const string MultiframeLzwPredictor = "Tiff/multipage_lzw.tiff"; public const string MultiframeDeflateWithPreview = "Tiff/multipage_deflate_withPreview.tiff"; public const string MultiframeDifferentSize = "Tiff/multipage_differentSize.tiff"; public const string MultiframeDifferentVariants = "Tiff/multipage_differentVariants.tiff"; @@ -554,20 +555,20 @@ namespace SixLabors.ImageSharp.Tests public static readonly string[] All = { Calliphora_PaletteUncompressed, Calliphora_RgbPackbits, - Calliphora_GrayscaleDeflate_Predictor, Calliphora_RgbDeflate_Predictor, RgbDeflate_Predictor, - Calliphora_RgbLzw_Predictor, RgbLzw_Predictor, // TODO: Undoing the horizontal prediction seems to fail for lzw. Do we need to do something different for lzw? + Calliphora_GrayscaleDeflate_Predictor, Calliphora_RgbDeflate_Predictor, RgbDeflatePredictor, + Calliphora_RgbLzw_Predictor, RgbLzwPredictor, // TODO: Undoing the horizontal prediction seems to fail for lzw. Do we need to do something different for lzw? Calliphora_BiColor, Calliphora_RgbUncompressed, Calliphora_HuffmanCompressed, Calliphora_Fax3Compressed, CcittFax3AllTermCodes, CcittFax3AllMakeupCodes, HuffmanRleAllTermCodes, HuffmanRleAllMakeupCodes, GrayscaleDeflateMultistrip, Calliphora_GrayscaleDeflate, Calliphora_GrayscaleUncompressed, GrayscaleUncompressed, PaletteDeflateMultistrip, PaletteUncompressed, RgbDeflateMultistrip, /*RgbJpeg,*/ /* RgbLzwMultistrip_Predictor,*/ - RgbLzw_NoPredictor_Multistrip, RgbLzw_NoPredictor_Multistrip_Motorola, RgbLzw_NoPredictor_Singlestrip_Motorola, RgbPackbits, RgbPackbitsMultistrip, RgbUncompressed, - /* MultiframeLzw_Predictor, MultiFrameDifferentVariants, SampleMetadata,*/ SmallRgbDeflate, SmallRgbLzw, + RgbLzwNoPredictorMultistrip, RgbLzwNoPredictorMultistripMotorola, RgbLzwNoPredictorSinglestripMotorola, RgbPackbits, RgbPackbitsMultistrip, RgbUncompressed, + /* MultiframeLzw_Predictor, MultiFrameDifferentVariants, SampleMetadata,*/ SmallRgbDeflate, SmallRgbLzw, RgbLzwNoPredictor }; public static readonly string[] Multiframes = { MultiframeDeflateWithPreview /*MultiframeLzw_Predictor, MultiFrameDifferentSize, MultiframeDifferentSizeTiled, MultiFrameDifferentVariants,*/ }; public static readonly string[] Metadata = { SampleMetadata }; - public static readonly string[] NotSupported = { Calliphora_RgbJpeg, RgbLzwMultistrip_Predictor, RgbJpeg, RgbUncompressedTiled, MultiframeLzw_Predictor, MultiframeDifferentSize, MultiframeDifferentVariants }; + public static readonly string[] NotSupported = { Calliphora_RgbJpeg, RgbLzwMultistripPredictor, RgbJpeg, RgbUncompressedTiled, MultiframeLzwPredictor, MultiframeDifferentSize, MultiframeDifferentVariants }; } } } diff --git a/tests/Images/Input/Tiff/rgb_lzw_no_predictor.tiff b/tests/Images/Input/Tiff/rgb_lzw_no_predictor.tiff new file mode 100644 index 000000000..1fa09b5e2 --- /dev/null +++ b/tests/Images/Input/Tiff/rgb_lzw_no_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:facf3ccfa0d01803e2eead6dfa6fb95133382c15cb991eb6a097678ca31b50dc +size 131081 From 48c83460062f6e2fc832287ab0a12144f524941d Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 1 Dec 2020 19:34:49 +0100 Subject: [PATCH 115/275] Fix issue comparing to wrong image in encode paletted color tiff tests --- src/ImageSharp/Formats/Tiff/README.md | 3 --- .../Formats/Tiff/TiffEncoderTests.cs | 15 ++++++--------- .../Formats/Tiff/TiffTestUtils.cs | 10 ++-------- 3 files changed, 8 insertions(+), 20 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/README.md b/src/ImageSharp/Formats/Tiff/README.md index bb5e93c72..a4c6a9426 100644 --- a/src/ImageSharp/Formats/Tiff/README.md +++ b/src/ImageSharp/Formats/Tiff/README.md @@ -31,9 +31,6 @@ Decoder: - Decoding HUffman RLE for `Calliphora_huffman_rle.tiff` has 4 pixels difference to the reference decoder. Al those are at the very edge of the image (reason unknown so far). - Decoding compressed images with HorizontalPrediction: Works for deflate, but not for lzw. -Encoder: -- Encoding image with a palette have a difference of 0.0043% to the ReferenceDecoder (ImageMagick) - ### Deviations from the TIFF spec (to be fixed) - Decoder diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 99d9a10d5..556486343 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -84,7 +84,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // Because a quantizer is used to create the palette (and therefore changes to the original are expected), // we do not compare the encoded image against the original: // Instead we load the encoded image with a reference decoder and compare against that image. - // TODO: There is a difference of 0,0043% using Image image = provider.GetImage(); using var memStream = new MemoryStream(); var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.None }; @@ -93,8 +92,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff memStream.Position = 0; using var encodedImage = (Image)Image.Load(memStream); - provider.Utility.SaveTestOutputFile(encodedImage, "tiff", encoder); - TiffTestUtils.CompareWithReferenceDecoder(provider, encodedImage); + var encodedImagePath = provider.Utility.SaveTestOutputFile(encodedImage, "tiff", encoder); + TiffTestUtils.CompareWithReferenceDecoder(encodedImagePath, encodedImage); } [Theory] @@ -102,7 +101,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public void TiffEncoder_EncodeColorPalette_WithDeflateCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel { - // TODO: There is a difference of 0,0043% using Image image = provider.GetImage(); using var memStream = new MemoryStream(); var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Deflate }; @@ -111,8 +109,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff memStream.Position = 0; using var encodedImage = (Image)Image.Load(memStream); - provider.Utility.SaveTestOutputFile(encodedImage, "tiff", encoder); - TiffTestUtils.CompareWithReferenceDecoder(provider, encodedImage); + var encodedImagePath = provider.Utility.SaveTestOutputFile(encodedImage, "tiff", encoder); + TiffTestUtils.CompareWithReferenceDecoder(encodedImagePath, encodedImage); } [Theory] @@ -120,7 +118,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public void TiffEncoder_EncodeColorPalette_WithPackBitsCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel { - // TODO: There is a difference of 0,0043% using Image image = provider.GetImage(); using var memStream = new MemoryStream(); var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.PackBits }; @@ -129,8 +126,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff memStream.Position = 0; using var encodedImage = (Image)Image.Load(memStream); - provider.Utility.SaveTestOutputFile(encodedImage, "tiff", encoder); - TiffTestUtils.CompareWithReferenceDecoder(provider, encodedImage); + var encodedImagePath = provider.Utility.SaveTestOutputFile(encodedImage, "tiff", encoder); + TiffTestUtils.CompareWithReferenceDecoder(encodedImagePath, encodedImage); } [Theory] diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffTestUtils.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffTestUtils.cs index b4ebd088e..5d81d3b3d 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffTestUtils.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffTestUtils.cs @@ -15,19 +15,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public static class TiffTestUtils { public static void CompareWithReferenceDecoder( - TestImageProvider provider, + string encodedImagePath, Image image, bool useExactComparer = true, float compareTolerance = 0.01f) where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel { - string path = TestImageProvider.GetFilePathOrNull(provider); - if (path == null) - { - throw new InvalidOperationException("CompareToOriginal() works only with file providers!"); - } - - var testFile = TestFile.Create(path); + var testFile = TestFile.Create(encodedImagePath); Image magickImage = DecodeWithMagick(Configuration.Default, new FileInfo(testFile.FullPath)); if (useExactComparer) { From 74771b366ccb5dd8ba630d51c244962bf0af0802 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 2 Dec 2020 13:25:47 +0100 Subject: [PATCH 116/275] Fix broken test images --- tests/Images/Input/Tiff/Calliphora_ccitt_fax3.tiff | 4 ++-- tests/Images/Input/Tiff/Calliphora_gray_deflate.tiff | 4 ++-- .../Images/Input/Tiff/Calliphora_gray_deflate_predictor.tiff | 4 ++-- tests/Images/Input/Tiff/Calliphora_rgb_deflate_predictor.tiff | 4 ++-- tests/Images/Input/Tiff/Calliphora_rgb_lzw_predictor.tiff | 4 ++-- tests/Images/Input/Tiff/rgb_lzw_no_predictor.tiff | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/Images/Input/Tiff/Calliphora_ccitt_fax3.tiff b/tests/Images/Input/Tiff/Calliphora_ccitt_fax3.tiff index df6f89b42..39852d534 100644 --- a/tests/Images/Input/Tiff/Calliphora_ccitt_fax3.tiff +++ b/tests/Images/Input/Tiff/Calliphora_ccitt_fax3.tiff @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c35abf4ea204b130c9c70590581c0bb88566630fbc0fe5bd2dabaa90379dc4f1 -size 125776 +oid sha256:8b9b105857723bca5f478a9ab23c0aeca93abe863781019bbd2da47f18c46f24 +size 125778 diff --git a/tests/Images/Input/Tiff/Calliphora_gray_deflate.tiff b/tests/Images/Input/Tiff/Calliphora_gray_deflate.tiff index 0cf2c2136..621ef158a 100644 --- a/tests/Images/Input/Tiff/Calliphora_gray_deflate.tiff +++ b/tests/Images/Input/Tiff/Calliphora_gray_deflate.tiff @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5a019baef7da23cb937a65131ee34b59988ca5ace5d26fe36139d6e6b12e8d59 -size 557710 +oid sha256:2314b31ca9938fa8b11cbabda0b118a90025a45d2931fca9afa131c0d6919aca +size 557717 diff --git a/tests/Images/Input/Tiff/Calliphora_gray_deflate_predictor.tiff b/tests/Images/Input/Tiff/Calliphora_gray_deflate_predictor.tiff index 1e316caa4..f44a6e934 100644 --- a/tests/Images/Input/Tiff/Calliphora_gray_deflate_predictor.tiff +++ b/tests/Images/Input/Tiff/Calliphora_gray_deflate_predictor.tiff @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7f57e87714dca75ce414b01e9a47e4fd0f0ecfd50bc408eb80f3a53cf758e148 -size 630942 +oid sha256:b9576b3a49b84e26938a7e9ded5f43a1a3c3390bf4824803f5aaab8e00c1afb4 +size 630947 diff --git a/tests/Images/Input/Tiff/Calliphora_rgb_deflate_predictor.tiff b/tests/Images/Input/Tiff/Calliphora_rgb_deflate_predictor.tiff index 0784d8875..c2ebed364 100644 --- a/tests/Images/Input/Tiff/Calliphora_rgb_deflate_predictor.tiff +++ b/tests/Images/Input/Tiff/Calliphora_rgb_deflate_predictor.tiff @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f614a127d6741b0ed335c5fc5e5a2917dd737a4db6afb40ff71b0346e691097a -size 1476268 +oid sha256:da6e6a35a0bb0f5f2d49e3c5f0eb2deb7118718dd08844f66a6cb72f48b5c489 +size 1476294 diff --git a/tests/Images/Input/Tiff/Calliphora_rgb_lzw_predictor.tiff b/tests/Images/Input/Tiff/Calliphora_rgb_lzw_predictor.tiff index c0ab53c1d..be84f0a30 100644 --- a/tests/Images/Input/Tiff/Calliphora_rgb_lzw_predictor.tiff +++ b/tests/Images/Input/Tiff/Calliphora_rgb_lzw_predictor.tiff @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9bbfce6af3942b2dc3edaaaac7de64956b1a532c48b43a7b0ba887b2dd98fcc8 -size 1792960 +oid sha256:29fa2b157c92f6a8bd4036e9d075e24fc451e72ec1a251d97a4b40454e01405c +size 792087 diff --git a/tests/Images/Input/Tiff/rgb_lzw_no_predictor.tiff b/tests/Images/Input/Tiff/rgb_lzw_no_predictor.tiff index 1fa09b5e2..44092f6c7 100644 --- a/tests/Images/Input/Tiff/rgb_lzw_no_predictor.tiff +++ b/tests/Images/Input/Tiff/rgb_lzw_no_predictor.tiff @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:facf3ccfa0d01803e2eead6dfa6fb95133382c15cb991eb6a097678ca31b50dc -size 131081 +oid sha256:895f7e1fb17e42175e6c0d67fbc08a7c65d7e19a71e67388034cdaecc407407a +size 131092 From ed894cd5d9fdf97f485e47b9b9e2e8c3fb75925b Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 2 Dec 2020 17:02:50 +0000 Subject: [PATCH 117/275] Fix build --- .gitattributes | 5 +++-- src/ImageSharp/ImageSharp.csproj | 2 -- tests/ImageSharp.Benchmarks/Codecs/DecodeTiffBig.cs | 9 +++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.gitattributes b/.gitattributes index f605a871c..7c648c077 100644 --- a/.gitattributes +++ b/.gitattributes @@ -80,10 +80,11 @@ *.pvr binary *.snk binary *.tga binary -*.ttc binary -*.ttf binary *.tif binary *.tiff binary +*.ttc binary +*.ttf binary +*.wbmp binary *.webp binary *.woff binary *.woff2 binary diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index a9f0fbd5a..65f59331d 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -22,8 +22,6 @@ - - diff --git a/tests/ImageSharp.Benchmarks/Codecs/DecodeTiffBig.cs b/tests/ImageSharp.Benchmarks/Codecs/DecodeTiffBig.cs index c7c1020ef..4210d0571 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/DecodeTiffBig.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/DecodeTiffBig.cs @@ -23,10 +23,10 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs { public LongClr() { - this.Add( - Job.Default.With(ClrRuntime.Net472).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(5), - Job.Default.With(CoreRuntime.Core31).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(5), - Job.Default.With(CoreRuntime.Core21).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(5)); + this.AddJob( + Job.Default.WithRuntime(ClrRuntime.Net472).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(5), + Job.Default.WithRuntime(CoreRuntime.Core31).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(5), + Job.Default.WithRuntime(CoreRuntime.Core21).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(5)); this.SummaryStyle = SummaryStyle.Default.WithMaxParameterColumnWidth(60); } @@ -40,6 +40,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); [Params(TestImages.Tiff.Benchmark_GrayscaleUncompressed, TestImages.Tiff.Benchmark_PaletteUncompressed, TestImages.Tiff.Benchmark_RgbDeflate, TestImages.Tiff.Benchmark_RgbLzw, TestImages.Tiff.Benchmark_RgbPackbits, TestImages.Tiff.Benchmark_RgbUncompressed)] + // [Params(TestImages.Tiff.GrayscaleUncompressed, TestImages.Tiff.PaletteUncompressed, TestImages.Tiff.RgbDeflate, TestImages.Tiff.RgbLzw, TestImages.Tiff.RgbPackbits, TestImages.Tiff.RgbUncompressed)] public string TestImage { get; set; } From 707d9f0e5aeb991d5522a9c6cef18a410250f9e3 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 2 Dec 2020 18:31:38 +0100 Subject: [PATCH 118/275] Use ReadFull extension to read the data from the stream --- .../Formats/Tiff/Compression/T4BitReader.cs | 55 +++---------------- 1 file changed, 8 insertions(+), 47 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs index 3a4f8bd51..7558382de 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs @@ -6,7 +6,7 @@ using System.Buffers; using System.Collections.Generic; using System.IO; using System.Linq; - +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression @@ -230,7 +230,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression public T4BitReader(Stream input, int bytesToRead, MemoryAllocator allocator, bool isModifiedHuffman = false) { this.Data = allocator.Allocate(bytesToRead); - this.ReadImageDataFromStream(input, bytesToRead, allocator); + this.ReadImageDataFromStream(input, bytesToRead); this.isModifiedHuffmanRle = isModifiedHuffman; this.dataLength = bytesToRead; @@ -253,46 +253,22 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression /// /// Gets a value indicating whether there is more data to read left. /// - public bool HasMoreData - { - get - { - return this.position < (ulong)this.dataLength - 1; - } - } + public bool HasMoreData => this.position < (ulong)this.dataLength - 1; /// /// Gets a value indicating whether the current run is a white pixel run, otherwise its a black pixel run. /// - public bool IsWhiteRun - { - get - { - return this.isWhiteRun; - } - } + public bool IsWhiteRun => this.isWhiteRun; /// /// Gets the number of pixels in the current run. /// - public uint RunLength - { - get - { - return this.runLength; - } - } + public uint RunLength => this.runLength; /// /// Gets a value indicating whether the end of a pixel row has been reached. /// - public bool IsEndOfScanLine - { - get - { - return this.curValueBitsRead == 12 && this.value == 1; - } - } + public bool IsEndOfScanLine => this.curValueBitsRead == 12 && this.value == 1; /// /// Read the next run of pixels. @@ -834,25 +810,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression } } - private void ReadImageDataFromStream(Stream input, int bytesToRead, MemoryAllocator allocator) + private void ReadImageDataFromStream(Stream input, int bytesToRead) { - int bufferLength = 4096; - IMemoryOwner buffer = allocator.Allocate(bufferLength); - Span bufferSpan = buffer.GetSpan(); Span dataSpan = this.Data.GetSpan(); - - int read; - while (bytesToRead > 0 && (read = input.Read(bufferSpan, 0, Math.Min(bufferLength, bytesToRead))) > 0) - { - buffer.Slice(0, read).CopyTo(dataSpan); - bytesToRead -= read; - dataSpan = dataSpan.Slice(read); - } - - if (bytesToRead > 0) - { - TiffThrowHelper.ThrowImageFormatException("tiff image file has insufficient data"); - } + input.ReadFull(dataSpan, bytesToRead); } } } From 73fdeee347438ef4181be3782893e57c1948a481 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 2 Dec 2020 19:47:15 +0100 Subject: [PATCH 119/275] Fix issue with huffman RLE where last bits of a row could get ignored --- .../Formats/Tiff/Compression/T4BitReader.cs | 18 +++++++++++++----- src/ImageSharp/Formats/Tiff/README.md | 3 +-- tests/ImageSharp.Tests/TestImages.cs | 5 ++++- .../Input/Tiff/basi3p02_huffman_rle.tiff | 3 +++ 4 files changed, 21 insertions(+), 8 deletions(-) create mode 100644 tests/Images/Input/Tiff/basi3p02_huffman_rle.tiff diff --git a/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs index 7558382de..f2609e05c 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs @@ -253,7 +253,18 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression /// /// Gets a value indicating whether there is more data to read left. /// - public bool HasMoreData => this.position < (ulong)this.dataLength - 1; + public bool HasMoreData + { + get + { + if (this.isModifiedHuffmanRle) + { + return this.position < (ulong)this.dataLength - 1 || (this.bitsRead > 0 && this.bitsRead < 7); + } + + return this.position < (ulong)this.dataLength - 1; + } + } /// /// Gets a value indicating whether the current run is a white pixel run, otherwise its a black pixel run. @@ -381,10 +392,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression } /// - public void Dispose() - { - this.Data.Dispose(); - } + public void Dispose() => this.Data.Dispose(); private uint WhiteTerminatingCodeRunLength() { diff --git a/src/ImageSharp/Formats/Tiff/README.md b/src/ImageSharp/Formats/Tiff/README.md index a4c6a9426..bdf5cf8a6 100644 --- a/src/ImageSharp/Formats/Tiff/README.md +++ b/src/ImageSharp/Formats/Tiff/README.md @@ -28,8 +28,7 @@ ### Know issue which need to be fixed Decoder: -- Decoding HUffman RLE for `Calliphora_huffman_rle.tiff` has 4 pixels difference to the reference decoder. Al those are at the very edge of the image (reason unknown so far). -- Decoding compressed images with HorizontalPrediction: Works for deflate, but not for lzw. +- Decoding compressed images with HorizontalPrediction: Works for deflate, but not for lzw (maybe an issue with lzw itself?). ### Deviations from the TIFF spec (to be fixed) diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index cc622c6df..415103931 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -522,6 +522,9 @@ namespace SixLabors.ImageSharp.Tests public const string HuffmanRleAllTermCodes = "Tiff/huffman_rle_all_terminating_codes.tiff"; public const string HuffmanRleAllMakeupCodes = "Tiff/huffman_rle_all_makeup_codes.tiff"; + // Test case for an issue, that the last bits in a row got ignored. + public const string HuffmanRle_basi3p02 = "Tiff/basi3p02_huffman_rle.tiff"; + public const string GrayscaleDeflateMultistrip = "Tiff/grayscale_deflate_multistrip.tiff"; public const string GrayscaleUncompressed = "Tiff/grayscale_uncompressed.tiff"; public const string PaletteDeflateMultistrip = "Tiff/palette_grayscale_deflate_multistrip.tiff"; @@ -558,7 +561,7 @@ namespace SixLabors.ImageSharp.Tests Calliphora_GrayscaleDeflate_Predictor, Calliphora_RgbDeflate_Predictor, RgbDeflatePredictor, Calliphora_RgbLzw_Predictor, RgbLzwPredictor, // TODO: Undoing the horizontal prediction seems to fail for lzw. Do we need to do something different for lzw? Calliphora_BiColor, Calliphora_RgbUncompressed, Calliphora_HuffmanCompressed, Calliphora_Fax3Compressed, CcittFax3AllTermCodes, CcittFax3AllMakeupCodes, - HuffmanRleAllTermCodes, HuffmanRleAllMakeupCodes, GrayscaleDeflateMultistrip, Calliphora_GrayscaleDeflate, Calliphora_GrayscaleUncompressed, + HuffmanRleAllTermCodes, HuffmanRleAllMakeupCodes, HuffmanRle_basi3p02, GrayscaleDeflateMultistrip, Calliphora_GrayscaleDeflate, Calliphora_GrayscaleUncompressed, GrayscaleUncompressed, PaletteDeflateMultistrip, PaletteUncompressed, RgbDeflateMultistrip, /*RgbJpeg,*/ /* RgbLzwMultistrip_Predictor,*/ RgbLzwNoPredictorMultistrip, RgbLzwNoPredictorMultistripMotorola, RgbLzwNoPredictorSinglestripMotorola, RgbPackbits, RgbPackbitsMultistrip, RgbUncompressed, /* MultiframeLzw_Predictor, MultiFrameDifferentVariants, SampleMetadata,*/ SmallRgbDeflate, SmallRgbLzw, RgbLzwNoPredictor diff --git a/tests/Images/Input/Tiff/basi3p02_huffman_rle.tiff b/tests/Images/Input/Tiff/basi3p02_huffman_rle.tiff new file mode 100644 index 000000000..2b290438a --- /dev/null +++ b/tests/Images/Input/Tiff/basi3p02_huffman_rle.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:af20deb1b64cac3272b6560565cb01f28247b9fd8b6d5a86eafbe7b0aea27d48 +size 340 From 163f49973e26eec693f9b8ad127f78ee115ef791 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 3 Dec 2020 06:50:18 +0100 Subject: [PATCH 120/275] Refactor Tiff decoder tests: split large "testall" test into smaller ones --- .../DeflateTiffCompressionTests.cs | 2 +- .../Compression/LzwTiffCompressionTests.cs | 2 +- .../Compression/NoneTiffCompressionTests.cs | 2 +- .../PackBitsTiffCompressionTests.cs | 2 +- .../PhotometricInterpretationTestBase.cs | 2 +- .../Formats/Tiff/TiffDecoderTests.cs | 117 ++++++++++++++---- .../Formats/Tiff/TiffEncoderHeaderTests.cs | 2 +- .../Formats/Tiff/TiffEncoderTests.cs | 14 +-- .../Formats/Tiff/TiffFormatTests.cs | 2 +- .../Formats/Tiff/TiffMetadataTests.cs | 2 +- .../Formats/Tiff/Utils/SubStreamTests.cs | 2 +- .../Formats/Tiff/Utils/TiffWriterTests.cs | 2 +- tests/ImageSharp.Tests/TestImages.cs | 19 +-- tests/Images/Input/Tiff/rgb_deflate.tiff | 4 +- .../Input/Tiff/rgb_deflate_predictor.tiff | 3 + tests/Images/Input/Tiff/rgb_palette.tiff | 3 + .../Input/Tiff/rgb_palette_deflate.tiff | 3 + 17 files changed, 125 insertions(+), 58 deletions(-) create mode 100644 tests/Images/Input/Tiff/rgb_deflate_predictor.tiff create mode 100644 tests/Images/Input/Tiff/rgb_palette.tiff create mode 100644 tests/Images/Input/Tiff/rgb_palette_deflate.tiff diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs index 0f0b867f7..46e9c2da5 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs @@ -8,7 +8,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression { - [Trait("Category", "Tiff")] + [Trait("Format", "Tiff")] public class DeflateTiffCompressionTests { [Theory] diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs index 789b7d441..f6a31f43a 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs @@ -10,7 +10,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression { - [Trait("Category", "Tiff")] + [Trait("Format", "Tiff")] public class LzwTiffCompressionTests { [Theory] diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs index 979af22e3..50a0b29f3 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs @@ -7,7 +7,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression { - [Trait("Category", "Tiff")] + [Trait("Format", "Tiff")] public class NoneTiffCompressionTests { [Theory] diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs index add5c8dbd..e95e6abbb 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs @@ -11,7 +11,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression { - [Trait("Category", "Tiff")] + [Trait("Format", "Tiff")] public class PackBitsTiffCompressionTests { [Theory] diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs index 3faedfa10..8ff099655 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs @@ -9,7 +9,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff { - [Trait("Category", "Tiff")] + [Trait("Format", "Tiff")] public abstract class PhotometricInterpretationTestBase { public static Rgba32 DefaultColor = new Rgba32(42, 96, 18, 128); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index 09f1c8d0c..a78a34939 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -14,21 +14,20 @@ using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; using Xunit; +using static SixLabors.ImageSharp.Tests.TestImages.Tiff; + namespace SixLabors.ImageSharp.Tests.Formats.Tiff { - [Trait("Category", "Tiff.BlackBox.Decoder")] - [Trait("Category", "Tiff")] + [Trait("Format", "Tiff")] public class TiffDecoderTests { - private static TiffDecoder TiffDecoder => new TiffDecoder(); - - private static MagickReferenceDecoder ReferenceDecoder => new MagickReferenceDecoder(); + public static readonly string[] MultiframeTestImages = Multiframes; - public static readonly string[] SingleTestImages = TestImages.Tiff.All; + public static readonly string[] NotSupportedImages = NotSupported; - public static readonly string[] MultiframeTestImages = TestImages.Tiff.Multiframes; + private static TiffDecoder TiffDecoder => new TiffDecoder(); - public static readonly string[] NotSupportedImages = TestImages.Tiff.NotSupported; + private static MagickReferenceDecoder ReferenceDecoder => new MagickReferenceDecoder(); [Theory] [WithFileCollection(nameof(NotSupportedImages), PixelTypes.Rgba32)] @@ -72,7 +71,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.NotNull(info.Metadata); Assert.Equal(expectedByteOrder, info.Metadata.GetTiffMetadata().ByteOrder); - // todo: it's not a mistake? stream.Seek(0, SeekOrigin.Begin); using var img = Image.Load(stream); @@ -81,15 +79,78 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff } [Theory] - [WithFileCollection(nameof(SingleTestImages), PixelTypes.Rgba32)] - public void Decode(TestImageProvider provider) - where TPixel : unmanaged, IPixel + [WithFile(RgbUncompressed, PixelTypes.Rgba32)] + [WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] + [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] + [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] + public void TiffDecoder_CanDecode_Uncompressed(TestImageProvider provider) + where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TiffDecoder)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider, ImageComparer.Exact, ReferenceDecoder); - } + TestTiffDecoder(provider); + } + + [Theory] + [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] + [WithFile(RgbPalette, PixelTypes.Rgba32)] + [WithFile(RgbPaletteDeflate, PixelTypes.Rgba32)] + [WithFile(PaletteUncompressed, PixelTypes.Rgba32)] + public void TiffDecoder_CanDecode_WithPalette(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + TestTiffDecoder(provider); + } + + [Theory] + [WithFile(Calliphora_GrayscaleDeflate, PixelTypes.Rgba32)] + [WithFile(Calliphora_GrayscaleDeflate_Predictor, PixelTypes.Rgba32)] + [WithFile(Calliphora_RgbDeflate_Predictor, PixelTypes.Rgba32)] + [WithFile(RgbDeflate, PixelTypes.Rgba32)] + [WithFile(RgbDeflatePredictor, PixelTypes.Rgba32)] + public void TiffDecoder_CanDecode_DeflateCompressed(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + TestTiffDecoder(provider); + } + + [Theory] + [WithFile(RgbLzwPredictor, PixelTypes.Rgba32)] + [WithFile(RgbLzwNoPredictor, PixelTypes.Rgba32)] + [WithFile(Calliphora_RgbLzw_Predictor, PixelTypes.Rgba32)] + [WithFile(SmallRgbLzw, PixelTypes.Rgba32)] + public void TiffDecoder_CanDecode_LzwCompressed(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + TestTiffDecoder(provider); + } + + [Theory] + [WithFile(HuffmanRleAllTermCodes, PixelTypes.Rgba32)] + [WithFile(HuffmanRleAllMakeupCodes, PixelTypes.Rgba32)] + [WithFile(HuffmanRle_basi3p02, PixelTypes.Rgba32)] + [WithFile(Calliphora_HuffmanCompressed, PixelTypes.Rgba32)] + public void TiffDecoder_CanDecode_HuffmanCompressed(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + TestTiffDecoder(provider); + } + + [Theory] + [WithFile(CcittFax3AllTermCodes, PixelTypes.Rgba32)] + [WithFile(CcittFax3AllMakeupCodes, PixelTypes.Rgba32)] + [WithFile(Calliphora_Fax3Compressed, PixelTypes.Rgba32)] + public void TiffDecoder_CanDecode_Fax3Compressed(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + TestTiffDecoder(provider); + } + + [Theory] + [WithFile(Calliphora_RgbPackbits, PixelTypes.Rgba32)] + [WithFile(RgbPackbits, PixelTypes.Rgba32)] + public void TiffDecoder_CanDecode_PackBitsCompressed(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + TestTiffDecoder(provider); } [Theory] @@ -97,16 +158,22 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public void DecodeMultiframe(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TiffDecoder)) - { - Assert.True(image.Frames.Count > 1); + using Image image = provider.GetImage(TiffDecoder); + Assert.True(image.Frames.Count > 1); - image.DebugSave(provider); - image.CompareToOriginal(provider, ImageComparer.Exact, ReferenceDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider, ImageComparer.Exact, ReferenceDecoder); - image.DebugSaveMultiFrame(provider); - image.CompareToOriginalMultiFrame(provider, ImageComparer.Exact, ReferenceDecoder); - } + image.DebugSaveMultiFrame(provider); + image.CompareToOriginalMultiFrame(provider, ImageComparer.Exact, ReferenceDecoder); + } + + private static void TestTiffDecoder(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(TiffDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider, ImageComparer.Exact, ReferenceDecoder); } } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs index c3c196ea9..e3f75ed8b 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs @@ -11,7 +11,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff { - [Trait("Category", "Tiff")] + [Trait("Format", "Tiff")] public class TiffEncoderHeaderTests { private static readonly MemoryAllocator MemoryAllocator = new ArrayPoolMemoryAllocator(); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 556486343..7f255d395 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -13,7 +13,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff { - [Trait("Category", "Tiff")] + [Trait("Format", "Tiff")] public class TiffEncoderTests { private static readonly IImageDecoder ReferenceDecoder = new MagickReferenceDecoder(); @@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public static readonly TheoryData TiffBitsPerPixelFiles = new TheoryData { - { TestImages.Tiff.Calliphora_BiColor, TiffBitsPerPixel.Pixel1 }, + { TestImages.Tiff.Calliphora_BiColorUncompressed, TiffBitsPerPixel.Pixel1 }, { TestImages.Tiff.GrayscaleUncompressed, TiffBitsPerPixel.Pixel8 }, { TestImages.Tiff.RgbUncompressed, TiffBitsPerPixel.Pixel24 }, }; @@ -131,27 +131,27 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff } [Theory] - [WithFile(TestImages.Tiff.Calliphora_BiColor, PixelTypes.Rgba32)] + [WithFile(TestImages.Tiff.Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeBiColor_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.BiColor); [Theory] - [WithFile(TestImages.Tiff.Calliphora_BiColor, PixelTypes.Rgba32)] + [WithFile(TestImages.Tiff.Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeBiColor_WithDeflateCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel1, TiffEncodingMode.BiColor, TiffEncoderCompression.Deflate); [Theory] - [WithFile(TestImages.Tiff.Calliphora_BiColor, PixelTypes.Rgba32)] + [WithFile(TestImages.Tiff.Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeBiColor_WithPackBitsCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel1, TiffEncodingMode.BiColor, TiffEncoderCompression.PackBits); [Theory] - [WithFile(TestImages.Tiff.Calliphora_BiColor, PixelTypes.Rgba32)] + [WithFile(TestImages.Tiff.Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeBiColor_WithCcittGroup3FaxCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel1, TiffEncodingMode.BiColor, TiffEncoderCompression.CcittGroup3Fax); [Theory] - [WithFile(TestImages.Tiff.Calliphora_BiColor, PixelTypes.Rgba32)] + [WithFile(TestImages.Tiff.Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeBiColor_WithModifiedHuffmanCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel1, TiffEncodingMode.BiColor, TiffEncoderCompression.ModifiedHuffman); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffFormatTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffFormatTests.cs index d35e48b5b..1bf891a36 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffFormatTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffFormatTests.cs @@ -6,7 +6,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff { - [Trait("Category", "Tiff")] + [Trait("Format", "Tiff")] public class TiffFormatTests { [Fact] diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index 2f32ad38f..e3b574abe 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -10,7 +10,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff { - [Trait("Category", "Tiff")] + [Trait("Format", "Tiff")] public class TiffMetadataTests { public static readonly string[] MetadataImages = TestImages.Tiff.Metadata; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Utils/SubStreamTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Utils/SubStreamTests.cs index 9bc6643a6..0aefa76cf 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Utils/SubStreamTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Utils/SubStreamTests.cs @@ -10,7 +10,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff { - [Trait("Category", "Tiff")] + [Trait("Format", "Tiff")] public class SubStreamTests { [Fact] diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs index e5e36184f..bd9ce37ca 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs @@ -10,7 +10,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Utils { - [Trait("Category", "Tiff")] + [Trait("Format", "Tiff")] public class TiffWriterTests { private static readonly MemoryAllocator MemoryAllocator = new ArrayPoolMemoryAllocator(); diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 415103931..a6a94c692 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -515,7 +515,7 @@ namespace SixLabors.ImageSharp.Tests public const string Calliphora_RgbUncompressed = "Tiff/Calliphora_rgb_uncompressed.tiff"; public const string Calliphora_Fax3Compressed = "Tiff/Calliphora_ccitt_fax3.tiff"; public const string Calliphora_HuffmanCompressed = "Tiff/Calliphora_huffman_rle.tiff"; - public const string Calliphora_BiColor = "Tiff/Calliphora_bicolor_uncompressed.tiff"; + public const string Calliphora_BiColorUncompressed = "Tiff/Calliphora_bicolor_uncompressed.tiff"; public const string CcittFax3AllTermCodes = "Tiff/ccitt_fax3_all_terminating_codes.tiff"; public const string CcittFax3AllMakeupCodes = "Tiff/ccitt_fax3_all_makeup_codes.tiff"; @@ -529,7 +529,8 @@ namespace SixLabors.ImageSharp.Tests public const string GrayscaleUncompressed = "Tiff/grayscale_uncompressed.tiff"; public const string PaletteDeflateMultistrip = "Tiff/palette_grayscale_deflate_multistrip.tiff"; public const string PaletteUncompressed = "Tiff/palette_uncompressed.tiff"; - public const string RgbDeflatePredictor = "Tiff/rgb_deflate.tiff"; + public const string RgbDeflate = "Tiff/rgb_deflate.tiff"; + public const string RgbDeflatePredictor = "Tiff/rgb_deflate_predictor.tiff"; public const string RgbDeflateMultistrip = "Tiff/rgb_deflate_multistrip.tiff"; public const string RgbJpeg = "Tiff/rgb_jpeg.tiff"; public const string RgbLzwPredictor = "Tiff/rgb_lzw_predictor.tiff"; @@ -541,6 +542,8 @@ namespace SixLabors.ImageSharp.Tests public const string RgbPackbits = "Tiff/rgb_packbits.tiff"; public const string RgbPackbitsMultistrip = "Tiff/rgb_packbits_multistrip.tiff"; public const string RgbUncompressed = "Tiff/rgb_uncompressed.tiff"; + public const string RgbPalette = "Tiff/rgb_palette.tiff"; + public const string RgbPaletteDeflate = "Tiff/rgb_palette_deflate.tiff"; public const string SmallRgbDeflate = "Tiff/rgb_small_deflate.tiff"; public const string SmallRgbLzw = "Tiff/rgb_small_lzw.tiff"; @@ -555,18 +558,6 @@ namespace SixLabors.ImageSharp.Tests public const string SampleMetadata = "Tiff/metadata_sample.tiff"; - public static readonly string[] All = - { - Calliphora_PaletteUncompressed, Calliphora_RgbPackbits, - Calliphora_GrayscaleDeflate_Predictor, Calliphora_RgbDeflate_Predictor, RgbDeflatePredictor, - Calliphora_RgbLzw_Predictor, RgbLzwPredictor, // TODO: Undoing the horizontal prediction seems to fail for lzw. Do we need to do something different for lzw? - Calliphora_BiColor, Calliphora_RgbUncompressed, Calliphora_HuffmanCompressed, Calliphora_Fax3Compressed, CcittFax3AllTermCodes, CcittFax3AllMakeupCodes, - HuffmanRleAllTermCodes, HuffmanRleAllMakeupCodes, HuffmanRle_basi3p02, GrayscaleDeflateMultistrip, Calliphora_GrayscaleDeflate, Calliphora_GrayscaleUncompressed, - GrayscaleUncompressed, PaletteDeflateMultistrip, PaletteUncompressed, RgbDeflateMultistrip, /*RgbJpeg,*/ /* RgbLzwMultistrip_Predictor,*/ - RgbLzwNoPredictorMultistrip, RgbLzwNoPredictorMultistripMotorola, RgbLzwNoPredictorSinglestripMotorola, RgbPackbits, RgbPackbitsMultistrip, RgbUncompressed, - /* MultiframeLzw_Predictor, MultiFrameDifferentVariants, SampleMetadata,*/ SmallRgbDeflate, SmallRgbLzw, RgbLzwNoPredictor - }; - public static readonly string[] Multiframes = { MultiframeDeflateWithPreview /*MultiframeLzw_Predictor, MultiFrameDifferentSize, MultiframeDifferentSizeTiled, MultiFrameDifferentVariants,*/ }; public static readonly string[] Metadata = { SampleMetadata }; diff --git a/tests/Images/Input/Tiff/rgb_deflate.tiff b/tests/Images/Input/Tiff/rgb_deflate.tiff index 97623cd5b..7abd84d86 100644 --- a/tests/Images/Input/Tiff/rgb_deflate.tiff +++ b/tests/Images/Input/Tiff/rgb_deflate.tiff @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a1db70e0cfb056cfc675db3a2b85a1f226c53cd70275808773ff580c738b3db1 -size 3158 +oid sha256:0af0db6a42424e3db5c6b84be6e253817413b2de68cc91f7288a8434150fe088 +size 67130 diff --git a/tests/Images/Input/Tiff/rgb_deflate_predictor.tiff b/tests/Images/Input/Tiff/rgb_deflate_predictor.tiff new file mode 100644 index 000000000..97623cd5b --- /dev/null +++ b/tests/Images/Input/Tiff/rgb_deflate_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a1db70e0cfb056cfc675db3a2b85a1f226c53cd70275808773ff580c738b3db1 +size 3158 diff --git a/tests/Images/Input/Tiff/rgb_palette.tiff b/tests/Images/Input/Tiff/rgb_palette.tiff new file mode 100644 index 000000000..b282d65b5 --- /dev/null +++ b/tests/Images/Input/Tiff/rgb_palette.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:75f1bcaff7dc09ddbe6ded7b764b8c0b17bffc3392bafdc7bc7a4c7d616a38e5 +size 67394 diff --git a/tests/Images/Input/Tiff/rgb_palette_deflate.tiff b/tests/Images/Input/Tiff/rgb_palette_deflate.tiff new file mode 100644 index 000000000..ef03cdb3e --- /dev/null +++ b/tests/Images/Input/Tiff/rgb_palette_deflate.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:434cc4c212dfa975c130e2acd7c704b9cc6d0bf168336b8f778f811ddaf6a812 +size 24990 From 56ac3f8f29db2953aedd9fe657db046ff929ff85 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 3 Dec 2020 13:13:54 +0100 Subject: [PATCH 121/275] Add support for encoding tiff with deflate and horizontal predictor --- .../Formats/Tiff/ITiffEncoderOptions.cs | 5 ++ .../Formats/Tiff/TiffDecoderCore.cs | 11 +++- src/ImageSharp/Formats/Tiff/TiffEncoder.cs | 5 ++ .../Formats/Tiff/TiffEncoderCore.cs | 20 +++++- .../Formats/Tiff/Utils/TiffWriter.cs | 65 ++++++++++++++++--- .../Formats/Tiff/TiffEncoderTests.cs | 13 +++- 6 files changed, 106 insertions(+), 13 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs index b24d7ff3d..0d3aa4bac 100644 --- a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs +++ b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs @@ -20,6 +20,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// TiffEncodingMode Mode { get; } + /// + /// Gets a value indicating whether to use horizontal prediction. This can improve the compression ratio with deflate compression. + /// + bool UseHorizontalPredictor { get; } + /// /// Gets the quantizer for creating a color palette image. /// diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index e19630f26..381162093 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -263,13 +263,20 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff if (tiffFormatMetaData.Predictor == TiffPredictor.Horizontal) { - this.UndoHorizontalPredictor(width, height, frame); + this.UndoHorizontalPredictor(frame, width, height); } return frame; } - private void UndoHorizontalPredictor(int width, int height, ImageFrame frame) + /// + /// This will reverse the horizontal prediction operation. + /// + /// The pixel format. + /// The image frame. + /// The width of the image. + /// The height of the image. + private void UndoHorizontalPredictor(ImageFrame frame, int width, int height) where TPixel : unmanaged, IPixel { using System.Buffers.IMemoryOwner rowRgbBuffer = this.memoryAllocator.Allocate(width); diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs index 84e9fb979..2deb063b0 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs @@ -25,6 +25,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// public TiffEncodingMode Mode { get; set; } + /// + /// Gets or sets a value indicating whether to use horizontal prediction. This can improve the compression ratio with deflate compression. + /// + public bool UseHorizontalPredictor { get; set; } + /// /// Gets or sets the quantizer for color images with a palette. /// Defaults to OctreeQuantizer. diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index c6fec546f..6bc3b7338 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -48,6 +48,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// private readonly IQuantizer quantizer; + /// + /// Indicating whether to use horizontal prediction. This can improve the compression ratio with deflate compression. + /// + private bool useHorizontalPredictor; + /// /// Initializes a new instance of the class. /// @@ -59,6 +64,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff this.CompressionType = options.Compression; this.Mode = options.Mode; this.quantizer = options.Quantizer ?? KnownQuantizers.Octree; + this.useHorizontalPredictor = options.UseHorizontalPredictor; } /// @@ -162,13 +168,13 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff imageDataBytes = writer.WritePalettedRgb(image, this.quantizer, this.padding, this.CompressionType, out colorMap); break; case TiffEncodingMode.Gray: - imageDataBytes = writer.WriteGray(image, this.padding, this.CompressionType); + imageDataBytes = writer.WriteGray(image, this.padding, this.CompressionType, this.useHorizontalPredictor); break; case TiffEncodingMode.BiColor: imageDataBytes = writer.WriteBiColor(image, this.CompressionType); break; default: - imageDataBytes = writer.WriteRgb(image, this.padding, this.CompressionType); + imageDataBytes = writer.WriteRgb(image, this.padding, this.CompressionType, this.useHorizontalPredictor); break; } @@ -337,6 +343,16 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff ifdEntries.Add(yResolution); ifdEntries.Add(resolutionUnit); ifdEntries.Add(software); + + if (this.useHorizontalPredictor) + { + if (this.Mode == TiffEncodingMode.Rgb || this.Mode == TiffEncodingMode.Gray) + { + var predictor = new ExifShort(ExifTagValue.Predictor) { Value = (ushort)TiffPredictor.Horizontal }; + + ifdEntries.Add(predictor); + } + } } private void SetPhotometricInterpretation() diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs index 774273b07..52edbdc41 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -137,15 +137,16 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// The image to write to the stream. /// The padding bytes for each row. /// The compression to use. + /// Indicates if horizontal prediction should be used. Should only be used with deflate compression. /// The number of bytes written. - public int WriteRgb(Image image, int padding, TiffEncoderCompression compression) + public int WriteRgb(Image image, int padding, TiffEncoderCompression compression, bool useHorizontalPredictor) where TPixel : unmanaged, IPixel { using IManagedByteBuffer row = this.AllocateRow(image.Width, 3, padding); Span rowSpan = row.GetSpan(); if (compression == TiffEncoderCompression.Deflate) { - return this.WriteDeflateCompressedRgb(image, rowSpan); + return this.WriteDeflateCompressedRgb(image, rowSpan, useHorizontalPredictor); } if (compression == TiffEncoderCompression.PackBits) @@ -172,8 +173,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// The pixel data. /// The image to write to the stream. /// A Span for a pixel row. + /// Indicates if horizontal prediction should be used. Should only be used with deflate compression. /// The number of bytes written. - private int WriteDeflateCompressedRgb(Image image, Span rowSpan) + private int WriteDeflateCompressedRgb(Image image, Span rowSpan, bool useHorizontalPredictor) where TPixel : unmanaged, IPixel { int bytesWritten = 0; @@ -186,6 +188,12 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils { Span pixelRow = image.GetPixelRowSpan(y); PixelOperations.Instance.ToRgb24Bytes(this.configuration, pixelRow, rowSpan, pixelRow.Length); + + if (useHorizontalPredictor) + { + this.ApplyHorizontalPredictionRgb(rowSpan); + } + deflateStream.Write(rowSpan); } @@ -197,6 +205,27 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils return bytesWritten; } + /// + /// Applies a horizontal predictor to the rgb row. + /// Make use of the fact that many continuous-tone images rarely vary much in pixel value from one pixel to the next. + /// In such images, if we replace the pixel values by differences between consecutive pixels, many of the differences should be 0, plus + /// or minus 1, and so on.This reduces the apparent information content and allows LZW to encode the data more compactly. + /// + /// The rgb pixel row. + private void ApplyHorizontalPredictionRgb(Span rowSpan) + { + Span rowRgb = MemoryMarshal.Cast(rowSpan); + + for (int x = rowRgb.Length - 1; x >= 1; x--) + { + byte r = (byte)(rowRgb[x].R - rowRgb[x - 1].R); + byte g = (byte)(rowRgb[x].G - rowRgb[x - 1].G); + byte b = (byte)(rowRgb[x].B - rowRgb[x - 1].B); + var rgb = new Rgb24(r, g, b); + rowRgb[x].FromRgb24(rgb); + } + } + /// /// Writes the image data as RGB with packed bits compression to the stream. /// @@ -208,7 +237,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils where TPixel : unmanaged, IPixel { // Worst case is that the actual compressed data is larger then the input data. In this case we need 1 additional byte per 127 bytes. - int additionalBytes = ((image.Width * 3) / 127) + 1; + int additionalBytes = (image.Width * 3 / 127) + 1; using IManagedByteBuffer compressedRow = this.memoryAllocator.AllocateManagedByteBuffer((image.Width * 3) + additionalBytes, AllocationOptions.Clean); Span compressedRowSpan = compressedRow.GetSpan(); int bytesWritten = 0; @@ -360,7 +389,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils where TPixel : unmanaged, IPixel { // Worst case is that the actual compressed data is larger then the input data. In this case we need 1 additional byte per 127 bytes. - int additionalBytes = (image.Width * 3 / 127) + 1; + int additionalBytes = ((image.Width * 3) / 127) + 1; using IManagedByteBuffer compressedRow = this.memoryAllocator.AllocateManagedByteBuffer((image.Width * 3) + additionalBytes, AllocationOptions.Clean); using IManagedByteBuffer pixelRowWithPadding = this.memoryAllocator.AllocateManagedByteBuffer((image.Width * 3) + padding, AllocationOptions.Clean); Span compressedRowSpan = compressedRow.GetSpan(); @@ -396,8 +425,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// The image to write to the stream. /// The padding bytes for each row. /// The compression to use. + /// Indicates if horizontal prediction should be used. Should only be used with deflate compression. /// The number of bytes written. - public int WriteGray(Image image, int padding, TiffEncoderCompression compression) + public int WriteGray(Image image, int padding, TiffEncoderCompression compression, bool useHorizontalPredictor) where TPixel : unmanaged, IPixel { using IManagedByteBuffer row = this.AllocateRow(image.Width, 1, padding); @@ -405,7 +435,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils if (compression == TiffEncoderCompression.Deflate) { - return this.WriteGrayDeflateCompressed(image, rowSpan); + return this.WriteGrayDeflateCompressed(image, rowSpan, useHorizontalPredictor); } if (compression == TiffEncoderCompression.PackBits) @@ -430,8 +460,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// /// The image to write to the stream. /// A span of a row of pixels. + /// Indicates if horizontal prediction should be used. Should only be used with deflate compression. /// The number of bytes written. - private int WriteGrayDeflateCompressed(Image image, Span rowSpan) + private int WriteGrayDeflateCompressed(Image image, Span rowSpan, bool useHorizontalPredictor) where TPixel : unmanaged, IPixel { int bytesWritten = 0; @@ -444,6 +475,12 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils { Span pixelRow = image.GetPixelRowSpan(y); PixelOperations.Instance.ToL8Bytes(this.configuration, pixelRow, rowSpan, pixelRow.Length); + + if (useHorizontalPredictor) + { + this.ApplyHorizontalPredictionGray(rowSpan); + } + deflateStream.Write(rowSpan); } @@ -455,6 +492,18 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils return bytesWritten; } + /// + /// Applies a horizontal predictor to a gray pixel row. + /// + /// The gray pixel row. + private void ApplyHorizontalPredictionGray(Span rowSpan) + { + for (int x = rowSpan.Length - 1; x >= 1; x--) + { + rowSpan[x] -= rowSpan[x - 1]; + } + } + /// /// Writes the image data as 8 bit gray to the stream. /// diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 7f255d395..ff00edb67 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -56,6 +56,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public void TiffEncoder_EncodeRgb_WithDeflateCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.Rgb, TiffEncoderCompression.Deflate); + [Theory] + [WithFile(TestImages.Tiff.Calliphora_RgbUncompressed, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeRgb_WithDeflateCompressionAndPredictor_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.Rgb, TiffEncoderCompression.Deflate, usePredictor: true); + [Theory] [WithFile(TestImages.Tiff.Calliphora_RgbUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeRgb_WithPackBitsCompression_Works(TestImageProvider provider) @@ -71,6 +76,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public void TiffEncoder_EncodeGray_WithDeflateCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.Gray, TiffEncoderCompression.Deflate); + [Theory] + [WithFile(TestImages.Tiff.Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeGray_WithDeflateCompressionAndPredictor_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.Gray, TiffEncoderCompression.Deflate, usePredictor: true); + [Theory] [WithFile(TestImages.Tiff.Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeGray_WithPackBitsCompression_Works(TestImageProvider provider) @@ -160,12 +170,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff TiffBitsPerPixel bitsPerPixel, TiffEncodingMode mode, TiffEncoderCompression compression = TiffEncoderCompression.None, + bool usePredictor = false, bool useExactComparer = true, float compareTolerance = 0.01f) where TPixel : unmanaged, IPixel { using Image image = provider.GetImage(); - var encoder = new TiffEncoder { Mode = mode, Compression = compression }; + var encoder = new TiffEncoder { Mode = mode, Compression = compression, UseHorizontalPredictor = usePredictor }; // Does DebugSave & load reference CompareToReferenceInput(): image.VerifyEncoder(provider, "tiff", bitsPerPixel, encoder, useExactComparer ? ImageComparer.Exact : ImageComparer.Tolerant(compareTolerance), referenceDecoder: ReferenceDecoder); From 624a36c0248d7f6d247791ef847b750c15d27794 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Thu, 3 Dec 2020 20:48:35 +0300 Subject: [PATCH 122/275] #12 Tiff specific fixes for lzw --- src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs index ddd63b910..1ce99980a 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs @@ -168,14 +168,12 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils pixelStack[top++] = suffix[code]; - // Fix for Gifs that have "deferred clear code" as per here : - // https://bugzilla.mozilla.org/show_bug.cgi?id=55918 if (availableCode < MaxStackSize) { prefix[availableCode] = oldCode; suffix[availableCode] = first; availableCode++; - if (availableCode == codeMask + 1 && availableCode < MaxStackSize) + if (availableCode > codeMask - 1 && availableCode < MaxStackSize) { codeSize++; codeMask = (1 << codeSize) - 1; From 7ac6fa6a82c51a9cf6189251b8beab8ebdf5e9e1 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 3 Dec 2020 19:11:22 +0100 Subject: [PATCH 123/275] Add support for encoding tiff's with lzw compression --- .../Formats/Tiff/ITiffEncoderOptions.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffEncoder.cs | 2 +- .../Formats/Tiff/TiffEncoderCompression.cs | 5 ++ .../Formats/Tiff/TiffEncoderCore.cs | 10 +++ .../Formats/Tiff/Utils/TiffLzwEncoder.cs | 12 +-- .../Formats/Tiff/Utils/TiffWriter.cs | 89 ++++++++++++++++++- .../Compression/LzwTiffCompressionTests.cs | 9 +- .../Formats/Tiff/TiffEncoderTests.cs | 20 +++++ 8 files changed, 137 insertions(+), 12 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs index 0d3aa4bac..78cf553d3 100644 --- a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs +++ b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs @@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff TiffEncodingMode Mode { get; } /// - /// Gets a value indicating whether to use horizontal prediction. This can improve the compression ratio with deflate compression. + /// Gets a value indicating whether to use horizontal prediction. This can improve the compression ratio with deflate or lzw compression. /// bool UseHorizontalPredictor { get; } diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs index 2deb063b0..0d1821704 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs @@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff public TiffEncodingMode Mode { get; set; } /// - /// Gets or sets a value indicating whether to use horizontal prediction. This can improve the compression ratio with deflate compression. + /// Gets or sets a value indicating whether to use horizontal prediction. This can improve the compression ratio with deflate or lzw compression. /// public bool UseHorizontalPredictor { get; set; } diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs index 145849d4f..f2e94d316 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs @@ -18,6 +18,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// Deflate, + /// + /// Use lzw compression. + /// + Lzw, + /// /// Use PackBits to compression the image data. /// diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 6bc3b7338..c5283c74b 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -433,6 +433,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff return (ushort)TiffCompression.Deflate; } + if (this.CompressionType == TiffEncoderCompression.Lzw && this.Mode == TiffEncodingMode.Rgb) + { + return (ushort)TiffCompression.Lzw; + } + if (this.CompressionType == TiffEncoderCompression.PackBits && this.Mode == TiffEncodingMode.Rgb) { return (ushort)TiffCompression.PackBits; @@ -443,6 +448,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff return (ushort)TiffCompression.Deflate; } + if (this.CompressionType == TiffEncoderCompression.Lzw && this.Mode == TiffEncodingMode.Gray) + { + return (ushort)TiffCompression.Lzw; + } + if (this.CompressionType == TiffEncoderCompression.PackBits && this.Mode == TiffEncodingMode.Gray) { return (ushort)TiffCompression.PackBits; diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs index d22f14ce5..b0c20a0db 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs @@ -5,6 +5,7 @@ using System; using System.Buffers; using System.IO; using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils { @@ -66,9 +67,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils }; /// - /// The working pixel array + /// The working pixel array. /// - private readonly byte[] pixelArray; + private readonly IMemoryOwner pixelArray; /// /// The initial code size. @@ -200,11 +201,12 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// /// The array of indexed pixels. /// The color depth in bits. - public TiffLzwEncoder(byte[] indexedPixels, int colorDepth) + public TiffLzwEncoder(IMemoryOwner indexedPixels, int colorDepth) { this.pixelArray = indexedPixels; this.initialCodeSize = Math.Max(2, colorDepth); + // TODO: use memory allocator this.hashTable = ArrayPool.Shared.Rent(HashSize); this.codeTable = ArrayPool.Shared.Rent(HashSize); Array.Clear(this.hashTable, 0, HashSize); @@ -404,13 +406,13 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// private int NextPixel() { - if (this.currentPixel == this.pixelArray.Length) + if (this.currentPixel == this.pixelArray.Length()) { return Eof; } this.currentPixel++; - return this.pixelArray[this.currentPixel - 1] & 0xff; + return this.pixelArray.GetSpan()[this.currentPixel - 1] & 0xff; } /// diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs index 52edbdc41..fce6ec462 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -7,6 +7,7 @@ using System.IO; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Png.Zlib; using SixLabors.ImageSharp.Memory; @@ -149,6 +150,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils return this.WriteDeflateCompressedRgb(image, rowSpan, useHorizontalPredictor); } + if (compression == TiffEncoderCompression.Lzw) + { + return this.WriteLzwCompressedRgb(image, rowSpan, useHorizontalPredictor); + } + if (compression == TiffEncoderCompression.PackBits) { return this.WriteRgbPackBitsCompressed(image, rowSpan); @@ -205,6 +211,44 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils return bytesWritten; } + /// + /// Writes the image data as RGB compressed with lzw to the stream. + /// + /// The pixel data. + /// The image to write to the stream. + /// A Span for a pixel row. + /// Indicates if horizontal prediction should be used. Should only be used with deflate compression. + /// The number of bytes written. + private int WriteLzwCompressedRgb(Image image, Span rowSpan, bool useHorizontalPredictor) + where TPixel : unmanaged, IPixel + { + int bytesWritten = 0; + using var memoryStream = new MemoryStream(); + + IMemoryOwner pixelData = this.memoryAllocator.Allocate(image.Width * image.Height * 3); + Span pixels = pixelData.GetSpan(); + for (int y = 0; y < image.Height; y++) + { + Span pixelRow = image.GetPixelRowSpan(y); + PixelOperations.Instance.ToRgb24Bytes(this.configuration, pixelRow, rowSpan, pixelRow.Length); + + if (useHorizontalPredictor) + { + this.ApplyHorizontalPredictionRgb(rowSpan); + } + + rowSpan.CopyTo(pixels.Slice(y * image.Width * 3)); + } + + using var lzwEncoder = new TiffLzwEncoder(pixelData, 8); + lzwEncoder.Encode(memoryStream); + + byte[] buffer = memoryStream.ToArray(); + this.output.Write(buffer); + bytesWritten += buffer.Length; + return bytesWritten; + } + /// /// Applies a horizontal predictor to the rgb row. /// Make use of the fact that many continuous-tone images rarely vary much in pixel value from one pixel to the next. @@ -425,7 +469,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// The image to write to the stream. /// The padding bytes for each row. /// The compression to use. - /// Indicates if horizontal prediction should be used. Should only be used with deflate compression. + /// Indicates if horizontal prediction should be used. Should only be used with deflate or lzw compression. /// The number of bytes written. public int WriteGray(Image image, int padding, TiffEncoderCompression compression, bool useHorizontalPredictor) where TPixel : unmanaged, IPixel @@ -438,6 +482,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils return this.WriteGrayDeflateCompressed(image, rowSpan, useHorizontalPredictor); } + if (compression == TiffEncoderCompression.Lzw) + { + return this.WriteGrayLzwCompressed(image, rowSpan, useHorizontalPredictor); + } + if (compression == TiffEncoderCompression.PackBits) { return this.WriteGrayPackBitsCompressed(image, rowSpan); @@ -460,7 +509,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// /// The image to write to the stream. /// A span of a row of pixels. - /// Indicates if horizontal prediction should be used. Should only be used with deflate compression. + /// Indicates if horizontal prediction should be used. /// The number of bytes written. private int WriteGrayDeflateCompressed(Image image, Span rowSpan, bool useHorizontalPredictor) where TPixel : unmanaged, IPixel @@ -492,6 +541,42 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils return bytesWritten; } + /// + /// Writes the image data as 8 bit gray with lzw compression to the stream. + /// + /// The image to write to the stream. + /// A span of a row of pixels. + /// Indicates if horizontal prediction should be used. + /// The number of bytes written. + private int WriteGrayLzwCompressed(Image image, Span rowSpan, bool useHorizontalPredictor) + where TPixel : unmanaged, IPixel + { + int bytesWritten = 0; + using var memoryStream = new MemoryStream(); + + IMemoryOwner pixelData = this.memoryAllocator.Allocate(image.Width * image.Height); + Span pixels = pixelData.GetSpan(); + for (int y = 0; y < image.Height; y++) + { + Span pixelRow = image.GetPixelRowSpan(y); + PixelOperations.Instance.ToL8Bytes(this.configuration, pixelRow, rowSpan, pixelRow.Length); + if (useHorizontalPredictor) + { + this.ApplyHorizontalPredictionGray(rowSpan); + } + + rowSpan.CopyTo(pixels.Slice(y * image.Width)); + } + + using var lzwEncoder = new TiffLzwEncoder(pixelData, 8); + lzwEncoder.Encode(memoryStream); + + byte[] buffer = memoryStream.ToArray(); + this.output.Write(buffer); + bytesWritten += buffer.Length; + return bytesWritten; + } + /// /// Applies a horizontal predictor to a gray pixel row. /// diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs index f6a31f43a..775d55af6 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs @@ -1,11 +1,12 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; using System.IO; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; - +using SixLabors.ImageSharp.Memory; using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression @@ -29,9 +30,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression Assert.Equal(data, buffer); } - private static Stream CreateCompressedStream(byte[] data) + private static Stream CreateCompressedStream(byte[] inputData) { - Stream compressedStream = new MemoryStream(); + using Stream compressedStream = new MemoryStream(); + using System.Buffers.IMemoryOwner data = Configuration.Default.MemoryAllocator.Allocate(inputData.Length); + inputData.AsSpan().CopyTo(data.GetSpan()); using (var encoder = new TiffLzwEncoder(data, 8)) { diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index ff00edb67..2f0d75388 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -61,6 +61,16 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public void TiffEncoder_EncodeRgb_WithDeflateCompressionAndPredictor_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.Rgb, TiffEncoderCompression.Deflate, usePredictor: true); + [Theory] + [WithFile(TestImages.Tiff.Calliphora_RgbUncompressed, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeRgb_WithLzwCompression_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.Rgb, TiffEncoderCompression.Lzw); + + [Theory] + [WithFile(TestImages.Tiff.Calliphora_RgbUncompressed, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeRgb_WithLzwCompressionAndPredictor_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.Rgb, TiffEncoderCompression.Lzw, usePredictor: true); + [Theory] [WithFile(TestImages.Tiff.Calliphora_RgbUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeRgb_WithPackBitsCompression_Works(TestImageProvider provider) @@ -81,6 +91,16 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public void TiffEncoder_EncodeGray_WithDeflateCompressionAndPredictor_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.Gray, TiffEncoderCompression.Deflate, usePredictor: true); + [Theory] + [WithFile(TestImages.Tiff.Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeGray_WithLzwCompression_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.Gray, TiffEncoderCompression.Lzw); + + [Theory] + [WithFile(TestImages.Tiff.Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeGray_WithLzwCompressionAndPredictor_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.Gray, TiffEncoderCompression.Lzw, usePredictor: true); + [Theory] [WithFile(TestImages.Tiff.Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeGray_WithPackBitsCompression_Works(TestImageProvider provider) From 8e34f989c91f19d7edd22802e49c59481701bd33 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 3 Dec 2020 19:29:49 +0100 Subject: [PATCH 124/275] LzwEncoder now uses the memory allocator --- .../Formats/Tiff/Utils/TiffLzwEncoder.cs | 41 ++++++++++--------- .../Formats/Tiff/Utils/TiffWriter.cs | 4 +- .../Compression/LzwTiffCompressionTests.cs | 2 +- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs index b0c20a0db..96db8e110 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs @@ -79,12 +79,12 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// /// The hash table. /// - private readonly int[] hashTable; + private readonly IMemoryOwner hashTable; /// /// The code table. /// - private readonly int[] codeTable; + private readonly IMemoryOwner codeTable; /// /// Define the storage for the packet accumulator. @@ -201,16 +201,14 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// /// The array of indexed pixels. /// The color depth in bits. - public TiffLzwEncoder(IMemoryOwner indexedPixels, int colorDepth) + /// The memory allocator. + public TiffLzwEncoder(MemoryAllocator memoryAllocator, IMemoryOwner indexedPixels, int colorDepth) { this.pixelArray = indexedPixels; this.initialCodeSize = Math.Max(2, colorDepth); - // TODO: use memory allocator - this.hashTable = ArrayPool.Shared.Rent(HashSize); - this.codeTable = ArrayPool.Shared.Rent(HashSize); - Array.Clear(this.hashTable, 0, HashSize); - Array.Clear(this.codeTable, 0, HashSize); + this.hashTable = memoryAllocator.Allocate(HashSize, AllocationOptions.Clean); + this.codeTable = memoryAllocator.Allocate(HashSize, AllocationOptions.Clean); } /// @@ -276,9 +274,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// The hash size. private void ResetCodeTable(int size) { + Span hashTableSpan = this.hashTable.GetSpan(); for (int i = 0; i < size; ++i) { - this.hashTable[i] = -1; + hashTableSpan[i] = -1; } } @@ -325,19 +324,21 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils this.Output(this.clearCode, stream); + Span hashTableSpan = this.hashTable.GetSpan(); + Span codeTableSpan = this.codeTable.GetSpan(); while ((c = this.NextPixel()) != Eof) { fcode = (c << this.maxbits) + ent; int i = (c << hshift) ^ ent /* = 0 */; - if (this.hashTable[i] == fcode) + if (hashTableSpan[i] == fcode) { - ent = this.codeTable[i]; + ent = codeTableSpan[i]; continue; } // Non-empty slot - if (this.hashTable[i] >= 0) + if (hashTableSpan[i] >= 0) { int disp = hsizeReg - i; if (i == 0) @@ -352,15 +353,15 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils i += hsizeReg; } - if (this.hashTable[i] == fcode) + if (hashTableSpan[i] == fcode) { - ent = this.codeTable[i]; + ent = codeTableSpan[i]; break; } } - while (this.hashTable[i] >= 0); + while (hashTableSpan[i] >= 0); - if (this.hashTable[i] == fcode) + if (hashTableSpan[i] == fcode) { continue; } @@ -370,8 +371,8 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils ent = c; if (this.freeEntry < this.maxmaxcode) { - this.codeTable[i] = this.freeEntry++; // code -> hashtable - this.hashTable[i] = fcode; + codeTableSpan[i] = this.freeEntry++; // code -> hashtable + hashTableSpan[i] = fcode; } else { @@ -487,8 +488,8 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils if (disposing) { - ArrayPool.Shared.Return(this.hashTable); - ArrayPool.Shared.Return(this.codeTable); + this.hashTable.Dispose(); + this.codeTable.Dispose(); } this.isDisposed = true; diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs index fce6ec462..1ce03cf2e 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -240,7 +240,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils rowSpan.CopyTo(pixels.Slice(y * image.Width * 3)); } - using var lzwEncoder = new TiffLzwEncoder(pixelData, 8); + using var lzwEncoder = new TiffLzwEncoder(this.memoryAllocator, pixelData, 8); lzwEncoder.Encode(memoryStream); byte[] buffer = memoryStream.ToArray(); @@ -568,7 +568,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils rowSpan.CopyTo(pixels.Slice(y * image.Width)); } - using var lzwEncoder = new TiffLzwEncoder(pixelData, 8); + using var lzwEncoder = new TiffLzwEncoder(this.memoryAllocator, pixelData, 8); lzwEncoder.Encode(memoryStream); byte[] buffer = memoryStream.ToArray(); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs index 775d55af6..730d6f366 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs @@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression using System.Buffers.IMemoryOwner data = Configuration.Default.MemoryAllocator.Allocate(inputData.Length); inputData.AsSpan().CopyTo(data.GetSpan()); - using (var encoder = new TiffLzwEncoder(data, 8)) + using (var encoder = new TiffLzwEncoder(Configuration.Default.MemoryAllocator, data, 8)) { encoder.Encode(compressedStream); } From 3a4c0d007ef921abd55528c7b30051fdcbd0a1ff Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Thu, 3 Dec 2020 22:07:10 +0300 Subject: [PATCH 125/275] Perform tests files --- .../Formats/Tiff/TiffDecoderTests.cs | 12 ++++++++++-- tests/ImageSharp.Tests/TestImages.cs | 4 ++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index a78a34939..c5e09b65e 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -59,8 +59,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff } [Theory] - [InlineData(TestImages.Tiff.RgbLzwNoPredictorMultistrip, TiffByteOrder.LittleEndian)] - [InlineData(TestImages.Tiff.RgbLzwNoPredictorMultistripMotorola, TiffByteOrder.BigEndian)] + [InlineData(RgbLzwNoPredictorMultistrip, TiffByteOrder.LittleEndian)] + [InlineData(RgbLzwNoPredictorMultistripMotorola, TiffByteOrder.BigEndian)] public void ByteOrder(string imagePath, TiffByteOrder expectedByteOrder) { var testFile = TestFile.Create(imagePath); @@ -91,6 +91,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Theory] [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] + [WithFile(PaletteDeflateMultistrip, PixelTypes.Rgba32)] [WithFile(RgbPalette, PixelTypes.Rgba32)] [WithFile(RgbPaletteDeflate, PixelTypes.Rgba32)] [WithFile(PaletteUncompressed, PixelTypes.Rgba32)] @@ -101,11 +102,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff } [Theory] + [WithFile(GrayscaleDeflateMultistrip, PixelTypes.Rgba32)] + [WithFile(RgbDeflateMultistrip, PixelTypes.Rgba32)] [WithFile(Calliphora_GrayscaleDeflate, PixelTypes.Rgba32)] [WithFile(Calliphora_GrayscaleDeflate_Predictor, PixelTypes.Rgba32)] [WithFile(Calliphora_RgbDeflate_Predictor, PixelTypes.Rgba32)] [WithFile(RgbDeflate, PixelTypes.Rgba32)] [WithFile(RgbDeflatePredictor, PixelTypes.Rgba32)] + [WithFile(SmallRgbDeflate, PixelTypes.Rgba32)] public void TiffDecoder_CanDecode_DeflateCompressed(TestImageProvider provider) where TPixel : unmanaged, IPixel { @@ -115,6 +119,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Theory] [WithFile(RgbLzwPredictor, PixelTypes.Rgba32)] [WithFile(RgbLzwNoPredictor, PixelTypes.Rgba32)] + [WithFile(RgbLzwNoPredictorSinglestripMotorola, PixelTypes.Rgba32)] + [WithFile(RgbLzwNoPredictorMultistripMotorola, PixelTypes.Rgba32)] + [WithFile(RgbLzwMultistripPredictor, PixelTypes.Rgba32)] [WithFile(Calliphora_RgbLzw_Predictor, PixelTypes.Rgba32)] [WithFile(SmallRgbLzw, PixelTypes.Rgba32)] public void TiffDecoder_CanDecode_LzwCompressed(TestImageProvider provider) @@ -147,6 +154,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Theory] [WithFile(Calliphora_RgbPackbits, PixelTypes.Rgba32)] [WithFile(RgbPackbits, PixelTypes.Rgba32)] + [WithFile(RgbPackbitsMultistrip, PixelTypes.Rgba32)] public void TiffDecoder_CanDecode_PackBitsCompressed(TestImageProvider provider) where TPixel : unmanaged, IPixel { diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index a6a94c692..ef46e2289 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -558,11 +558,11 @@ namespace SixLabors.ImageSharp.Tests public const string SampleMetadata = "Tiff/metadata_sample.tiff"; - public static readonly string[] Multiframes = { MultiframeDeflateWithPreview /*MultiframeLzw_Predictor, MultiFrameDifferentSize, MultiframeDifferentSizeTiled, MultiFrameDifferentVariants,*/ }; + public static readonly string[] Multiframes = { MultiframeDeflateWithPreview, MultiframeLzwPredictor /*, MultiFrameDifferentSize, MultiframeDifferentSizeTiled, MultiFrameDifferentVariants,*/ }; public static readonly string[] Metadata = { SampleMetadata }; - public static readonly string[] NotSupported = { Calliphora_RgbJpeg, RgbLzwMultistripPredictor, RgbJpeg, RgbUncompressedTiled, MultiframeLzwPredictor, MultiframeDifferentSize, MultiframeDifferentVariants }; + public static readonly string[] NotSupported = { Calliphora_RgbJpeg, RgbJpeg, RgbUncompressedTiled, MultiframeDifferentSize, MultiframeDifferentVariants }; } } } From b00104d769e940d4a0627b1d085376ccd412e06a Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 4 Dec 2020 10:06:23 +0100 Subject: [PATCH 126/275] Rework horizontal predictor: Fixes issue with paletted images which use a predictor --- .../Compression/DeflateTiffCompression.cs | 19 ++++- .../Tiff/Compression/HorizontalPredictor.cs | 75 +++++++++++++++++++ .../Tiff/Compression/LzwTiffCompression.cs | 18 ++++- .../Tiff/Compression/NoneTiffCompression.cs | 8 +- .../Compression/PackBitsTiffCompression.cs | 8 +- .../Tiff/Compression/TiffBaseCompression.cs | 19 ++++- .../Compression/TiffCompressionFactory.cs | 6 +- .../Formats/Tiff/TiffBitsPerPixel.cs | 2 +- .../Formats/Tiff/TiffDecoderCore.cs | 60 +++++---------- .../DeflateTiffCompressionTests.cs | 3 +- .../Compression/LzwTiffCompressionTests.cs | 5 +- .../Formats/Tiff/TiffDecoderTests.cs | 4 +- tests/ImageSharp.Tests/TestImages.cs | 7 +- .../Tiff/Calliphora_gray_lzw_predictor.tiff | 3 + .../Images/Input/Tiff/Calliphora_rgb_lzw.tif | 3 + .../Tiff/Calliphora_rgb_lzw_predictor.tiff | 4 +- .../Calliphora_rgb_palette_lzw_predictor.tiff | 3 + 17 files changed, 184 insertions(+), 63 deletions(-) create mode 100644 src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs create mode 100644 tests/Images/Input/Tiff/Calliphora_gray_lzw_predictor.tiff create mode 100644 tests/Images/Input/Tiff/Calliphora_rgb_lzw.tif create mode 100644 tests/Images/Input/Tiff/Calliphora_rgb_palette_lzw_predictor.tiff diff --git a/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs index 6e206f0ed..4cc7008eb 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs @@ -4,7 +4,10 @@ using System; using System.IO; using System.IO.Compression; + +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; +using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression @@ -17,8 +20,15 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression /// internal class DeflateTiffCompression : TiffBaseCompression { - public DeflateTiffCompression(MemoryAllocator allocator) - : base(allocator) + /// + /// Initializes a new instance of the class. + /// + /// The memoryAllocator to use for buffer allocations. + /// The image width. + /// The bits used per pixel. + /// The tiff predictor used. + public DeflateTiffCompression(MemoryAllocator memoryAllocator, int width, int bitsPerPixel, TiffPredictor predictor) + : base(memoryAllocator, width, bitsPerPixel, predictor) { } @@ -52,6 +62,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression { deflateStream.ReadFull(buffer); } + + if (this.Predictor == TiffPredictor.Horizontal) + { + HorizontalPredictor.Undo(buffer, this.Width, this.BitsPerPixel); + } } } } diff --git a/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs b/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs new file mode 100644 index 000000000..3cbe5a81d --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs @@ -0,0 +1,75 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.InteropServices; + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Tiff.Compression +{ + /// + /// Methods for undoing the horizontal prediction used in combination with deflate and LZW compressed TIFF images. + /// + public static class HorizontalPredictor + { + /// + /// Inverts the horizontal prediction. + /// + /// Buffer with decompressed pixel data. + /// The width of the image or strip. + /// Bits per pixel. + public static void Undo(Span pixelBytes, int width, int bitsPerPixel) + { + if (bitsPerPixel == 8) + { + Undo8Bit(pixelBytes, width); + } + else if (bitsPerPixel == 24) + { + Undo24Bit(pixelBytes, width); + } + } + + private static void Undo8Bit(Span pixelBytes, int width) + { + var rowBytesCount = width; + int height = pixelBytes.Length / rowBytesCount; + for (int y = 0; y < height; y++) + { + Span rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount); + + byte pixelValue = rowBytes[0]; + for (int x = 1; x < width; x++) + { + pixelValue += rowBytes[x]; + rowBytes[x] = pixelValue; + } + } + } + + private static void Undo24Bit(Span pixelBytes, int width) + { + var rowBytesCount = width * 3; + int height = pixelBytes.Length / rowBytesCount; + for (int y = 0; y < height; y++) + { + Span rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount); + Span rowRgb = MemoryMarshal.Cast(rowBytes); + + byte r = rowRgb[0].R; + byte g = rowRgb[0].G; + byte b = rowRgb[0].B; + for (int x = 1; x < width; x++) + { + ref Rgb24 pixel = ref rowRgb[x]; + r += rowRgb[x].R; + g += rowRgb[x].G; + b += rowRgb[x].B; + var rgb = new Rgb24(r, g, b); + pixel.FromRgb24(rgb); + } + } + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs index 14f45fc9d..b01f14191 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs @@ -3,7 +3,9 @@ using System; using System.IO; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; +using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression @@ -13,8 +15,15 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression /// internal class LzwTiffCompression : TiffBaseCompression { - public LzwTiffCompression(MemoryAllocator allocator) - : base(allocator) + /// + /// Initializes a new instance of the class. + /// + /// The memoryAllocator to use for buffer allocations. + /// The image width. + /// The bits used per pixel. + /// The tiff predictor used. + public LzwTiffCompression(MemoryAllocator memoryAllocator, int width, int bitsPerPixel, TiffPredictor predictor) + : base(memoryAllocator, width, bitsPerPixel, predictor) { } @@ -24,6 +33,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression var subStream = new SubStream(stream, byteCount); var decoder = new TiffLzwDecoder(subStream, this.Allocator); decoder.DecodePixels(buffer.Length, 8, buffer); + + if (this.Predictor == TiffPredictor.Horizontal) + { + HorizontalPredictor.Undo(buffer, this.Width, this.BitsPerPixel); + } } } } diff --git a/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs index 94cf5a9ca..5ca112f3b 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs @@ -14,8 +14,12 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression /// internal class NoneTiffCompression : TiffBaseCompression { - public NoneTiffCompression(MemoryAllocator allocator) - : base(allocator) + /// + /// Initializes a new instance of the class. + /// + /// The memoryAllocator to use for buffer allocations. + public NoneTiffCompression(MemoryAllocator memoryAllocator) + : base(memoryAllocator) { } diff --git a/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs index d49aced44..2fbb7e6f1 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs @@ -15,8 +15,12 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression /// internal class PackBitsTiffCompression : TiffBaseCompression { - public PackBitsTiffCompression(MemoryAllocator allocator) - : base(allocator) + /// + /// Initializes a new instance of the class. + /// + /// The memoryAllocator to use for buffer allocations. + public PackBitsTiffCompression(MemoryAllocator memoryAllocator) + : base(memoryAllocator) { } diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs index 45571d503..d4f287adc 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs @@ -5,6 +5,7 @@ using System; using System.IO; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression @@ -20,21 +21,37 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression private int width; + private int bitsPerPixel; + + private TiffPredictor predictor; + public TiffBaseCompression(MemoryAllocator allocator) => this.allocator = allocator; public TiffBaseCompression(MemoryAllocator allocator, TiffPhotometricInterpretation photometricInterpretation, int width) + : this(allocator) { - this.allocator = allocator; this.photometricInterpretation = photometricInterpretation; this.width = width; } + public TiffBaseCompression(MemoryAllocator allocator, int width, int bitsPerPixel, TiffPredictor predictor) + : this(allocator) + { + this.width = width; + this.bitsPerPixel = bitsPerPixel; + this.predictor = predictor; + } + protected MemoryAllocator Allocator => this.allocator; protected TiffPhotometricInterpretation PhotometricInterpretation => this.photometricInterpretation; protected int Width => this.width; + protected int BitsPerPixel => this.bitsPerPixel; + + protected TiffPredictor Predictor => this.predictor; + /// /// Decompresses image data into the supplied buffer. /// diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs index 6f785bb31..c261e91d3 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression { internal static class TiffCompressionFactory { - public static TiffBaseCompression Create(TiffDecoderCompressionType compressionType, MemoryAllocator allocator, TiffPhotometricInterpretation photometricInterpretation, int width) + public static TiffBaseCompression Create(TiffDecoderCompressionType compressionType, MemoryAllocator allocator, TiffPhotometricInterpretation photometricInterpretation, int width, int bitsPerPixel, TiffPredictor predictor) { switch (compressionType) { @@ -17,9 +17,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression case TiffDecoderCompressionType.PackBits: return new PackBitsTiffCompression(allocator); case TiffDecoderCompressionType.Deflate: - return new DeflateTiffCompression(allocator); + return new DeflateTiffCompression(allocator, width, bitsPerPixel, predictor); case TiffDecoderCompressionType.Lzw: - return new LzwTiffCompression(allocator); + return new LzwTiffCompression(allocator, width, bitsPerPixel, predictor); case TiffDecoderCompressionType.T4: return new T4TiffCompression(allocator, photometricInterpretation, width); case TiffDecoderCompressionType.HuffmanRle: diff --git a/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs b/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs index 163876a3f..0e314e6ee 100644 --- a/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs +++ b/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff Pixel1 = 1, /// - /// 8 bits per pixel, grayscale image. Each pixel consists of 1 byte. + /// 8 bits per pixel, grayscale or indexed image. Each pixel consists of 1 byte. /// Pixel8 = 8, diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 381162093..8a6ac48fe 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -241,6 +241,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff frameMetaData = coreMetadata.GetTiffMetadata(); frameMetaData.Tags = tags; TiffFrameMetadata tiffFormatMetaData = coreMetadata.GetFormatMetadata(TiffFormat.Instance); + TiffPredictor predictor = tiffFormatMetaData.Predictor; this.VerifyAndParseOptions(frameMetaData); @@ -254,52 +255,16 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff if (this.PlanarConfiguration == TiffPlanarConfiguration.Planar) { - this.DecodeStripsPlanar(frame, rowsPerStrip, stripOffsets, stripByteCounts, width); + this.DecodeStripsPlanar(frame, rowsPerStrip, stripOffsets, stripByteCounts, width, predictor); } else { - this.DecodeStripsChunky(frame, rowsPerStrip, stripOffsets, stripByteCounts, width); - } - - if (tiffFormatMetaData.Predictor == TiffPredictor.Horizontal) - { - this.UndoHorizontalPredictor(frame, width, height); + this.DecodeStripsChunky(frame, rowsPerStrip, stripOffsets, stripByteCounts, width, predictor); } return frame; } - /// - /// This will reverse the horizontal prediction operation. - /// - /// The pixel format. - /// The image frame. - /// The width of the image. - /// The height of the image. - private void UndoHorizontalPredictor(ImageFrame frame, int width, int height) - where TPixel : unmanaged, IPixel - { - using System.Buffers.IMemoryOwner rowRgbBuffer = this.memoryAllocator.Allocate(width); - System.Span rowRgb = rowRgbBuffer.GetSpan(); - for (int y = 0; y < height; y++) - { - System.Span pixelRow = frame.GetPixelRowSpan(y); - PixelOperations.Instance.ToRgb24(this.configuration, pixelRow, rowRgb); - byte r = rowRgb[0].R; - byte g = rowRgb[0].G; - byte b = rowRgb[0].B; - for (int x = 1; x < width; x++) - { - ref TPixel pixel = ref pixelRow[x]; - r += rowRgb[x].R; - g += rowRgb[x].G; - b += rowRgb[x].B; - var rgb = new Rgb24(r, g, b); - pixel.FromRgb24(rgb); - } - } - } - /// /// Calculates the size (in bytes) for a pixel buffer using the determined color format. /// @@ -339,11 +304,17 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// An array of byte offsets to each strip in the image. /// An array of the size of each strip (in bytes). /// The image width. - private void DecodeStripsPlanar(ImageFrame frame, int rowsPerStrip, uint[] stripOffsets, uint[] stripByteCounts, int width) + /// The tiff predictor used. + private void DecodeStripsPlanar(ImageFrame frame, int rowsPerStrip, uint[] stripOffsets, uint[] stripByteCounts, int width, TiffPredictor predictor) where TPixel : unmanaged, IPixel { int stripsPerPixel = this.BitsPerSample.Length; int stripsPerPlane = stripOffsets.Length / stripsPerPixel; + int bitsPerPixel = 0; + foreach (var bits in this.BitsPerSample) + { + bitsPerPixel += bits; + } Buffer2D pixels = frame.PixelBuffer; @@ -357,7 +328,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff stripBuffers[stripIndex] = this.memoryAllocator.AllocateManagedByteBuffer(uncompressedStripSize); } - TiffBaseCompression decompressor = TiffCompressionFactory.Create(this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, width); + TiffBaseCompression decompressor = TiffCompressionFactory.Create(this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, width, bitsPerPixel, predictor); RgbPlanarTiffColor colorDecoder = TiffColorDecoderFactory.CreatePlanar(this.ColorType, this.BitsPerSample, this.ColorMap); @@ -385,16 +356,21 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff } } - private void DecodeStripsChunky(ImageFrame frame, int rowsPerStrip, uint[] stripOffsets, uint[] stripByteCounts, int width) + private void DecodeStripsChunky(ImageFrame frame, int rowsPerStrip, uint[] stripOffsets, uint[] stripByteCounts, int width, TiffPredictor predictor) where TPixel : unmanaged, IPixel { int uncompressedStripSize = this.CalculateStripBufferSize(frame.Width, rowsPerStrip); + int bitsPerPixel = 0; + foreach (var bits in this.BitsPerSample) + { + bitsPerPixel += bits; + } using IManagedByteBuffer stripBuffer = this.memoryAllocator.AllocateManagedByteBuffer(uncompressedStripSize, AllocationOptions.Clean); Buffer2D pixels = frame.PixelBuffer; - TiffBaseCompression decompressor = TiffCompressionFactory.Create(this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, width); + TiffBaseCompression decompressor = TiffCompressionFactory.Create(this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, width, bitsPerPixel, predictor); TiffBaseColorDecoder colorDecoder = TiffColorDecoderFactory.Create(this.ColorType, this.BitsPerSample, this.ColorMap); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs index 46e9c2da5..692f92c9f 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs @@ -3,6 +3,7 @@ using System.IO; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Formats.Png.Zlib; using Xunit; @@ -23,7 +24,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression { var buffer = new byte[data.Length]; - new DeflateTiffCompression(null).Decompress(stream, (int)stream.Length, buffer); + new DeflateTiffCompression(Configuration.Default.MemoryAllocator, 10, 8, TiffPredictor.None).Decompress(stream, (int)stream.Length, buffer); Assert.Equal(data, buffer); } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs index 730d6f366..1837fe98e 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs @@ -5,6 +5,7 @@ using System; using System.IO; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; using SixLabors.ImageSharp.Memory; using Xunit; @@ -25,14 +26,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression using Stream stream = CreateCompressedStream(data); var buffer = new byte[data.Length]; - new LzwTiffCompression(Configuration.Default.MemoryAllocator).Decompress(stream, (int)stream.Length, buffer); + new LzwTiffCompression(Configuration.Default.MemoryAllocator, 10, 8, TiffPredictor.None).Decompress(stream, (int)stream.Length, buffer); Assert.Equal(data, buffer); } private static Stream CreateCompressedStream(byte[] inputData) { - using Stream compressedStream = new MemoryStream(); + Stream compressedStream = new MemoryStream(); using System.Buffers.IMemoryOwner data = Configuration.Default.MemoryAllocator.Allocate(inputData.Length); inputData.AsSpan().CopyTo(data.GetSpan()); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index c5e09b65e..24c0a712b 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -122,7 +122,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [WithFile(RgbLzwNoPredictorSinglestripMotorola, PixelTypes.Rgba32)] [WithFile(RgbLzwNoPredictorMultistripMotorola, PixelTypes.Rgba32)] [WithFile(RgbLzwMultistripPredictor, PixelTypes.Rgba32)] - [WithFile(Calliphora_RgbLzw_Predictor, PixelTypes.Rgba32)] + [WithFile(Calliphora_RgbPaletteLzw_Predictor, PixelTypes.Rgba32)] + [WithFile(Calliphora_RgbLzwPredictor, PixelTypes.Rgba32)] + [WithFile(Calliphora_GrayscaleLzw_Predictor, PixelTypes.Rgba32)] [WithFile(SmallRgbLzw, PixelTypes.Rgba32)] public void TiffDecoder_CanDecode_LzwCompressed(TestImageProvider provider) where TPixel : unmanaged, IPixel diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index ef46e2289..93277550b 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -506,11 +506,14 @@ namespace SixLabors.ImageSharp.Tests public const string Calliphora_GrayscaleUncompressed = "Tiff/Calliphora_grayscale_uncompressed.tiff"; public const string Calliphora_GrayscaleDeflate_Predictor = "Tiff/Calliphora_gray_deflate_predictor.tiff"; + public const string Calliphora_GrayscaleLzw_Predictor = "Tiff/Calliphora_gray_lzw_predictor.tiff"; public const string Calliphora_GrayscaleDeflate = "Tiff/Calliphora_gray_deflate.tiff"; - public const string Calliphora_PaletteUncompressed = "Tiff/Calliphora_palette_uncompressed.tiff"; public const string Calliphora_RgbDeflate_Predictor = "Tiff/Calliphora_rgb_deflate_predictor.tiff"; public const string Calliphora_RgbJpeg = "Tiff/Calliphora_rgb_jpeg.tiff"; - public const string Calliphora_RgbLzw_Predictor = "Tiff/Calliphora_rgb_lzw_predictor.tiff"; + public const string Calliphora_PaletteUncompressed = "Tiff/Calliphora_palette_uncompressed.tiff"; + public const string Calliphora_RgbLzwPredictor = "Tiff/Calliphora_rgb_lzw_predictor.tiff"; + public const string Calliphora_RgbPaletteLzw = "Tiff/Calliphora_rgb_palette_lzw.tiff"; + public const string Calliphora_RgbPaletteLzw_Predictor = "Tiff/Calliphora_rgb_palette_lzw_predictor.tiff"; public const string Calliphora_RgbPackbits = "Tiff/Calliphora_rgb_packbits.tiff"; public const string Calliphora_RgbUncompressed = "Tiff/Calliphora_rgb_uncompressed.tiff"; public const string Calliphora_Fax3Compressed = "Tiff/Calliphora_ccitt_fax3.tiff"; diff --git a/tests/Images/Input/Tiff/Calliphora_gray_lzw_predictor.tiff b/tests/Images/Input/Tiff/Calliphora_gray_lzw_predictor.tiff new file mode 100644 index 000000000..b14eeba8d --- /dev/null +++ b/tests/Images/Input/Tiff/Calliphora_gray_lzw_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3f24fd8f36a4847fcb84a317de4fd2eacd5eb0c58ef4436d33919f0a6658d0d9 +size 698309 diff --git a/tests/Images/Input/Tiff/Calliphora_rgb_lzw.tif b/tests/Images/Input/Tiff/Calliphora_rgb_lzw.tif new file mode 100644 index 000000000..745052267 --- /dev/null +++ b/tests/Images/Input/Tiff/Calliphora_rgb_lzw.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:53006876fcdc655a794462de57eb6b56f4d0cdd3cb8b752c63328db0eb4aa3c1 +size 725085 diff --git a/tests/Images/Input/Tiff/Calliphora_rgb_lzw_predictor.tiff b/tests/Images/Input/Tiff/Calliphora_rgb_lzw_predictor.tiff index be84f0a30..99642af52 100644 --- a/tests/Images/Input/Tiff/Calliphora_rgb_lzw_predictor.tiff +++ b/tests/Images/Input/Tiff/Calliphora_rgb_lzw_predictor.tiff @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:29fa2b157c92f6a8bd4036e9d075e24fc451e72ec1a251d97a4b40454e01405c -size 792087 +oid sha256:ecb529e5e3e0eca6f5e407b034fa8ba67bb4b9068af9e9b30425b08d30a249c0 +size 1756355 diff --git a/tests/Images/Input/Tiff/Calliphora_rgb_palette_lzw_predictor.tiff b/tests/Images/Input/Tiff/Calliphora_rgb_palette_lzw_predictor.tiff new file mode 100644 index 000000000..be84f0a30 --- /dev/null +++ b/tests/Images/Input/Tiff/Calliphora_rgb_palette_lzw_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:29fa2b157c92f6a8bd4036e9d075e24fc451e72ec1a251d97a4b40454e01405c +size 792087 From 9c05a3a30a16ad5e1550b817054d835395965cb7 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 4 Dec 2020 12:47:57 +0100 Subject: [PATCH 127/275] Move apply horizontal prediction to appropriate class --- .../Tiff/Compression/HorizontalPredictor.cs | 36 +++++++++++++++ .../Formats/Tiff/Utils/TiffWriter.cs | 45 +++---------------- 2 files changed, 42 insertions(+), 39 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs b/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs index 3cbe5a81d..108b6ae6e 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats; @@ -31,6 +32,41 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression } } + /// + /// Applies a horizontal predictor to the rgb row. + /// Make use of the fact that many continuous-tone images rarely vary much in pixel value from one pixel to the next. + /// In such images, if we replace the pixel values by differences between consecutive pixels, many of the differences should be 0, plus + /// or minus 1, and so on.This reduces the apparent information content and allows LZW to encode the data more compactly. + /// + /// The rgb pixel row. + [MethodImpl(InliningOptions.ShortMethod)] + public static void ApplyHorizontalPrediction24Bit(Span rowSpan) + { + Span rowRgb = MemoryMarshal.Cast(rowSpan); + + for (int x = rowRgb.Length - 1; x >= 1; x--) + { + byte r = (byte)(rowRgb[x].R - rowRgb[x - 1].R); + byte g = (byte)(rowRgb[x].G - rowRgb[x - 1].G); + byte b = (byte)(rowRgb[x].B - rowRgb[x - 1].B); + var rgb = new Rgb24(r, g, b); + rowRgb[x].FromRgb24(rgb); + } + } + + /// + /// Applies a horizontal predictor to a gray pixel row. + /// + /// The gray pixel row. + [MethodImpl(InliningOptions.ShortMethod)] + public static void ApplyHorizontalPrediction8Bit(Span rowSpan) + { + for (int x = rowSpan.Length - 1; x >= 1; x--) + { + rowSpan[x] -= rowSpan[x - 1]; + } + } + private static void Undo8Bit(Span pixelBytes, int width) { var rowBytesCount = width; diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs index 1ce03cf2e..d427e9c66 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -7,9 +7,9 @@ using System.IO; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; -using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Png.Zlib; +using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; @@ -197,7 +197,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils if (useHorizontalPredictor) { - this.ApplyHorizontalPredictionRgb(rowSpan); + HorizontalPredictor.ApplyHorizontalPrediction24Bit(rowSpan); } deflateStream.Write(rowSpan); @@ -234,7 +234,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils if (useHorizontalPredictor) { - this.ApplyHorizontalPredictionRgb(rowSpan); + HorizontalPredictor.ApplyHorizontalPrediction24Bit(rowSpan); } rowSpan.CopyTo(pixels.Slice(y * image.Width * 3)); @@ -249,27 +249,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils return bytesWritten; } - /// - /// Applies a horizontal predictor to the rgb row. - /// Make use of the fact that many continuous-tone images rarely vary much in pixel value from one pixel to the next. - /// In such images, if we replace the pixel values by differences between consecutive pixels, many of the differences should be 0, plus - /// or minus 1, and so on.This reduces the apparent information content and allows LZW to encode the data more compactly. - /// - /// The rgb pixel row. - private void ApplyHorizontalPredictionRgb(Span rowSpan) - { - Span rowRgb = MemoryMarshal.Cast(rowSpan); - - for (int x = rowRgb.Length - 1; x >= 1; x--) - { - byte r = (byte)(rowRgb[x].R - rowRgb[x - 1].R); - byte g = (byte)(rowRgb[x].G - rowRgb[x - 1].G); - byte b = (byte)(rowRgb[x].B - rowRgb[x - 1].B); - var rgb = new Rgb24(r, g, b); - rowRgb[x].FromRgb24(rgb); - } - } - /// /// Writes the image data as RGB with packed bits compression to the stream. /// @@ -281,7 +260,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils where TPixel : unmanaged, IPixel { // Worst case is that the actual compressed data is larger then the input data. In this case we need 1 additional byte per 127 bytes. - int additionalBytes = (image.Width * 3 / 127) + 1; + int additionalBytes = ((image.Width * 3) / 127) + 1; using IManagedByteBuffer compressedRow = this.memoryAllocator.AllocateManagedByteBuffer((image.Width * 3) + additionalBytes, AllocationOptions.Clean); Span compressedRowSpan = compressedRow.GetSpan(); int bytesWritten = 0; @@ -527,7 +506,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils if (useHorizontalPredictor) { - this.ApplyHorizontalPredictionGray(rowSpan); + HorizontalPredictor.ApplyHorizontalPrediction8Bit(rowSpan); } deflateStream.Write(rowSpan); @@ -562,7 +541,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils PixelOperations.Instance.ToL8Bytes(this.configuration, pixelRow, rowSpan, pixelRow.Length); if (useHorizontalPredictor) { - this.ApplyHorizontalPredictionGray(rowSpan); + HorizontalPredictor.ApplyHorizontalPrediction8Bit(rowSpan); } rowSpan.CopyTo(pixels.Slice(y * image.Width)); @@ -577,18 +556,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils return bytesWritten; } - /// - /// Applies a horizontal predictor to a gray pixel row. - /// - /// The gray pixel row. - private void ApplyHorizontalPredictionGray(Span rowSpan) - { - for (int x = rowSpan.Length - 1; x >= 1; x--) - { - rowSpan[x] -= rowSpan[x - 1]; - } - } - /// /// Writes the image data as 8 bit gray to the stream. /// From c119adb18ea6099d73246e701b36b9a3ca89c9b2 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 4 Dec 2020 13:14:57 +0100 Subject: [PATCH 128/275] Allow horizontal prediction with palette and deflate --- .../Formats/Tiff/TiffEncoderCore.cs | 4 +-- .../Formats/Tiff/Utils/TiffWriter.cs | 28 ++++++++++++++----- .../Formats/Tiff/TiffEncoderTests.cs | 17 +++++++++++ 3 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index c5283c74b..d64ac2e7d 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -165,7 +165,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff switch (this.Mode) { case TiffEncodingMode.ColorPalette: - imageDataBytes = writer.WritePalettedRgb(image, this.quantizer, this.padding, this.CompressionType, out colorMap); + imageDataBytes = writer.WritePalettedRgb(image, this.quantizer, this.padding, this.CompressionType, this.useHorizontalPredictor, out colorMap); break; case TiffEncodingMode.Gray: imageDataBytes = writer.WriteGray(image, this.padding, this.CompressionType, this.useHorizontalPredictor); @@ -346,7 +346,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff if (this.useHorizontalPredictor) { - if (this.Mode == TiffEncodingMode.Rgb || this.Mode == TiffEncodingMode.Gray) + if (this.Mode == TiffEncodingMode.Rgb || this.Mode == TiffEncodingMode.Gray || this.Mode == TiffEncodingMode.ColorPalette) { var predictor = new ExifShort(ExifTagValue.Predictor) { Value = (ushort)TiffPredictor.Horizontal }; diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs index d427e9c66..53f763a02 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -217,7 +217,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// The pixel data. /// The image to write to the stream. /// A Span for a pixel row. - /// Indicates if horizontal prediction should be used. Should only be used with deflate compression. + /// Indicates if horizontal prediction should be used. /// The number of bytes written. private int WriteLzwCompressedRgb(Image image, Span rowSpan, bool useHorizontalPredictor) where TPixel : unmanaged, IPixel @@ -286,9 +286,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// The quantizer to use. /// The padding bytes for each row. /// The compression to use. + /// Indicates if horizontal prediction should be used. Should only be used in combination with deflate or LZW compression. /// The color map. /// The number of bytes written. - public int WritePalettedRgb(Image image, IQuantizer quantizer, int padding, TiffEncoderCompression compression, out IExifValue colorMap) + public int WritePalettedRgb(Image image, IQuantizer quantizer, int padding, TiffEncoderCompression compression, bool useHorizontalPredictor, out IExifValue colorMap) where TPixel : unmanaged, IPixel { int colorsPerChannel = 256; @@ -340,7 +341,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils if (compression == TiffEncoderCompression.Deflate) { - return this.WriteDeflateCompressedPalettedRgb(image, quantized, padding); + return this.WriteDeflateCompressedPalettedRgb(image, quantized, padding, useHorizontalPredictor); } if (compression == TiffEncoderCompression.PackBits) @@ -373,18 +374,31 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// The image to write to the stream. /// The quantized frame. /// The padding bytes for each row. + /// Indicates if horizontal prediction should be used. /// The number of bytes written. - public int WriteDeflateCompressedPalettedRgb(Image image, IndexedImageFrame quantized, int padding) + public int WriteDeflateCompressedPalettedRgb(Image image, IndexedImageFrame quantized, int padding, bool useHorizontalPredictor) where TPixel : unmanaged, IPixel { + using IManagedByteBuffer tmpBuffer = this.memoryAllocator.AllocateManagedByteBuffer(image.Width); using var memoryStream = new MemoryStream(); using var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, PngCompressionLevel.Level6); // TODO: make compression level configurable int bytesWritten = 0; for (int y = 0; y < image.Height; y++) { - ReadOnlySpan pixelSpan = quantized.GetPixelRowSpan(y); - deflateStream.Write(pixelSpan); + ReadOnlySpan pixelRow = quantized.GetPixelRowSpan(y); + if (useHorizontalPredictor) + { + // We need a writable Span here. + Span pixelRowCopy = tmpBuffer.GetSpan(); + pixelRow.CopyTo(pixelRowCopy); + HorizontalPredictor.ApplyHorizontalPrediction8Bit(pixelRowCopy); + deflateStream.Write(pixelRowCopy); + } + else + { + deflateStream.Write(pixelRow); + } for (int i = 0; i < padding; i++) { @@ -423,7 +437,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils { ReadOnlySpan pixelSpan = quantized.GetPixelRowSpan(y); - int size = 0; + int size; if (padding != 0) { pixelSpan.CopyTo(pixelRowWithPaddingSpan); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 2f0d75388..aa5a84d83 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -143,6 +143,23 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff TiffTestUtils.CompareWithReferenceDecoder(encodedImagePath, encodedImage); } + [Theory] + [WithFile(TestImages.Tiff.Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeColorPalette_WithDeflateCompressionAndPredictor_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(); + using var memStream = new MemoryStream(); + var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Deflate, UseHorizontalPredictor = true}; + + image.Save(memStream, encoder); + memStream.Position = 0; + + using var encodedImage = (Image)Image.Load(memStream); + var encodedImagePath = provider.Utility.SaveTestOutputFile(encodedImage, "tiff", encoder); + TiffTestUtils.CompareWithReferenceDecoder(encodedImagePath, encodedImage); + } + [Theory] [WithFile(TestImages.Tiff.Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_WithPackBitsCompression_Works(TestImageProvider provider) From f6f673e742c504af1555552eeeda121fe6e9e91f Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 4 Dec 2020 16:27:29 +0100 Subject: [PATCH 129/275] Add option to use lzw with paletted image --- .../Formats/Tiff/TiffEncoderCore.cs | 5 ++ .../Formats/Tiff/Utils/TiffWriter.cs | 51 +++++++++++++++ .../Formats/Tiff/TiffEncoderTests.cs | 62 ++++++++++--------- 3 files changed, 90 insertions(+), 28 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index d64ac2e7d..93deca380 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -463,6 +463,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff return (ushort)TiffCompression.Deflate; } + if (this.CompressionType == TiffEncoderCompression.Lzw && this.Mode == TiffEncodingMode.ColorPalette) + { + return (ushort)TiffCompression.Lzw; + } + if (this.CompressionType == TiffEncoderCompression.PackBits && this.Mode == TiffEncodingMode.ColorPalette) { return (ushort)TiffCompression.PackBits; diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs index 53f763a02..db8bad133 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -344,6 +344,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils return this.WriteDeflateCompressedPalettedRgb(image, quantized, padding, useHorizontalPredictor); } + if (compression == TiffEncoderCompression.Lzw) + { + return this.WriteLzwCompressedPalettedRgb(image, quantized, padding, useHorizontalPredictor); + } + if (compression == TiffEncoderCompression.PackBits) { return this.WritePackBitsCompressedPalettedRgb(image, quantized, padding); @@ -414,6 +419,52 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils return bytesWritten; } + /// + /// Writes the image data as indices into a color map compressed with lzw compression to the stream. + /// + /// The pixel data. + /// The image to write to the stream. + /// The quantized frame. + /// The padding bytes for each row. + /// Indicates if horizontal prediction should be used. + /// The number of bytes written. + public int WriteLzwCompressedPalettedRgb(Image image, IndexedImageFrame quantized, int padding, bool useHorizontalPredictor) + where TPixel : unmanaged, IPixel + { + IMemoryOwner pixelData = this.memoryAllocator.Allocate(image.Width * image.Height); + using IManagedByteBuffer tmpBuffer = this.memoryAllocator.AllocateManagedByteBuffer(image.Width); + using var memoryStream = new MemoryStream(); + + int bytesWritten = 0; + Span pixels = pixelData.GetSpan(); + for (int y = 0; y < image.Height; y++) + { + ReadOnlySpan indexedPixelRow = quantized.GetPixelRowSpan(y); + + if (useHorizontalPredictor) + { + // We need a writable Span here. + Span pixelRowCopy = tmpBuffer.GetSpan(); + indexedPixelRow.CopyTo(pixelRowCopy); + HorizontalPredictor.ApplyHorizontalPrediction8Bit(pixelRowCopy); + pixelRowCopy.CopyTo(pixels.Slice(y * image.Width)); + } + else + { + indexedPixelRow.CopyTo(pixels.Slice(y * image.Width)); + } + } + + using var lzwEncoder = new TiffLzwEncoder(this.memoryAllocator, pixelData, 8); + lzwEncoder.Encode(memoryStream); + + byte[] buffer = memoryStream.ToArray(); + this.output.Write(buffer); + bytesWritten += buffer.Length; + + return bytesWritten; + } + /// /// Writes the image data as indices into a color map compressed with deflate compression to the stream. /// diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index aa5a84d83..2e6ca6318 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -111,19 +111,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public void TiffEncoder_EncodeColorPalette_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel { - // Because a quantizer is used to create the palette (and therefore changes to the original are expected), - // we do not compare the encoded image against the original: - // Instead we load the encoded image with a reference decoder and compare against that image. - using Image image = provider.GetImage(); - using var memStream = new MemoryStream(); var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.None }; - image.Save(memStream, encoder); - memStream.Position = 0; - - using var encodedImage = (Image)Image.Load(memStream); - var encodedImagePath = provider.Utility.SaveTestOutputFile(encodedImage, "tiff", encoder); - TiffTestUtils.CompareWithReferenceDecoder(encodedImagePath, encodedImage); + TiffEncoderPaletteTest(provider, encoder); } [Theory] @@ -131,16 +121,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public void TiffEncoder_EncodeColorPalette_WithDeflateCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(); - using var memStream = new MemoryStream(); var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Deflate }; - image.Save(memStream, encoder); - memStream.Position = 0; - - using var encodedImage = (Image)Image.Load(memStream); - var encodedImagePath = provider.Utility.SaveTestOutputFile(encodedImage, "tiff", encoder); - TiffTestUtils.CompareWithReferenceDecoder(encodedImagePath, encodedImage); + TiffEncoderPaletteTest(provider, encoder); } [Theory] @@ -148,16 +131,29 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public void TiffEncoder_EncodeColorPalette_WithDeflateCompressionAndPredictor_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(); - using var memStream = new MemoryStream(); - var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Deflate, UseHorizontalPredictor = true}; + var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Deflate, UseHorizontalPredictor = true }; - image.Save(memStream, encoder); - memStream.Position = 0; + TiffEncoderPaletteTest(provider, encoder); + } - using var encodedImage = (Image)Image.Load(memStream); - var encodedImagePath = provider.Utility.SaveTestOutputFile(encodedImage, "tiff", encoder); - TiffTestUtils.CompareWithReferenceDecoder(encodedImagePath, encodedImage); + [Theory] + [WithFile(TestImages.Tiff.Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeColorPalette_WithLzwCompression_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Lzw }; + + TiffEncoderPaletteTest(provider, encoder); + } + + [Theory] + [WithFile(TestImages.Tiff.Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeColorPalette_WithLzwCompressionAndPredictor_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Lzw, UseHorizontalPredictor = true }; + + TiffEncoderPaletteTest(provider, encoder); } [Theory] @@ -165,9 +161,19 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public void TiffEncoder_EncodeColorPalette_WithPackBitsCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel { + var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.PackBits }; + + TiffEncoderPaletteTest(provider, encoder); + } + + private static void TiffEncoderPaletteTest(TestImageProvider provider, TiffEncoder encoder) + where TPixel : unmanaged, IPixel + { + // Because a quantizer is used to create the palette (and therefore changes to the original are expected), + // we do not compare the encoded image against the original: + // Instead we load the encoded image with a reference decoder and compare against that image. using Image image = provider.GetImage(); using var memStream = new MemoryStream(); - var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.PackBits }; image.Save(memStream, encoder); memStream.Position = 0; From cbb69113cd0a75b30a86fc59d4aedba091d50d6d Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 4 Dec 2020 19:15:01 +0100 Subject: [PATCH 130/275] Add compression to the tiff metadata --- .../Formats/Tiff/TiffDecoderCore.cs | 63 ++++++++------ .../Formats/Tiff/TiffDecoderHelpers.cs | 16 ---- src/ImageSharp/Formats/Tiff/TiffMetadata.cs | 5 ++ .../Formats/Bmp/BmpMetadataTests.cs | 4 +- .../Formats/Tiff/TiffEncoderTests.cs | 48 ++++++----- .../Formats/Tiff/TiffMetadataTests.cs | 86 ++++++++++++++++++- tests/ImageSharp.Tests/TestImages.cs | 3 + .../Input/Tiff/Calliphora_ccitt_fax4.tiff | 3 + tests/Images/Input/Tiff/b0350_lsb_to_msb.tiff | 3 + 9 files changed, 160 insertions(+), 71 deletions(-) create mode 100644 tests/Images/Input/Tiff/Calliphora_ccitt_fax4.tiff create mode 100644 tests/Images/Input/Tiff/b0350_lsb_to_msb.tiff diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 8a6ac48fe..e4962759d 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -82,7 +82,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff public TiffColorType ColorType { get; set; } /// - /// Gets or sets the compression implementation to use when decoding the image. + /// Gets or sets the compression used, when the image was encoded. /// public TiffDecoderCompressionType CompressionType { get; set; } @@ -122,8 +122,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff } this.metadata = framesMetadata.CreateMetadata(this.ignoreMetadata, tiffStream.ByteOrder); - this.tiffMetaData = this.metadata.GetTiffMetadata(); - this.SetBitsPerPixel(framesMetadata); + this.SetTiffFormatMetaData(framesMetadata, tiffStream.ByteOrder); // todo: tiff frames can have different sizes { @@ -143,30 +142,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff return image; } - private void SetBitsPerPixel(List framesMetadata) - { - TiffFrameMetadata firstMetaData = framesMetadata.First(); - ushort[] bitsPerSample = firstMetaData.BitsPerSample; - var bitsPerPixel = 0; - foreach (var bps in bitsPerSample) - { - bitsPerPixel += bps; - } - - if (bitsPerPixel == 24) - { - this.tiffMetaData.BitsPerPixel = TiffBitsPerPixel.Pixel24; - } - else if (bitsPerPixel == 8) - { - this.tiffMetaData.BitsPerPixel = TiffBitsPerPixel.Pixel8; - } - else if (bitsPerPixel == 1) - { - this.tiffMetaData.BitsPerPixel = TiffBitsPerPixel.Pixel1; - } - } - /// public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { @@ -182,7 +157,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff framesMetadata.Add(new TiffFrameMetadata() { Tags = ifd }); } - ImageMetadata metadata = framesMetadata.CreateMetadata(this.ignoreMetadata, tiffStream.ByteOrder); + this.SetTiffFormatMetaData(framesMetadata, tiffStream.ByteOrder); TiffFrameMetadata root = framesMetadata.First(); int bitsPerPixel = 0; @@ -194,6 +169,38 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff return new ImageInfo(new PixelTypeInfo(bitsPerPixel), (int)root.Width, (int)root.Height, metadata); } + private void SetTiffFormatMetaData(List framesMetadata, TiffByteOrder byteOrder) + { + this.metadata = framesMetadata.CreateMetadata(this.ignoreMetadata, byteOrder); + this.tiffMetaData = this.metadata.GetTiffMetadata(); + TiffFrameMetadata firstFrameMetaData = framesMetadata.First(); + this.SetBitsPerPixel(firstFrameMetaData); + this.tiffMetaData.Compression = firstFrameMetaData.Compression; + } + + private void SetBitsPerPixel(TiffFrameMetadata firstFrameMetaData) + { + ushort[] bitsPerSample = firstFrameMetaData.BitsPerSample; + var bitsPerPixel = 0; + foreach (var bps in bitsPerSample) + { + bitsPerPixel += bps; + } + + if (bitsPerPixel == 24) + { + this.tiffMetaData.BitsPerPixel = TiffBitsPerPixel.Pixel24; + } + else if (bitsPerPixel == 8) + { + this.tiffMetaData.BitsPerPixel = TiffBitsPerPixel.Pixel8; + } + else if (bitsPerPixel == 1) + { + this.tiffMetaData.BitsPerPixel = TiffBitsPerPixel.Pixel1; + } + } + private static TiffStream CreateStream(Stream stream) { TiffByteOrder byteOrder = ReadByteOrder(stream); diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs index b69c57093..c54a2cc90 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs @@ -300,22 +300,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff private static void ParsePhotometric(this TiffDecoderCore options, TiffFrameMetadata entries) { - /* - if (!entries.TryGetSingleNumber(ExifTag.PhotometricInterpretation, out uint photometricInterpretation)) - { - if (entries.Compression == TiffCompression.Ccitt1D) - { - photometricInterpretation = (uint)TiffPhotometricInterpretation.WhiteIsZero; - } - else - { - TiffThrowHelper.ThrowNotSupported("The TIFF photometric interpretation entry is missing."); - } - } - - options.PhotometricInterpretation = (TiffPhotometricInterpretation)photometricInterpretation; - /* */ - // There is no default for PhotometricInterpretation, and it is required. options.PhotometricInterpretation = entries.PhotometricInterpretation; } diff --git a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs index a3ee80fc3..cfb9fa8bb 100644 --- a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs @@ -38,6 +38,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// public TiffBitsPerPixel BitsPerPixel { get; set; } = TiffBitsPerPixel.Pixel24; + /// + /// Gets or sets the compression used to create the TIFF file. + /// + public TiffCompression Compression { get; set; } = TiffCompression.None; + /// /// Gets or sets the XMP profile. /// diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpMetadataTests.cs index b14956379..143eac4aa 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpMetadataTests.cs @@ -16,12 +16,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp [Fact] public void CloneIsDeep() { - var meta = new BmpMetadata { BitsPerPixel = BmpBitsPerPixel.Pixel24 }; + var meta = new BmpMetadata { BitsPerPixel = BmpBitsPerPixel.Pixel24, InfoHeaderType = BmpInfoHeaderType.Os2Version2 }; var clone = (BmpMetadata)meta.DeepClone(); clone.BitsPerPixel = BmpBitsPerPixel.Pixel32; + clone.InfoHeaderType = BmpInfoHeaderType.WinVersion2; Assert.False(meta.BitsPerPixel.Equals(clone.BitsPerPixel)); + Assert.False(meta.InfoHeaderType.Equals(clone.InfoHeaderType)); } [Theory] diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 2e6ca6318..86b4e32ac 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -11,6 +11,8 @@ using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; using Xunit; +using static SixLabors.ImageSharp.Tests.TestImages.Tiff; + namespace SixLabors.ImageSharp.Tests.Formats.Tiff { [Trait("Format", "Tiff")] @@ -47,67 +49,67 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff } [Theory] - [WithFile(TestImages.Tiff.Calliphora_RgbUncompressed, PixelTypes.Rgba32)] + [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeRgb_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.Rgb); [Theory] - [WithFile(TestImages.Tiff.Calliphora_RgbUncompressed, PixelTypes.Rgba32)] + [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeRgb_WithDeflateCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.Rgb, TiffEncoderCompression.Deflate); [Theory] - [WithFile(TestImages.Tiff.Calliphora_RgbUncompressed, PixelTypes.Rgba32)] + [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeRgb_WithDeflateCompressionAndPredictor_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.Rgb, TiffEncoderCompression.Deflate, usePredictor: true); [Theory] - [WithFile(TestImages.Tiff.Calliphora_RgbUncompressed, PixelTypes.Rgba32)] + [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeRgb_WithLzwCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.Rgb, TiffEncoderCompression.Lzw); [Theory] - [WithFile(TestImages.Tiff.Calliphora_RgbUncompressed, PixelTypes.Rgba32)] + [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeRgb_WithLzwCompressionAndPredictor_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.Rgb, TiffEncoderCompression.Lzw, usePredictor: true); [Theory] - [WithFile(TestImages.Tiff.Calliphora_RgbUncompressed, PixelTypes.Rgba32)] + [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeRgb_WithPackBitsCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.Rgb, TiffEncoderCompression.PackBits); [Theory] - [WithFile(TestImages.Tiff.Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] + [WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeGray_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.Gray); [Theory] - [WithFile(TestImages.Tiff.Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] + [WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeGray_WithDeflateCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.Gray, TiffEncoderCompression.Deflate); [Theory] - [WithFile(TestImages.Tiff.Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] + [WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeGray_WithDeflateCompressionAndPredictor_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.Gray, TiffEncoderCompression.Deflate, usePredictor: true); [Theory] - [WithFile(TestImages.Tiff.Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] + [WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeGray_WithLzwCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.Gray, TiffEncoderCompression.Lzw); [Theory] - [WithFile(TestImages.Tiff.Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] + [WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeGray_WithLzwCompressionAndPredictor_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.Gray, TiffEncoderCompression.Lzw, usePredictor: true); [Theory] - [WithFile(TestImages.Tiff.Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] + [WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeGray_WithPackBitsCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.Gray, TiffEncoderCompression.PackBits); [Theory] - [WithFile(TestImages.Tiff.Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] + [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel { @@ -117,7 +119,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff } [Theory] - [WithFile(TestImages.Tiff.Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] + [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_WithDeflateCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel { @@ -127,7 +129,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff } [Theory] - [WithFile(TestImages.Tiff.Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] + [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_WithDeflateCompressionAndPredictor_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel { @@ -137,7 +139,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff } [Theory] - [WithFile(TestImages.Tiff.Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] + [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_WithLzwCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel { @@ -147,7 +149,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff } [Theory] - [WithFile(TestImages.Tiff.Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] + [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_WithLzwCompressionAndPredictor_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel { @@ -157,7 +159,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff } [Theory] - [WithFile(TestImages.Tiff.Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] + [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_WithPackBitsCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel { @@ -184,27 +186,27 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff } [Theory] - [WithFile(TestImages.Tiff.Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] + [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeBiColor_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.BiColor); [Theory] - [WithFile(TestImages.Tiff.Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] + [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeBiColor_WithDeflateCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel1, TiffEncodingMode.BiColor, TiffEncoderCompression.Deflate); [Theory] - [WithFile(TestImages.Tiff.Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] + [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeBiColor_WithPackBitsCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel1, TiffEncodingMode.BiColor, TiffEncoderCompression.PackBits); [Theory] - [WithFile(TestImages.Tiff.Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] + [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeBiColor_WithCcittGroup3FaxCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel1, TiffEncodingMode.BiColor, TiffEncoderCompression.CcittGroup3Fax); [Theory] - [WithFile(TestImages.Tiff.Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] + [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeBiColor_WithModifiedHuffmanCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel1, TiffEncodingMode.BiColor, TiffEncoderCompression.ModifiedHuffman); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index e3b574abe..9616ce1cf 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System.IO; +using System.Linq; using SixLabors.ImageSharp.Formats.Experimental.Tiff; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Metadata; @@ -8,16 +10,94 @@ using SixLabors.ImageSharp.PixelFormats; using Xunit; +using static SixLabors.ImageSharp.Tests.TestImages.Tiff; + namespace SixLabors.ImageSharp.Tests.Formats.Tiff { [Trait("Format", "Tiff")] public class TiffMetadataTests { - public static readonly string[] MetadataImages = TestImages.Tiff.Metadata; + [Fact] + public void CloneIsDeep() + { + var meta = new TiffMetadata + { + Compression = TiffCompression.Deflate, + BitsPerPixel = TiffBitsPerPixel.Pixel8, + ByteOrder = TiffByteOrder.BigEndian, + XmpProfile = new byte[3] + }; + + var clone = (TiffMetadata)meta.DeepClone(); + + clone.Compression = TiffCompression.None; + clone.BitsPerPixel = TiffBitsPerPixel.Pixel24; + clone.ByteOrder = TiffByteOrder.LittleEndian; + clone.XmpProfile = new byte[1]; + + Assert.False(meta.Compression == clone.Compression); + Assert.False(meta.BitsPerPixel == clone.BitsPerPixel); + Assert.False(meta.ByteOrder == clone.ByteOrder); + Assert.False(meta.XmpProfile.SequenceEqual(clone.XmpProfile)); + } + + [Theory] + [InlineData(Calliphora_BiColorUncompressed, TiffBitsPerPixel.Pixel1)] + [InlineData(GrayscaleUncompressed, TiffBitsPerPixel.Pixel8)] + [InlineData(RgbUncompressed, TiffBitsPerPixel.Pixel24)] + public void Identify_DetectsCorrectBitPerPixel(string imagePath, TiffBitsPerPixel expectedBitsPerPixel) + { + var testFile = TestFile.Create(imagePath); + using var stream = new MemoryStream(testFile.Bytes, false); + + IImageInfo imageInfo = Image.Identify(stream); + + Assert.NotNull(imageInfo); + TiffMetadata tiffMetadata = imageInfo.Metadata.GetTiffMetadata(); + Assert.NotNull(tiffMetadata); + Assert.Equal(expectedBitsPerPixel, tiffMetadata.BitsPerPixel); + } + + [Theory] + [InlineData(GrayscaleUncompressed, TiffCompression.None)] + [InlineData(RgbDeflate, TiffCompression.Deflate)] + [InlineData(SmallRgbLzw, TiffCompression.Lzw)] + [InlineData(Calliphora_Fax3Compressed, TiffCompression.CcittGroup3Fax)] + [InlineData(Calliphora_Fax4Compressed, TiffCompression.CcittGroup4Fax)] + [InlineData(Calliphora_HuffmanCompressed, TiffCompression.Ccitt1D)] + [InlineData(Calliphora_RgbPackbits, TiffCompression.PackBits)] + public void Identify_DetectsCorrectCompression(string imagePath, TiffCompression expectedCompression) + { + var testFile = TestFile.Create(imagePath); + using var stream = new MemoryStream(testFile.Bytes, false); + + IImageInfo imageInfo = Image.Identify(stream); + + Assert.NotNull(imageInfo); + TiffMetadata tiffMetadata = imageInfo.Metadata.GetTiffMetadata(); + Assert.NotNull(tiffMetadata); + Assert.Equal(expectedCompression, tiffMetadata.Compression); + } + + [Theory] + [InlineData(GrayscaleUncompressed, TiffByteOrder.BigEndian)] + [InlineData(LsbToMsbByteOrder, TiffByteOrder.LittleEndian)] + public void Identify_DetectsCorrectByteOrder(string imagePath, TiffByteOrder expectedByteOrder) + { + var testFile = TestFile.Create(imagePath); + using var stream = new MemoryStream(testFile.Bytes, false); + + IImageInfo imageInfo = Image.Identify(stream); + + Assert.NotNull(imageInfo); + TiffMetadata tiffMetadata = imageInfo.Metadata.GetTiffMetadata(); + Assert.NotNull(tiffMetadata); + Assert.Equal(expectedByteOrder, tiffMetadata.ByteOrder); + } [Theory] - [WithFile(TestImages.Tiff.SampleMetadata, PixelTypes.Rgba32, false)] - [WithFile(TestImages.Tiff.SampleMetadata, PixelTypes.Rgba32, true)] + [WithFile(SampleMetadata, PixelTypes.Rgba32, false)] + [WithFile(SampleMetadata, PixelTypes.Rgba32, true)] public void MetadataProfiles(TestImageProvider provider, bool ignoreMetadata) where TPixel : unmanaged, IPixel { diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 93277550b..b2e312ddc 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -517,6 +517,7 @@ namespace SixLabors.ImageSharp.Tests public const string Calliphora_RgbPackbits = "Tiff/Calliphora_rgb_packbits.tiff"; public const string Calliphora_RgbUncompressed = "Tiff/Calliphora_rgb_uncompressed.tiff"; public const string Calliphora_Fax3Compressed = "Tiff/Calliphora_ccitt_fax3.tiff"; + public const string Calliphora_Fax4Compressed = "Tiff/Calliphora_ccitt_fax4.tiff"; public const string Calliphora_HuffmanCompressed = "Tiff/Calliphora_huffman_rle.tiff"; public const string Calliphora_BiColorUncompressed = "Tiff/Calliphora_bicolor_uncompressed.tiff"; @@ -559,6 +560,8 @@ namespace SixLabors.ImageSharp.Tests public const string MultiframeDifferentSize = "Tiff/multipage_differentSize.tiff"; public const string MultiframeDifferentVariants = "Tiff/multipage_differentVariants.tiff"; + public const string LsbToMsbByteOrder = "Tiff/b0350_lsb_to_msb.tiff"; + public const string SampleMetadata = "Tiff/metadata_sample.tiff"; public static readonly string[] Multiframes = { MultiframeDeflateWithPreview, MultiframeLzwPredictor /*, MultiFrameDifferentSize, MultiframeDifferentSizeTiled, MultiFrameDifferentVariants,*/ }; diff --git a/tests/Images/Input/Tiff/Calliphora_ccitt_fax4.tiff b/tests/Images/Input/Tiff/Calliphora_ccitt_fax4.tiff new file mode 100644 index 000000000..384d00eaa --- /dev/null +++ b/tests/Images/Input/Tiff/Calliphora_ccitt_fax4.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7a2c95aec08b96bca30af344f7d9952a603a951802ce534a5f2c5f563795cbd2 +size 117704 diff --git a/tests/Images/Input/Tiff/b0350_lsb_to_msb.tiff b/tests/Images/Input/Tiff/b0350_lsb_to_msb.tiff new file mode 100644 index 000000000..3b7ee6ac3 --- /dev/null +++ b/tests/Images/Input/Tiff/b0350_lsb_to_msb.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:37c6a28f460d8781fdc3bcf0cc9bd23f633b03899563546bfc6234a8478f67f0 +size 68637 From 998d62930c16862986bc52628e18dafec02db696 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 4 Dec 2020 19:35:01 +0100 Subject: [PATCH 131/275] Add tiff encoder option to choose the deflate compression level --- ...ionLevel.cs => DeflateCompressionLevel.cs} | 2 +- .../Formats/Png/IPngEncoderOptions.cs | 4 +- src/ImageSharp/Formats/Png/PngEncoder.cs | 2 +- .../Formats/Png/PngEncoderOptions.cs | 2 +- .../Formats/Png/Zlib/ZlibDeflateStream.cs | 2 +- .../Formats/Tiff/ITiffEncoderOptions.cs | 7 +++ src/ImageSharp/Formats/Tiff/TiffEncoder.cs | 22 ++++------ .../Formats/Tiff/TiffEncoderCore.cs | 17 ++++--- .../Formats/Tiff/Utils/TiffWriter.cs | 44 ++++++++++--------- .../Formats/Png/PngEncoderTests.cs | 28 ++++++------ .../DeflateTiffCompressionTests.cs | 2 +- .../Image/ImageTests.SaveAsync.cs | 2 +- 12 files changed, 74 insertions(+), 60 deletions(-) rename src/ImageSharp/Formats/Png/{PngCompressionLevel.cs => DeflateCompressionLevel.cs} (97%) diff --git a/src/ImageSharp/Formats/Png/PngCompressionLevel.cs b/src/ImageSharp/Formats/Png/DeflateCompressionLevel.cs similarity index 97% rename from src/ImageSharp/Formats/Png/PngCompressionLevel.cs rename to src/ImageSharp/Formats/Png/DeflateCompressionLevel.cs index 7516e0987..9421bb19b 100644 --- a/src/ImageSharp/Formats/Png/PngCompressionLevel.cs +++ b/src/ImageSharp/Formats/Png/DeflateCompressionLevel.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// Provides enumeration of available PNG compression levels. /// - public enum PngCompressionLevel + public enum DeflateCompressionLevel { /// /// Level 0. Equivalent to . diff --git a/src/ImageSharp/Formats/Png/IPngEncoderOptions.cs b/src/ImageSharp/Formats/Png/IPngEncoderOptions.cs index 2c05019ed..3a453d916 100644 --- a/src/ImageSharp/Formats/Png/IPngEncoderOptions.cs +++ b/src/ImageSharp/Formats/Png/IPngEncoderOptions.cs @@ -28,9 +28,9 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// Gets the compression level 1-9. - /// Defaults to . + /// Defaults to . /// - PngCompressionLevel CompressionLevel { get; } + DeflateCompressionLevel CompressionLevel { get; } /// /// Gets the threshold of characters in text metadata, when compression should be used. diff --git a/src/ImageSharp/Formats/Png/PngEncoder.cs b/src/ImageSharp/Formats/Png/PngEncoder.cs index e72e8d3d5..b6cac806d 100644 --- a/src/ImageSharp/Formats/Png/PngEncoder.cs +++ b/src/ImageSharp/Formats/Png/PngEncoder.cs @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Formats.Png public PngFilterMethod? FilterMethod { get; set; } /// - public PngCompressionLevel CompressionLevel { get; set; } = PngCompressionLevel.DefaultCompression; + public DeflateCompressionLevel CompressionLevel { get; set; } = DeflateCompressionLevel.DefaultCompression; /// public int TextCompressionThreshold { get; set; } = 1024; diff --git a/src/ImageSharp/Formats/Png/PngEncoderOptions.cs b/src/ImageSharp/Formats/Png/PngEncoderOptions.cs index 3c17c2463..2b8a6b192 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderOptions.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderOptions.cs @@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Formats.Png public PngFilterMethod? FilterMethod { get; } /// - public PngCompressionLevel CompressionLevel { get; } = PngCompressionLevel.DefaultCompression; + public DeflateCompressionLevel CompressionLevel { get; } = DeflateCompressionLevel.DefaultCompression; /// public int TextCompressionThreshold { get; } diff --git a/src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs b/src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs index 06c6e3dea..5ebbdf1f1 100644 --- a/src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs +++ b/src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs @@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib /// The memory allocator to use for buffer allocations. /// The stream to compress. /// The compression level. - public ZlibDeflateStream(MemoryAllocator memoryAllocator, Stream stream, PngCompressionLevel level) + public ZlibDeflateStream(MemoryAllocator memoryAllocator, Stream stream, DeflateCompressionLevel level) { int compressionLevel = (int)level; this.rawStream = stream; diff --git a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs index 78cf553d3..c3df04186 100644 --- a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs +++ b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Processing.Processors.Quantization; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff @@ -15,6 +16,12 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// TiffEncoderCompression Compression { get; } + /// + /// Gets the compression level 1-9 for the deflate compression mode. + /// Defaults to . + /// + DeflateCompressionLevel CompressionLevel { get; } + /// /// Gets the encoding mode to use. RGB, RGB with color palette or gray. /// diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs index 0d1821704..479ff470b 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs @@ -4,7 +4,9 @@ using System.IO; using System.Threading; using System.Threading.Tasks; + using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Quantization; @@ -15,25 +17,19 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// public class TiffEncoder : IImageEncoder, ITiffEncoderOptions { - /// - /// Gets or sets a value indicating which compression to use. - /// + /// public TiffEncoderCompression Compression { get; set; } = TiffEncoderCompression.None; - /// - /// Gets or sets the encoding mode to use. RGB, RGB with a color palette or gray. - /// + /// + public DeflateCompressionLevel CompressionLevel { get; } = DeflateCompressionLevel.DefaultCompression; + + /// public TiffEncodingMode Mode { get; set; } - /// - /// Gets or sets a value indicating whether to use horizontal prediction. This can improve the compression ratio with deflate or lzw compression. - /// + /// public bool UseHorizontalPredictor { get; set; } - /// - /// Gets or sets the quantizer for color images with a palette. - /// Defaults to OctreeQuantizer. - /// + /// public IQuantizer Quantizer { get; set; } /// diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 93deca380..277b833a6 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -9,6 +9,7 @@ using System.Threading; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; +using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; @@ -51,7 +52,12 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// /// Indicating whether to use horizontal prediction. This can improve the compression ratio with deflate compression. /// - private bool useHorizontalPredictor; + private readonly bool useHorizontalPredictor; + + /// + /// Sets the deflate compression level. + /// + private readonly DeflateCompressionLevel compressionLevel; /// /// Initializes a new instance of the class. @@ -65,6 +71,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff this.Mode = options.Mode; this.quantizer = options.Quantizer ?? KnownQuantizers.Octree; this.useHorizontalPredictor = options.UseHorizontalPredictor; + this.compressionLevel = options.CompressionLevel; } /// @@ -165,16 +172,16 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff switch (this.Mode) { case TiffEncodingMode.ColorPalette: - imageDataBytes = writer.WritePalettedRgb(image, this.quantizer, this.padding, this.CompressionType, this.useHorizontalPredictor, out colorMap); + imageDataBytes = writer.WritePalettedRgb(image, this.quantizer, this.padding, this.CompressionType, this.compressionLevel, this.useHorizontalPredictor, out colorMap); break; case TiffEncodingMode.Gray: - imageDataBytes = writer.WriteGray(image, this.padding, this.CompressionType, this.useHorizontalPredictor); + imageDataBytes = writer.WriteGray(image, this.padding, this.CompressionType, this.compressionLevel, this.useHorizontalPredictor); break; case TiffEncodingMode.BiColor: - imageDataBytes = writer.WriteBiColor(image, this.CompressionType); + imageDataBytes = writer.WriteBiColor(image, this.CompressionType, this.compressionLevel); break; default: - imageDataBytes = writer.WriteRgb(image, this.padding, this.CompressionType, this.useHorizontalPredictor); + imageDataBytes = writer.WriteRgb(image, this.padding, this.CompressionType, this.compressionLevel, this.useHorizontalPredictor); break; } diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs index db8bad133..d91de83e6 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -138,16 +138,17 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// The image to write to the stream. /// The padding bytes for each row. /// The compression to use. + /// The compression level for deflate compression. /// Indicates if horizontal prediction should be used. Should only be used with deflate compression. /// The number of bytes written. - public int WriteRgb(Image image, int padding, TiffEncoderCompression compression, bool useHorizontalPredictor) + public int WriteRgb(Image image, int padding, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) where TPixel : unmanaged, IPixel { using IManagedByteBuffer row = this.AllocateRow(image.Width, 3, padding); Span rowSpan = row.GetSpan(); if (compression == TiffEncoderCompression.Deflate) { - return this.WriteDeflateCompressedRgb(image, rowSpan, useHorizontalPredictor); + return this.WriteDeflateCompressedRgb(image, rowSpan, compressionLevel, useHorizontalPredictor); } if (compression == TiffEncoderCompression.Lzw) @@ -179,16 +180,15 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// The pixel data. /// The image to write to the stream. /// A Span for a pixel row. + /// The compression level for deflate compression. /// Indicates if horizontal prediction should be used. Should only be used with deflate compression. /// The number of bytes written. - private int WriteDeflateCompressedRgb(Image image, Span rowSpan, bool useHorizontalPredictor) + private int WriteDeflateCompressedRgb(Image image, Span rowSpan, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) where TPixel : unmanaged, IPixel { int bytesWritten = 0; using var memoryStream = new MemoryStream(); - - // TODO: move zlib compression from png to a common place? - using var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, PngCompressionLevel.Level6); // TODO: make compression level configurable + using var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, compressionLevel); // TODO: move zlib compression from png to a common place? for (int y = 0; y < image.Height; y++) { @@ -286,10 +286,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// The quantizer to use. /// The padding bytes for each row. /// The compression to use. + /// The compression level for deflate compression. /// Indicates if horizontal prediction should be used. Should only be used in combination with deflate or LZW compression. /// The color map. /// The number of bytes written. - public int WritePalettedRgb(Image image, IQuantizer quantizer, int padding, TiffEncoderCompression compression, bool useHorizontalPredictor, out IExifValue colorMap) + public int WritePalettedRgb(Image image, IQuantizer quantizer, int padding, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor, out IExifValue colorMap) where TPixel : unmanaged, IPixel { int colorsPerChannel = 256; @@ -341,7 +342,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils if (compression == TiffEncoderCompression.Deflate) { - return this.WriteDeflateCompressedPalettedRgb(image, quantized, padding, useHorizontalPredictor); + return this.WriteDeflateCompressedPalettedRgb(image, quantized, padding, compressionLevel, useHorizontalPredictor); } if (compression == TiffEncoderCompression.Lzw) @@ -379,14 +380,15 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// The image to write to the stream. /// The quantized frame. /// The padding bytes for each row. + /// The compression level for deflate compression. /// Indicates if horizontal prediction should be used. /// The number of bytes written. - public int WriteDeflateCompressedPalettedRgb(Image image, IndexedImageFrame quantized, int padding, bool useHorizontalPredictor) + public int WriteDeflateCompressedPalettedRgb(Image image, IndexedImageFrame quantized, int padding, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) where TPixel : unmanaged, IPixel { using IManagedByteBuffer tmpBuffer = this.memoryAllocator.AllocateManagedByteBuffer(image.Width); using var memoryStream = new MemoryStream(); - using var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, PngCompressionLevel.Level6); // TODO: make compression level configurable + using var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, compressionLevel); int bytesWritten = 0; for (int y = 0; y < image.Height; y++) @@ -513,9 +515,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// The image to write to the stream. /// The padding bytes for each row. /// The compression to use. + /// The compression level for deflate compression. /// Indicates if horizontal prediction should be used. Should only be used with deflate or lzw compression. /// The number of bytes written. - public int WriteGray(Image image, int padding, TiffEncoderCompression compression, bool useHorizontalPredictor) + public int WriteGray(Image image, int padding, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) where TPixel : unmanaged, IPixel { using IManagedByteBuffer row = this.AllocateRow(image.Width, 1, padding); @@ -523,7 +526,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils if (compression == TiffEncoderCompression.Deflate) { - return this.WriteGrayDeflateCompressed(image, rowSpan, useHorizontalPredictor); + return this.WriteGrayDeflateCompressed(image, rowSpan, compressionLevel, useHorizontalPredictor); } if (compression == TiffEncoderCompression.Lzw) @@ -553,16 +556,15 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// /// The image to write to the stream. /// A span of a row of pixels. + /// The compression level for deflate compression. /// Indicates if horizontal prediction should be used. /// The number of bytes written. - private int WriteGrayDeflateCompressed(Image image, Span rowSpan, bool useHorizontalPredictor) + private int WriteGrayDeflateCompressed(Image image, Span rowSpan, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) where TPixel : unmanaged, IPixel { int bytesWritten = 0; using var memoryStream = new MemoryStream(); - - // TODO: move zlib compression from png to a common place? - using var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, PngCompressionLevel.Level6); // TODO: make compression level configurable + using var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, compressionLevel); // TODO: move zlib compression from png to a common place? for (int y = 0; y < image.Height; y++) { @@ -656,8 +658,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// The pixel data. /// The image to write to the stream. /// The compression to use. + /// The compression level for deflate compression. /// The number of bytes written. - public int WriteBiColor(Image image, TiffEncoderCompression compression) + public int WriteBiColor(Image image, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel) where TPixel : unmanaged, IPixel { int padding = image.Width % 8 == 0 ? 0 : 1; @@ -674,7 +677,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils if (compression == TiffEncoderCompression.Deflate) { - return this.WriteBiColorDeflate(imageBlackWhite, pixelRowAsGraySpan, outputRow); + return this.WriteBiColorDeflate(imageBlackWhite, pixelRowAsGraySpan, outputRow, compressionLevel); } if (compression == TiffEncoderCompression.PackBits) @@ -734,12 +737,13 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// The image to write to the stream. /// A span for converting a pixel row to gray. /// A span which will be used to store the output pixels. + /// The compression level for deflate compression. /// The number of bytes written. - public int WriteBiColorDeflate(Image image, Span pixelRowAsGraySpan, Span outputRow) + public int WriteBiColorDeflate(Image image, Span pixelRowAsGraySpan, Span outputRow, DeflateCompressionLevel compressionLevel) where TPixel : unmanaged, IPixel { using var memoryStream = new MemoryStream(); - using var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, PngCompressionLevel.Level6); // TODO: make compression level configurable + using var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, compressionLevel); int bytesWritten = 0; for (int y = 0; y < image.Height; y++) diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index 11bab17fb..5e062a649 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -62,19 +62,19 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png /// /// All types except Palette /// - public static readonly TheoryData CompressionLevels - = new TheoryData + public static readonly TheoryData CompressionLevels + = new TheoryData { - PngCompressionLevel.Level0, - PngCompressionLevel.Level1, - PngCompressionLevel.Level2, - PngCompressionLevel.Level3, - PngCompressionLevel.Level4, - PngCompressionLevel.Level5, - PngCompressionLevel.Level6, - PngCompressionLevel.Level7, - PngCompressionLevel.Level8, - PngCompressionLevel.Level9, + DeflateCompressionLevel.Level0, + DeflateCompressionLevel.Level1, + DeflateCompressionLevel.Level2, + DeflateCompressionLevel.Level3, + DeflateCompressionLevel.Level4, + DeflateCompressionLevel.Level5, + DeflateCompressionLevel.Level6, + DeflateCompressionLevel.Level7, + DeflateCompressionLevel.Level8, + DeflateCompressionLevel.Level9, }; public static readonly TheoryData PaletteSizes = new TheoryData @@ -157,7 +157,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png [Theory] [WithTestPatternImages(nameof(CompressionLevels), 24, 24, PixelTypes.Rgba32)] - public void WorksWithAllCompressionLevels(TestImageProvider provider, PngCompressionLevel compressionLevel) + public void WorksWithAllCompressionLevels(TestImageProvider provider, DeflateCompressionLevel compressionLevel) where TPixel : unmanaged, IPixel { foreach (PngInterlaceMode interlaceMode in InterlaceMode) @@ -573,7 +573,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png PngFilterMethod pngFilterMethod, PngBitDepth bitDepth, PngInterlaceMode interlaceMode, - PngCompressionLevel compressionLevel = PngCompressionLevel.DefaultCompression, + DeflateCompressionLevel compressionLevel = DeflateCompressionLevel.DefaultCompression, int paletteSize = 255, bool appendPngColorType = false, bool appendPngFilterMethod = false, diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs index 692f92c9f..441328dfe 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs @@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression Stream compressedStream = new MemoryStream(); using (Stream uncompressedStream = new MemoryStream(data), - deflateStream = new ZlibDeflateStream(Configuration.Default.MemoryAllocator, compressedStream, ImageSharp.Formats.Png.PngCompressionLevel.Level6)) + deflateStream = new ZlibDeflateStream(Configuration.Default.MemoryAllocator, compressedStream, ImageSharp.Formats.Png.DeflateCompressionLevel.Level6)) { uncompressedStream.CopyTo(deflateStream); } diff --git a/tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs b/tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs index 4e6b002d0..79bfdc054 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs @@ -138,7 +138,7 @@ namespace SixLabors.ImageSharp.Tests public async Task SaveAsync_WithNonSeekableStream_IsCancellable() { using var image = new Image(4000, 4000); - var encoder = new PngEncoder() { CompressionLevel = PngCompressionLevel.BestCompression }; + var encoder = new PngEncoder() { CompressionLevel = DeflateCompressionLevel.BestCompression }; using var stream = new MemoryStream(); var asyncStream = new AsyncStreamWrapper(stream, () => false); var cts = new CancellationTokenSource(); From 25255072f1adec5f7d1f06679a48d500e465cbd6 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 5 Dec 2020 18:05:07 +0100 Subject: [PATCH 132/275] This reverts changes from commit 20fcf84311246ef08f98fa9b554b9eeb26004b01, affecting the png encoder Renamed DeflateCompressionLevel back to PngCompressionLevel --- .../Formats/Png/IPngEncoderOptions.cs | 4 +-- ...ressionLevel.cs => PngCompressionLevel.cs} | 2 +- src/ImageSharp/Formats/Png/PngEncoder.cs | 2 +- .../Formats/Png/PngEncoderOptions.cs | 2 +- .../Formats/Png/Zlib/ZlibDeflateStream.cs | 13 ++++++++- .../Formats/Png/PngEncoderTests.cs | 28 +++++++++---------- .../Image/ImageTests.SaveAsync.cs | 2 +- 7 files changed, 32 insertions(+), 21 deletions(-) rename src/ImageSharp/Formats/Png/{DeflateCompressionLevel.cs => PngCompressionLevel.cs} (97%) diff --git a/src/ImageSharp/Formats/Png/IPngEncoderOptions.cs b/src/ImageSharp/Formats/Png/IPngEncoderOptions.cs index 3a453d916..2c05019ed 100644 --- a/src/ImageSharp/Formats/Png/IPngEncoderOptions.cs +++ b/src/ImageSharp/Formats/Png/IPngEncoderOptions.cs @@ -28,9 +28,9 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// Gets the compression level 1-9. - /// Defaults to . + /// Defaults to . /// - DeflateCompressionLevel CompressionLevel { get; } + PngCompressionLevel CompressionLevel { get; } /// /// Gets the threshold of characters in text metadata, when compression should be used. diff --git a/src/ImageSharp/Formats/Png/DeflateCompressionLevel.cs b/src/ImageSharp/Formats/Png/PngCompressionLevel.cs similarity index 97% rename from src/ImageSharp/Formats/Png/DeflateCompressionLevel.cs rename to src/ImageSharp/Formats/Png/PngCompressionLevel.cs index 9421bb19b..7516e0987 100644 --- a/src/ImageSharp/Formats/Png/DeflateCompressionLevel.cs +++ b/src/ImageSharp/Formats/Png/PngCompressionLevel.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// Provides enumeration of available PNG compression levels. /// - public enum DeflateCompressionLevel + public enum PngCompressionLevel { /// /// Level 0. Equivalent to . diff --git a/src/ImageSharp/Formats/Png/PngEncoder.cs b/src/ImageSharp/Formats/Png/PngEncoder.cs index b6cac806d..e72e8d3d5 100644 --- a/src/ImageSharp/Formats/Png/PngEncoder.cs +++ b/src/ImageSharp/Formats/Png/PngEncoder.cs @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Formats.Png public PngFilterMethod? FilterMethod { get; set; } /// - public DeflateCompressionLevel CompressionLevel { get; set; } = DeflateCompressionLevel.DefaultCompression; + public PngCompressionLevel CompressionLevel { get; set; } = PngCompressionLevel.DefaultCompression; /// public int TextCompressionThreshold { get; set; } = 1024; diff --git a/src/ImageSharp/Formats/Png/PngEncoderOptions.cs b/src/ImageSharp/Formats/Png/PngEncoderOptions.cs index 2b8a6b192..3c17c2463 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderOptions.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderOptions.cs @@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Formats.Png public PngFilterMethod? FilterMethod { get; } /// - public DeflateCompressionLevel CompressionLevel { get; } = DeflateCompressionLevel.DefaultCompression; + public PngCompressionLevel CompressionLevel { get; } = PngCompressionLevel.DefaultCompression; /// public int TextCompressionThreshold { get; } diff --git a/src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs b/src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs index 5ebbdf1f1..89280ee44 100644 --- a/src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs +++ b/src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs @@ -4,6 +4,7 @@ using System; using System.IO; using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Compression; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Png.Zlib @@ -39,7 +40,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib /// /// The stream responsible for compressing the input stream. /// - // private DeflateStream deflateStream; private DeflaterOutputStream deflateStream; /// @@ -49,6 +49,17 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib /// The stream to compress. /// The compression level. public ZlibDeflateStream(MemoryAllocator memoryAllocator, Stream stream, DeflateCompressionLevel level) + : this(memoryAllocator, stream, (PngCompressionLevel)level) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The memory allocator to use for buffer allocations. + /// The stream to compress. + /// The compression level. + public ZlibDeflateStream(MemoryAllocator memoryAllocator, Stream stream, PngCompressionLevel level) { int compressionLevel = (int)level; this.rawStream = stream; diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index 18b8c32c8..58d733c4f 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -63,19 +63,19 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png /// /// All types except Palette /// - public static readonly TheoryData CompressionLevels - = new TheoryData + public static readonly TheoryData CompressionLevels + = new TheoryData { - DeflateCompressionLevel.Level0, - DeflateCompressionLevel.Level1, - DeflateCompressionLevel.Level2, - DeflateCompressionLevel.Level3, - DeflateCompressionLevel.Level4, - DeflateCompressionLevel.Level5, - DeflateCompressionLevel.Level6, - DeflateCompressionLevel.Level7, - DeflateCompressionLevel.Level8, - DeflateCompressionLevel.Level9, + PngCompressionLevel.Level0, + PngCompressionLevel.Level1, + PngCompressionLevel.Level2, + PngCompressionLevel.Level3, + PngCompressionLevel.Level4, + PngCompressionLevel.Level5, + PngCompressionLevel.Level6, + PngCompressionLevel.Level7, + PngCompressionLevel.Level8, + PngCompressionLevel.Level9, }; public static readonly TheoryData PaletteSizes = new TheoryData @@ -158,7 +158,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png [Theory] [WithTestPatternImages(nameof(CompressionLevels), 24, 24, PixelTypes.Rgba32)] - public void WorksWithAllCompressionLevels(TestImageProvider provider, DeflateCompressionLevel compressionLevel) + public void WorksWithAllCompressionLevels(TestImageProvider provider, PngCompressionLevel compressionLevel) where TPixel : unmanaged, IPixel { foreach (PngInterlaceMode interlaceMode in InterlaceMode) @@ -574,7 +574,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png PngFilterMethod pngFilterMethod, PngBitDepth bitDepth, PngInterlaceMode interlaceMode, - DeflateCompressionLevel compressionLevel = DeflateCompressionLevel.DefaultCompression, + PngCompressionLevel compressionLevel = PngCompressionLevel.DefaultCompression, int paletteSize = 255, bool appendPngColorType = false, bool appendPngFilterMethod = false, diff --git a/tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs b/tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs index 79bfdc054..4e6b002d0 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs @@ -138,7 +138,7 @@ namespace SixLabors.ImageSharp.Tests public async Task SaveAsync_WithNonSeekableStream_IsCancellable() { using var image = new Image(4000, 4000); - var encoder = new PngEncoder() { CompressionLevel = DeflateCompressionLevel.BestCompression }; + var encoder = new PngEncoder() { CompressionLevel = PngCompressionLevel.BestCompression }; using var stream = new MemoryStream(); var asyncStream = new AsyncStreamWrapper(stream, () => false); var cts = new CancellationTokenSource(); From c09d1f797860090c37689ffdf77af94b2f578246 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 5 Dec 2020 18:07:58 +0100 Subject: [PATCH 133/275] Duplicate PngCompressionLevel as DeflateCompressionLevel, mark PngCompressionLevel as EditorBrowsableState.Never --- .../Compression/DeflateCompressionLevel.cs | 81 +++++++++++++++++++ .../Formats/Png/PngCompressionLevel.cs | 3 + .../Formats/Tiff/ITiffEncoderOptions.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffEncoder.cs | 2 +- .../Formats/Tiff/TiffEncoderCore.cs | 2 +- .../Formats/Tiff/Utils/TiffWriter.cs | 4 +- .../DeflateTiffCompressionTests.cs | 3 +- 7 files changed, 91 insertions(+), 6 deletions(-) create mode 100644 src/ImageSharp/Compression/DeflateCompressionLevel.cs diff --git a/src/ImageSharp/Compression/DeflateCompressionLevel.cs b/src/ImageSharp/Compression/DeflateCompressionLevel.cs new file mode 100644 index 000000000..9656bf4cc --- /dev/null +++ b/src/ImageSharp/Compression/DeflateCompressionLevel.cs @@ -0,0 +1,81 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Compression +{ + /// + /// Provides enumeration of available deflate compression levels. + /// + public enum DeflateCompressionLevel + { + /// + /// Level 0. Equivalent to . + /// + Level0 = 0, + + /// + /// No compression. Equivalent to . + /// + NoCompression = Level0, + + /// + /// Level 1. Equivalent to . + /// + Level1 = 1, + + /// + /// Best speed compression level. + /// + BestSpeed = Level1, + + /// + /// Level 2. + /// + Level2 = 2, + + /// + /// Level 3. + /// + Level3 = 3, + + /// + /// Level 4. + /// + Level4 = 4, + + /// + /// Level 5. + /// + Level5 = 5, + + /// + /// Level 6. Equivalent to . + /// + Level6 = 6, + + /// + /// The default compression level. Equivalent to . + /// + DefaultCompression = Level6, + + /// + /// Level 7. + /// + Level7 = 7, + + /// + /// Level 8. + /// + Level8 = 8, + + /// + /// Level 9. Equivalent to . + /// + Level9 = 9, + + /// + /// Best compression level. Equivalent to . + /// + BestCompression = Level9, + } +} diff --git a/src/ImageSharp/Formats/Png/PngCompressionLevel.cs b/src/ImageSharp/Formats/Png/PngCompressionLevel.cs index 7516e0987..961f9b05b 100644 --- a/src/ImageSharp/Formats/Png/PngCompressionLevel.cs +++ b/src/ImageSharp/Formats/Png/PngCompressionLevel.cs @@ -1,11 +1,14 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System.ComponentModel; + namespace SixLabors.ImageSharp.Formats.Png { /// /// Provides enumeration of available PNG compression levels. /// + [EditorBrowsable(EditorBrowsableState.Never)] public enum PngCompressionLevel { /// diff --git a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs index c3df04186..cbc1f7c76 100644 --- a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs +++ b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.Compression; using SixLabors.ImageSharp.Processing.Processors.Quantization; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs index 479ff470b..38fdac98b 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs @@ -6,7 +6,7 @@ using System.Threading; using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.Compression; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Quantization; diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 277b833a6..6973f21a3 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -7,9 +7,9 @@ using System.IO; using System.Threading; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Compression; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; -using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs index d91de83e6..e2fca49e8 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -4,10 +4,10 @@ using System; using System.Buffers; using System.IO; -using System.Runtime.InteropServices; +using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Compression; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; -using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Png.Zlib; using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Memory; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs index 441328dfe..5235fb011 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System.IO; +using SixLabors.ImageSharp.Compression; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Formats.Png.Zlib; @@ -35,7 +36,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression Stream compressedStream = new MemoryStream(); using (Stream uncompressedStream = new MemoryStream(data), - deflateStream = new ZlibDeflateStream(Configuration.Default.MemoryAllocator, compressedStream, ImageSharp.Formats.Png.DeflateCompressionLevel.Level6)) + deflateStream = new ZlibDeflateStream(Configuration.Default.MemoryAllocator, compressedStream, DeflateCompressionLevel.Level6)) { uncompressedStream.CopyTo(deflateStream); } From 333ed01dd071d037255aa00bc7eb0a6378a33a6f Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 5 Dec 2020 18:29:18 +0100 Subject: [PATCH 134/275] Rename TiffByteOrder To ByteOrder, moved it to Common folder --- .../TiffByteOrder.cs => Common/ByteOrder.cs} | 6 ++++-- .../Formats/Tiff/Streams/TiffBigEndianStream.cs | 2 +- .../Formats/Tiff/Streams/TiffLittleEndianStream.cs | 2 +- src/ImageSharp/Formats/Tiff/Streams/TiffStream.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs | 14 +++++++------- src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffMetadata.cs | 2 +- .../Formats/Tiff/TiffDecoderTests.cs | 6 +++--- .../Formats/Tiff/TiffMetadataTests.cs | 12 ++++++------ 9 files changed, 25 insertions(+), 23 deletions(-) rename src/ImageSharp/{Formats/Tiff/Constants/TiffByteOrder.cs => Common/ByteOrder.cs} (63%) diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffByteOrder.cs b/src/ImageSharp/Common/ByteOrder.cs similarity index 63% rename from src/ImageSharp/Formats/Tiff/Constants/TiffByteOrder.cs rename to src/ImageSharp/Common/ByteOrder.cs index e83fc6bec..8daa35eb3 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffByteOrder.cs +++ b/src/ImageSharp/Common/ByteOrder.cs @@ -1,20 +1,22 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants +namespace SixLabors.ImageSharp { /// /// The tiff data stream byte order enum. /// - public enum TiffByteOrder + public enum ByteOrder { /// /// The big-endian byte order (Motorola). + /// Most-significant byte comes first, and ends with the least-significant byte. /// BigEndian, /// /// The little-endian byte order (Intel). + /// Least-significant byte comes first and ends with the most-significant byte. /// LittleEndian } diff --git a/src/ImageSharp/Formats/Tiff/Streams/TiffBigEndianStream.cs b/src/ImageSharp/Formats/Tiff/Streams/TiffBigEndianStream.cs index ba0364b88..845353acb 100644 --- a/src/ImageSharp/Formats/Tiff/Streams/TiffBigEndianStream.cs +++ b/src/ImageSharp/Formats/Tiff/Streams/TiffBigEndianStream.cs @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Streams { } - public override TiffByteOrder ByteOrder => TiffByteOrder.BigEndian; + public override ByteOrder ByteOrder => ByteOrder.BigEndian; /// /// Converts buffer data into an using the correct endianness. diff --git a/src/ImageSharp/Formats/Tiff/Streams/TiffLittleEndianStream.cs b/src/ImageSharp/Formats/Tiff/Streams/TiffLittleEndianStream.cs index f15b465ba..c31c2320a 100644 --- a/src/ImageSharp/Formats/Tiff/Streams/TiffLittleEndianStream.cs +++ b/src/ImageSharp/Formats/Tiff/Streams/TiffLittleEndianStream.cs @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Streams { } - public override TiffByteOrder ByteOrder => TiffByteOrder.LittleEndian; + public override ByteOrder ByteOrder => ByteOrder.LittleEndian; /// /// Converts buffer data into an using the correct endianness. diff --git a/src/ImageSharp/Formats/Tiff/Streams/TiffStream.cs b/src/ImageSharp/Formats/Tiff/Streams/TiffStream.cs index cc469db59..930be0d05 100644 --- a/src/ImageSharp/Formats/Tiff/Streams/TiffStream.cs +++ b/src/ImageSharp/Formats/Tiff/Streams/TiffStream.cs @@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Streams /// /// Gets a value indicating whether the file is encoded in little-endian or big-endian format. /// - public abstract TiffByteOrder ByteOrder { get; } + public abstract ByteOrder ByteOrder { get; } /// /// Gets the input stream. diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 1e4ea26fe..e6595653c 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -169,7 +169,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff return new ImageInfo(new PixelTypeInfo(bitsPerPixel), (int)root.Width, (int)root.Height, this.metadata); } - private void SetTiffFormatMetaData(List framesMetadata, TiffByteOrder byteOrder) + private void SetTiffFormatMetaData(List framesMetadata, ByteOrder byteOrder) { this.metadata = framesMetadata.CreateMetadata(this.ignoreMetadata, byteOrder); this.tiffMetaData = this.metadata.GetTiffMetadata(); @@ -203,12 +203,12 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff private static TiffStream CreateStream(Stream stream) { - TiffByteOrder byteOrder = ReadByteOrder(stream); - if (byteOrder == TiffByteOrder.BigEndian) + ByteOrder byteOrder = ReadByteOrder(stream); + if (byteOrder == ByteOrder.BigEndian) { return new TiffBigEndianStream(stream); } - else if (byteOrder == TiffByteOrder.LittleEndian) + else if (byteOrder == ByteOrder.LittleEndian) { return new TiffLittleEndianStream(stream); } @@ -216,17 +216,17 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff throw TiffThrowHelper.InvalidHeader(); } - private static TiffByteOrder ReadByteOrder(Stream stream) + private static ByteOrder ReadByteOrder(Stream stream) { var headerBytes = new byte[2]; stream.Read(headerBytes, 0, 2); if (headerBytes[0] == TiffConstants.ByteOrderLittleEndian && headerBytes[1] == TiffConstants.ByteOrderLittleEndian) { - return TiffByteOrder.LittleEndian; + return ByteOrder.LittleEndian; } else if (headerBytes[0] == TiffConstants.ByteOrderBigEndian && headerBytes[1] == TiffConstants.ByteOrderBigEndian) { - return TiffByteOrder.BigEndian; + return ByteOrder.BigEndian; } throw TiffThrowHelper.InvalidHeader(); diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs index c54a2cc90..25f43a0a8 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs @@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// internal static class TiffDecoderHelpers { - public static ImageMetadata CreateMetadata(this IList frames, bool ignoreMetadata, TiffByteOrder byteOrder) + public static ImageMetadata CreateMetadata(this IList frames, bool ignoreMetadata, ByteOrder byteOrder) { var coreMetadata = new ImageMetadata(); TiffMetadata tiffMetadata = coreMetadata.GetTiffMetadata(); diff --git a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs index cfb9fa8bb..276e8ad80 100644 --- a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs @@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// /// Gets or sets the byte order. /// - public TiffByteOrder ByteOrder { get; set; } + public ByteOrder ByteOrder { get; set; } /// /// Gets or sets the number of bits per pixel. diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index 24c0a712b..f66012b08 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -59,9 +59,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff } [Theory] - [InlineData(RgbLzwNoPredictorMultistrip, TiffByteOrder.LittleEndian)] - [InlineData(RgbLzwNoPredictorMultistripMotorola, TiffByteOrder.BigEndian)] - public void ByteOrder(string imagePath, TiffByteOrder expectedByteOrder) + [InlineData(RgbLzwNoPredictorMultistrip, ImageSharp.ByteOrder.LittleEndian)] + [InlineData(RgbLzwNoPredictorMultistripMotorola, ImageSharp.ByteOrder.BigEndian)] + public void ByteOrder(string imagePath, ByteOrder expectedByteOrder) { var testFile = TestFile.Create(imagePath); using (var stream = new MemoryStream(testFile.Bytes, false)) diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index 9616ce1cf..91deb44cd 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { Compression = TiffCompression.Deflate, BitsPerPixel = TiffBitsPerPixel.Pixel8, - ByteOrder = TiffByteOrder.BigEndian, + ByteOrder = ByteOrder.BigEndian, XmpProfile = new byte[3] }; @@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff clone.Compression = TiffCompression.None; clone.BitsPerPixel = TiffBitsPerPixel.Pixel24; - clone.ByteOrder = TiffByteOrder.LittleEndian; + clone.ByteOrder = ByteOrder.LittleEndian; clone.XmpProfile = new byte[1]; Assert.False(meta.Compression == clone.Compression); @@ -80,9 +80,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff } [Theory] - [InlineData(GrayscaleUncompressed, TiffByteOrder.BigEndian)] - [InlineData(LsbToMsbByteOrder, TiffByteOrder.LittleEndian)] - public void Identify_DetectsCorrectByteOrder(string imagePath, TiffByteOrder expectedByteOrder) + [InlineData(GrayscaleUncompressed, ByteOrder.BigEndian)] + [InlineData(LsbToMsbByteOrder, ByteOrder.LittleEndian)] + public void Identify_DetectsCorrectByteOrder(string imagePath, ByteOrder expectedByteOrder) { var testFile = TestFile.Create(imagePath); using var stream = new MemoryStream(testFile.Bytes, false); @@ -127,7 +127,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff TiffMetadata meta = image.Metadata.GetTiffMetadata(); Assert.NotNull(meta); - Assert.Equal(TiffByteOrder.LittleEndian, meta.ByteOrder); + Assert.Equal(ByteOrder.LittleEndian, meta.ByteOrder); Assert.Equal(PixelResolutionUnit.PixelsPerInch, image.Metadata.ResolutionUnits); Assert.Equal(10, image.Metadata.HorizontalResolution); Assert.Equal(10, image.Metadata.VerticalResolution); From 0e42ebba33eb9e6232baf4a7377f44dc38aaafbe Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 5 Dec 2020 19:48:38 +0100 Subject: [PATCH 135/275] Remove Tiff ImageExtension, this is already generated. Add Experimental tag to the Tiff SaveAs docs --- .../Formats/ImageExtensions.Save.cs | 21 +-- .../Formats/ImageExtensions.Save.tt | 20 ++- .../Formats/Tiff/ImageExtensions.cs | 51 ------ src/ImageSharp/Formats/Tiff/TiffDecoder.cs | 1 - .../Formats/Tiff/Utils/TiffUtils.cs | 6 + .../Formats/Tiff/ImageExtensionsTest.cs | 156 ++++++++++++++++++ .../Formats/Tiff/TiffDecoderTests.cs | 1 - 7 files changed, 184 insertions(+), 72 deletions(-) delete mode 100644 src/ImageSharp/Formats/Tiff/ImageExtensions.cs create mode 100644 tests/ImageSharp.Tests/Formats/Tiff/ImageExtensionsTest.cs diff --git a/src/ImageSharp/Formats/ImageExtensions.Save.cs b/src/ImageSharp/Formats/ImageExtensions.Save.cs index 7084f1542..07c6b37b8 100644 --- a/src/ImageSharp/Formats/ImageExtensions.Save.cs +++ b/src/ImageSharp/Formats/ImageExtensions.Save.cs @@ -6,13 +6,14 @@ using System.IO; using System.Threading; using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats.Experimental.Tiff; using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Tga; -using SixLabors.ImageSharp.Formats.Experimental.Tiff; +using SixLabors.ImageSharp.Formats.Tiff; namespace SixLabors.ImageSharp { @@ -537,7 +538,7 @@ namespace SixLabors.ImageSharp cancellationToken); /// - /// Saves the image to the given stream with the Tiff format. + /// EXPERIMENTAL! Saves the image to the given stream with the Tiff format. /// /// The image this method extends. /// The file path to save the image to. @@ -545,7 +546,7 @@ namespace SixLabors.ImageSharp public static void SaveAsTiff(this Image source, string path) => SaveAsTiff(source, path, null); /// - /// Saves the image to the given stream with the Tiff format. + /// EXPERIMENTAL! Saves the image to the given stream with the Tiff format. /// /// The image this method extends. /// The file path to save the image to. @@ -554,7 +555,7 @@ namespace SixLabors.ImageSharp public static Task SaveAsTiffAsync(this Image source, string path) => SaveAsTiffAsync(source, path, null); /// - /// Saves the image to the given stream with the Tiff format. + /// EXPERIMENTAL! Saves the image to the given stream with the Tiff format. /// /// The image this method extends. /// The file path to save the image to. @@ -565,7 +566,7 @@ namespace SixLabors.ImageSharp => SaveAsTiffAsync(source, path, null, cancellationToken); /// - /// Saves the image to the given stream with the Tiff format. + /// EXPERIMENTAL! Saves the image to the given stream with the Tiff format. /// /// The image this method extends. /// The file path to save the image to. @@ -577,7 +578,7 @@ namespace SixLabors.ImageSharp encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TiffFormat.Instance)); /// - /// Saves the image to the given stream with the Tiff format. + /// EXPERIMENTAL! Saves the image to the given stream with the Tiff format. /// /// The image this method extends. /// The file path to save the image to. @@ -592,7 +593,7 @@ namespace SixLabors.ImageSharp cancellationToken); /// - /// Saves the image to the given stream with the Tiff format. + /// EXPERIMENTAL! Saves the image to the given stream with the Tiff format. /// /// The image this method extends. /// The stream to save the image to. @@ -601,7 +602,7 @@ namespace SixLabors.ImageSharp => SaveAsTiff(source, stream, null); /// - /// Saves the image to the given stream with the Tiff format. + /// EXPERIMENTAL! Saves the image to the given stream with the Tiff format. /// /// The image this method extends. /// The stream to save the image to. @@ -612,7 +613,7 @@ namespace SixLabors.ImageSharp => SaveAsTiffAsync(source, stream, null, cancellationToken); /// - /// Saves the image to the given stream with the Tiff format. + /// EXPERIMENTAL! Saves the image to the given stream with the Tiff format. /// /// The image this method extends. /// The stream to save the image to. @@ -625,7 +626,7 @@ namespace SixLabors.ImageSharp encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TiffFormat.Instance)); /// - /// Saves the image to the given stream with the Tiff format. + /// EXPERIMENTAL! Saves the image to the given stream with the Tiff format. /// /// The image this method extends. /// The stream to save the image to. diff --git a/src/ImageSharp/Formats/ImageExtensions.Save.tt b/src/ImageSharp/Formats/ImageExtensions.Save.tt index af9531225..8954c19e5 100644 --- a/src/ImageSharp/Formats/ImageExtensions.Save.tt +++ b/src/ImageSharp/Formats/ImageExtensions.Save.tt @@ -9,6 +9,7 @@ using System.IO; using System.Threading; using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats.Experimental.Tiff; <# var formats = new []{ @@ -39,9 +40,10 @@ namespace SixLabors.ImageSharp <# foreach (string fmt in formats) { + string experimentalString = fmt == "Tiff" || fmt == "Webp" ? @"EXPERIMENTAL! " : ""; #> /// - /// Saves the image to the given stream with the <#= fmt #> format. + /// <#= experimentalString #>Saves the image to the given stream with the <#= fmt #> format. /// /// The image this method extends. /// The file path to save the image to. @@ -49,7 +51,7 @@ namespace SixLabors.ImageSharp public static void SaveAs<#= fmt #>(this Image source, string path) => SaveAs<#= fmt #>(source, path, null); /// - /// Saves the image to the given stream with the <#= fmt #> format. + /// <#= experimentalString #>Saves the image to the given stream with the <#= fmt #> format. /// /// The image this method extends. /// The file path to save the image to. @@ -58,7 +60,7 @@ namespace SixLabors.ImageSharp public static Task SaveAs<#= fmt #>Async(this Image source, string path) => SaveAs<#= fmt #>Async(source, path, null); /// - /// Saves the image to the given stream with the <#= fmt #> format. + /// <#= experimentalString #>Saves the image to the given stream with the <#= fmt #> format. /// /// The image this method extends. /// The file path to save the image to. @@ -69,7 +71,7 @@ namespace SixLabors.ImageSharp => SaveAs<#= fmt #>Async(source, path, null, cancellationToken); /// - /// Saves the image to the given stream with the <#= fmt #> format. + /// <#= experimentalString #>Saves the image to the given stream with the <#= fmt #> format. /// /// The image this method extends. /// The file path to save the image to. @@ -81,7 +83,7 @@ namespace SixLabors.ImageSharp encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(<#= fmt #>Format.Instance)); /// - /// Saves the image to the given stream with the <#= fmt #> format. + /// <#= experimentalString #>Saves the image to the given stream with the <#= fmt #> format. /// /// The image this method extends. /// The file path to save the image to. @@ -96,7 +98,7 @@ namespace SixLabors.ImageSharp cancellationToken); /// - /// Saves the image to the given stream with the <#= fmt #> format. + /// <#= experimentalString #>Saves the image to the given stream with the <#= fmt #> format. /// /// The image this method extends. /// The stream to save the image to. @@ -105,7 +107,7 @@ namespace SixLabors.ImageSharp => SaveAs<#= fmt #>(source, stream, null); /// - /// Saves the image to the given stream with the <#= fmt #> format. + /// <#= experimentalString #>Saves the image to the given stream with the <#= fmt #> format. /// /// The image this method extends. /// The stream to save the image to. @@ -116,7 +118,7 @@ namespace SixLabors.ImageSharp => SaveAs<#= fmt #>Async(source, stream, null, cancellationToken); /// - /// Saves the image to the given stream with the <#= fmt #> format. + /// <#= experimentalString #>Saves the image to the given stream with the <#= fmt #> format. /// /// The image this method extends. /// The stream to save the image to. @@ -129,7 +131,7 @@ namespace SixLabors.ImageSharp encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(<#= fmt #>Format.Instance)); /// - /// Saves the image to the given stream with the <#= fmt #> format. + /// <#= experimentalString #>Saves the image to the given stream with the <#= fmt #> format. /// /// The image this method extends. /// The stream to save the image to. diff --git a/src/ImageSharp/Formats/Tiff/ImageExtensions.cs b/src/ImageSharp/Formats/Tiff/ImageExtensions.cs deleted file mode 100644 index 2583dbd01..000000000 --- a/src/ImageSharp/Formats/Tiff/ImageExtensions.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -using System.IO; -using SixLabors.ImageSharp.Formats.Experimental.Tiff; -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp -{ - /// - /// Extension methods for the type. - /// - public static partial class ImageExtensions - { - /// - /// Saves the image to the given stream with the tiff format. - /// - /// The pixel format. - /// The image this method extends. - /// The stream to save the image to. - /// Thrown if the stream is null. - /// - /// The . - /// - public static Image SaveAsTiff(this Image source, Stream stream) - where TPixel : unmanaged, IPixel - { - return SaveAsTiff(source, stream, null); - } - - /// - /// Saves the image to the given stream with the tiff format. - /// - /// The pixel format. - /// The image this method extends. - /// The stream to save the image to. - /// The options for the encoder. - /// Thrown if the stream is null. - /// - /// The . - /// - public static Image SaveAsTiff(this Image source, Stream stream, TiffEncoder encoder) - where TPixel : unmanaged, IPixel - { - encoder = encoder ?? new TiffEncoder(); - encoder.Encode(source, stream); - - return source; - } - } -} diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs index ec67c815e..455d71aae 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs @@ -4,7 +4,6 @@ using System.IO; using System.Threading; using System.Threading.Tasks; -using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs index ec4a87664..e81a7f4c4 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs @@ -23,6 +23,12 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils while (count > 0) { + int bytesLeftInBuffer = buffer.Length - offset; + if (bytesLeftInBuffer < count) + { + TiffThrowHelper.ThrowImageFormatException("Error reading data from the stream. The provided buffer was too small."); + } + int bytesRead = stream.Read(buffer, offset, count); if (bytesRead == 0) diff --git a/tests/ImageSharp.Tests/Formats/Tiff/ImageExtensionsTest.cs b/tests/ImageSharp.Tests/Formats/Tiff/ImageExtensionsTest.cs new file mode 100644 index 000000000..45a86185e --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Tiff/ImageExtensionsTest.cs @@ -0,0 +1,156 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System.IO; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Experimental.Tiff; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Formats.Tiff +{ + [Trait("Format", "Tiff")] + public class ImageExtensionsTest + { + [Fact] + public void SaveAsTiff_Path() + { + string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest)); + string file = Path.Combine(dir, "SaveAsTiff_Path.tiff"); + + using (var image = new Image(10, 10)) + { + image.SaveAsTiff(file); + } + + using (Image.Load(file, out IImageFormat mime)) + { + Assert.Equal("image/tiff", mime.DefaultMimeType); + } + } + + [Fact] + public async Task SaveAsTiffAsync_Path() + { + string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest)); + string file = Path.Combine(dir, "SaveAsTiffAsync_Path.tiff"); + + using (var image = new Image(10, 10)) + { + await image.SaveAsTiffAsync(file); + } + + using (Image.Load(file, out IImageFormat mime)) + { + Assert.Equal("image/tiff", mime.DefaultMimeType); + } + } + + [Fact] + public void SaveAsTiff_Path_Encoder() + { + string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions)); + string file = Path.Combine(dir, "SaveAsTiff_Path_Encoder.tiff"); + + using (var image = new Image(10, 10)) + { + image.SaveAsTiff(file, new TiffEncoder()); + } + + using (Image.Load(file, out IImageFormat mime)) + { + Assert.Equal("image/tiff", mime.DefaultMimeType); + } + } + + [Fact] + public async Task SaveAsTiffAsync_Path_Encoder() + { + string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions)); + string file = Path.Combine(dir, "SaveAsTiffAsync_Path_Encoder.tiff"); + + using (var image = new Image(10, 10)) + { + await image.SaveAsTiffAsync(file, new TiffEncoder()); + } + + using (Image.Load(file, out IImageFormat mime)) + { + Assert.Equal("image/tiff", mime.DefaultMimeType); + } + } + + [Fact] + public void SaveAsTiff_Stream() + { + using var memoryStream = new MemoryStream(); + + using (var image = new Image(10, 10)) + { + image.SaveAsTiff(memoryStream); + } + + memoryStream.Position = 0; + + using (Image.Load(memoryStream, out IImageFormat mime)) + { + Assert.Equal("image/tiff", mime.DefaultMimeType); + } + } + + [Fact] + public async Task SaveAsTiffAsync_StreamAsync() + { + using var memoryStream = new MemoryStream(); + + using (var image = new Image(10, 10)) + { + await image.SaveAsTiffAsync(memoryStream); + } + + memoryStream.Position = 0; + + using (Image.Load(memoryStream, out IImageFormat mime)) + { + Assert.Equal("image/tiff", mime.DefaultMimeType); + } + } + + [Fact] + public void SaveAsTiff_Stream_Encoder() + { + using var memoryStream = new MemoryStream(); + + using (var image = new Image(10, 10)) + { + image.SaveAsTiff(memoryStream, new TiffEncoder()); + } + + memoryStream.Position = 0; + + using (Image.Load(memoryStream, out IImageFormat mime)) + { + Assert.Equal("image/tiff", mime.DefaultMimeType); + } + } + + [Fact] + public async Task SaveAsTiffAsync_Stream_Encoder() + { + using var memoryStream = new MemoryStream(); + + using (var image = new Image(10, 10)) + { + await image.SaveAsTiffAsync(memoryStream, new TiffEncoder()); + } + + memoryStream.Position = 0; + + using (Image.Load(memoryStream, out IImageFormat mime)) + { + Assert.Equal("image/tiff", mime.DefaultMimeType); + } + } + } +} diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index f66012b08..16f174720 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -6,7 +6,6 @@ using System; using System.IO; using SixLabors.ImageSharp.Formats.Experimental.Tiff; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; From cff52ba6f53544e99b93949a1478d3579ead4cce Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 5 Dec 2020 20:11:41 +0100 Subject: [PATCH 136/275] Remove writing padding bytes, this seems not necessary --- .../Formats/Tiff/TiffEncoderCore.cs | 15 +---- .../Formats/Tiff/Utils/TiffWriter.cs | 57 +++++-------------- 2 files changed, 16 insertions(+), 56 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 6973f21a3..11775bd70 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -24,11 +24,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// internal sealed class TiffEncoderCore : IImageEncoderInternals { - /// - /// The amount to pad each row by in bytes. - /// - private int padding; - /// /// Used for allocating memory during processing operations. /// @@ -121,10 +116,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff this.SetPhotometricInterpretation(); - short bpp = (short)this.bitsPerPixel; - int bytesPerLine = 4 * (((image.Width * bpp) + 31) / 32); - this.padding = bytesPerLine - (int)(image.Width * (bpp / 8F)); - using (var writer = new TiffWriter(stream, this.memoryAllocator, this.configuration)) { long firstIfdMarker = this.WriteHeader(writer); @@ -172,16 +163,16 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff switch (this.Mode) { case TiffEncodingMode.ColorPalette: - imageDataBytes = writer.WritePalettedRgb(image, this.quantizer, this.padding, this.CompressionType, this.compressionLevel, this.useHorizontalPredictor, out colorMap); + imageDataBytes = writer.WritePalettedRgb(image, this.quantizer, this.CompressionType, this.compressionLevel, this.useHorizontalPredictor, out colorMap); break; case TiffEncodingMode.Gray: - imageDataBytes = writer.WriteGray(image, this.padding, this.CompressionType, this.compressionLevel, this.useHorizontalPredictor); + imageDataBytes = writer.WriteGray(image, this.CompressionType, this.compressionLevel, this.useHorizontalPredictor); break; case TiffEncodingMode.BiColor: imageDataBytes = writer.WriteBiColor(image, this.CompressionType, this.compressionLevel); break; default: - imageDataBytes = writer.WriteRgb(image, this.padding, this.CompressionType, this.compressionLevel, this.useHorizontalPredictor); + imageDataBytes = writer.WriteRgb(image, this.CompressionType, this.compressionLevel, this.useHorizontalPredictor); break; } diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs index e2fca49e8..f08241fbf 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -136,15 +136,14 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// /// The pixel data. /// The image to write to the stream. - /// The padding bytes for each row. /// The compression to use. /// The compression level for deflate compression. /// Indicates if horizontal prediction should be used. Should only be used with deflate compression. /// The number of bytes written. - public int WriteRgb(Image image, int padding, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) + public int WriteRgb(Image image, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) where TPixel : unmanaged, IPixel { - using IManagedByteBuffer row = this.AllocateRow(image.Width, 3, padding); + using IManagedByteBuffer row = this.memoryAllocator.AllocateManagedByteBuffer(image.Width * 3); Span rowSpan = row.GetSpan(); if (compression == TiffEncoderCompression.Deflate) { @@ -284,19 +283,18 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// The pixel data. /// The image to write to the stream. /// The quantizer to use. - /// The padding bytes for each row. /// The compression to use. /// The compression level for deflate compression. /// Indicates if horizontal prediction should be used. Should only be used in combination with deflate or LZW compression. /// The color map. /// The number of bytes written. - public int WritePalettedRgb(Image image, IQuantizer quantizer, int padding, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor, out IExifValue colorMap) + public int WritePalettedRgb(Image image, IQuantizer quantizer, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor, out IExifValue colorMap) where TPixel : unmanaged, IPixel { int colorsPerChannel = 256; int colorPaletteSize = colorsPerChannel * 3; int colorPaletteBytes = colorPaletteSize * 2; - using IManagedByteBuffer row = this.AllocateRow(image.Width, 1, padding); + using IManagedByteBuffer row = this.memoryAllocator.AllocateManagedByteBuffer(image.Width); using IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(this.configuration); using IndexedImageFrame quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(image.Frames.RootFrame, image.Bounds()); using IMemoryOwner colorPaletteBuffer = this.memoryAllocator.AllocateManagedByteBuffer(colorPaletteBytes); @@ -342,17 +340,17 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils if (compression == TiffEncoderCompression.Deflate) { - return this.WriteDeflateCompressedPalettedRgb(image, quantized, padding, compressionLevel, useHorizontalPredictor); + return this.WriteDeflateCompressedPalettedRgb(image, quantized, compressionLevel, useHorizontalPredictor); } if (compression == TiffEncoderCompression.Lzw) { - return this.WriteLzwCompressedPalettedRgb(image, quantized, padding, useHorizontalPredictor); + return this.WriteLzwCompressedPalettedRgb(image, quantized, useHorizontalPredictor); } if (compression == TiffEncoderCompression.PackBits) { - return this.WritePackBitsCompressedPalettedRgb(image, quantized, padding); + return this.WritePackBitsCompressedPalettedRgb(image, quantized); } // No compression. @@ -362,12 +360,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils ReadOnlySpan pixelSpan = quantized.GetPixelRowSpan(y); this.output.Write(pixelSpan); bytesWritten += pixelSpan.Length; - - for (int i = 0; i < padding; i++) - { - this.output.WriteByte(0); - bytesWritten++; - } } return bytesWritten; @@ -379,11 +371,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// The pixel data. /// The image to write to the stream. /// The quantized frame. - /// The padding bytes for each row. /// The compression level for deflate compression. /// Indicates if horizontal prediction should be used. /// The number of bytes written. - public int WriteDeflateCompressedPalettedRgb(Image image, IndexedImageFrame quantized, int padding, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) + public int WriteDeflateCompressedPalettedRgb(Image image, IndexedImageFrame quantized, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) where TPixel : unmanaged, IPixel { using IManagedByteBuffer tmpBuffer = this.memoryAllocator.AllocateManagedByteBuffer(image.Width); @@ -406,11 +397,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils { deflateStream.Write(pixelRow); } - - for (int i = 0; i < padding; i++) - { - deflateStream.WriteByte(0); - } } deflateStream.Flush(); @@ -427,10 +413,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// The pixel data. /// The image to write to the stream. /// The quantized frame. - /// The padding bytes for each row. /// Indicates if horizontal prediction should be used. /// The number of bytes written. - public int WriteLzwCompressedPalettedRgb(Image image, IndexedImageFrame quantized, int padding, bool useHorizontalPredictor) + public int WriteLzwCompressedPalettedRgb(Image image, IndexedImageFrame quantized, bool useHorizontalPredictor) where TPixel : unmanaged, IPixel { IMemoryOwner pixelData = this.memoryAllocator.Allocate(image.Width * image.Height); @@ -473,34 +458,21 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// The pixel data. /// The image to write to the stream. /// The quantized frame. - /// The padding bytes for each row. /// The number of bytes written. - public int WritePackBitsCompressedPalettedRgb(Image image, IndexedImageFrame quantized, int padding) + public int WritePackBitsCompressedPalettedRgb(Image image, IndexedImageFrame quantized) where TPixel : unmanaged, IPixel { // Worst case is that the actual compressed data is larger then the input data. In this case we need 1 additional byte per 127 bytes. int additionalBytes = ((image.Width * 3) / 127) + 1; using IManagedByteBuffer compressedRow = this.memoryAllocator.AllocateManagedByteBuffer((image.Width * 3) + additionalBytes, AllocationOptions.Clean); - using IManagedByteBuffer pixelRowWithPadding = this.memoryAllocator.AllocateManagedByteBuffer((image.Width * 3) + padding, AllocationOptions.Clean); Span compressedRowSpan = compressedRow.GetSpan(); - Span pixelRowWithPaddingSpan = pixelRowWithPadding.GetSpan(); int bytesWritten = 0; for (int y = 0; y < image.Height; y++) { ReadOnlySpan pixelSpan = quantized.GetPixelRowSpan(y); - int size; - if (padding != 0) - { - pixelSpan.CopyTo(pixelRowWithPaddingSpan); - size = PackBitsWriter.PackBits(pixelRowWithPaddingSpan, compressedRowSpan); - } - else - { - size = PackBitsWriter.PackBits(pixelSpan, compressedRowSpan); - } - + int size = PackBitsWriter.PackBits(pixelSpan, compressedRowSpan); this.output.Write(compressedRowSpan.Slice(0, size)); bytesWritten += size; } @@ -513,15 +485,14 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// /// The pixel data. /// The image to write to the stream. - /// The padding bytes for each row. /// The compression to use. /// The compression level for deflate compression. /// Indicates if horizontal prediction should be used. Should only be used with deflate or lzw compression. /// The number of bytes written. - public int WriteGray(Image image, int padding, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) + public int WriteGray(Image image, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) where TPixel : unmanaged, IPixel { - using IManagedByteBuffer row = this.AllocateRow(image.Width, 1, padding); + using IManagedByteBuffer row = this.memoryAllocator.AllocateManagedByteBuffer(image.Width); Span rowSpan = row.GetSpan(); if (compression == TiffEncoderCompression.Deflate) @@ -831,8 +802,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils return bytesWritten; } - private IManagedByteBuffer AllocateRow(int width, int bytesPerPixel, int padding) => this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, bytesPerPixel, padding); - /// /// Disposes instance, ensuring any unwritten data is flushed. /// From f2c262dc225a05493faf56c44e72cf396430cd5b Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 5 Dec 2020 20:58:34 +0100 Subject: [PATCH 137/275] Remove TiffUtils, use Stream extensions instead --- .../Compression/DeflateTiffCompression.cs | 2 +- .../Tiff/Compression/NoneTiffCompression.cs | 2 +- .../Compression/PackBitsTiffCompression.cs | 2 +- .../Formats/Tiff/Compression/T4BitReader.cs | 2 +- .../Formats/Tiff/Utils/TiffUtils.cs | 54 ------------------- 5 files changed, 4 insertions(+), 58 deletions(-) delete mode 100644 src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs diff --git a/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs index 4cc7008eb..4ea98d95a 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs @@ -60,7 +60,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression var subStream = new SubStream(stream, byteCount - headerLength); using (var deflateStream = new DeflateStream(subStream, CompressionMode.Decompress, true)) { - deflateStream.ReadFull(buffer); + deflateStream.Read(buffer, 0, buffer.Length); } if (this.Predictor == TiffPredictor.Horizontal) diff --git a/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs index 5ca112f3b..a07b42112 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs @@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression /// public override void Decompress(Stream stream, int byteCount, Span buffer) { - stream.ReadFull(buffer, byteCount); + stream.Read(buffer, 0, byteCount); } } } diff --git a/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs index 2fbb7e6f1..d77ee78e4 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs @@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression Span compressedData = compressedDataMemory.GetSpan(); - stream.ReadFull(compressedData, byteCount); + stream.Read(compressedData, 0, byteCount); int compressedOffset = 0; int decompressedOffset = 0; diff --git a/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs index f2609e05c..d5435f546 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs @@ -821,7 +821,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression private void ReadImageDataFromStream(Stream input, int bytesToRead) { Span dataSpan = this.Data.GetSpan(); - input.ReadFull(dataSpan, bytesToRead); + input.Read(dataSpan, 0, bytesToRead); } } } diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs deleted file mode 100644 index e81a7f4c4..000000000 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.IO; - -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils -{ - /// - /// TIFF specific utilities and extension methods. - /// - internal static class TiffUtils - { - /// - /// Reads a sequence of bytes from the input stream into a buffer. - /// - /// The stream to read from. - /// A buffer to store the retrieved data. - /// The number of bytes to read. - public static void ReadFull(this Stream stream, Span buffer, int count) - { - int offset = 0; - - while (count > 0) - { - int bytesLeftInBuffer = buffer.Length - offset; - if (bytesLeftInBuffer < count) - { - TiffThrowHelper.ThrowImageFormatException("Error reading data from the stream. The provided buffer was too small."); - } - - int bytesRead = stream.Read(buffer, offset, count); - - if (bytesRead == 0) - { - break; - } - - offset += bytesRead; - count -= bytesRead; - } - } - - /// - /// Reads all bytes from the input stream into a buffer until the end of stream or the buffer is full. - /// - /// The stream to read from. - /// A buffer to store the retrieved data. - public static void ReadFull(this Stream stream, Span buffer) - { - ReadFull(stream, buffer, buffer.Length); - } - } -} From a28b9c5db8f521540c50b0a568dc29f98c67f90c Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 7 Dec 2020 10:06:00 +0100 Subject: [PATCH 138/275] Fix little endian test file --- tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs | 2 +- tests/ImageSharp.Tests/TestImages.cs | 5 +++-- .../Tiff/{b0350_lsb_to_msb.tiff => b0350_fillorder2.tiff} | 0 tests/Images/Input/Tiff/little_endian.tiff | 3 +++ 4 files changed, 7 insertions(+), 3 deletions(-) rename tests/Images/Input/Tiff/{b0350_lsb_to_msb.tiff => b0350_fillorder2.tiff} (100%) create mode 100644 tests/Images/Input/Tiff/little_endian.tiff diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index 91deb44cd..f32cbdd9d 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Theory] [InlineData(GrayscaleUncompressed, ByteOrder.BigEndian)] - [InlineData(LsbToMsbByteOrder, ByteOrder.LittleEndian)] + [InlineData(LittleEndianByteOrder, ByteOrder.LittleEndian)] public void Identify_DetectsCorrectByteOrder(string imagePath, ByteOrder expectedByteOrder) { var testFile = TestFile.Create(imagePath); diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index b2e312ddc..27ec39e70 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -560,7 +560,8 @@ namespace SixLabors.ImageSharp.Tests public const string MultiframeDifferentSize = "Tiff/multipage_differentSize.tiff"; public const string MultiframeDifferentVariants = "Tiff/multipage_differentVariants.tiff"; - public const string LsbToMsbByteOrder = "Tiff/b0350_lsb_to_msb.tiff"; + public const string FillOrder2 = "Tiff/b0350_fillorder2.tiff"; + public const string LittleEndianByteOrder = "Tiff/little_endian.tiff"; public const string SampleMetadata = "Tiff/metadata_sample.tiff"; @@ -568,7 +569,7 @@ namespace SixLabors.ImageSharp.Tests public static readonly string[] Metadata = { SampleMetadata }; - public static readonly string[] NotSupported = { Calliphora_RgbJpeg, RgbJpeg, RgbUncompressedTiled, MultiframeDifferentSize, MultiframeDifferentVariants }; + public static readonly string[] NotSupported = { Calliphora_RgbJpeg, RgbJpeg, RgbUncompressedTiled, MultiframeDifferentSize, MultiframeDifferentVariants, FillOrder2 }; } } } diff --git a/tests/Images/Input/Tiff/b0350_lsb_to_msb.tiff b/tests/Images/Input/Tiff/b0350_fillorder2.tiff similarity index 100% rename from tests/Images/Input/Tiff/b0350_lsb_to_msb.tiff rename to tests/Images/Input/Tiff/b0350_fillorder2.tiff diff --git a/tests/Images/Input/Tiff/little_endian.tiff b/tests/Images/Input/Tiff/little_endian.tiff new file mode 100644 index 000000000..64653d620 --- /dev/null +++ b/tests/Images/Input/Tiff/little_endian.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3b42205ddeb20f8bdb1182bdf1345e695be4bf9617ba0576bef0d5b76642fa1a +size 191232 From 4175fb8bf63a3fd66ca98068b0fafe705cfaa7bb Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 7 Dec 2020 15:16:09 +0100 Subject: [PATCH 139/275] Add tests for tiff encoder options --- .../Formats/Tiff/Compression/T4BitReader.cs | 1 - .../Formats/Tiff/Compression/T4BitWriter.cs | 5 +- .../Formats/Tiff/TiffEncoderCore.cs | 10 ++++ .../Formats/Tiff/Utils/TiffWriter.cs | 2 +- .../Formats/Tiff/TiffEncoderTests.cs | 46 +++++++++++++++++-- 5 files changed, 57 insertions(+), 7 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs index d5435f546..a614235be 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs @@ -6,7 +6,6 @@ using System.Buffers; using System.Collections.Generic; using System.IO; using System.Linq; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression diff --git a/src/ImageSharp/Formats/Tiff/Compression/T4BitWriter.cs b/src/ImageSharp/Formats/Tiff/Compression/T4BitWriter.cs index 72605b74c..2e168de59 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/T4BitWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/T4BitWriter.cs @@ -314,9 +314,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression } // Write the compressed data to the stream. - stream.Write(compressedData.Slice(0, this.bytePosition)); + int bytesToWrite = this.bitPosition != 0 ? this.bytePosition + 1 : this.bytePosition; + stream.Write(compressedData.Slice(0, bytesToWrite)); - return this.bytePosition; + return bytesToWrite; } private void WriteEndOfLine(Span compressedData) diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 11775bd70..50f93f71f 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -426,6 +426,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff private ushort GetCompressionType() { + if (this.CompressionType == TiffEncoderCompression.Deflate && this.Mode == TiffEncodingMode.Default) + { + return (ushort)TiffCompression.Deflate; + } + if (this.CompressionType == TiffEncoderCompression.Deflate && this.Mode == TiffEncodingMode.Rgb) { return (ushort)TiffCompression.Deflate; @@ -436,6 +441,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff return (ushort)TiffCompression.Lzw; } + if (this.CompressionType == TiffEncoderCompression.PackBits && this.Mode == TiffEncodingMode.Default) + { + return (ushort)TiffCompression.PackBits; + } + if (this.CompressionType == TiffEncoderCompression.PackBits && this.Mode == TiffEncodingMode.Rgb) { return (ushort)TiffCompression.PackBits; diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs index f08241fbf..bec317031 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -764,7 +764,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils where TPixel : unmanaged, IPixel { // Worst case is that the actual compressed data is larger then the input data. In this case we need 1 additional byte per 127 bits. - int additionalBytes = (image.Width / 127) + 1; + int additionalBytes = (image.Width / 127) + 2; int compressedRowBytes = (image.Width / 8) + additionalBytes; using IManagedByteBuffer compressedRow = this.memoryAllocator.AllocateManagedByteBuffer(compressedRowBytes, AllocationOptions.Clean); Span compressedRowSpan = compressedRow.GetSpan(); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 86b4e32ac..01196da50 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -5,6 +5,7 @@ using System.IO; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Experimental.Tiff; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; @@ -23,11 +24,50 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public static readonly TheoryData TiffBitsPerPixelFiles = new TheoryData { - { TestImages.Tiff.Calliphora_BiColorUncompressed, TiffBitsPerPixel.Pixel1 }, - { TestImages.Tiff.GrayscaleUncompressed, TiffBitsPerPixel.Pixel8 }, - { TestImages.Tiff.RgbUncompressed, TiffBitsPerPixel.Pixel24 }, + { Calliphora_BiColorUncompressed, TiffBitsPerPixel.Pixel1 }, + { GrayscaleUncompressed, TiffBitsPerPixel.Pixel8 }, + { RgbUncompressed, TiffBitsPerPixel.Pixel24 }, }; + [Theory] + [InlineData(TiffEncodingMode.Default, TiffEncoderCompression.None, TiffBitsPerPixel.Pixel24, TiffCompression.None)] + [InlineData(TiffEncodingMode.Rgb, TiffEncoderCompression.None, TiffBitsPerPixel.Pixel24, TiffCompression.None)] + [InlineData(TiffEncodingMode.ColorPalette, TiffEncoderCompression.None, TiffBitsPerPixel.Pixel8, TiffCompression.None)] + [InlineData(TiffEncodingMode.Gray, TiffEncoderCompression.None, TiffBitsPerPixel.Pixel8, TiffCompression.None)] + [InlineData(TiffEncodingMode.BiColor, TiffEncoderCompression.None, TiffBitsPerPixel.Pixel1, TiffCompression.None)] + [InlineData(TiffEncodingMode.Default, TiffEncoderCompression.Deflate, TiffBitsPerPixel.Pixel24, TiffCompression.Deflate)] + [InlineData(TiffEncodingMode.Rgb, TiffEncoderCompression.Deflate, TiffBitsPerPixel.Pixel24, TiffCompression.Deflate)] + [InlineData(TiffEncodingMode.ColorPalette, TiffEncoderCompression.Deflate, TiffBitsPerPixel.Pixel8, TiffCompression.Deflate)] + [InlineData(TiffEncodingMode.Gray, TiffEncoderCompression.Deflate, TiffBitsPerPixel.Pixel8, TiffCompression.Deflate)] + [InlineData(TiffEncodingMode.BiColor, TiffEncoderCompression.Deflate, TiffBitsPerPixel.Pixel1, TiffCompression.Deflate)] + [InlineData(TiffEncodingMode.Default, TiffEncoderCompression.PackBits, TiffBitsPerPixel.Pixel24, TiffCompression.PackBits)] + [InlineData(TiffEncodingMode.Rgb, TiffEncoderCompression.PackBits, TiffBitsPerPixel.Pixel24, TiffCompression.PackBits)] + [InlineData(TiffEncodingMode.ColorPalette, TiffEncoderCompression.PackBits, TiffBitsPerPixel.Pixel8, TiffCompression.PackBits)] + [InlineData(TiffEncodingMode.Gray, TiffEncoderCompression.PackBits, TiffBitsPerPixel.Pixel8, TiffCompression.PackBits)] + [InlineData(TiffEncodingMode.BiColor, TiffEncoderCompression.PackBits, TiffBitsPerPixel.Pixel1, TiffCompression.PackBits)] + [InlineData(TiffEncodingMode.Rgb, TiffEncoderCompression.Lzw, TiffBitsPerPixel.Pixel24, TiffCompression.Lzw)] + [InlineData(TiffEncodingMode.ColorPalette, TiffEncoderCompression.Lzw, TiffBitsPerPixel.Pixel8, TiffCompression.Lzw)] + [InlineData(TiffEncodingMode.Gray, TiffEncoderCompression.Lzw, TiffBitsPerPixel.Pixel8, TiffCompression.Lzw)] + [InlineData(TiffEncodingMode.BiColor, TiffEncoderCompression.CcittGroup3Fax, TiffBitsPerPixel.Pixel1, TiffCompression.CcittGroup3Fax)] + [InlineData(TiffEncodingMode.BiColor, TiffEncoderCompression.ModifiedHuffman, TiffBitsPerPixel.Pixel1, TiffCompression.Ccitt1D)] + public void EncoderOptions_Work(TiffEncodingMode mode, TiffEncoderCompression compression, TiffBitsPerPixel expectedBitsPerPixel, TiffCompression expectedCompression) + { + // arrange + var tiffEncoder = new TiffEncoder { Mode = mode, Compression = compression }; + Image input = new Image(10, 10); + using var memStream = new MemoryStream(); + + // act + input.Save(memStream, tiffEncoder); + + // assert + memStream.Position = 0; + using var output = Image.Load(memStream); + TiffMetadata meta = output.Metadata.GetTiffMetadata(); + Assert.Equal(expectedBitsPerPixel, meta.BitsPerPixel); + Assert.Equal(expectedCompression, meta.Compression); + } + [Theory] [MemberData(nameof(TiffBitsPerPixelFiles))] public void TiffEncoder_PreserveBitsPerPixel(string imagePath, TiffBitsPerPixel expectedBitsPerPixel) From f9d953cdadcefcd4ab0d15d130a2978ed9b48f37 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 7 Dec 2020 16:29:47 +0100 Subject: [PATCH 140/275] Simplified setting the compression in the tiff encoder --- .../Formats/Tiff/TiffEncoderCore.cs | 94 +++++-------------- 1 file changed, 26 insertions(+), 68 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 50f93f71f..f7f98929b 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -426,79 +426,37 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff private ushort GetCompressionType() { - if (this.CompressionType == TiffEncoderCompression.Deflate && this.Mode == TiffEncodingMode.Default) - { - return (ushort)TiffCompression.Deflate; - } - - if (this.CompressionType == TiffEncoderCompression.Deflate && this.Mode == TiffEncodingMode.Rgb) - { - return (ushort)TiffCompression.Deflate; - } - - if (this.CompressionType == TiffEncoderCompression.Lzw && this.Mode == TiffEncodingMode.Rgb) - { - return (ushort)TiffCompression.Lzw; - } - - if (this.CompressionType == TiffEncoderCompression.PackBits && this.Mode == TiffEncodingMode.Default) - { - return (ushort)TiffCompression.PackBits; - } - - if (this.CompressionType == TiffEncoderCompression.PackBits && this.Mode == TiffEncodingMode.Rgb) - { - return (ushort)TiffCompression.PackBits; - } - - if (this.CompressionType == TiffEncoderCompression.Deflate && this.Mode == TiffEncodingMode.Gray) - { - return (ushort)TiffCompression.Deflate; - } - - if (this.CompressionType == TiffEncoderCompression.Lzw && this.Mode == TiffEncodingMode.Gray) - { - return (ushort)TiffCompression.Lzw; - } - - if (this.CompressionType == TiffEncoderCompression.PackBits && this.Mode == TiffEncodingMode.Gray) - { - return (ushort)TiffCompression.PackBits; - } - - if (this.CompressionType == TiffEncoderCompression.Deflate && this.Mode == TiffEncodingMode.ColorPalette) - { - return (ushort)TiffCompression.Deflate; - } - - if (this.CompressionType == TiffEncoderCompression.Lzw && this.Mode == TiffEncodingMode.ColorPalette) - { - return (ushort)TiffCompression.Lzw; - } + switch (this.CompressionType) + { + case TiffEncoderCompression.Deflate: + // Deflate is allowed for all modes. + return (ushort)TiffCompression.Deflate; + case TiffEncoderCompression.PackBits: + // PackBits is allowed for all modes. + return (ushort)TiffCompression.PackBits; + case TiffEncoderCompression.Lzw: + if (this.Mode == TiffEncodingMode.Rgb || this.Mode == TiffEncodingMode.Gray || this.Mode == TiffEncodingMode.ColorPalette) + { + return (ushort)TiffCompression.Lzw; + } - if (this.CompressionType == TiffEncoderCompression.PackBits && this.Mode == TiffEncodingMode.ColorPalette) - { - return (ushort)TiffCompression.PackBits; - } + break; - if (this.CompressionType == TiffEncoderCompression.Deflate && this.Mode == TiffEncodingMode.BiColor) - { - return (ushort)TiffCompression.Deflate; - } + case TiffEncoderCompression.CcittGroup3Fax: + if (this.Mode == TiffEncodingMode.BiColor) + { + return (ushort)TiffCompression.CcittGroup3Fax; + } - if (this.CompressionType == TiffEncoderCompression.PackBits && this.Mode == TiffEncodingMode.BiColor) - { - return (ushort)TiffCompression.PackBits; - } + break; - if (this.CompressionType == TiffEncoderCompression.CcittGroup3Fax && this.Mode == TiffEncodingMode.BiColor) - { - return (ushort)TiffCompression.CcittGroup3Fax; - } + case TiffEncoderCompression.ModifiedHuffman: + if (this.Mode == TiffEncodingMode.BiColor) + { + return (ushort)TiffCompression.Ccitt1D; + } - if (this.CompressionType == TiffEncoderCompression.ModifiedHuffman && this.Mode == TiffEncodingMode.BiColor) - { - return (ushort)TiffCompression.Ccitt1D; + break; } return (ushort)TiffCompression.None; From 5db5dd13f19bb153868fddf982c9aaba1e4333f8 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 7 Dec 2020 16:49:31 +0100 Subject: [PATCH 141/275] Move ZLib related classes from PNG folder to ImageSharp/Compression folder --- .../{Formats/Png => Compression}/Zlib/Adler32.cs | 2 +- .../{Formats/Png => Compression}/Zlib/Crc32.Lut.cs | 2 +- .../{Formats/Png => Compression}/Zlib/Crc32.cs | 2 +- .../{ => Zlib}/DeflateCompressionLevel.cs | 2 +- .../Png => Compression}/Zlib/DeflateThrowHelper.cs | 2 +- .../{Formats/Png => Compression}/Zlib/Deflater.cs | 2 +- .../Png => Compression}/Zlib/DeflaterConstants.cs | 2 +- .../Png => Compression}/Zlib/DeflaterEngine.cs | 2 +- .../Png => Compression}/Zlib/DeflaterHuffman.cs | 2 +- .../Zlib/DeflaterOutputStream.cs | 2 +- .../Zlib/DeflaterPendingBuffer.cs | 2 +- .../{Formats/Png => Compression}/Zlib/README.md | 0 .../Png => Compression}/Zlib/ZlibDeflateStream.cs | 4 ++-- .../Png => Compression}/Zlib/ZlibInflateStream.cs | 2 +- ...putation-generic-polynomials-pclmulqdq-paper.pdf | Bin src/ImageSharp/Formats/Png/PngDecoderCore.cs | 3 +-- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 4 +--- src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffEncoder.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs | 2 +- src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs | 5 ++--- .../General/Adler32Benchmark.cs | 2 +- .../ImageSharp.Benchmarks/General/Crc32Benchmark.cs | 2 +- tests/ImageSharp.Tests/Formats/Png/Adler32Tests.cs | 2 +- tests/ImageSharp.Tests/Formats/Png/Crc32Tests.cs | 2 +- .../Tiff/Compression/DeflateTiffCompressionTests.cs | 3 +-- 26 files changed, 26 insertions(+), 31 deletions(-) rename src/ImageSharp/{Formats/Png => Compression}/Zlib/Adler32.cs (99%) rename src/ImageSharp/{Formats/Png => Compression}/Zlib/Crc32.Lut.cs (98%) rename src/ImageSharp/{Formats/Png => Compression}/Zlib/Crc32.cs (99%) rename src/ImageSharp/Compression/{ => Zlib}/DeflateCompressionLevel.cs (97%) rename src/ImageSharp/{Formats/Png => Compression}/Zlib/DeflateThrowHelper.cs (96%) rename src/ImageSharp/{Formats/Png => Compression}/Zlib/Deflater.cs (99%) rename src/ImageSharp/{Formats/Png => Compression}/Zlib/DeflaterConstants.cs (98%) rename src/ImageSharp/{Formats/Png => Compression}/Zlib/DeflaterEngine.cs (99%) rename src/ImageSharp/{Formats/Png => Compression}/Zlib/DeflaterHuffman.cs (99%) rename src/ImageSharp/{Formats/Png => Compression}/Zlib/DeflaterOutputStream.cs (98%) rename src/ImageSharp/{Formats/Png => Compression}/Zlib/DeflaterPendingBuffer.cs (99%) rename src/ImageSharp/{Formats/Png => Compression}/Zlib/README.md (100%) rename src/ImageSharp/{Formats/Png => Compression}/Zlib/ZlibDeflateStream.cs (98%) rename src/ImageSharp/{Formats/Png => Compression}/Zlib/ZlibInflateStream.cs (99%) rename src/ImageSharp/{Formats/Png => Compression}/Zlib/fast-crc-computation-generic-polynomials-pclmulqdq-paper.pdf (100%) diff --git a/src/ImageSharp/Formats/Png/Zlib/Adler32.cs b/src/ImageSharp/Compression/Zlib/Adler32.cs similarity index 99% rename from src/ImageSharp/Formats/Png/Zlib/Adler32.cs rename to src/ImageSharp/Compression/Zlib/Adler32.cs index 534aba8f5..9b3abd298 100644 --- a/src/ImageSharp/Formats/Png/Zlib/Adler32.cs +++ b/src/ImageSharp/Compression/Zlib/Adler32.cs @@ -9,7 +9,7 @@ using System.Runtime.Intrinsics.X86; #endif #pragma warning disable IDE0007 // Use implicit type -namespace SixLabors.ImageSharp.Formats.Png.Zlib +namespace SixLabors.ImageSharp.Compression.Zlib { /// /// Calculates the 32 bit Adler checksum of a given buffer according to diff --git a/src/ImageSharp/Formats/Png/Zlib/Crc32.Lut.cs b/src/ImageSharp/Compression/Zlib/Crc32.Lut.cs similarity index 98% rename from src/ImageSharp/Formats/Png/Zlib/Crc32.Lut.cs rename to src/ImageSharp/Compression/Zlib/Crc32.Lut.cs index 500783353..059bd9f31 100644 --- a/src/ImageSharp/Formats/Png/Zlib/Crc32.Lut.cs +++ b/src/ImageSharp/Compression/Zlib/Crc32.Lut.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Png.Zlib +namespace SixLabors.ImageSharp.Compression.Zlib { /// /// Contains precalulated tables for scalar calculations. diff --git a/src/ImageSharp/Formats/Png/Zlib/Crc32.cs b/src/ImageSharp/Compression/Zlib/Crc32.cs similarity index 99% rename from src/ImageSharp/Formats/Png/Zlib/Crc32.cs rename to src/ImageSharp/Compression/Zlib/Crc32.cs index 6b19987cb..0ba368df6 100644 --- a/src/ImageSharp/Formats/Png/Zlib/Crc32.cs +++ b/src/ImageSharp/Compression/Zlib/Crc32.cs @@ -9,7 +9,7 @@ using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.X86; #endif -namespace SixLabors.ImageSharp.Formats.Png.Zlib +namespace SixLabors.ImageSharp.Compression.Zlib { /// /// Calculates the 32 bit Cyclic Redundancy Check (CRC) checksum of a given buffer diff --git a/src/ImageSharp/Compression/DeflateCompressionLevel.cs b/src/ImageSharp/Compression/Zlib/DeflateCompressionLevel.cs similarity index 97% rename from src/ImageSharp/Compression/DeflateCompressionLevel.cs rename to src/ImageSharp/Compression/Zlib/DeflateCompressionLevel.cs index 9656bf4cc..2edf76e7d 100644 --- a/src/ImageSharp/Compression/DeflateCompressionLevel.cs +++ b/src/ImageSharp/Compression/Zlib/DeflateCompressionLevel.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Compression +namespace SixLabors.ImageSharp.Compression.Zlib { /// /// Provides enumeration of available deflate compression levels. diff --git a/src/ImageSharp/Formats/Png/Zlib/DeflateThrowHelper.cs b/src/ImageSharp/Compression/Zlib/DeflateThrowHelper.cs similarity index 96% rename from src/ImageSharp/Formats/Png/Zlib/DeflateThrowHelper.cs rename to src/ImageSharp/Compression/Zlib/DeflateThrowHelper.cs index a5d129c92..02590ca25 100644 --- a/src/ImageSharp/Formats/Png/Zlib/DeflateThrowHelper.cs +++ b/src/ImageSharp/Compression/Zlib/DeflateThrowHelper.cs @@ -4,7 +4,7 @@ using System; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.Formats.Png.Zlib +namespace SixLabors.ImageSharp.Compression.Zlib { internal static class DeflateThrowHelper { diff --git a/src/ImageSharp/Formats/Png/Zlib/Deflater.cs b/src/ImageSharp/Compression/Zlib/Deflater.cs similarity index 99% rename from src/ImageSharp/Formats/Png/Zlib/Deflater.cs rename to src/ImageSharp/Compression/Zlib/Deflater.cs index 838921581..800c96703 100644 --- a/src/ImageSharp/Formats/Png/Zlib/Deflater.cs +++ b/src/ImageSharp/Compression/Zlib/Deflater.cs @@ -5,7 +5,7 @@ using System; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Png.Zlib +namespace SixLabors.ImageSharp.Compression.Zlib { /// /// This class compresses input with the deflate algorithm described in RFC 1951. diff --git a/src/ImageSharp/Formats/Png/Zlib/DeflaterConstants.cs b/src/ImageSharp/Compression/Zlib/DeflaterConstants.cs similarity index 98% rename from src/ImageSharp/Formats/Png/Zlib/DeflaterConstants.cs rename to src/ImageSharp/Compression/Zlib/DeflaterConstants.cs index ec224d748..30bd75ffc 100644 --- a/src/ImageSharp/Formats/Png/Zlib/DeflaterConstants.cs +++ b/src/ImageSharp/Compression/Zlib/DeflaterConstants.cs @@ -4,7 +4,7 @@ // using System; -namespace SixLabors.ImageSharp.Formats.Png.Zlib +namespace SixLabors.ImageSharp.Compression.Zlib { /// /// This class contains constants used for deflation. diff --git a/src/ImageSharp/Formats/Png/Zlib/DeflaterEngine.cs b/src/ImageSharp/Compression/Zlib/DeflaterEngine.cs similarity index 99% rename from src/ImageSharp/Formats/Png/Zlib/DeflaterEngine.cs rename to src/ImageSharp/Compression/Zlib/DeflaterEngine.cs index 797f5d210..d3cfa7c3d 100644 --- a/src/ImageSharp/Formats/Png/Zlib/DeflaterEngine.cs +++ b/src/ImageSharp/Compression/Zlib/DeflaterEngine.cs @@ -6,7 +6,7 @@ using System.Buffers; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Png.Zlib +namespace SixLabors.ImageSharp.Compression.Zlib { /// /// Strategies for deflater diff --git a/src/ImageSharp/Formats/Png/Zlib/DeflaterHuffman.cs b/src/ImageSharp/Compression/Zlib/DeflaterHuffman.cs similarity index 99% rename from src/ImageSharp/Formats/Png/Zlib/DeflaterHuffman.cs rename to src/ImageSharp/Compression/Zlib/DeflaterHuffman.cs index 96b47fb24..d6892dfd2 100644 --- a/src/ImageSharp/Formats/Png/Zlib/DeflaterHuffman.cs +++ b/src/ImageSharp/Compression/Zlib/DeflaterHuffman.cs @@ -7,7 +7,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Png.Zlib +namespace SixLabors.ImageSharp.Compression.Zlib { /// /// Performs Deflate Huffman encoding. diff --git a/src/ImageSharp/Formats/Png/Zlib/DeflaterOutputStream.cs b/src/ImageSharp/Compression/Zlib/DeflaterOutputStream.cs similarity index 98% rename from src/ImageSharp/Formats/Png/Zlib/DeflaterOutputStream.cs rename to src/ImageSharp/Compression/Zlib/DeflaterOutputStream.cs index 5c5651996..cbbf7ea79 100644 --- a/src/ImageSharp/Formats/Png/Zlib/DeflaterOutputStream.cs +++ b/src/ImageSharp/Compression/Zlib/DeflaterOutputStream.cs @@ -5,7 +5,7 @@ using System; using System.IO; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Png.Zlib +namespace SixLabors.ImageSharp.Compression.Zlib { /// /// A special stream deflating or compressing the bytes that are diff --git a/src/ImageSharp/Formats/Png/Zlib/DeflaterPendingBuffer.cs b/src/ImageSharp/Compression/Zlib/DeflaterPendingBuffer.cs similarity index 99% rename from src/ImageSharp/Formats/Png/Zlib/DeflaterPendingBuffer.cs rename to src/ImageSharp/Compression/Zlib/DeflaterPendingBuffer.cs index f702a7ead..36dfd92da 100644 --- a/src/ImageSharp/Formats/Png/Zlib/DeflaterPendingBuffer.cs +++ b/src/ImageSharp/Compression/Zlib/DeflaterPendingBuffer.cs @@ -6,7 +6,7 @@ using System.Buffers; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Png.Zlib +namespace SixLabors.ImageSharp.Compression.Zlib { /// /// Stores pending data for writing data to the Deflater. diff --git a/src/ImageSharp/Formats/Png/Zlib/README.md b/src/ImageSharp/Compression/Zlib/README.md similarity index 100% rename from src/ImageSharp/Formats/Png/Zlib/README.md rename to src/ImageSharp/Compression/Zlib/README.md diff --git a/src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs b/src/ImageSharp/Compression/Zlib/ZlibDeflateStream.cs similarity index 98% rename from src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs rename to src/ImageSharp/Compression/Zlib/ZlibDeflateStream.cs index 89280ee44..44883665a 100644 --- a/src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs +++ b/src/ImageSharp/Compression/Zlib/ZlibDeflateStream.cs @@ -4,10 +4,10 @@ using System; using System.IO; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Compression; +using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Png.Zlib +namespace SixLabors.ImageSharp.Compression.Zlib { /// /// Provides methods and properties for compressing streams by using the Zlib Deflate algorithm. diff --git a/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs b/src/ImageSharp/Compression/Zlib/ZlibInflateStream.cs similarity index 99% rename from src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs rename to src/ImageSharp/Compression/Zlib/ZlibInflateStream.cs index 52ef0e85b..f4b0543b8 100644 --- a/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs +++ b/src/ImageSharp/Compression/Zlib/ZlibInflateStream.cs @@ -6,7 +6,7 @@ using System.IO; using System.IO.Compression; using SixLabors.ImageSharp.IO; -namespace SixLabors.ImageSharp.Formats.Png.Zlib +namespace SixLabors.ImageSharp.Compression.Zlib { /// /// Provides methods and properties for deframing streams from PNGs. diff --git a/src/ImageSharp/Formats/Png/Zlib/fast-crc-computation-generic-polynomials-pclmulqdq-paper.pdf b/src/ImageSharp/Compression/Zlib/fast-crc-computation-generic-polynomials-pclmulqdq-paper.pdf similarity index 100% rename from src/ImageSharp/Formats/Png/Zlib/fast-crc-computation-generic-polynomials-pclmulqdq-paper.pdf rename to src/ImageSharp/Compression/Zlib/fast-crc-computation-generic-polynomials-pclmulqdq-paper.pdf diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 3fa0e3f58..c2c336c03 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -10,10 +10,9 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using System.Threading; -using System.Threading.Tasks; +using SixLabors.ImageSharp.Compression.Zlib; using SixLabors.ImageSharp.Formats.Png.Chunks; using SixLabors.ImageSharp.Formats.Png.Filters; -using SixLabors.ImageSharp.Formats.Png.Zlib; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 5d2af8ec6..7a285eb70 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -8,11 +8,9 @@ using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; -using System.Threading.Tasks; -using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Compression.Zlib; using SixLabors.ImageSharp.Formats.Png.Chunks; using SixLabors.ImageSharp.Formats.Png.Filters; -using SixLabors.ImageSharp.Formats.Png.Zlib; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; diff --git a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs index cbc1f7c76..46fbf1c57 100644 --- a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs +++ b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Compression; +using SixLabors.ImageSharp.Compression.Zlib; using SixLabors.ImageSharp.Processing.Processors.Quantization; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs index 38fdac98b..1546aa803 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs @@ -6,7 +6,7 @@ using System.Threading; using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Compression; +using SixLabors.ImageSharp.Compression.Zlib; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Quantization; diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index f7f98929b..6afe5f933 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -7,7 +7,7 @@ using System.IO; using System.Threading; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Compression; +using SixLabors.ImageSharp.Compression.Zlib; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; using SixLabors.ImageSharp.Memory; diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs index bec317031..6deeb0a63 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -4,11 +4,10 @@ using System; using System.Buffers; using System.IO; - using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Compression; + +using SixLabors.ImageSharp.Compression.Zlib; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; -using SixLabors.ImageSharp.Formats.Png.Zlib; using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata.Profiles.Exif; diff --git a/tests/ImageSharp.Benchmarks/General/Adler32Benchmark.cs b/tests/ImageSharp.Benchmarks/General/Adler32Benchmark.cs index eba4bcbb4..4cf442fc8 100644 --- a/tests/ImageSharp.Benchmarks/General/Adler32Benchmark.cs +++ b/tests/ImageSharp.Benchmarks/General/Adler32Benchmark.cs @@ -3,7 +3,7 @@ using System; using BenchmarkDotNet.Attributes; -using SixLabors.ImageSharp.Formats.Png.Zlib; +using SixLabors.ImageSharp.Compression.Zlib; using SharpAdler32 = ICSharpCode.SharpZipLib.Checksum.Adler32; namespace SixLabors.ImageSharp.Benchmarks.General diff --git a/tests/ImageSharp.Benchmarks/General/Crc32Benchmark.cs b/tests/ImageSharp.Benchmarks/General/Crc32Benchmark.cs index 2dcf03627..82f0aa33d 100644 --- a/tests/ImageSharp.Benchmarks/General/Crc32Benchmark.cs +++ b/tests/ImageSharp.Benchmarks/General/Crc32Benchmark.cs @@ -3,7 +3,7 @@ using System; using BenchmarkDotNet.Attributes; -using SixLabors.ImageSharp.Formats.Png.Zlib; +using SixLabors.ImageSharp.Compression.Zlib; using SharpCrc32 = ICSharpCode.SharpZipLib.Checksum.Crc32; namespace SixLabors.ImageSharp.Benchmarks.General diff --git a/tests/ImageSharp.Tests/Formats/Png/Adler32Tests.cs b/tests/ImageSharp.Tests/Formats/Png/Adler32Tests.cs index aadd30f2b..0886bd84d 100644 --- a/tests/ImageSharp.Tests/Formats/Png/Adler32Tests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/Adler32Tests.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; -using SixLabors.ImageSharp.Formats.Png.Zlib; +using SixLabors.ImageSharp.Compression.Zlib; using Xunit; using SharpAdler32 = ICSharpCode.SharpZipLib.Checksum.Adler32; diff --git a/tests/ImageSharp.Tests/Formats/Png/Crc32Tests.cs b/tests/ImageSharp.Tests/Formats/Png/Crc32Tests.cs index 1ae21e771..6bdad6ed4 100644 --- a/tests/ImageSharp.Tests/Formats/Png/Crc32Tests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/Crc32Tests.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; -using SixLabors.ImageSharp.Formats.Png.Zlib; +using SixLabors.ImageSharp.Compression.Zlib; using Xunit; using SharpCrc32 = ICSharpCode.SharpZipLib.Checksum.Crc32; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs index 5235fb011..17a18a346 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs @@ -2,10 +2,9 @@ // Licensed under the Apache License, Version 2.0. using System.IO; -using SixLabors.ImageSharp.Compression; +using SixLabors.ImageSharp.Compression.Zlib; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; -using SixLabors.ImageSharp.Formats.Png.Zlib; using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression From 4bf3d16789e11f1a6c72825d96d68065d96db0ce Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 8 Dec 2020 13:50:01 +0100 Subject: [PATCH 142/275] Reworked lzw encoder with a tree based approach based on a java implementation --- .../Formats/Tiff/TiffEncoderCore.cs | 2 +- .../Formats/Tiff/Utils/TiffLzwEncoder.cs | 571 ++++++------------ .../Formats/Tiff/Utils/TiffWriter.cs | 6 +- .../DeflateTiffCompressionTests.cs | 2 +- .../Compression/LzwTiffCompressionTests.cs | 18 +- 5 files changed, 194 insertions(+), 405 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 6afe5f933..2e8fdfc36 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -297,7 +297,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff var rowsPerStrip = new ExifLong(ExifTagValue.RowsPerStrip) { - // TODO: all rows in one strip for the start + // All rows in one strip. Value = (uint)image.Height }; diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs index 96db8e110..072b548a7 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs @@ -9,28 +9,46 @@ using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils { + /* + This implementation is a port of a java tiff encoder by Harald Kuhr: https://github.com/haraldk/TwelveMonkeys + + Original licence: + + BSD 3-Clause License + + * Copyright (c) 2015, Harald Kuhr + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + ** Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + /// /// Encodes and compresses the image data using dynamic Lempel-Ziv compression. /// /// - /// Adapted from Jef Poskanzer's Java port by way of J. M. G. Elliott. K Weiner 12/00 - /// - /// GIFCOMPR.C - GIF Image compression routines - /// - /// - /// Lempel-Ziv compression based on 'compress'. GIF modifications by - /// David Rowley (mgardi@watdcsu.waterloo.edu) - /// - /// GIF Image compression - modified 'compress' - /// - /// Based on: compress.c - File compression ala IEEE Computer, June 1984. - /// By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas) - /// Jim McKie (decvax!mcvax!jim) - /// Steve Davies (decvax!vax135!petsd!peora!srd) - /// Ken Turkowski (decvax!decwrl!turtlevax!ken) - /// James A. Woods (decvax!ihnp4!ames!jaw) - /// Joe Orost (decvax!vax135!petsd!joe) - /// /// /// This code is based on the used for GIF encoding. There is potential /// for a shared implementation. Differences between the GIF and TIFF implementations of the LZW @@ -42,173 +60,54 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// internal sealed class TiffLzwEncoder : IDisposable { - /// - /// The end-of-file marker - /// - private const int Eof = -1; - - /// - /// The maximum number of bits. - /// - private const int Bits = 12; - - /// - /// 80% occupancy - /// - private const int HashSize = 5003; - - /// - /// Mask used when shifting pixel values - /// - private static readonly int[] Masks = - { - 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, - 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF - }; - - /// - /// The working pixel array. - /// - private readonly IMemoryOwner pixelArray; + // Clear: Re-initialize tables. + private static readonly int ClearCode = 256; - /// - /// The initial code size. - /// - private readonly int initialCodeSize; + // End of Information. + private static readonly int EoiCode = 257; - /// - /// The hash table. - /// - private readonly IMemoryOwner hashTable; + private static readonly int MinBits = 9; + private static readonly int MaxBits = 12; - /// - /// The code table. - /// - private readonly IMemoryOwner codeTable; + private static readonly int TableSize = 1 << MaxBits; - /// - /// Define the storage for the packet accumulator. - /// - private readonly byte[] accumulators = new byte[256]; - - /// - /// A value indicating whether this instance of the given entity has been disposed. - /// - /// if this instance has been disposed; otherwise, . - /// - /// If the entity is disposed, it must not be disposed a second - /// time. The isDisposed field is set the first time the entity - /// is disposed. If the isDisposed field is true, then the Dispose() - /// method will not dispose again. This help not to prolong the entity's - /// life in the Garbage Collector. - /// - private bool isDisposed; + private readonly IMemoryOwner data; - /// - /// The current pixel - /// - private int currentPixel; + // A child is made up of a parent (or prefix) code plus a suffix byte + // and siblings are strings with a common parent(or prefix) and different suffix bytes. + private readonly IMemoryOwner children; - /// - /// Number of bits/code - /// - private int bitCount; + private readonly IMemoryOwner siblings; - /// - /// User settable max # bits/code - /// - private int maxbits = Bits; + private readonly IMemoryOwner suffixes; - /// - /// maximum code, given bitCount - /// - private int maxcode; + // Initial setup + private int parent; + private int bitsPerCode; + private int nextValidCode; + private int maxCode; - /// - /// should NEVER generate this code - /// - private int maxmaxcode = 1 << Bits; - - /// - /// For dynamic table sizing - /// - private int hsize = HashSize; - - /// - /// First unused entry - /// - private int freeEntry; - - /// - /// Block compression parameters -- after all codes are used up, - /// and compression rate changes, start over. - /// - private bool clearFlag; - - /// - /// Algorithm: use open addressing double hashing (no chaining) on the - /// prefix code / next character combination. We do a variant of Knuth's - /// algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime - /// secondary probe. Here, the modular division first probe is gives way - /// to a faster exclusive-or manipulation. Also do block compression with - /// an adaptive reset, whereby the code table is cleared when the compression - /// ratio decreases, but after the table fills. The variable-length output - /// codes are re-sized at this point, and a special CLEAR code is generated - /// for the decompressor. Late addition: construct the table according to - /// file size for noticeable speed improvement on small files. Please direct - /// questions about this implementation to ames!jaw. - /// - private int globalInitialBits; - - /// - /// The clear code. - /// - private int clearCode; - - /// - /// The end-of-file code. - /// - private int eofCode; - - /// - /// Output the given code. - /// Inputs: - /// code: A bitCount-bit integer. If == -1, then EOF. This assumes - /// that bitCount =< wordsize - 1. - /// Outputs: - /// Outputs code to the file. - /// Assumptions: - /// Chars are 8 bits long. - /// Algorithm: - /// Maintain a BITS character long buffer (so that 8 codes will - /// fit in it exactly). Use the VAX insv instruction to insert each - /// code in turn. When the buffer fills up empty it and start over. - /// - private int currentAccumulator; - - /// - /// The current bits. - /// - private int currentBits; - - /// - /// Number of characters so far in this 'packet' - /// - private int accumulatorCount; + // Buffer for partial codes + private int bits; + private int bitPos; + private int bufferPosition; /// /// Initializes a new instance of the class. /// - /// The array of indexed pixels. - /// The color depth in bits. + /// The data to compress. /// The memory allocator. - public TiffLzwEncoder(MemoryAllocator memoryAllocator, IMemoryOwner indexedPixels, int colorDepth) + public TiffLzwEncoder(MemoryAllocator memoryAllocator, IMemoryOwner data) { - this.pixelArray = indexedPixels; - this.initialCodeSize = Math.Max(2, colorDepth); - - this.hashTable = memoryAllocator.Allocate(HashSize, AllocationOptions.Clean); - this.codeTable = memoryAllocator.Allocate(HashSize, AllocationOptions.Clean); + this.data = data; + this.children = memoryAllocator.Allocate(TableSize, AllocationOptions.Clean); + this.siblings = memoryAllocator.Allocate(TableSize, AllocationOptions.Clean); + this.suffixes = memoryAllocator.Allocate(TableSize, AllocationOptions.Clean); + + this.parent = -1; + this.bitsPerCode = MinBits; + this.nextValidCode = EoiCode + 1; + this.maxCode = (1 << this.bitsPerCode) - 1; } /// @@ -217,282 +116,158 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// The stream to write to. public void Encode(Stream stream) { - this.currentPixel = 0; - - // Compress and write the pixel data - this.Compress(this.initialCodeSize + 1, stream); - } - - /// - public void Dispose() - { - // Do not change this code. Put cleanup code in Dispose(bool disposing) above. - this.Dispose(true); - } - - /// - /// Gets the maximum code value - /// - /// The number of bits - /// See - private static int GetMaxcode(int bitCount) - { - return (1 << bitCount) - 1; - } - - /// - /// Add a character to the end of the current packet, and if it is 254 characters, - /// flush the packet to disk. - /// - /// The character to add. - /// The stream to write to. - private void AddCharacter(byte c, Stream stream) - { - this.accumulators[this.accumulatorCount++] = c; - if (this.accumulatorCount >= 254) - { - this.FlushPacket(stream); - } - } + Span childrenSpan = this.children.GetSpan(); + Span suffixesSpan = this.suffixes.GetSpan(); + Span siblingsSpan = this.siblings.GetSpan(); + int length = this.data.Length(); - /// - /// Table clear for block compress - /// - /// The output stream. - private void ClearBlock(Stream stream) - { - this.ResetCodeTable(this.hsize); - this.freeEntry = this.clearCode + 2; - this.clearFlag = true; - - this.Output(this.clearCode, stream); - } - - /// - /// Reset the code table. - /// - /// The hash size. - private void ResetCodeTable(int size) - { - Span hashTableSpan = this.hashTable.GetSpan(); - for (int i = 0; i < size; ++i) + if (length == 0) { - hashTableSpan[i] = -1; + return; } - } - - /// - /// Compress the packets to the stream. - /// - /// The initial bits. - /// The stream to write to. - private void Compress(int intialBits, Stream stream) - { - int fcode; - int c; - int ent; - int hsizeReg; - int hshift; - - // Set up the globals: globalInitialBits - initial number of bits - this.globalInitialBits = intialBits; - - // Set up the necessary values - this.clearFlag = false; - this.bitCount = this.globalInitialBits; - this.maxcode = GetMaxcode(this.bitCount); - this.clearCode = 1 << (intialBits - 1); - this.eofCode = this.clearCode + 1; - this.freeEntry = this.clearCode + 2; - - this.accumulatorCount = 0; // clear packet - - ent = this.NextPixel(); - - hshift = 0; - for (fcode = this.hsize; fcode < 65536; fcode *= 2) + if (this.parent == -1) { - ++hshift; + // Init stream. + this.WriteCode(stream, ClearCode); + this.parent = this.ReadNextByte() & 0xff; } - hshift = 8 - hshift; // set hash code range bound - - hsizeReg = this.hsize; - - this.ResetCodeTable(hsizeReg); // clear hash table - - this.Output(this.clearCode, stream); - - Span hashTableSpan = this.hashTable.GetSpan(); - Span codeTableSpan = this.codeTable.GetSpan(); - while ((c = this.NextPixel()) != Eof) + while (this.bufferPosition < this.data.Length()) { - fcode = (c << this.maxbits) + ent; - int i = (c << hshift) ^ ent /* = 0 */; + int value = this.ReadNextByte() & 0xff; + int child = childrenSpan[this.parent]; - if (hashTableSpan[i] == fcode) + if (child > 0) { - ent = codeTableSpan[i]; - continue; - } - - // Non-empty slot - if (hashTableSpan[i] >= 0) - { - int disp = hsizeReg - i; - if (i == 0) + if (suffixesSpan[child] == value) { - disp = 1; + this.parent = child; } - - do + else { - if ((i -= disp) < 0) - { - i += hsizeReg; - } + int sibling = child; - if (hashTableSpan[i] == fcode) + while (true) { - ent = codeTableSpan[i]; - break; + if (siblingsSpan[sibling] > 0) + { + sibling = siblingsSpan[sibling]; + + if (suffixesSpan[sibling] == value) + { + this.parent = sibling; + break; + } + } + else + { + siblingsSpan[sibling] = (short)this.nextValidCode; + suffixesSpan[this.nextValidCode] = (short)value; + this.WriteCode(stream, this.parent); + this.parent = value; + this.nextValidCode++; + + this.IncreaseCodeSizeOrResetIfNeeded(stream); + + break; + } } } - while (hashTableSpan[i] >= 0); - - if (hashTableSpan[i] == fcode) - { - continue; - } - } - - this.Output(ent, stream); - ent = c; - if (this.freeEntry < this.maxmaxcode) - { - codeTableSpan[i] = this.freeEntry++; // code -> hashtable - hashTableSpan[i] = fcode; } else { - this.ClearBlock(stream); + childrenSpan[this.parent] = (short)this.nextValidCode; + suffixesSpan[this.nextValidCode] = (short)value; + this.WriteCode(stream, this.parent); + this.parent = value; + this.nextValidCode++; + + this.IncreaseCodeSizeOrResetIfNeeded(stream); } } - // Put out the final code. - this.Output(ent, stream); - - this.Output(this.eofCode, stream); - } + // Write EOI when we are done. + this.WriteCode(stream, this.parent); + this.WriteCode(stream, EoiCode); - /// - /// Flush the packet to disk, and reset the accumulator. - /// - /// The output stream. - private void FlushPacket(Stream outStream) - { - if (this.accumulatorCount > 0) + // Flush partial codes by writing 0 pad. + if (this.bitPos > 0) { - outStream.Write(this.accumulators, 0, this.accumulatorCount); - this.accumulatorCount = 0; + this.WriteCode(stream, 0); } } - /// - /// Return the next pixel from the image - /// - /// - /// The - /// - private int NextPixel() + /// + public void Dispose() { - if (this.currentPixel == this.pixelArray.Length()) - { - return Eof; - } - - this.currentPixel++; - return this.pixelArray.GetSpan()[this.currentPixel - 1] & 0xff; + this.children.Dispose(); + this.siblings.Dispose(); + this.suffixes.Dispose(); } - /// - /// Output the current code to the stream. - /// - /// The code. - /// The stream to write to. - private void Output(int code, Stream outs) + private byte ReadNextByte() { - this.currentAccumulator &= Masks[this.currentBits]; - - if (this.currentBits > 0) - { - this.currentAccumulator |= code << this.currentBits; - } - else - { - this.currentAccumulator = code; - } - - this.currentBits += this.bitCount; - - while (this.currentBits >= 8) - { - this.AddCharacter((byte)(this.currentAccumulator & 0xff), outs); - this.currentAccumulator >>= 8; - this.currentBits -= 8; - } + Span dataSpan = this.data.GetSpan(); + var nextByte = dataSpan[this.bufferPosition]; + this.bufferPosition++; + return nextByte; + } - // If the next entry is going to be too big for the code size, - // then increase it, if possible. - if (this.freeEntry > this.maxcode || this.clearFlag) + private void IncreaseCodeSizeOrResetIfNeeded(Stream stream) + { + if (this.nextValidCode > this.maxCode) { - if (this.clearFlag) + if (this.bitsPerCode == MaxBits) { - this.maxcode = GetMaxcode(this.bitCount = this.globalInitialBits); - this.clearFlag = false; + // Reset stream by writing Clear code. + this.WriteCode(stream, ClearCode); + + // Reset tables. + this.ResetTables(); } else { - ++this.bitCount; - this.maxcode = this.bitCount == this.maxbits - ? this.maxmaxcode - : GetMaxcode(this.bitCount); + // Increase code size. + this.bitsPerCode++; + this.maxCode = MaxValue(this.bitsPerCode); } } + } - if (code == this.eofCode) - { - // At EOF, write the rest of the buffer. - while (this.currentBits > 0) - { - this.AddCharacter((byte)(this.currentAccumulator & 0xff), outs); - this.currentAccumulator >>= 8; - this.currentBits -= 8; - } + private void WriteCode(Stream stream, int code) + { + this.bits = (this.bits << this.bitsPerCode) | (code & this.maxCode); + this.bitPos += this.bitsPerCode; - this.FlushPacket(outs); + while (this.bitPos >= 8) + { + int b = (this.bits >> (this.bitPos - 8)) & 0xff; + stream.WriteByte((byte)b); + this.bitPos -= 8; } + + this.bits &= BitmaskFor(this.bitPos); } - /// - /// Disposes the object and frees resources for the Garbage Collector. - /// - /// If true, the object gets disposed. - private void Dispose(bool disposing) + private void ResetTables() { - if (this.isDisposed) - { - return; - } + this.children.GetSpan().Fill(0); + this.siblings.GetSpan().Fill(0); - if (disposing) - { - this.hashTable.Dispose(); - this.codeTable.Dispose(); - } + this.bitsPerCode = MinBits; + this.maxCode = MaxValue(this.bitsPerCode); + this.nextValidCode = EoiCode + 1; + } - this.isDisposed = true; + private static int MaxValue(int codeLen) + { + return (1 << codeLen) - 1; + } + + private static int BitmaskFor(int bits) + { + return MaxValue(bits); } } } diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs index 6deeb0a63..09db48890 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -238,7 +238,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils rowSpan.CopyTo(pixels.Slice(y * image.Width * 3)); } - using var lzwEncoder = new TiffLzwEncoder(this.memoryAllocator, pixelData, 8); + using var lzwEncoder = new TiffLzwEncoder(this.memoryAllocator, pixelData); lzwEncoder.Encode(memoryStream); byte[] buffer = memoryStream.ToArray(); @@ -441,7 +441,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils } } - using var lzwEncoder = new TiffLzwEncoder(this.memoryAllocator, pixelData, 8); + using var lzwEncoder = new TiffLzwEncoder(this.memoryAllocator, pixelData); lzwEncoder.Encode(memoryStream); byte[] buffer = memoryStream.ToArray(); @@ -584,7 +584,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils rowSpan.CopyTo(pixels.Slice(y * image.Width)); } - using var lzwEncoder = new TiffLzwEncoder(this.memoryAllocator, pixelData, 8); + using var lzwEncoder = new TiffLzwEncoder(this.memoryAllocator, pixelData); lzwEncoder.Encode(memoryStream); byte[] buffer = memoryStream.ToArray(); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs index 17a18a346..fbac89e9a 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs @@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression [InlineData(new byte[] { 42, 16, 128, 53, 96, 218, 7, 64, 3, 4, 97 })] // Random bytes [InlineData(new byte[] { 1, 2, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 3, 4 })] // Repeated bytes [InlineData(new byte[] { 1, 2, 42, 53, 42, 53, 42, 53, 42, 53, 42, 53, 3, 4 })] // Repeated sequence - public void Decompress_ReadsData(byte[] data) + public void Compress_Decompress_Roundtrip_Works(byte[] data) { using (Stream stream = CreateCompressedStream(data)) { diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs index 1837fe98e..79cc1b1a8 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs @@ -15,13 +15,26 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression [Trait("Format", "Tiff")] public class LzwTiffCompressionTests { + [Theory] + [InlineData(new byte[] { 1, 2, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 3, 4 }, new byte[] { 128, 0, 64, 66, 168, 36, 22, 12, 3, 2, 64, 64, 0, 0 })] // Repeated bytes + + public void Compress_Works(byte[] inputData, byte[] expectedCompressedData) + { + var compressedData = new byte[expectedCompressedData.Length]; + Stream streamData = CreateCompressedStream(inputData); + streamData.Read(compressedData, 0, expectedCompressedData.Length); + + Assert.Equal(expectedCompressedData, compressedData); + } + [Theory] [InlineData(new byte[] { })] [InlineData(new byte[] { 42 })] // One byte [InlineData(new byte[] { 42, 16, 128, 53, 96, 218, 7, 64, 3, 4, 97 })] // Random bytes [InlineData(new byte[] { 1, 2, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 3, 4 })] // Repeated bytes [InlineData(new byte[] { 1, 2, 42, 53, 42, 53, 42, 53, 42, 53, 42, 53, 3, 4 })] // Repeated sequence - public void Decompress_ReadsData(byte[] data) + + public void Compress_Decompress_Roundtrip_Works(byte[] data) { using Stream stream = CreateCompressedStream(data); var buffer = new byte[data.Length]; @@ -37,12 +50,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression using System.Buffers.IMemoryOwner data = Configuration.Default.MemoryAllocator.Allocate(inputData.Length); inputData.AsSpan().CopyTo(data.GetSpan()); - using (var encoder = new TiffLzwEncoder(Configuration.Default.MemoryAllocator, data, 8)) + using (var encoder = new TiffLzwEncoder(Configuration.Default.MemoryAllocator, data)) { encoder.Encode(compressedStream); } compressedStream.Seek(0, SeekOrigin.Begin); + return compressedStream; } } From 57b19f519e881c66043caa99a131ac2bbb29b880 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 8 Dec 2020 16:01:15 +0100 Subject: [PATCH 143/275] Do not register Tiff in the default config: The user must do this manually as long as this feature is considered experimental --- src/ImageSharp/Configuration.cs | 7 +++---- src/ImageSharp/Formats/Tiff/TiffDecoder.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffEncoder.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs | 4 ++-- tests/ImageSharp.Tests/ConfigurationTests.cs | 2 +- .../ImageSharp.Tests/Formats/Tiff/ImageExtensionsTest.cs | 8 ++++++++ tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs | 7 +++++++ tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs | 8 ++++++++ 8 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index 192bf5a73..7b39abd04 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -95,9 +95,9 @@ namespace SixLabors.ImageSharp } /// - /// Gets a set of properties for the Congiguration. + /// Gets a set of properties for the Configuration. /// - /// This can be used for storing global settings and defaults to be accessable to processors. + /// This can be used for storing global settings and defaults to be accessible to processors. public IDictionary Properties { get; } = new ConcurrentDictionary(); /// @@ -191,8 +191,7 @@ namespace SixLabors.ImageSharp new JpegConfigurationModule(), new GifConfigurationModule(), new BmpConfigurationModule(), - new TgaConfigurationModule(), - new TiffConfigurationModule()); + new TgaConfigurationModule()); } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs index 455d71aae..b7bce0a84 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs @@ -9,7 +9,7 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { /// - /// Image decoder for generating an image out of a TIFF stream. + /// EXPERIMENTAL! Image decoder for generating an image out of a TIFF stream. /// public class TiffDecoder : IImageDecoder, ITiffDecoderOptions, IImageInfoDetector { diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs index 1546aa803..0f333679e 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs @@ -13,7 +13,7 @@ using SixLabors.ImageSharp.Processing.Processors.Quantization; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { /// - /// Encoder for writing the data image to a stream in TIFF format. + /// EXPERIMENTAL! Encoder for writing the data image to a stream in TIFF format. /// public class TiffEncoder : IImageEncoder, ITiffEncoderOptions { diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 2e8fdfc36..1d16d51c4 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -308,13 +308,13 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff var xResolution = new ExifRational(ExifTagValue.XResolution) { - // TODO: what to use here as a default? + // TODO: This field is required according to the spec, what to use here as a default? Value = Rational.FromDouble(1.0d) }; var yResolution = new ExifRational(ExifTagValue.YResolution) { - // TODO: what to use here as a default? + // TODO: This field is required according to the spec, what to use here as a default? Value = Rational.FromDouble(1.0d) }; diff --git a/tests/ImageSharp.Tests/ConfigurationTests.cs b/tests/ImageSharp.Tests/ConfigurationTests.cs index f6111da5a..655e98c7f 100644 --- a/tests/ImageSharp.Tests/ConfigurationTests.cs +++ b/tests/ImageSharp.Tests/ConfigurationTests.cs @@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Tests public Configuration DefaultConfiguration { get; } - private readonly int expectedDefaultConfigurationCount = 6; + private readonly int expectedDefaultConfigurationCount = 5; public ConfigurationTests() { diff --git a/tests/ImageSharp.Tests/Formats/Tiff/ImageExtensionsTest.cs b/tests/ImageSharp.Tests/Formats/Tiff/ImageExtensionsTest.cs index 45a86185e..cc72560ac 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/ImageExtensionsTest.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/ImageExtensionsTest.cs @@ -13,6 +13,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Trait("Format", "Tiff")] public class ImageExtensionsTest { + public ImageExtensionsTest() + { + Configuration.Default.ImageFormatsManager.AddImageFormat(TiffFormat.Instance); + Configuration.Default.ImageFormatsManager.AddImageFormatDetector(new TiffImageFormatDetector()); + Configuration.Default.ImageFormatsManager.SetDecoder(TiffFormat.Instance, new TiffDecoder()); + Configuration.Default.ImageFormatsManager.SetEncoder(TiffFormat.Instance, new TiffEncoder()); + } + [Fact] public void SaveAsTiff_Path() { diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index 16f174720..90430fe11 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -28,6 +28,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff private static MagickReferenceDecoder ReferenceDecoder => new MagickReferenceDecoder(); + public TiffDecoderTests() + { + Configuration.Default.ImageFormatsManager.AddImageFormat(TiffFormat.Instance); + Configuration.Default.ImageFormatsManager.AddImageFormatDetector(new TiffImageFormatDetector()); + Configuration.Default.ImageFormatsManager.SetDecoder(TiffFormat.Instance, new TiffDecoder()); + } + [Theory] [WithFileCollection(nameof(NotSupportedImages), PixelTypes.Rgba32)] public void ThrowsNotSupported(TestImageProvider provider) diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 01196da50..a7e55d700 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -29,6 +29,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { RgbUncompressed, TiffBitsPerPixel.Pixel24 }, }; + public TiffEncoderTests() + { + Configuration.Default.ImageFormatsManager.AddImageFormat(TiffFormat.Instance); + Configuration.Default.ImageFormatsManager.AddImageFormatDetector(new TiffImageFormatDetector()); + Configuration.Default.ImageFormatsManager.SetDecoder(TiffFormat.Instance, new TiffDecoder()); + Configuration.Default.ImageFormatsManager.SetEncoder(TiffFormat.Instance, new TiffEncoder()); + } + [Theory] [InlineData(TiffEncodingMode.Default, TiffEncoderCompression.None, TiffBitsPerPixel.Pixel24, TiffCompression.None)] [InlineData(TiffEncodingMode.Rgb, TiffEncoderCompression.None, TiffBitsPerPixel.Pixel24, TiffCompression.None)] From 901979b9a41da615ccceb03ee6551e618bb26da6 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 8 Dec 2020 16:13:58 +0100 Subject: [PATCH 144/275] Register tiff decoder/encoder in tiff meta data tests --- tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index f32cbdd9d..4afbb5469 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -17,6 +17,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Trait("Format", "Tiff")] public class TiffMetadataTests { + public TiffMetadataTests() + { + Configuration.Default.ImageFormatsManager.AddImageFormat(TiffFormat.Instance); + Configuration.Default.ImageFormatsManager.AddImageFormatDetector(new TiffImageFormatDetector()); + Configuration.Default.ImageFormatsManager.SetDecoder(TiffFormat.Instance, new TiffDecoder()); + Configuration.Default.ImageFormatsManager.SetEncoder(TiffFormat.Instance, new TiffEncoder()); + } + [Fact] public void CloneIsDeep() { From 2f1f77597f2f6bf7b90962339e6edb054d94fe6a Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 9 Dec 2020 09:37:52 +0100 Subject: [PATCH 145/275] Use configuration instance in tiff tests instead of changing the default config --- .../Formats/Tiff/ImageExtensionsTest.cs | 43 +++++++++-------- .../Formats/Tiff/TiffDecoderTests.cs | 15 +++--- .../Formats/Tiff/TiffEncoderTests.cs | 47 +++++++++---------- .../Formats/Tiff/TiffMetadataTests.cs | 17 ++++--- 4 files changed, 64 insertions(+), 58 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Tiff/ImageExtensionsTest.cs b/tests/ImageSharp.Tests/Formats/Tiff/ImageExtensionsTest.cs index cc72560ac..3ead9776f 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/ImageExtensionsTest.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/ImageExtensionsTest.cs @@ -13,12 +13,15 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Trait("Format", "Tiff")] public class ImageExtensionsTest { + private readonly Configuration configuration; + public ImageExtensionsTest() { - Configuration.Default.ImageFormatsManager.AddImageFormat(TiffFormat.Instance); - Configuration.Default.ImageFormatsManager.AddImageFormatDetector(new TiffImageFormatDetector()); - Configuration.Default.ImageFormatsManager.SetDecoder(TiffFormat.Instance, new TiffDecoder()); - Configuration.Default.ImageFormatsManager.SetEncoder(TiffFormat.Instance, new TiffEncoder()); + this.configuration = new Configuration(); + this.configuration.ImageFormatsManager.AddImageFormat(TiffFormat.Instance); + this.configuration.ImageFormatsManager.AddImageFormatDetector(new TiffImageFormatDetector()); + this.configuration.ImageFormatsManager.SetDecoder(TiffFormat.Instance, new TiffDecoder()); + this.configuration.ImageFormatsManager.SetEncoder(TiffFormat.Instance, new TiffEncoder()); } [Fact] @@ -27,12 +30,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest)); string file = Path.Combine(dir, "SaveAsTiff_Path.tiff"); - using (var image = new Image(10, 10)) + using (var image = new Image(this.configuration, 10, 10)) { image.SaveAsTiff(file); } - using (Image.Load(file, out IImageFormat mime)) + using (Image.Load(this.configuration, file, out IImageFormat mime)) { Assert.Equal("image/tiff", mime.DefaultMimeType); } @@ -44,12 +47,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest)); string file = Path.Combine(dir, "SaveAsTiffAsync_Path.tiff"); - using (var image = new Image(10, 10)) + using (var image = new Image(this.configuration, 10, 10)) { await image.SaveAsTiffAsync(file); } - using (Image.Load(file, out IImageFormat mime)) + using (Image.Load(this.configuration, file, out IImageFormat mime)) { Assert.Equal("image/tiff", mime.DefaultMimeType); } @@ -61,12 +64,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions)); string file = Path.Combine(dir, "SaveAsTiff_Path_Encoder.tiff"); - using (var image = new Image(10, 10)) + using (var image = new Image(this.configuration, 10, 10)) { image.SaveAsTiff(file, new TiffEncoder()); } - using (Image.Load(file, out IImageFormat mime)) + using (Image.Load(this.configuration, file, out IImageFormat mime)) { Assert.Equal("image/tiff", mime.DefaultMimeType); } @@ -78,12 +81,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions)); string file = Path.Combine(dir, "SaveAsTiffAsync_Path_Encoder.tiff"); - using (var image = new Image(10, 10)) + using (var image = new Image(this.configuration, 10, 10)) { await image.SaveAsTiffAsync(file, new TiffEncoder()); } - using (Image.Load(file, out IImageFormat mime)) + using (Image.Load(this.configuration, file, out IImageFormat mime)) { Assert.Equal("image/tiff", mime.DefaultMimeType); } @@ -94,14 +97,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { using var memoryStream = new MemoryStream(); - using (var image = new Image(10, 10)) + using (var image = new Image(this.configuration, 10, 10)) { image.SaveAsTiff(memoryStream); } memoryStream.Position = 0; - using (Image.Load(memoryStream, out IImageFormat mime)) + using (Image.Load(this.configuration, memoryStream, out IImageFormat mime)) { Assert.Equal("image/tiff", mime.DefaultMimeType); } @@ -112,14 +115,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { using var memoryStream = new MemoryStream(); - using (var image = new Image(10, 10)) + using (var image = new Image(this.configuration, 10, 10)) { await image.SaveAsTiffAsync(memoryStream); } memoryStream.Position = 0; - using (Image.Load(memoryStream, out IImageFormat mime)) + using (Image.Load(this.configuration, memoryStream, out IImageFormat mime)) { Assert.Equal("image/tiff", mime.DefaultMimeType); } @@ -130,14 +133,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { using var memoryStream = new MemoryStream(); - using (var image = new Image(10, 10)) + using (var image = new Image(this.configuration, 10, 10)) { image.SaveAsTiff(memoryStream, new TiffEncoder()); } memoryStream.Position = 0; - using (Image.Load(memoryStream, out IImageFormat mime)) + using (Image.Load(this.configuration, memoryStream, out IImageFormat mime)) { Assert.Equal("image/tiff", mime.DefaultMimeType); } @@ -148,14 +151,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { using var memoryStream = new MemoryStream(); - using (var image = new Image(10, 10)) + using (var image = new Image(this.configuration, 10, 10)) { await image.SaveAsTiffAsync(memoryStream, new TiffEncoder()); } memoryStream.Position = 0; - using (Image.Load(memoryStream, out IImageFormat mime)) + using (Image.Load(this.configuration, memoryStream, out IImageFormat mime)) { Assert.Equal("image/tiff", mime.DefaultMimeType); } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index 90430fe11..c7b48e4ce 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -28,11 +28,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff private static MagickReferenceDecoder ReferenceDecoder => new MagickReferenceDecoder(); + private readonly Configuration configuration; + public TiffDecoderTests() { - Configuration.Default.ImageFormatsManager.AddImageFormat(TiffFormat.Instance); - Configuration.Default.ImageFormatsManager.AddImageFormatDetector(new TiffImageFormatDetector()); - Configuration.Default.ImageFormatsManager.SetDecoder(TiffFormat.Instance, new TiffDecoder()); + this.configuration = new Configuration(); + this.configuration.ImageFormatsManager.AddImageFormat(TiffFormat.Instance); + this.configuration.ImageFormatsManager.AddImageFormatDetector(new TiffImageFormatDetector()); + this.configuration.ImageFormatsManager.SetDecoder(TiffFormat.Instance, new TiffDecoder()); } [Theory] @@ -52,7 +55,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff var testFile = TestFile.Create(imagePath); using (var stream = new MemoryStream(testFile.Bytes, false)) { - IImageInfo info = Image.Identify(stream); + IImageInfo info = Image.Identify(this.configuration, stream); Assert.Equal(expectedPixelSize, info.PixelType?.BitsPerPixel); Assert.Equal(expectedWidth, info.Width); @@ -72,14 +75,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff var testFile = TestFile.Create(imagePath); using (var stream = new MemoryStream(testFile.Bytes, false)) { - IImageInfo info = Image.Identify(stream); + IImageInfo info = Image.Identify(this.configuration, stream); Assert.NotNull(info.Metadata); Assert.Equal(expectedByteOrder, info.Metadata.GetTiffMetadata().ByteOrder); stream.Seek(0, SeekOrigin.Begin); - using var img = Image.Load(stream); + using var img = Image.Load(this.configuration, stream); Assert.Equal(expectedByteOrder, img.Metadata.GetTiffMetadata().ByteOrder); } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index a7e55d700..ddc453f67 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -21,20 +21,15 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { private static readonly IImageDecoder ReferenceDecoder = new MagickReferenceDecoder(); - public static readonly TheoryData TiffBitsPerPixelFiles = - new TheoryData - { - { Calliphora_BiColorUncompressed, TiffBitsPerPixel.Pixel1 }, - { GrayscaleUncompressed, TiffBitsPerPixel.Pixel8 }, - { RgbUncompressed, TiffBitsPerPixel.Pixel24 }, - }; + private readonly Configuration configuration; public TiffEncoderTests() { - Configuration.Default.ImageFormatsManager.AddImageFormat(TiffFormat.Instance); - Configuration.Default.ImageFormatsManager.AddImageFormatDetector(new TiffImageFormatDetector()); - Configuration.Default.ImageFormatsManager.SetDecoder(TiffFormat.Instance, new TiffDecoder()); - Configuration.Default.ImageFormatsManager.SetEncoder(TiffFormat.Instance, new TiffEncoder()); + this.configuration = new Configuration(); + this.configuration.ImageFormatsManager.AddImageFormat(TiffFormat.Instance); + this.configuration.ImageFormatsManager.AddImageFormatDetector(new TiffImageFormatDetector()); + this.configuration.ImageFormatsManager.SetDecoder(TiffFormat.Instance, new TiffDecoder()); + this.configuration.ImageFormatsManager.SetEncoder(TiffFormat.Instance, new TiffEncoder()); } [Theory] @@ -70,20 +65,22 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // assert memStream.Position = 0; - using var output = Image.Load(memStream); + using var output = Image.Load(this.configuration, memStream); TiffMetadata meta = output.Metadata.GetTiffMetadata(); Assert.Equal(expectedBitsPerPixel, meta.BitsPerPixel); Assert.Equal(expectedCompression, meta.Compression); } [Theory] - [MemberData(nameof(TiffBitsPerPixelFiles))] - public void TiffEncoder_PreserveBitsPerPixel(string imagePath, TiffBitsPerPixel expectedBitsPerPixel) + [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32, TiffBitsPerPixel.Pixel1)] + [WithFile(GrayscaleUncompressed, PixelTypes.Rgba32, TiffBitsPerPixel.Pixel8)] + [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffBitsPerPixel.Pixel24)] + public void TiffEncoder_PreserveBitsPerPixel(TestImageProvider provider, TiffBitsPerPixel expectedBitsPerPixel) + where TPixel : unmanaged, IPixel { // arrange var tiffEncoder = new TiffEncoder(); - var testFile = TestFile.Create(imagePath); - using Image input = testFile.CreateRgba32Image(); + using Image input = provider.GetImage(); using var memStream = new MemoryStream(); // act @@ -91,7 +88,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // assert memStream.Position = 0; - using var output = Image.Load(memStream); + using var output = Image.Load(this.configuration, memStream); TiffMetadata meta = output.Metadata.GetTiffMetadata(); Assert.Equal(expectedBitsPerPixel, meta.BitsPerPixel); } @@ -163,7 +160,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.None }; - TiffEncoderPaletteTest(provider, encoder); + this.TiffEncoderPaletteTest(provider, encoder); } [Theory] @@ -173,7 +170,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Deflate }; - TiffEncoderPaletteTest(provider, encoder); + this.TiffEncoderPaletteTest(provider, encoder); } [Theory] @@ -183,7 +180,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Deflate, UseHorizontalPredictor = true }; - TiffEncoderPaletteTest(provider, encoder); + this.TiffEncoderPaletteTest(provider, encoder); } [Theory] @@ -193,7 +190,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Lzw }; - TiffEncoderPaletteTest(provider, encoder); + this.TiffEncoderPaletteTest(provider, encoder); } [Theory] @@ -203,7 +200,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Lzw, UseHorizontalPredictor = true }; - TiffEncoderPaletteTest(provider, encoder); + this.TiffEncoderPaletteTest(provider, encoder); } [Theory] @@ -213,10 +210,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.PackBits }; - TiffEncoderPaletteTest(provider, encoder); + this.TiffEncoderPaletteTest(provider, encoder); } - private static void TiffEncoderPaletteTest(TestImageProvider provider, TiffEncoder encoder) + private void TiffEncoderPaletteTest(TestImageProvider provider, TiffEncoder encoder) where TPixel : unmanaged, IPixel { // Because a quantizer is used to create the palette (and therefore changes to the original are expected), @@ -228,7 +225,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff image.Save(memStream, encoder); memStream.Position = 0; - using var encodedImage = (Image)Image.Load(memStream); + using var encodedImage = (Image)Image.Load(this.configuration, memStream); var encodedImagePath = provider.Utility.SaveTestOutputFile(encodedImage, "tiff", encoder); TiffTestUtils.CompareWithReferenceDecoder(encodedImagePath, encodedImage); } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index 4afbb5469..db6c86c4c 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -17,12 +17,15 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Trait("Format", "Tiff")] public class TiffMetadataTests { + private readonly Configuration configuration; + public TiffMetadataTests() { - Configuration.Default.ImageFormatsManager.AddImageFormat(TiffFormat.Instance); - Configuration.Default.ImageFormatsManager.AddImageFormatDetector(new TiffImageFormatDetector()); - Configuration.Default.ImageFormatsManager.SetDecoder(TiffFormat.Instance, new TiffDecoder()); - Configuration.Default.ImageFormatsManager.SetEncoder(TiffFormat.Instance, new TiffEncoder()); + this.configuration = new Configuration(); + this.configuration.ImageFormatsManager.AddImageFormat(TiffFormat.Instance); + this.configuration.ImageFormatsManager.AddImageFormatDetector(new TiffImageFormatDetector()); + this.configuration.ImageFormatsManager.SetDecoder(TiffFormat.Instance, new TiffDecoder()); + this.configuration.ImageFormatsManager.SetEncoder(TiffFormat.Instance, new TiffEncoder()); } [Fact] @@ -58,7 +61,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff var testFile = TestFile.Create(imagePath); using var stream = new MemoryStream(testFile.Bytes, false); - IImageInfo imageInfo = Image.Identify(stream); + IImageInfo imageInfo = Image.Identify(this.configuration, stream); Assert.NotNull(imageInfo); TiffMetadata tiffMetadata = imageInfo.Metadata.GetTiffMetadata(); @@ -79,7 +82,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff var testFile = TestFile.Create(imagePath); using var stream = new MemoryStream(testFile.Bytes, false); - IImageInfo imageInfo = Image.Identify(stream); + IImageInfo imageInfo = Image.Identify(this.configuration, stream); Assert.NotNull(imageInfo); TiffMetadata tiffMetadata = imageInfo.Metadata.GetTiffMetadata(); @@ -95,7 +98,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff var testFile = TestFile.Create(imagePath); using var stream = new MemoryStream(testFile.Bytes, false); - IImageInfo imageInfo = Image.Identify(stream); + IImageInfo imageInfo = Image.Identify(this.configuration, stream); Assert.NotNull(imageInfo); TiffMetadata tiffMetadata = imageInfo.Metadata.GetTiffMetadata(); From bc1bc8f4079c7bbbee4f6aa09151b0c79564546e Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Tue, 8 Dec 2020 18:36:13 +0300 Subject: [PATCH 146/275] Update tiff benchmarks --- .../Codecs/DecodeTiffBig.cs | 26 +++- tests/ImageSharp.Tests/TestImages.cs | 16 +- ...arks.Codecs.DecodeTiffBig-report-github.md | 140 ++++++++++-------- ...enchmarks.Codecs.DecodeTiffBig-report.html | 100 +++++++------ .../Images/Input/Tiff/Benchmarks/gen_big.ps1 | 6 +- .../Input/Tiff/Benchmarks/gen_medium.ps1 | 6 +- 6 files changed, 174 insertions(+), 120 deletions(-) diff --git a/tests/ImageSharp.Benchmarks/Codecs/DecodeTiffBig.cs b/tests/ImageSharp.Benchmarks/Codecs/DecodeTiffBig.cs index 4210d0571..9f8f53a37 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/DecodeTiffBig.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/DecodeTiffBig.cs @@ -7,8 +7,10 @@ using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Environments; using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Reports; + using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; + using SDImage = System.Drawing.Image; using SDSize = System.Drawing.Size; @@ -37,11 +39,18 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs private byte[] data; - private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); - - [Params(TestImages.Tiff.Benchmark_GrayscaleUncompressed, TestImages.Tiff.Benchmark_PaletteUncompressed, TestImages.Tiff.Benchmark_RgbDeflate, TestImages.Tiff.Benchmark_RgbLzw, TestImages.Tiff.Benchmark_RgbPackbits, TestImages.Tiff.Benchmark_RgbUncompressed)] + private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, Path.Combine(TestImages.Tiff.Benchmark_Path, this.TestImage)); - // [Params(TestImages.Tiff.GrayscaleUncompressed, TestImages.Tiff.PaletteUncompressed, TestImages.Tiff.RgbDeflate, TestImages.Tiff.RgbLzw, TestImages.Tiff.RgbPackbits, TestImages.Tiff.RgbUncompressed)] + [Params( + TestImages.Tiff.Benchmark_BwFax3, + //// TestImages.Tiff.Benchmark_RgbFax4, + TestImages.Tiff.Benchmark_BwRle, + TestImages.Tiff.Benchmark_GrayscaleUncompressed, + TestImages.Tiff.Benchmark_PaletteUncompressed, + TestImages.Tiff.Benchmark_RgbDeflate, + TestImages.Tiff.Benchmark_RgbLzw, + TestImages.Tiff.Benchmark_RgbPackbits, + TestImages.Tiff.Benchmark_RgbUncompressed)] public string TestImage { get; set; } [IterationSetup] @@ -67,8 +76,15 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs [Benchmark(Description = "ImageSharp Tiff")] public Size TiffCore() { + Configuration config = Configuration.Default.Clone(); + config.StreamProcessingBufferSize = 1024 * 64; + + config.ImageFormatsManager.AddImageFormat(Formats.Experimental.Tiff.TiffFormat.Instance); + config.ImageFormatsManager.AddImageFormatDetector(new Formats.Experimental.Tiff.TiffImageFormatDetector()); + config.ImageFormatsManager.SetDecoder(Formats.Experimental.Tiff.TiffFormat.Instance, new Formats.Experimental.Tiff.TiffDecoder()); + using (var ms = new MemoryStream(this.data)) - using (var image = Image.Load(ms)) + using (var image = Image.Load(config, ms)) { return image.Size(); } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 27ec39e70..92d5f1bcf 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -497,12 +497,16 @@ namespace SixLabors.ImageSharp.Tests public static class Tiff { - public const string Benchmark_GrayscaleUncompressed = "Tiff/Benchmarks/jpeg444_big_grayscale_uncompressed.tiff"; - public const string Benchmark_PaletteUncompressed = "Tiff/Benchmarks/jpeg444_big_palette_uncompressed.tiff"; - public const string Benchmark_RgbDeflate = "Tiff/Benchmarks/jpeg444_big_rgb_deflate.tiff"; - public const string Benchmark_RgbLzw = "Tiff/Benchmarks/jpeg444_big_rgb_lzw.tiff"; - public const string Benchmark_RgbPackbits = "Tiff/Benchmarks/jpeg444_big_rgb_packbits.tiff"; - public const string Benchmark_RgbUncompressed = "Tiff/Benchmarks/jpeg444_big_rgb_uncompressed.tiff"; + public const string Benchmark_Path = "Tiff/Benchmarks/"; + public const string Benchmark_BwFax3 = "medium_bw_Fax3.tiff"; + public const string Benchmark_BwFax4 = "medium_bw_Fax4.tiff"; + public const string Benchmark_BwRle = "medium_bw_Rle.tiff"; + public const string Benchmark_GrayscaleUncompressed = "medium_grayscale_uncompressed.tiff"; + public const string Benchmark_PaletteUncompressed = "medium_palette_uncompressed.tiff"; + public const string Benchmark_RgbDeflate = "medium_rgb_deflate.tiff"; + public const string Benchmark_RgbLzw = "medium_rgb_lzw.tiff"; + public const string Benchmark_RgbPackbits = "medium_rgb_packbits.tiff"; + public const string Benchmark_RgbUncompressed = "medium_rgb_uncompressed.tiff"; public const string Calliphora_GrayscaleUncompressed = "Tiff/Calliphora_grayscale_uncompressed.tiff"; public const string Calliphora_GrayscaleDeflate_Predictor = "Tiff/Calliphora_gray_deflate_predictor.tiff"; diff --git a/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiffBig-report-github.md b/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiffBig-report-github.md index 68b149c50..c1b35502c 100644 --- a/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiffBig-report-github.md +++ b/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiffBig-report-github.md @@ -1,69 +1,87 @@ ``` ini -BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.450 (2004/?/20H1) +BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19042 Intel Core i7-3610QM CPU 2.30GHz (Ivy Bridge), 1 CPU, 8 logical and 4 physical cores -.NET Core SDK=3.1.401 - [Host] : .NET Core 3.1.7 (CoreCLR 4.700.20.36602, CoreFX 4.700.20.37001), X64 RyuJIT - Job-MTZTUC : .NET Framework 4.8 (4.8.4200.0), X64 RyuJIT - Job-BGVYTJ : .NET Core 2.1.21 (CoreCLR 4.6.29130.01, CoreFX 4.6.29130.02), X64 RyuJIT - Job-ZDUDFU : .NET Core 3.1.7 (CoreCLR 4.700.20.36602, CoreFX 4.700.20.37001), X64 RyuJIT +.NET Core SDK=5.0.100 + [Host] : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT + Job-ORBNFQ : .NET Framework 4.8 (4.8.4250.0), X64 RyuJIT + Job-OLKFNC : .NET Core 2.1.23 (CoreCLR 4.6.29321.03, CoreFX 4.6.29321.01), X64 RyuJIT + Job-PCYTCM : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT InvocationCount=1 IterationCount=5 LaunchCount=1 UnrollFactor=1 WarmupCount=3 ``` -| Method | Job | Runtime | TestImage | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | -|---------------------- |----------- |-------------- |-------------------------------------------------------- |------------:|------------:|------------:|-------:|--------:|------------:|----------:|----------:|-------------:| -| **'System.Drawing Tiff'** | **Job-MTZTUC** | **.NET 4.7.2** | **Tiff/Benchmarks/jpeg444_big_grayscale_uncompressed.tiff** | **180.2 ms** | **15.21 ms** | **2.35 ms** | **1.00** | **0.00** | **85000.0000** | **-** | **-** | **269221840 B** | -| 'ImageSharp Tiff' | Job-MTZTUC | .NET 4.7.2 | Tiff/Benchmarks/jpeg444_big_grayscale_uncompressed.tiff | 31,527.8 ms | 4,371.70 ms | 1,135.32 ms | 176.11 | 8.81 | 1000.0000 | 1000.0000 | 1000.0000 | 1342029912 B | -| | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-BGVYTJ | .NET Core 2.1 | Tiff/Benchmarks/jpeg444_big_grayscale_uncompressed.tiff | 185.5 ms | 15.88 ms | 2.46 ms | 1.00 | 0.00 | 85000.0000 | - | - | 268813936 B | -| 'ImageSharp Tiff' | Job-BGVYTJ | .NET Core 2.1 | Tiff/Benchmarks/jpeg444_big_grayscale_uncompressed.tiff | 17,768.7 ms | 116.03 ms | 30.13 ms | 95.84 | 1.13 | 1000.0000 | 1000.0000 | 1000.0000 | 1342016464 B | -| | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-ZDUDFU | .NET Core 3.1 | Tiff/Benchmarks/jpeg444_big_grayscale_uncompressed.tiff | 149.9 ms | 8.23 ms | 1.27 ms | 1.00 | 0.00 | - | - | - | 176 B | -| 'ImageSharp Tiff' | Job-ZDUDFU | .NET Core 3.1 | Tiff/Benchmarks/jpeg444_big_grayscale_uncompressed.tiff | 16,782.2 ms | 718.14 ms | 111.13 ms | 111.94 | 0.80 | 1000.0000 | 1000.0000 | 1000.0000 | 1342016440 B | -| | | | | | | | | | | | | | -| **'System.Drawing Tiff'** | **Job-MTZTUC** | **.NET 4.7.2** | **Tiff/Benchmarks/jpeg444_big_palette_uncompressed.tiff** | **178.0 ms** | **7.07 ms** | **1.83 ms** | **1.00** | **0.00** | **85000.0000** | **-** | **-** | **269221840 B** | -| 'ImageSharp Tiff' | Job-MTZTUC | .NET 4.7.2 | Tiff/Benchmarks/jpeg444_big_palette_uncompressed.tiff | 33,721.9 ms | 78.03 ms | 12.08 ms | 188.96 | 1.80 | 1000.0000 | 1000.0000 | 1000.0000 | 1342023280 B | -| | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-BGVYTJ | .NET Core 2.1 | Tiff/Benchmarks/jpeg444_big_palette_uncompressed.tiff | 180.1 ms | 8.81 ms | 2.29 ms | 1.00 | 0.00 | 85000.0000 | - | - | 268815616 B | -| 'ImageSharp Tiff' | Job-BGVYTJ | .NET Core 2.1 | Tiff/Benchmarks/jpeg444_big_palette_uncompressed.tiff | 22,941.4 ms | 728.12 ms | 189.09 ms | 127.37 | 1.07 | 1000.0000 | 1000.0000 | 1000.0000 | 1342022368 B | -| | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-ZDUDFU | .NET Core 3.1 | Tiff/Benchmarks/jpeg444_big_palette_uncompressed.tiff | 145.5 ms | 3.20 ms | 0.50 ms | 1.00 | 0.00 | - | - | - | 176 B | -| 'ImageSharp Tiff' | Job-ZDUDFU | .NET Core 3.1 | Tiff/Benchmarks/jpeg444_big_palette_uncompressed.tiff | 21,485.0 ms | 711.10 ms | 184.67 ms | 148.04 | 0.66 | 1000.0000 | 1000.0000 | 1000.0000 | 1342025632 B | -| | | | | | | | | | | | | | -| **'System.Drawing Tiff'** | **Job-MTZTUC** | **.NET 4.7.2** | **Tiff/Benchmarks/jpeg444_big_rgb_deflate.tiff** | **2,518.2 ms** | **76.22 ms** | **19.79 ms** | **1.00** | **0.00** | **6000.0000** | **-** | **-** | **29598616 B** | -| 'ImageSharp Tiff' | Job-MTZTUC | .NET 4.7.2 | Tiff/Benchmarks/jpeg444_big_rgb_deflate.tiff | 29,327.2 ms | 102.72 ms | 26.68 ms | 11.65 | 0.10 | 1000.0000 | 1000.0000 | 1000.0000 | 1124088224 B | -| | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-BGVYTJ | .NET Core 2.1 | Tiff/Benchmarks/jpeg444_big_rgb_deflate.tiff | 2,500.3 ms | 67.24 ms | 10.41 ms | 1.00 | 0.00 | 6000.0000 | - | - | 29528752 B | -| 'ImageSharp Tiff' | Job-BGVYTJ | .NET Core 2.1 | Tiff/Benchmarks/jpeg444_big_rgb_deflate.tiff | 18,974.7 ms | 199.58 ms | 30.89 ms | 7.59 | 0.04 | 1000.0000 | 1000.0000 | 1000.0000 | 1123947608 B | -| | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-ZDUDFU | .NET Core 3.1 | Tiff/Benchmarks/jpeg444_big_rgb_deflate.tiff | 2,541.1 ms | 21.36 ms | 5.55 ms | 1.00 | 0.00 | - | - | - | 176 B | -| 'ImageSharp Tiff' | Job-ZDUDFU | .NET Core 3.1 | Tiff/Benchmarks/jpeg444_big_rgb_deflate.tiff | 17,974.8 ms | 751.73 ms | 116.33 ms | 7.07 | 0.04 | 1000.0000 | 1000.0000 | 1000.0000 | 1123949960 B | -| | | | | | | | | | | | | | -| **'System.Drawing Tiff'** | **Job-MTZTUC** | **.NET 4.7.2** | **Tiff/Benchmarks/jpeg444_big_rgb_lzw.tiff** | **3,368.4 ms** | **40.71 ms** | **6.30 ms** | **1.00** | **0.00** | **4000.0000** | **-** | **-** | **22835824 B** | -| 'ImageSharp Tiff' | Job-MTZTUC | .NET 4.7.2 | Tiff/Benchmarks/jpeg444_big_rgb_lzw.tiff | 28,919.9 ms | 705.58 ms | 183.24 ms | 8.57 | 0.04 | 1000.0000 | 1000.0000 | 1000.0000 | 1123956384 B | -| | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-BGVYTJ | .NET Core 2.1 | Tiff/Benchmarks/jpeg444_big_rgb_lzw.tiff | 3,365.1 ms | 36.93 ms | 5.72 ms | 1.00 | 0.00 | 4000.0000 | - | - | 22789840 B | -| 'ImageSharp Tiff' | Job-BGVYTJ | .NET Core 2.1 | Tiff/Benchmarks/jpeg444_big_rgb_lzw.tiff | 17,905.1 ms | 40.08 ms | 10.41 ms | 5.32 | 0.01 | 1000.0000 | 1000.0000 | 1000.0000 | 1123949072 B | -| | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-ZDUDFU | .NET Core 3.1 | Tiff/Benchmarks/jpeg444_big_rgb_lzw.tiff | 3,377.6 ms | 125.36 ms | 32.56 ms | 1.00 | 0.00 | - | - | - | 176 B | -| 'ImageSharp Tiff' | Job-ZDUDFU | .NET Core 3.1 | Tiff/Benchmarks/jpeg444_big_rgb_lzw.tiff | 16,998.0 ms | 460.59 ms | 119.61 ms | 5.03 | 0.07 | 1000.0000 | 1000.0000 | 1000.0000 | 1123952144 B | -| | | | | | | | | | | | | | -| **'System.Drawing Tiff'** | **Job-MTZTUC** | **.NET 4.7.2** | **Tiff/Benchmarks/jpeg444_big_rgb_packbits.tiff** | **1,849.3 ms** | **43.52 ms** | **11.30 ms** | **1.00** | **0.00** | **255000.0000** | **-** | **-** | **812350880 B** | -| 'ImageSharp Tiff' | Job-MTZTUC | .NET 4.7.2 | Tiff/Benchmarks/jpeg444_big_rgb_packbits.tiff | 29,360.0 ms | 157.78 ms | 40.98 ms | 15.88 | 0.12 | - | - | - | 2690323752 B | -| | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-BGVYTJ | .NET Core 2.1 | Tiff/Benchmarks/jpeg444_big_rgb_packbits.tiff | 1,882.7 ms | 64.85 ms | 16.84 ms | 1.00 | 0.00 | 255000.0000 | - | - | 811943568 B | -| 'ImageSharp Tiff' | Job-BGVYTJ | .NET Core 2.1 | Tiff/Benchmarks/jpeg444_big_rgb_packbits.tiff | 18,967.7 ms | 445.86 ms | 115.79 ms | 10.08 | 0.09 | - | - | - | 2690318648 B | -| | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-ZDUDFU | .NET Core 3.1 | Tiff/Benchmarks/jpeg444_big_rgb_packbits.tiff | 1,743.2 ms | 78.50 ms | 20.39 ms | 1.00 | 0.00 | - | - | - | 176 B | -| 'ImageSharp Tiff' | Job-ZDUDFU | .NET Core 3.1 | Tiff/Benchmarks/jpeg444_big_rgb_packbits.tiff | 17,379.6 ms | 243.53 ms | 63.24 ms | 9.97 | 0.10 | - | - | - | 2690321912 B | -| | | | | | | | | | | | | | -| **'System.Drawing Tiff'** | **Job-MTZTUC** | **.NET 4.7.2** | **Tiff/Benchmarks/jpeg444_big_rgb_uncompressed.tiff** | **758.5 ms** | **9.75 ms** | **2.53 ms** | **1.00** | **0.00** | **255000.0000** | **-** | **-** | **806059984 B** | -| 'ImageSharp Tiff' | Job-MTZTUC | .NET 4.7.2 | Tiff/Benchmarks/jpeg444_big_rgb_uncompressed.tiff | 29,198.2 ms | 677.81 ms | 176.03 ms | 38.50 | 0.19 | - | - | - | 1878827096 B | -| | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-BGVYTJ | .NET Core 2.1 | Tiff/Benchmarks/jpeg444_big_rgb_uncompressed.tiff | 760.1 ms | 15.95 ms | 2.47 ms | 1.00 | 0.00 | 255000.0000 | - | - | 805652192 B | -| 'ImageSharp Tiff' | Job-BGVYTJ | .NET Core 2.1 | Tiff/Benchmarks/jpeg444_big_rgb_uncompressed.tiff | 18,457.2 ms | 35.60 ms | 5.51 ms | 24.28 | 0.08 | - | - | - | 1878821992 B | -| | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-ZDUDFU | .NET Core 3.1 | Tiff/Benchmarks/jpeg444_big_rgb_uncompressed.tiff | 629.5 ms | 11.40 ms | 2.96 ms | 1.00 | 0.00 | - | - | - | 176 B | -| 'ImageSharp Tiff' | Job-ZDUDFU | .NET Core 3.1 | Tiff/Benchmarks/jpeg444_big_rgb_uncompressed.tiff | 17,579.8 ms | 371.72 ms | 96.54 ms | 27.93 | 0.11 | - | - | - | 1878825256 B | +| Method | Job | Runtime | TestImage | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | +|---------------------- |----------- |-------------- |----------------------------------- |-----------:|----------:|----------:|------:|--------:|-----------:|----------:|----------:|------------:| +| **'System.Drawing Tiff'** | **Job-ORBNFQ** | **.NET 4.7.2** | **medium_bw_Fax3.tiff** | **483.0 ms** | **25.89 ms** | **6.72 ms** | **1.00** | **0.00** | **1000.0000** | **-** | **-** | **5768128 B** | +| 'ImageSharp Tiff' | Job-ORBNFQ | .NET 4.7.2 | medium_bw_Fax3.tiff | 6,920.1 ms | 50.09 ms | 13.01 ms | 14.33 | 0.22 | 1000.0000 | 1000.0000 | 1000.0000 | 241519088 B | +| | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-OLKFNC | .NET Core 2.1 | medium_bw_Fax3.tiff | 480.6 ms | 15.76 ms | 4.09 ms | 1.00 | 0.00 | 1000.0000 | - | - | 5751016 B | +| 'ImageSharp Tiff' | Job-OLKFNC | .NET Core 2.1 | medium_bw_Fax3.tiff | 4,024.8 ms | 67.05 ms | 17.41 ms | 8.37 | 0.09 | - | - | - | 235961088 B | +| | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-PCYTCM | .NET Core 3.1 | medium_bw_Fax3.tiff | 494.7 ms | 66.04 ms | 10.22 ms | 1.00 | 0.00 | - | - | - | 176 B | +| 'ImageSharp Tiff' | Job-PCYTCM | .NET Core 3.1 | medium_bw_Fax3.tiff | 3,609.1 ms | 40.03 ms | 10.40 ms | 7.29 | 0.15 | - | - | - | 235961328 B | +| | | | | | | | | | | | | | +| **'System.Drawing Tiff'** | **Job-ORBNFQ** | **.NET 4.7.2** | **medium_bw_Rle.tiff** | **508.8 ms** | **70.45 ms** | **18.30 ms** | **1.00** | **0.00** | **1000.0000** | **-** | **-** | **8494472 B** | +| 'ImageSharp Tiff' | Job-ORBNFQ | .NET 4.7.2 | medium_bw_Rle.tiff | 7,256.1 ms | 862.61 ms | 224.02 ms | 14.26 | 0.19 | 1000.0000 | 1000.0000 | 1000.0000 | 237020384 B | +| | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-OLKFNC | .NET Core 2.1 | medium_bw_Rle.tiff | 498.6 ms | 19.57 ms | 5.08 ms | 1.00 | 0.00 | 1000.0000 | - | - | 8475688 B | +| 'ImageSharp Tiff' | Job-OLKFNC | .NET Core 2.1 | medium_bw_Rle.tiff | 4,077.0 ms | 63.52 ms | 16.50 ms | 8.18 | 0.08 | - | - | - | 235961944 B | +| | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-PCYTCM | .NET Core 3.1 | medium_bw_Rle.tiff | 484.9 ms | 9.27 ms | 1.44 ms | 1.00 | 0.00 | - | - | - | 176 B | +| 'ImageSharp Tiff' | Job-PCYTCM | .NET Core 3.1 | medium_bw_Rle.tiff | 3,544.6 ms | 67.38 ms | 17.50 ms | 7.32 | 0.00 | - | - | - | 235962272 B | +| | | | | | | | | | | | | | +| **'System.Drawing Tiff'** | **Job-ORBNFQ** | **.NET 4.7.2** | **medium_grayscale_uncompressed.tiff** | **603.1 ms** | **12.35 ms** | **3.21 ms** | **1.00** | **0.00** | **18000.0000** | **-** | **-** | **90301696 B** | +| 'ImageSharp Tiff' | Job-ORBNFQ | .NET 4.7.2 | medium_grayscale_uncompressed.tiff | 1,815.4 ms | 29.18 ms | 7.58 ms | 3.01 | 0.02 | - | - | - | 235970584 B | +| | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-OLKFNC | .NET Core 2.1 | medium_grayscale_uncompressed.tiff | 608.9 ms | 30.77 ms | 7.99 ms | 1.00 | 0.00 | 18000.0000 | - | - | 90104048 B | +| 'ImageSharp Tiff' | Job-OLKFNC | .NET Core 2.1 | medium_grayscale_uncompressed.tiff | 1,001.3 ms | 10.80 ms | 1.67 ms | 1.65 | 0.02 | - | - | - | 235965376 B | +| | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-PCYTCM | .NET Core 3.1 | medium_grayscale_uncompressed.tiff | 567.6 ms | 14.90 ms | 3.87 ms | 1.00 | 0.00 | - | - | - | 176 B | +| 'ImageSharp Tiff' | Job-PCYTCM | .NET Core 3.1 | medium_grayscale_uncompressed.tiff | 910.8 ms | 22.95 ms | 5.96 ms | 1.60 | 0.01 | - | - | - | 235965440 B | +| | | | | | | | | | | | | | +| **'System.Drawing Tiff'** | **Job-ORBNFQ** | **.NET 4.7.2** | **medium_palette_uncompressed.tiff** | **602.2 ms** | **5.20 ms** | **0.80 ms** | **1.00** | **0.00** | **18000.0000** | **-** | **-** | **90301696 B** | +| 'ImageSharp Tiff' | Job-ORBNFQ | .NET 4.7.2 | medium_palette_uncompressed.tiff | 3,329.3 ms | 38.02 ms | 5.88 ms | 5.53 | 0.01 | - | - | - | 236004096 B | +| | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-OLKFNC | .NET Core 2.1 | medium_palette_uncompressed.tiff | 601.8 ms | 21.00 ms | 5.45 ms | 1.00 | 0.00 | 18000.0000 | - | - | 90107368 B | +| 'ImageSharp Tiff' | Job-OLKFNC | .NET Core 2.1 | medium_palette_uncompressed.tiff | 1,954.6 ms | 21.60 ms | 5.61 ms | 3.25 | 0.03 | - | - | - | 235996096 B | +| | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-PCYTCM | .NET Core 3.1 | medium_palette_uncompressed.tiff | 575.5 ms | 25.83 ms | 6.71 ms | 1.00 | 0.00 | - | - | - | 176 B | +| 'ImageSharp Tiff' | Job-PCYTCM | .NET Core 3.1 | medium_palette_uncompressed.tiff | 1,656.7 ms | 15.51 ms | 2.40 ms | 2.88 | 0.04 | - | - | - | 235996256 B | +| | | | | | | | | | | | | | +| **'System.Drawing Tiff'** | **Job-ORBNFQ** | **.NET 4.7.2** | **medium_rgb_deflate.tiff** | **358.0 ms** | **8.50 ms** | **2.21 ms** | **1.00** | **0.00** | **3000.0000** | **-** | **-** | **9662560 B** | +| 'ImageSharp Tiff' | Job-ORBNFQ | .NET 4.7.2 | medium_rgb_deflate.tiff | 1,020.5 ms | 14.93 ms | 2.31 ms | 2.84 | 0.02 | 22000.0000 | 1000.0000 | 1000.0000 | 302745704 B | +| | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-OLKFNC | .NET Core 2.1 | medium_rgb_deflate.tiff | 356.9 ms | 11.32 ms | 1.75 ms | 1.00 | 0.00 | 3000.0000 | - | - | 9629400 B | +| 'ImageSharp Tiff' | Job-OLKFNC | .NET Core 2.1 | medium_rgb_deflate.tiff | 921.4 ms | 8.62 ms | 1.33 ms | 2.58 | 0.01 | - | - | - | 238909800 B | +| | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-PCYTCM | .NET Core 3.1 | medium_rgb_deflate.tiff | 357.3 ms | 28.17 ms | 7.32 ms | 1.00 | 0.00 | - | - | - | 176 B | +| 'ImageSharp Tiff' | Job-PCYTCM | .NET Core 3.1 | medium_rgb_deflate.tiff | 929.0 ms | 10.26 ms | 2.66 ms | 2.60 | 0.05 | - | - | - | 238664536 B | +| | | | | | | | | | | | | | +| **'System.Drawing Tiff'** | **Job-ORBNFQ** | **.NET 4.7.2** | **medium_rgb_lzw.tiff** | **509.2 ms** | **8.93 ms** | **2.32 ms** | **1.00** | **0.00** | **3000.0000** | **-** | **-** | **11600840 B** | +| 'ImageSharp Tiff' | Job-ORBNFQ | .NET 4.7.2 | medium_rgb_lzw.tiff | 2,967.3 ms | 23.69 ms | 6.15 ms | 5.83 | 0.03 | - | - | - | 236060696 B | +| | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-OLKFNC | .NET Core 2.1 | medium_rgb_lzw.tiff | 508.9 ms | 15.11 ms | 3.93 ms | 1.00 | 0.00 | 3000.0000 | - | - | 11569776 B | +| 'ImageSharp Tiff' | Job-OLKFNC | .NET Core 2.1 | medium_rgb_lzw.tiff | 2,046.1 ms | 24.58 ms | 6.38 ms | 4.02 | 0.04 | - | - | - | 236056952 B | +| | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-PCYTCM | .NET Core 3.1 | medium_rgb_lzw.tiff | 511.1 ms | 16.58 ms | 4.31 ms | 1.00 | 0.00 | - | - | - | 176 B | +| 'ImageSharp Tiff' | Job-PCYTCM | .NET Core 3.1 | medium_rgb_lzw.tiff | 2,072.9 ms | 9.12 ms | 2.37 ms | 4.06 | 0.03 | - | - | - | 236057016 B | +| | | | | | | | | | | | | | +| **'System.Drawing Tiff'** | **Job-ORBNFQ** | **.NET 4.7.2** | **medium_rgb_packbits.tiff** | **779.8 ms** | **51.30 ms** | **13.32 ms** | **1.00** | **0.00** | **56000.0000** | **-** | **-** | **304057016 B** | +| 'ImageSharp Tiff' | Job-ORBNFQ | .NET 4.7.2 | medium_rgb_packbits.tiff | 778.8 ms | 14.17 ms | 3.68 ms | 1.00 | 0.02 | - | - | - | 236003352 B | +| | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-OLKFNC | .NET Core 2.1 | medium_rgb_packbits.tiff | 769.3 ms | 57.35 ms | 14.89 ms | 1.00 | 0.00 | 56000.0000 | - | - | 303861120 B | +| 'ImageSharp Tiff' | Job-OLKFNC | .NET Core 2.1 | medium_rgb_packbits.tiff | 675.7 ms | 13.16 ms | 3.42 ms | 0.88 | 0.02 | - | - | - | 235998408 B | +| | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-PCYTCM | .NET Core 3.1 | medium_rgb_packbits.tiff | 665.7 ms | 32.83 ms | 8.53 ms | 1.00 | 0.00 | - | - | - | 176 B | +| 'ImageSharp Tiff' | Job-PCYTCM | .NET Core 3.1 | medium_rgb_packbits.tiff | 671.7 ms | 14.76 ms | 2.28 ms | 1.01 | 0.02 | - | - | - | 235998568 B | +| | | | | | | | | | | | | | +| **'System.Drawing Tiff'** | **Job-ORBNFQ** | **.NET 4.7.2** | **medium_rgb_uncompressed.tiff** | **738.3 ms** | **26.41 ms** | **6.86 ms** | **1.00** | **0.00** | **55000.0000** | **-** | **-** | **302644272 B** | +| 'ImageSharp Tiff' | Job-ORBNFQ | .NET 4.7.2 | medium_rgb_uncompressed.tiff | 740.1 ms | 8.51 ms | 1.32 ms | 1.00 | 0.01 | - | - | - | 235986968 B | +| | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-OLKFNC | .NET Core 2.1 | medium_rgb_uncompressed.tiff | 747.5 ms | 64.06 ms | 16.64 ms | 1.00 | 0.00 | 55000.0000 | - | - | 302448096 B | +| 'ImageSharp Tiff' | Job-OLKFNC | .NET Core 2.1 | medium_rgb_uncompressed.tiff | 654.6 ms | 10.01 ms | 2.60 ms | 0.88 | 0.02 | - | - | - | 235981128 B | +| | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-PCYTCM | .NET Core 3.1 | medium_rgb_uncompressed.tiff | 664.0 ms | 51.23 ms | 13.30 ms | 1.00 | 0.00 | - | - | - | 176 B | +| 'ImageSharp Tiff' | Job-PCYTCM | .NET Core 3.1 | medium_rgb_uncompressed.tiff | 653.0 ms | 4.88 ms | 1.27 ms | 0.98 | 0.02 | - | - | - | 235981192 B | diff --git a/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiffBig-report.html b/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiffBig-report.html index 406b72819..92f9d57c8 100644 --- a/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiffBig-report.html +++ b/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiffBig-report.html @@ -2,7 +2,7 @@ -SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiffBig-20200824-095044 +SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiffBig-20201209-164216 + + +

+BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19042
+Intel Core i7-3610QM CPU 2.30GHz (Ivy Bridge), 1 CPU, 8 logical and 4 physical cores
+.NET Core SDK=5.0.101
+  [Host]     : .NET Core 5.0.1 (CoreCLR 5.0.120.57516, CoreFX 5.0.120.57516), X64 RyuJIT
+  Job-EMDSBW : .NET Framework 4.8 (4.8.4300.0), X64 RyuJIT
+  Job-KCUIVJ : .NET Core 2.1.23 (CoreCLR 4.6.29321.03, CoreFX 4.6.29321.01), X64 RyuJIT
+  Job-NIWDJE : .NET Core 3.1.10 (CoreCLR 4.700.20.51601, CoreFX 4.700.20.51901), X64 RyuJIT
+
+
InvocationCount=1  IterationCount=3  LaunchCount=1  
+UnrollFactor=1  WarmupCount=3  
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Method JobRuntime TestImage Mean ErrorStdDevRatioRatioSDGen 0Gen 1Gen 2Allocated
'System.Drawing Tiff'Job-EMDSBW.NET 4.7.2Tiff/Calliphora_grayscale_uncompressed.tiff1,107.9 μs260.10 μs14.26 μs1.000.00---974848 B
'ImageSharp Tiff'Job-EMDSBW.NET 4.7.2Tiff/Calliphora_grayscale_uncompressed.tiff29,794.8 μs3,103.68 μs170.12 μs26.900.49---32768 B
'System.Drawing Tiff'Job-KCUIVJ.NET Core 2.1Tiff/Calliphora_grayscale_uncompressed.tiff1,020.4 μs641.11 μs35.14 μs1.000.00---968832 B
'ImageSharp Tiff'Job-KCUIVJ.NET Core 2.1Tiff/Calliphora_grayscale_uncompressed.tiff12,593.4 μs4,807.87 μs263.54 μs12.360.67---29976 B
'System.Drawing Tiff'Job-NIWDJE.NET Core 3.1Tiff/Calliphora_grayscale_uncompressed.tiff987.2 μs2,211.93 μs121.24 μs1.000.00---176 B
'ImageSharp Tiff'Job-NIWDJE.NET Core 3.1Tiff/Calliphora_grayscale_uncompressed.tiff44,255.5 μs13,031.10 μs714.28 μs45.234.88---29896 B
'System.Drawing Tiff'Job-EMDSBW.NET 4.7.2Tiff/Calliphora_rgb_deflate_predictor.tiff16,118.9 μs2,095.51 μs114.86 μs1.000.00---1483440 B
'ImageSharp Tiff'Job-EMDSBW.NET 4.7.2Tiff/Calliphora_rgb_deflate_predictor.tiff25,967.5 μs4,545.04 μs249.13 μs1.610.01---848240 B
'System.Drawing Tiff'Job-KCUIVJ.NET Core 2.1Tiff/Calliphora_rgb_deflate_predictor.tiff16,465.6 μs7,761.65 μs425.44 μs1.000.00---1480344 B
'ImageSharp Tiff'Job-KCUIVJ.NET Core 2.1Tiff/Calliphora_rgb_deflate_predictor.tiff18,536.9 μs3,415.62 μs187.22 μs1.130.02---68176 B
'System.Drawing Tiff'Job-NIWDJE.NET Core 3.1Tiff/Calliphora_rgb_deflate_predictor.tiff16,216.2 μs3,288.12 μs180.23 μs1.000.00---176 B
'ImageSharp Tiff'Job-NIWDJE.NET Core 3.1Tiff/Calliphora_rgb_deflate_predictor.tiff20,740.6 μs54,608.55 μs2,993.28 μs1.280.17---65120 B
'System.Drawing Tiff'Job-EMDSBW.NET 4.7.2Tiff/Calliphora_rgb_lzw_predictor.tiff83,012.1 μs14,786.35 μs810.49 μs1.000.00---2545736 B
'ImageSharp Tiff'Job-EMDSBW.NET 4.7.2Tiff/Calliphora_rgb_lzw_predictor.tiff64,895.5 μs11,397.89 μs624.76 μs0.780.01---24576 B
'System.Drawing Tiff'Job-KCUIVJ.NET Core 2.1Tiff/Calliphora_rgb_lzw_predictor.tiff82,854.1 μs45,495.28 μs2,493.75 μs1.000.00---2541376 B
'ImageSharp Tiff'Job-KCUIVJ.NET Core 2.1Tiff/Calliphora_rgb_lzw_predictor.tiff44,307.1 μs15,595.85 μs854.86 μs0.530.01---23832 B
'System.Drawing Tiff'Job-NIWDJE.NET Core 3.1Tiff/Calliphora_rgb_lzw_predictor.tiff83,297.5 μs15,796.71 μs865.87 μs1.000.00---176 B
'ImageSharp Tiff'Job-NIWDJE.NET Core 3.1Tiff/Calliphora_rgb_lzw_predictor.tiff59,464.0 μs13,870.15 μs760.27 μs0.710.01---23760 B
'System.Drawing Tiff'Job-EMDSBW.NET 4.7.2Tiff/Calliphora_rgb_packbits.tiff3,707.2 μs6,293.27 μs344.96 μs1.000.00---2916008 B
'ImageSharp Tiff'Job-EMDSBW.NET 4.7.2Tiff/Calliphora_rgb_packbits.tiff7,526.9 μs5,965.86 μs327.01 μs2.040.24---81920 B
'System.Drawing Tiff'Job-KCUIVJ.NET Core 2.1Tiff/Calliphora_rgb_packbits.tiff4,037.7 μs9,243.97 μs506.69 μs1.000.00---2903544 B
'ImageSharp Tiff'Job-KCUIVJ.NET Core 2.1Tiff/Calliphora_rgb_packbits.tiff4,395.7 μs1,394.13 μs76.42 μs1.100.15---80256 B
'System.Drawing Tiff'Job-NIWDJE.NET Core 3.1Tiff/Calliphora_rgb_packbits.tiff3,456.3 μs4,443.73 μs243.58 μs1.000.00---176 B
'ImageSharp Tiff'Job-NIWDJE.NET Core 3.1Tiff/Calliphora_rgb_packbits.tiff4,542.9 μs3,820.61 μs209.42 μs1.320.13---80184 B
'System.Drawing Tiff'Job-EMDSBW.NET 4.7.2Tiff/Calliphora_rgb_palette_lzw_predictor.tiff60,298.5 μs24,263.76 μs1,329.98 μs1.000.00---827416 B
'ImageSharp Tiff'Job-EMDSBW.NET 4.7.2Tiff/Calliphora_rgb_palette_lzw_predictor.tiff76,021.3 μs4,206.79 μs230.59 μs1.260.02---49152 B
'System.Drawing Tiff'Job-KCUIVJ.NET Core 2.1Tiff/Calliphora_rgb_palette_lzw_predictor.tiff59,122.1 μs9,681.07 μs530.65 μs1.000.00---825648 B
'ImageSharp Tiff'Job-KCUIVJ.NET Core 2.1Tiff/Calliphora_rgb_palette_lzw_predictor.tiff45,789.3 μs7,453.72 μs408.56 μs0.770.00---45936 B
'System.Drawing Tiff'Job-NIWDJE.NET Core 3.1Tiff/Calliphora_rgb_palette_lzw_predictor.tiff61,361.5 μs25,759.90 μs1,411.99 μs1.000.00---176 B
'ImageSharp Tiff'Job-NIWDJE.NET Core 3.1Tiff/Calliphora_rgb_palette_lzw_predictor.tiff68,134.6 μs303,212.80 μs16,620.12 μs1.110.25---45864 B
'System.Drawing Tiff'Job-EMDSBW.NET 4.7.2Tiff/Calliphora_rgb_uncompressed.tiff3,431.7 μs7,649.10 μs419.27 μs1.000.00---2915944 B
'ImageSharp Tiff'Job-EMDSBW.NET 4.7.2Tiff/Calliphora_rgb_uncompressed.tiff6,382.4 μs2,573.27 μs141.05 μs1.870.18---57344 B
'System.Drawing Tiff'Job-KCUIVJ.NET Core 2.1Tiff/Calliphora_rgb_uncompressed.tiff3,636.1 μs8,607.66 μs471.81 μs1.000.00---2905840 B
'ImageSharp Tiff'Job-KCUIVJ.NET Core 2.1Tiff/Calliphora_rgb_uncompressed.tiff4,018.7 μs1,662.68 μs91.14 μs1.120.16---51472 B
'System.Drawing Tiff'Job-NIWDJE.NET Core 3.1Tiff/Calliphora_rgb_uncompressed.tiff2,970.8 μs5,028.62 μs275.64 μs1.000.00---176 B
'ImageSharp Tiff'Job-NIWDJE.NET Core 3.1Tiff/Calliphora_rgb_uncompressed.tiff4,009.6 μs3,007.19 μs164.83 μs1.360.17---51400 B
'System.Drawing Tiff'Job-EMDSBW.NET 4.7.2Tiff/ccitt_fax3_all_terminating_codes.tiff178.4 μs375.89 μs20.60 μs1.000.00---8192 B
'ImageSharp Tiff'Job-EMDSBW.NET 4.7.2Tiff/ccitt_fax3_all_terminating_codes.tiff634.5 μs251.14 μs13.77 μs3.580.37---24576 B
'System.Drawing Tiff'Job-KCUIVJ.NET Core 2.1Tiff/ccitt_fax3_all_terminating_codes.tiff171.7 μs606.95 μs33.27 μs1.000.00---2032 B
'ImageSharp Tiff'Job-KCUIVJ.NET Core 2.1Tiff/ccitt_fax3_all_terminating_codes.tiff421.0 μs31.60 μs1.73 μs2.510.49---17848 B
'System.Drawing Tiff'Job-NIWDJE.NET Core 3.1Tiff/ccitt_fax3_all_terminating_codes.tiff137.2 μs78.18 μs4.29 μs1.000.00---176 B
'ImageSharp Tiff'Job-NIWDJE.NET Core 3.1Tiff/ccitt_fax3_all_terminating_codes.tiff888.5 μs495.11 μs27.14 μs6.470.05---17768 B
'System.Drawing Tiff'Job-EMDSBW.NET 4.7.2Tiff/huffman_rle_all_makeup_codes.tiff189.8 μs818.95 μs44.89 μs1.000.00---8192 B
'ImageSharp Tiff'Job-EMDSBW.NET 4.7.2Tiff/huffman_rle_all_makeup_codes.tiff9,137.1 μs1,178.82 μs64.62 μs49.8510.86---24576 B
'System.Drawing Tiff'Job-KCUIVJ.NET Core 2.1Tiff/huffman_rle_all_makeup_codes.tiff298.5 μs1,361.33 μs74.62 μs1.000.00---2088 B
'ImageSharp Tiff'Job-KCUIVJ.NET Core 2.1Tiff/huffman_rle_all_makeup_codes.tiff5,717.5 μs2,533.21 μs138.85 μs19.894.51---18328 B
'System.Drawing Tiff'Job-NIWDJE.NET Core 3.1Tiff/huffman_rle_all_makeup_codes.tiff159.5 μs140.52 μs7.70 μs1.000.00---176 B
'ImageSharp Tiff'Job-NIWDJE.NET Core 3.1Tiff/huffman_rle_all_makeup_codes.tiff15,047.7 μs2,686.03 μs147.23 μs94.474.56---18248 B
+ + diff --git a/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.EncodeTiff-report-default.md b/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.EncodeTiff-report-default.md new file mode 100644 index 000000000..c1d700442 --- /dev/null +++ b/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.EncodeTiff-report-default.md @@ -0,0 +1,74 @@ + +BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19042 +Intel Core i7-3610QM CPU 2.30GHz (Ivy Bridge), 1 CPU, 8 logical and 4 physical cores +.NET Core SDK=5.0.101 + [Host] : .NET Core 5.0.1 (CoreCLR 5.0.120.57516, CoreFX 5.0.120.57516), X64 RyuJIT + Job-BXRYWG : .NET Framework 4.8 (4.8.4300.0), X64 RyuJIT + Job-YFKMTZ : .NET Core 2.1.23 (CoreCLR 4.6.29321.03, CoreFX 4.6.29321.01), X64 RyuJIT + Job-ONTENJ : .NET Core 3.1.10 (CoreCLR 4.700.20.51601, CoreFX 4.700.20.51901), X64 RyuJIT + +IterationCount=3 LaunchCount=1 WarmupCount=3 + + Method | Job | Runtime | TestImage | Compression | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | +---------------------- |----------- |-------------- |-------------------------------------- |---------------- |-----------:|------------:|-----------:|------:|--------:|----------:|----------:|----------:|-----------:| + **'System.Drawing Tiff'** | **Job-BXRYWG** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_uncompressed.tiff** | **None** | **6.520 ms** | **2.1764 ms** | **0.1193 ms** | **1.00** | **0.00** | **984.3750** | **984.3750** | **984.3750** | **11570062 B** | + 'ImageSharp Tiff' | Job-BXRYWG | .NET 4.7.2 | Tiff/Calliphora_rgb_uncompressed.tiff | None | 5.698 ms | 8.2629 ms | 0.4529 ms | 0.87 | 0.06 | 539.0625 | 500.0000 | 492.1875 | 9919288 B | + | | | | | | | | | | | | | | + 'System.Drawing Tiff' | Job-YFKMTZ | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | None | 6.851 ms | 1.4499 ms | 0.0795 ms | 1.00 | 0.00 | 984.3750 | 984.3750 | 984.3750 | 11562768 B | + 'ImageSharp Tiff' | Job-YFKMTZ | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | None | 4.294 ms | 2.0150 ms | 0.1104 ms | 0.63 | 0.02 | 539.0625 | 500.0000 | 492.1875 | 9918144 B | + | | | | | | | | | | | | | | + 'System.Drawing Tiff' | Job-ONTENJ | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | None | 5.835 ms | 1.7302 ms | 0.0948 ms | 1.00 | 0.00 | 984.3750 | 984.3750 | 984.3750 | 8672224 B | + 'ImageSharp Tiff' | Job-ONTENJ | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | None | 5.167 ms | 1.1793 ms | 0.0646 ms | 0.89 | 0.02 | 539.0625 | 500.0000 | 492.1875 | 9918112 B | + | | | | | | | | | | | | | | + **'System.Drawing Tiff'** | **Job-BXRYWG** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_uncompressed.tiff** | **Deflate** | **NA** | **NA** | **NA** | **?** | **?** | **-** | **-** | **-** | **-** | + 'ImageSharp Tiff' | Job-BXRYWG | .NET 4.7.2 | Tiff/Calliphora_rgb_uncompressed.tiff | Deflate | 125.909 ms | 2.8957 ms | 0.1587 ms | ? | ? | 750.0000 | 750.0000 | 750.0000 | 11167960 B | + | | | | | | | | | | | | | | + 'System.Drawing Tiff' | Job-YFKMTZ | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | Deflate | NA | NA | NA | ? | ? | - | - | - | - | + 'ImageSharp Tiff' | Job-YFKMTZ | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | Deflate | 125.041 ms | 6.3920 ms | 0.3504 ms | ? | ? | 750.0000 | 750.0000 | 750.0000 | 11164792 B | + | | | | | | | | | | | | | | + 'System.Drawing Tiff' | Job-ONTENJ | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | Deflate | NA | NA | NA | ? | ? | - | - | - | - | + 'ImageSharp Tiff' | Job-ONTENJ | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | Deflate | 125.139 ms | 16.3106 ms | 0.8940 ms | ? | ? | 750.0000 | 750.0000 | 750.0000 | 11168428 B | + | | | | | | | | | | | | | | + **'System.Drawing Tiff'** | **Job-BXRYWG** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_uncompressed.tiff** | **Lzw** | **49.024 ms** | **35.9580 ms** | **1.9710 ms** | **1.00** | **0.00** | **800.0000** | **800.0000** | **800.0000** | **10673371 B** | + 'ImageSharp Tiff' | Job-BXRYWG | .NET 4.7.2 | Tiff/Calliphora_rgb_uncompressed.tiff | Lzw | 411.728 ms | 47.6380 ms | 2.6112 ms | 8.41 | 0.39 | 1000.0000 | 1000.0000 | 1000.0000 | 23265464 B | + | | | | | | | | | | | | | | + 'System.Drawing Tiff' | Job-YFKMTZ | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | Lzw | 47.288 ms | 1.4131 ms | 0.0775 ms | 1.00 | 0.00 | 818.1818 | 818.1818 | 818.1818 | 10668688 B | + 'ImageSharp Tiff' | Job-YFKMTZ | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | Lzw | 201.643 ms | 5.6002 ms | 0.3070 ms | 4.26 | 0.00 | 333.3333 | 333.3333 | 333.3333 | 27451168 B | + | | | | | | | | | | | | | | + 'System.Drawing Tiff' | Job-ONTENJ | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | Lzw | 46.526 ms | 6.2383 ms | 0.3419 ms | 1.00 | 0.00 | 818.1818 | 818.1818 | 818.1818 | 8001741 B | + 'ImageSharp Tiff' | Job-ONTENJ | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | Lzw | 170.276 ms | 20.5515 ms | 1.1265 ms | 3.66 | 0.04 | 333.3333 | 333.3333 | 333.3333 | 27451445 B | + | | | | | | | | | | | | | | + **'System.Drawing Tiff'** | **Job-BXRYWG** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_uncompressed.tiff** | **PackBits** | **NA** | **NA** | **NA** | **?** | **?** | **-** | **-** | **-** | **-** | + 'ImageSharp Tiff' | Job-BXRYWG | .NET 4.7.2 | Tiff/Calliphora_rgb_uncompressed.tiff | PackBits | 28.948 ms | 7.0740 ms | 0.3877 ms | ? | ? | 500.0000 | 468.7500 | 468.7500 | 9943858 B | + | | | | | | | | | | | | | | + 'System.Drawing Tiff' | Job-YFKMTZ | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | PackBits | NA | NA | NA | ? | ? | - | - | - | - | + 'ImageSharp Tiff' | Job-YFKMTZ | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | PackBits | 22.611 ms | 0.9267 ms | 0.0508 ms | ? | ? | 500.0000 | 468.7500 | 468.7500 | 9942792 B | + | | | | | | | | | | | | | | + 'System.Drawing Tiff' | Job-ONTENJ | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | PackBits | NA | NA | NA | ? | ? | - | - | - | - | + 'ImageSharp Tiff' | Job-ONTENJ | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | PackBits | 23.465 ms | 4.7353 ms | 0.2596 ms | ? | ? | 531.2500 | 500.0000 | 500.0000 | 9942772 B | + | | | | | | | | | | | | | | + **'System.Drawing Tiff'** | **Job-BXRYWG** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_uncompressed.tiff** | **CcittGroup3Fax** | **43.618 ms** | **6.0416 ms** | **0.3312 ms** | **1.00** | **0.00** | **-** | **-** | **-** | **1169683 B** | + 'ImageSharp Tiff' | Job-BXRYWG | .NET 4.7.2 | Tiff/Calliphora_rgb_uncompressed.tiff | CcittGroup3Fax | 191.602 ms | 34.9864 ms | 1.9177 ms | 4.39 | 0.04 | 3333.3333 | 1333.3333 | 333.3333 | 24829048 B | + | | | | | | | | | | | | | | + 'System.Drawing Tiff' | Job-YFKMTZ | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | CcittGroup3Fax | 43.258 ms | 3.5472 ms | 0.1944 ms | 1.00 | 0.00 | - | - | - | 1169200 B | + 'ImageSharp Tiff' | Job-YFKMTZ | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | CcittGroup3Fax | 177.930 ms | 50.1223 ms | 2.7474 ms | 4.11 | 0.04 | 3666.6667 | 2000.0000 | 666.6667 | 24772997 B | + | | | | | | | | | | | | | | + 'System.Drawing Tiff' | Job-ONTENJ | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | CcittGroup3Fax | 43.330 ms | 2.8194 ms | 0.1545 ms | 1.00 | 0.00 | - | - | - | 850189 B | + 'ImageSharp Tiff' | Job-ONTENJ | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | CcittGroup3Fax | 168.846 ms | 19.1390 ms | 1.0491 ms | 3.90 | 0.01 | 3333.3333 | 1333.3333 | 333.3333 | 24774571 B | + | | | | | | | | | | | | | | + **'System.Drawing Tiff'** | **Job-BXRYWG** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_uncompressed.tiff** | **ModifiedHuffman** | **17.106 ms** | **12.6692 ms** | **0.6944 ms** | **1.00** | **0.00** | **937.5000** | **937.5000** | **937.5000** | **11561706 B** | + 'ImageSharp Tiff' | Job-BXRYWG | .NET 4.7.2 | Tiff/Calliphora_rgb_uncompressed.tiff | ModifiedHuffman | 192.530 ms | 7.9946 ms | 0.4382 ms | 11.27 | 0.47 | 3333.3333 | 1333.3333 | 333.3333 | 24826163 B | + | | | | | | | | | | | | | | + 'System.Drawing Tiff' | Job-YFKMTZ | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | ModifiedHuffman | 16.988 ms | 2.7313 ms | 0.1497 ms | 1.00 | 0.00 | 937.5000 | 937.5000 | 937.5000 | 11555088 B | + 'ImageSharp Tiff' | Job-YFKMTZ | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | ModifiedHuffman | 180.265 ms | 78.0340 ms | 4.2773 ms | 10.61 | 0.18 | 3666.6667 | 2000.0000 | 666.6667 | 24769453 B | + | | | | | | | | | | | | | | + 'System.Drawing Tiff' | Job-ONTENJ | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | ModifiedHuffman | 15.989 ms | 2.7139 ms | 0.1488 ms | 1.00 | 0.00 | 937.5000 | 937.5000 | 937.5000 | 8666467 B | + 'ImageSharp Tiff' | Job-ONTENJ | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | ModifiedHuffman | 181.295 ms | 231.7796 ms | 12.7046 ms | 11.34 | 0.90 | 3333.3333 | 1333.3333 | 333.3333 | 24770275 B | + +Benchmarks with issues: + EncodeTiff.'System.Drawing Tiff': Job-BXRYWG(Runtime=.NET 4.7.2, IterationCount=3, LaunchCount=1, WarmupCount=3) [TestImage=Tiff/Calliphora_rgb_uncompressed.tiff, Compression=Deflate] + EncodeTiff.'System.Drawing Tiff': Job-YFKMTZ(Runtime=.NET Core 2.1, IterationCount=3, LaunchCount=1, WarmupCount=3) [TestImage=Tiff/Calliphora_rgb_uncompressed.tiff, Compression=Deflate] + EncodeTiff.'System.Drawing Tiff': Job-ONTENJ(Runtime=.NET Core 3.1, IterationCount=3, LaunchCount=1, WarmupCount=3) [TestImage=Tiff/Calliphora_rgb_uncompressed.tiff, Compression=Deflate] + EncodeTiff.'System.Drawing Tiff': Job-BXRYWG(Runtime=.NET 4.7.2, IterationCount=3, LaunchCount=1, WarmupCount=3) [TestImage=Tiff/Calliphora_rgb_uncompressed.tiff, Compression=PackBits] + EncodeTiff.'System.Drawing Tiff': Job-YFKMTZ(Runtime=.NET Core 2.1, IterationCount=3, LaunchCount=1, WarmupCount=3) [TestImage=Tiff/Calliphora_rgb_uncompressed.tiff, Compression=PackBits] + EncodeTiff.'System.Drawing Tiff': Job-ONTENJ(Runtime=.NET Core 3.1, IterationCount=3, LaunchCount=1, WarmupCount=3) [TestImage=Tiff/Calliphora_rgb_uncompressed.tiff, Compression=PackBits] diff --git a/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.EncodeTiff-report-github.md b/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.EncodeTiff-report-github.md new file mode 100644 index 000000000..3dc7f0c2f --- /dev/null +++ b/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.EncodeTiff-report-github.md @@ -0,0 +1,76 @@ +``` ini + +BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19042 +Intel Core i7-3610QM CPU 2.30GHz (Ivy Bridge), 1 CPU, 8 logical and 4 physical cores +.NET Core SDK=5.0.101 + [Host] : .NET Core 5.0.1 (CoreCLR 5.0.120.57516, CoreFX 5.0.120.57516), X64 RyuJIT + Job-BXRYWG : .NET Framework 4.8 (4.8.4300.0), X64 RyuJIT + Job-YFKMTZ : .NET Core 2.1.23 (CoreCLR 4.6.29321.03, CoreFX 4.6.29321.01), X64 RyuJIT + Job-ONTENJ : .NET Core 3.1.10 (CoreCLR 4.700.20.51601, CoreFX 4.700.20.51901), X64 RyuJIT + +IterationCount=3 LaunchCount=1 WarmupCount=3 + +``` +| Method | Job | Runtime | TestImage | Compression | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | +|---------------------- |----------- |-------------- |-------------------------------------- |---------------- |-----------:|------------:|-----------:|------:|--------:|----------:|----------:|----------:|-----------:| +| **'System.Drawing Tiff'** | **Job-BXRYWG** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_uncompressed.tiff** | **None** | **6.520 ms** | **2.1764 ms** | **0.1193 ms** | **1.00** | **0.00** | **984.3750** | **984.3750** | **984.3750** | **11570062 B** | +| 'ImageSharp Tiff' | Job-BXRYWG | .NET 4.7.2 | Tiff/Calliphora_rgb_uncompressed.tiff | None | 5.698 ms | 8.2629 ms | 0.4529 ms | 0.87 | 0.06 | 539.0625 | 500.0000 | 492.1875 | 9919288 B | +| | | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-YFKMTZ | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | None | 6.851 ms | 1.4499 ms | 0.0795 ms | 1.00 | 0.00 | 984.3750 | 984.3750 | 984.3750 | 11562768 B | +| 'ImageSharp Tiff' | Job-YFKMTZ | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | None | 4.294 ms | 2.0150 ms | 0.1104 ms | 0.63 | 0.02 | 539.0625 | 500.0000 | 492.1875 | 9918144 B | +| | | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-ONTENJ | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | None | 5.835 ms | 1.7302 ms | 0.0948 ms | 1.00 | 0.00 | 984.3750 | 984.3750 | 984.3750 | 8672224 B | +| 'ImageSharp Tiff' | Job-ONTENJ | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | None | 5.167 ms | 1.1793 ms | 0.0646 ms | 0.89 | 0.02 | 539.0625 | 500.0000 | 492.1875 | 9918112 B | +| | | | | | | | | | | | | | | +| **'System.Drawing Tiff'** | **Job-BXRYWG** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_uncompressed.tiff** | **Deflate** | **NA** | **NA** | **NA** | **?** | **?** | **-** | **-** | **-** | **-** | +| 'ImageSharp Tiff' | Job-BXRYWG | .NET 4.7.2 | Tiff/Calliphora_rgb_uncompressed.tiff | Deflate | 125.909 ms | 2.8957 ms | 0.1587 ms | ? | ? | 750.0000 | 750.0000 | 750.0000 | 11167960 B | +| | | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-YFKMTZ | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | Deflate | NA | NA | NA | ? | ? | - | - | - | - | +| 'ImageSharp Tiff' | Job-YFKMTZ | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | Deflate | 125.041 ms | 6.3920 ms | 0.3504 ms | ? | ? | 750.0000 | 750.0000 | 750.0000 | 11164792 B | +| | | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-ONTENJ | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | Deflate | NA | NA | NA | ? | ? | - | - | - | - | +| 'ImageSharp Tiff' | Job-ONTENJ | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | Deflate | 125.139 ms | 16.3106 ms | 0.8940 ms | ? | ? | 750.0000 | 750.0000 | 750.0000 | 11168428 B | +| | | | | | | | | | | | | | | +| **'System.Drawing Tiff'** | **Job-BXRYWG** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_uncompressed.tiff** | **Lzw** | **49.024 ms** | **35.9580 ms** | **1.9710 ms** | **1.00** | **0.00** | **800.0000** | **800.0000** | **800.0000** | **10673371 B** | +| 'ImageSharp Tiff' | Job-BXRYWG | .NET 4.7.2 | Tiff/Calliphora_rgb_uncompressed.tiff | Lzw | 411.728 ms | 47.6380 ms | 2.6112 ms | 8.41 | 0.39 | 1000.0000 | 1000.0000 | 1000.0000 | 23265464 B | +| | | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-YFKMTZ | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | Lzw | 47.288 ms | 1.4131 ms | 0.0775 ms | 1.00 | 0.00 | 818.1818 | 818.1818 | 818.1818 | 10668688 B | +| 'ImageSharp Tiff' | Job-YFKMTZ | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | Lzw | 201.643 ms | 5.6002 ms | 0.3070 ms | 4.26 | 0.00 | 333.3333 | 333.3333 | 333.3333 | 27451168 B | +| | | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-ONTENJ | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | Lzw | 46.526 ms | 6.2383 ms | 0.3419 ms | 1.00 | 0.00 | 818.1818 | 818.1818 | 818.1818 | 8001741 B | +| 'ImageSharp Tiff' | Job-ONTENJ | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | Lzw | 170.276 ms | 20.5515 ms | 1.1265 ms | 3.66 | 0.04 | 333.3333 | 333.3333 | 333.3333 | 27451445 B | +| | | | | | | | | | | | | | | +| **'System.Drawing Tiff'** | **Job-BXRYWG** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_uncompressed.tiff** | **PackBits** | **NA** | **NA** | **NA** | **?** | **?** | **-** | **-** | **-** | **-** | +| 'ImageSharp Tiff' | Job-BXRYWG | .NET 4.7.2 | Tiff/Calliphora_rgb_uncompressed.tiff | PackBits | 28.948 ms | 7.0740 ms | 0.3877 ms | ? | ? | 500.0000 | 468.7500 | 468.7500 | 9943858 B | +| | | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-YFKMTZ | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | PackBits | NA | NA | NA | ? | ? | - | - | - | - | +| 'ImageSharp Tiff' | Job-YFKMTZ | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | PackBits | 22.611 ms | 0.9267 ms | 0.0508 ms | ? | ? | 500.0000 | 468.7500 | 468.7500 | 9942792 B | +| | | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-ONTENJ | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | PackBits | NA | NA | NA | ? | ? | - | - | - | - | +| 'ImageSharp Tiff' | Job-ONTENJ | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | PackBits | 23.465 ms | 4.7353 ms | 0.2596 ms | ? | ? | 531.2500 | 500.0000 | 500.0000 | 9942772 B | +| | | | | | | | | | | | | | | +| **'System.Drawing Tiff'** | **Job-BXRYWG** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_uncompressed.tiff** | **CcittGroup3Fax** | **43.618 ms** | **6.0416 ms** | **0.3312 ms** | **1.00** | **0.00** | **-** | **-** | **-** | **1169683 B** | +| 'ImageSharp Tiff' | Job-BXRYWG | .NET 4.7.2 | Tiff/Calliphora_rgb_uncompressed.tiff | CcittGroup3Fax | 191.602 ms | 34.9864 ms | 1.9177 ms | 4.39 | 0.04 | 3333.3333 | 1333.3333 | 333.3333 | 24829048 B | +| | | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-YFKMTZ | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | CcittGroup3Fax | 43.258 ms | 3.5472 ms | 0.1944 ms | 1.00 | 0.00 | - | - | - | 1169200 B | +| 'ImageSharp Tiff' | Job-YFKMTZ | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | CcittGroup3Fax | 177.930 ms | 50.1223 ms | 2.7474 ms | 4.11 | 0.04 | 3666.6667 | 2000.0000 | 666.6667 | 24772997 B | +| | | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-ONTENJ | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | CcittGroup3Fax | 43.330 ms | 2.8194 ms | 0.1545 ms | 1.00 | 0.00 | - | - | - | 850189 B | +| 'ImageSharp Tiff' | Job-ONTENJ | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | CcittGroup3Fax | 168.846 ms | 19.1390 ms | 1.0491 ms | 3.90 | 0.01 | 3333.3333 | 1333.3333 | 333.3333 | 24774571 B | +| | | | | | | | | | | | | | | +| **'System.Drawing Tiff'** | **Job-BXRYWG** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_uncompressed.tiff** | **ModifiedHuffman** | **17.106 ms** | **12.6692 ms** | **0.6944 ms** | **1.00** | **0.00** | **937.5000** | **937.5000** | **937.5000** | **11561706 B** | +| 'ImageSharp Tiff' | Job-BXRYWG | .NET 4.7.2 | Tiff/Calliphora_rgb_uncompressed.tiff | ModifiedHuffman | 192.530 ms | 7.9946 ms | 0.4382 ms | 11.27 | 0.47 | 3333.3333 | 1333.3333 | 333.3333 | 24826163 B | +| | | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-YFKMTZ | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | ModifiedHuffman | 16.988 ms | 2.7313 ms | 0.1497 ms | 1.00 | 0.00 | 937.5000 | 937.5000 | 937.5000 | 11555088 B | +| 'ImageSharp Tiff' | Job-YFKMTZ | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | ModifiedHuffman | 180.265 ms | 78.0340 ms | 4.2773 ms | 10.61 | 0.18 | 3666.6667 | 2000.0000 | 666.6667 | 24769453 B | +| | | | | | | | | | | | | | | +| 'System.Drawing Tiff' | Job-ONTENJ | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | ModifiedHuffman | 15.989 ms | 2.7139 ms | 0.1488 ms | 1.00 | 0.00 | 937.5000 | 937.5000 | 937.5000 | 8666467 B | +| 'ImageSharp Tiff' | Job-ONTENJ | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | ModifiedHuffman | 181.295 ms | 231.7796 ms | 12.7046 ms | 11.34 | 0.90 | 3333.3333 | 1333.3333 | 333.3333 | 24770275 B | + +Benchmarks with issues: + EncodeTiff.'System.Drawing Tiff': Job-BXRYWG(Runtime=.NET 4.7.2, IterationCount=3, LaunchCount=1, WarmupCount=3) [TestImage=Tiff/Calliphora_rgb_uncompressed.tiff, Compression=Deflate] + EncodeTiff.'System.Drawing Tiff': Job-YFKMTZ(Runtime=.NET Core 2.1, IterationCount=3, LaunchCount=1, WarmupCount=3) [TestImage=Tiff/Calliphora_rgb_uncompressed.tiff, Compression=Deflate] + EncodeTiff.'System.Drawing Tiff': Job-ONTENJ(Runtime=.NET Core 3.1, IterationCount=3, LaunchCount=1, WarmupCount=3) [TestImage=Tiff/Calliphora_rgb_uncompressed.tiff, Compression=Deflate] + EncodeTiff.'System.Drawing Tiff': Job-BXRYWG(Runtime=.NET 4.7.2, IterationCount=3, LaunchCount=1, WarmupCount=3) [TestImage=Tiff/Calliphora_rgb_uncompressed.tiff, Compression=PackBits] + EncodeTiff.'System.Drawing Tiff': Job-YFKMTZ(Runtime=.NET Core 2.1, IterationCount=3, LaunchCount=1, WarmupCount=3) [TestImage=Tiff/Calliphora_rgb_uncompressed.tiff, Compression=PackBits] + EncodeTiff.'System.Drawing Tiff': Job-ONTENJ(Runtime=.NET Core 3.1, IterationCount=3, LaunchCount=1, WarmupCount=3) [TestImage=Tiff/Calliphora_rgb_uncompressed.tiff, Compression=PackBits] diff --git a/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.EncodeTiff-report.html b/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.EncodeTiff-report.html new file mode 100644 index 000000000..1549909fd --- /dev/null +++ b/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.EncodeTiff-report.html @@ -0,0 +1,68 @@ + + + + +SixLabors.ImageSharp.Benchmarks.Codecs.EncodeTiff-20210207-115408 + + + + +

+BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19042
+Intel Core i7-3610QM CPU 2.30GHz (Ivy Bridge), 1 CPU, 8 logical and 4 physical cores
+.NET Core SDK=5.0.101
+  [Host]     : .NET Core 5.0.1 (CoreCLR 5.0.120.57516, CoreFX 5.0.120.57516), X64 RyuJIT
+  Job-BXRYWG : .NET Framework 4.8 (4.8.4300.0), X64 RyuJIT
+  Job-YFKMTZ : .NET Core 2.1.23 (CoreCLR 4.6.29321.03, CoreFX 4.6.29321.01), X64 RyuJIT
+  Job-ONTENJ : .NET Core 3.1.10 (CoreCLR 4.700.20.51601, CoreFX 4.700.20.51901), X64 RyuJIT
+
+
IterationCount=3  LaunchCount=1  WarmupCount=3  
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Method JobRuntime TestImageCompressionMeanErrorStdDevRatioRatioSDGen 0Gen 1Gen 2Allocated
'System.Drawing Tiff'Job-BXRYWG.NET 4.7.2Tiff/Calliphora_rgb_uncompressed.tiffNone6.520 ms2.1764 ms0.1193 ms1.000.00984.3750984.3750984.375011570062 B
'ImageSharp Tiff'Job-BXRYWG.NET 4.7.2Tiff/Calliphora_rgb_uncompressed.tiffNone5.698 ms8.2629 ms0.4529 ms0.870.06539.0625500.0000492.18759919288 B
'System.Drawing Tiff'Job-YFKMTZ.NET Core 2.1Tiff/Calliphora_rgb_uncompressed.tiffNone6.851 ms1.4499 ms0.0795 ms1.000.00984.3750984.3750984.375011562768 B
'ImageSharp Tiff'Job-YFKMTZ.NET Core 2.1Tiff/Calliphora_rgb_uncompressed.tiffNone4.294 ms2.0150 ms0.1104 ms0.630.02539.0625500.0000492.18759918144 B
'System.Drawing Tiff'Job-ONTENJ.NET Core 3.1Tiff/Calliphora_rgb_uncompressed.tiffNone5.835 ms1.7302 ms0.0948 ms1.000.00984.3750984.3750984.37508672224 B
'ImageSharp Tiff'Job-ONTENJ.NET Core 3.1Tiff/Calliphora_rgb_uncompressed.tiffNone5.167 ms1.1793 ms0.0646 ms0.890.02539.0625500.0000492.18759918112 B
'System.Drawing Tiff'Job-BXRYWG.NET 4.7.2Tiff/Calliphora_rgb_uncompressed.tiffDeflateNANANA??----
'ImageSharp Tiff'Job-BXRYWG.NET 4.7.2Tiff/Calliphora_rgb_uncompressed.tiffDeflate125.909 ms2.8957 ms0.1587 ms??750.0000750.0000750.000011167960 B
'System.Drawing Tiff'Job-YFKMTZ.NET Core 2.1Tiff/Calliphora_rgb_uncompressed.tiffDeflateNANANA??----
'ImageSharp Tiff'Job-YFKMTZ.NET Core 2.1Tiff/Calliphora_rgb_uncompressed.tiffDeflate125.041 ms6.3920 ms0.3504 ms??750.0000750.0000750.000011164792 B
'System.Drawing Tiff'Job-ONTENJ.NET Core 3.1Tiff/Calliphora_rgb_uncompressed.tiffDeflateNANANA??----
'ImageSharp Tiff'Job-ONTENJ.NET Core 3.1Tiff/Calliphora_rgb_uncompressed.tiffDeflate125.139 ms16.3106 ms0.8940 ms??750.0000750.0000750.000011168428 B
'System.Drawing Tiff'Job-BXRYWG.NET 4.7.2Tiff/Calliphora_rgb_uncompressed.tiffLzw49.024 ms35.9580 ms1.9710 ms1.000.00800.0000800.0000800.000010673371 B
'ImageSharp Tiff'Job-BXRYWG.NET 4.7.2Tiff/Calliphora_rgb_uncompressed.tiffLzw411.728 ms47.6380 ms2.6112 ms8.410.391000.00001000.00001000.000023265464 B
'System.Drawing Tiff'Job-YFKMTZ.NET Core 2.1Tiff/Calliphora_rgb_uncompressed.tiffLzw47.288 ms1.4131 ms0.0775 ms1.000.00818.1818818.1818818.181810668688 B
'ImageSharp Tiff'Job-YFKMTZ.NET Core 2.1Tiff/Calliphora_rgb_uncompressed.tiffLzw201.643 ms5.6002 ms0.3070 ms4.260.00333.3333333.3333333.333327451168 B
'System.Drawing Tiff'Job-ONTENJ.NET Core 3.1Tiff/Calliphora_rgb_uncompressed.tiffLzw46.526 ms6.2383 ms0.3419 ms1.000.00818.1818818.1818818.18188001741 B
'ImageSharp Tiff'Job-ONTENJ.NET Core 3.1Tiff/Calliphora_rgb_uncompressed.tiffLzw170.276 ms20.5515 ms1.1265 ms3.660.04333.3333333.3333333.333327451445 B
'System.Drawing Tiff'Job-BXRYWG.NET 4.7.2Tiff/Calliphora_rgb_uncompressed.tiffPackBitsNANANA??----
'ImageSharp Tiff'Job-BXRYWG.NET 4.7.2Tiff/Calliphora_rgb_uncompressed.tiffPackBits28.948 ms7.0740 ms0.3877 ms??500.0000468.7500468.75009943858 B
'System.Drawing Tiff'Job-YFKMTZ.NET Core 2.1Tiff/Calliphora_rgb_uncompressed.tiffPackBitsNANANA??----
'ImageSharp Tiff'Job-YFKMTZ.NET Core 2.1Tiff/Calliphora_rgb_uncompressed.tiffPackBits22.611 ms0.9267 ms0.0508 ms??500.0000468.7500468.75009942792 B
'System.Drawing Tiff'Job-ONTENJ.NET Core 3.1Tiff/Calliphora_rgb_uncompressed.tiffPackBitsNANANA??----
'ImageSharp Tiff'Job-ONTENJ.NET Core 3.1Tiff/Calliphora_rgb_uncompressed.tiffPackBits23.465 ms4.7353 ms0.2596 ms??531.2500500.0000500.00009942772 B
'System.Drawing Tiff'Job-BXRYWG.NET 4.7.2Tiff/Calliphora_rgb_uncompressed.tiffCcittGroup3Fax43.618 ms6.0416 ms0.3312 ms1.000.00---1169683 B
'ImageSharp Tiff'Job-BXRYWG.NET 4.7.2Tiff/Calliphora_rgb_uncompressed.tiffCcittGroup3Fax191.602 ms34.9864 ms1.9177 ms4.390.043333.33331333.3333333.333324829048 B
'System.Drawing Tiff'Job-YFKMTZ.NET Core 2.1Tiff/Calliphora_rgb_uncompressed.tiffCcittGroup3Fax43.258 ms3.5472 ms0.1944 ms1.000.00---1169200 B
'ImageSharp Tiff'Job-YFKMTZ.NET Core 2.1Tiff/Calliphora_rgb_uncompressed.tiffCcittGroup3Fax177.930 ms50.1223 ms2.7474 ms4.110.043666.66672000.0000666.666724772997 B
'System.Drawing Tiff'Job-ONTENJ.NET Core 3.1Tiff/Calliphora_rgb_uncompressed.tiffCcittGroup3Fax43.330 ms2.8194 ms0.1545 ms1.000.00---850189 B
'ImageSharp Tiff'Job-ONTENJ.NET Core 3.1Tiff/Calliphora_rgb_uncompressed.tiffCcittGroup3Fax168.846 ms19.1390 ms1.0491 ms3.900.013333.33331333.3333333.333324774571 B
'System.Drawing Tiff'Job-BXRYWG.NET 4.7.2Tiff/Calliphora_rgb_uncompressed.tiffModifiedHuffman17.106 ms12.6692 ms0.6944 ms1.000.00937.5000937.5000937.500011561706 B
'ImageSharp Tiff'Job-BXRYWG.NET 4.7.2Tiff/Calliphora_rgb_uncompressed.tiffModifiedHuffman192.530 ms7.9946 ms0.4382 ms11.270.473333.33331333.3333333.333324826163 B
'System.Drawing Tiff'Job-YFKMTZ.NET Core 2.1Tiff/Calliphora_rgb_uncompressed.tiffModifiedHuffman16.988 ms2.7313 ms0.1497 ms1.000.00937.5000937.5000937.500011555088 B
'ImageSharp Tiff'Job-YFKMTZ.NET Core 2.1Tiff/Calliphora_rgb_uncompressed.tiffModifiedHuffman180.265 ms78.0340 ms4.2773 ms10.610.183666.66672000.0000666.666724769453 B
'System.Drawing Tiff'Job-ONTENJ.NET Core 3.1Tiff/Calliphora_rgb_uncompressed.tiffModifiedHuffman15.989 ms2.7139 ms0.1488 ms1.000.00937.5000937.5000937.50008666467 B
'ImageSharp Tiff'Job-ONTENJ.NET Core 3.1Tiff/Calliphora_rgb_uncompressed.tiffModifiedHuffman181.295 ms231.7796 ms12.7046 ms11.340.903333.33331333.3333333.333324770275 B
+ + From 9e139882c4aaa26513e0685a3c986b3ef162a488 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sat, 6 Feb 2021 22:05:16 +0300 Subject: [PATCH 191/275] Support multi strip encoding for tiff. Improve performance and memory usage of decoders and encoders. # Conflicts: # tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs --- .../Formats/ImageExtensions.Save.cs | 1 - .../Tiff/Compression/BitWriterUtils.cs | 38 +-- .../Compressors/DeflateCompressor.cs | 59 +++++ .../Compression/Compressors/LzwCompressor.cs | 36 +++ .../Compression/Compressors/NoCompressor.cs | 28 +++ .../Compressors/PackBitsCompressor.cs | 39 +++ .../{T4BitWriter.cs => T4BitCompressor.cs} | 88 ++++--- .../Compression/Compressors/TiffLzwEncoder.cs | 52 ++-- .../Decompressors/DeflateTiffCompression.cs | 7 +- .../Decompressors/LzwTiffCompression.cs | 7 +- .../ModifiedHuffmanTiffCompression.cs | 15 +- .../Decompressors/NoneTiffCompression.cs | 12 +- .../Decompressors/PackBitsTiffCompression.cs | 20 +- .../Decompressors/T4TiffCompression.cs | 27 ++- .../FaxCompressionOptions.cs | 2 +- .../Tiff/Compression/HorizontalPredictor.cs | 56 +++-- .../Tiff/Compression/TiffBaseCompression.cs | 47 ++++ .../Tiff/Compression/TiffBaseCompressor.cs | 25 ++ ...eCompression.cs => TiffBaseDecompresor.cs} | 32 +-- .../Tiff/Compression/TiffCompressorFactory.cs | 61 +++++ .../TiffDecoderCompressionType.cs | 2 +- .../TiffDecompressorsFactory.cs | 27 ++- .../Formats/Tiff/ITiffEncoderOptions.cs | 10 + .../Formats/Tiff/TiffDecoderCore.cs | 6 +- .../Formats/Tiff/TiffDecoderOptionsParser.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffEncoder.cs | 6 + .../Formats/Tiff/TiffEncoderCore.cs | 135 ++++++----- .../Tiff/TiffEncoderPixelStorageMethod.cs | 26 ++ .../Formats/Tiff/TiffThrowHelper.cs | 5 +- .../Tiff/Writers/TiffBaseColorWriter.cs | 103 ++++++-- .../Formats/Tiff/Writers/TiffBiColorWriter.cs | 225 +++++------------- .../Tiff/Writers/TiffColorWriterFactory.cs | 19 +- .../Tiff/Writers/TiffCompositeColorWriter.cs | 45 ++++ .../Formats/Tiff/Writers/TiffGrayWriter.cs | 162 +------------ .../Formats/Tiff/Writers/TiffPaletteWriter.cs | 212 +++-------------- .../Formats/Tiff/Writers/TiffRgbWriter.cs | 165 +------------ .../Formats/Tiff/Writers/TiffStreamWriter.cs | 1 - .../DeflateTiffCompressionTests.cs | 4 +- .../Compression/LzwTiffCompressionTests.cs | 13 +- .../Compression/NoneTiffCompressionTests.cs | 2 +- .../PackBitsTiffCompressionTests.cs | 3 +- .../Formats/Tiff/TiffEncoderHeaderTests.cs | 1 - .../Formats/Tiff/TiffEncoderTests.cs | 102 +++++++- .../Formats/Tiff/Utils/TiffWriterTests.cs | 2 - 44 files changed, 987 insertions(+), 943 deletions(-) create mode 100644 src/ImageSharp/Formats/Tiff/Compression/Compressors/DeflateCompressor.cs create mode 100644 src/ImageSharp/Formats/Tiff/Compression/Compressors/LzwCompressor.cs create mode 100644 src/ImageSharp/Formats/Tiff/Compression/Compressors/NoCompressor.cs create mode 100644 src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsCompressor.cs rename src/ImageSharp/Formats/Tiff/Compression/Compressors/{T4BitWriter.cs => T4BitCompressor.cs} (88%) rename src/ImageSharp/Formats/Tiff/Compression/{Decompressors => }/FaxCompressionOptions.cs (98%) create mode 100644 src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs create mode 100644 src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompressor.cs rename src/ImageSharp/Formats/Tiff/Compression/{Decompressors/TiffBaseCompression.cs => TiffBaseDecompresor.cs} (65%) create mode 100644 src/ImageSharp/Formats/Tiff/Compression/TiffCompressorFactory.cs rename src/ImageSharp/Formats/Tiff/Compression/{Decompressors => }/TiffDecoderCompressionType.cs (98%) rename src/ImageSharp/Formats/Tiff/Compression/{Decompressors => }/TiffDecompressorsFactory.cs (56%) create mode 100644 src/ImageSharp/Formats/Tiff/TiffEncoderPixelStorageMethod.cs create mode 100644 src/ImageSharp/Formats/Tiff/Writers/TiffCompositeColorWriter.cs diff --git a/src/ImageSharp/Formats/ImageExtensions.Save.cs b/src/ImageSharp/Formats/ImageExtensions.Save.cs index 07c6b37b8..6673803a4 100644 --- a/src/ImageSharp/Formats/ImageExtensions.Save.cs +++ b/src/ImageSharp/Formats/ImageExtensions.Save.cs @@ -13,7 +13,6 @@ using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Tga; -using SixLabors.ImageSharp.Formats.Tiff; namespace SixLabors.ImageSharp { diff --git a/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs b/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs index 9c857eccd..597a91b68 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs @@ -14,34 +14,38 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression int startIdx = bufferPos + bitPos; int endIdx = (int)(startIdx + count); - for (int i = startIdx; i < endIdx; i++) + if (value == 1) { - if (value == 1) + for (int i = startIdx; i < endIdx; i++) { WriteBit(buffer, bufferPos, bitPos); + + bitPos++; + if (bitPos >= 8) + { + bitPos = 0; + bufferPos++; + } } - else + } + else + { + for (int i = startIdx; i < endIdx; i++) { WriteZeroBit(buffer, bufferPos, bitPos); - } - bitPos++; - if (bitPos >= 8) - { - bitPos = 0; - bufferPos++; + bitPos++; + if (bitPos >= 8) + { + bitPos = 0; + bufferPos++; + } } } } - public static void WriteBit(Span buffer, int bufferPos, int bitPos) - { - buffer[bufferPos] |= (byte)(1 << (7 - bitPos)); - } + public static void WriteBit(Span buffer, int bufferPos, int bitPos) => buffer[bufferPos] |= (byte)(1 << (7 - bitPos)); - public static void WriteZeroBit(Span buffer, int bufferPos, int bitPos) - { - buffer[bufferPos] = (byte)(buffer[bufferPos] & ~(1 << (7 - bitPos))); - } + public static void WriteZeroBit(Span buffer, int bufferPos, int bitPos) => buffer[bufferPos] = (byte)(buffer[bufferPos] & ~(1 << (7 - bitPos))); } } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/DeflateCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/DeflateCompressor.cs new file mode 100644 index 000000000..f4b6c6ad7 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/DeflateCompressor.cs @@ -0,0 +1,59 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; +using SixLabors.ImageSharp.Compression.Zlib; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors +{ + internal class DeflateCompressor : TiffBaseCompressor + { + private readonly DeflateCompressionLevel compressionLevel; + + private readonly MemoryStream memoryStream = new MemoryStream(); + + public DeflateCompressor(Stream output, MemoryAllocator allocator, int width, int bitsPerPixel, TiffPredictor predictor, DeflateCompressionLevel compressionLevel) + : base(output, allocator, width, bitsPerPixel, predictor) + => this.compressionLevel = compressionLevel; + + public override TiffEncoderCompression Method => TiffEncoderCompression.Deflate; + + public override void Initialize(int rowsPerStrip) + { + } + + public override void CompressStrip(Span rows, int height) + { + this.memoryStream.Seek(0, SeekOrigin.Begin); + using var stream = new ZlibDeflateStream(this.Allocator, this.memoryStream, this.compressionLevel); + + if (this.Predictor == TiffPredictor.Horizontal) + { + HorizontalPredictor.ApplyHorizontalPrediction(rows, this.BytesPerRow, this.BitsPerPixel); + } + + stream.Write(rows); + + stream.Flush(); + //// stream.Dispose(); // todo: dispose write crc + + int size = (int)this.memoryStream.Position; + +#if !NETSTANDARD1_3 + byte[] buffer = this.memoryStream.GetBuffer(); + this.Output.Write(buffer, 0, size); +#else + this.memoryStream.SetLength(size); + this.memoryStream.Position = 0; + this.memoryStream.CopyTo(this.Output); +#endif + } + + protected override void Dispose(bool disposing) + { + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/LzwCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/LzwCompressor.cs new file mode 100644 index 000000000..84dc95b5f --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/LzwCompressor.cs @@ -0,0 +1,36 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors +{ + internal class LzwCompressor : TiffBaseCompressor + { + private TiffLzwEncoder lzwEncoder; + + public LzwCompressor(Stream output, MemoryAllocator allocator, int width, int bitsPerPixel, TiffPredictor predictor) + : base(output, allocator, width, bitsPerPixel, predictor) + { + } + + public override TiffEncoderCompression Method => TiffEncoderCompression.Lzw; + + public override void Initialize(int rowsPerStrip) => this.lzwEncoder = new TiffLzwEncoder(this.Allocator); + + public override void CompressStrip(Span rows, int height) + { + if (this.Predictor == TiffPredictor.Horizontal) + { + HorizontalPredictor.ApplyHorizontalPrediction(rows, this.BytesPerRow, this.BitsPerPixel); + } + + this.lzwEncoder.Encode(rows, this.Output); + } + + protected override void Dispose(bool disposing) => this.lzwEncoder?.Dispose(); + } +} diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/NoCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/NoCompressor.cs new file mode 100644 index 000000000..6c6b9ef34 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/NoCompressor.cs @@ -0,0 +1,28 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; + +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors +{ + internal class NoCompressor : TiffBaseCompressor + { + public NoCompressor(Stream output) + : base(output, default, default, default) + { + } + + public override TiffEncoderCompression Method => TiffEncoderCompression.None; + + public override void Initialize(int rowsPerStrip) + { + } + + public override void CompressStrip(Span rows, int height) => this.Output.Write(rows); + + protected override void Dispose(bool disposing) + { + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsCompressor.cs new file mode 100644 index 000000000..627ca6cbb --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsCompressor.cs @@ -0,0 +1,39 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors +{ + internal class PackBitsCompressor : TiffBaseCompressor + { + private IManagedByteBuffer pixelData; + + public PackBitsCompressor(Stream output, MemoryAllocator allocator, int width, int bitsPerPixel) + : base(output, allocator, width, bitsPerPixel) + { + } + + public override TiffEncoderCompression Method => TiffEncoderCompression.PackBits; + + public override void Initialize(int rowsPerStrip) + { + int additionalBytes = (this.BytesPerRow / 127) + 1; + this.pixelData = this.Allocator.AllocateManagedByteBuffer((this.BytesPerRow + additionalBytes) * rowsPerStrip); + } + + public override void CompressStrip(Span rows, int height) + { + this.pixelData.Clear(); + Span span = this.pixelData.GetSpan(); + int size = PackBitsWriter.PackBits(rows, span); + this.Output.Write(span.Slice(0, size)); + } + + protected override void Dispose(bool disposing) => this.pixelData?.Dispose(); + } +} diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitWriter.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitCompressor.cs similarity index 88% rename from src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitWriter.cs rename to src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitCompressor.cs index 99e2a148c..f96fa071d 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitCompressor.cs @@ -5,16 +5,14 @@ using System; using System.Buffers; using System.Collections.Generic; using System.IO; - using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors { /// /// Bitwriter for writing compressed CCITT T4 1D data. /// - internal class T4BitWriter + internal class T4BitCompressor : TiffBaseCompressor { private const uint WhiteZeroRunTermCode = 0x35; @@ -176,49 +174,52 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors { 1408, 0x54 }, { 1472, 0x55 }, { 1536, 0x5A }, { 1600, 0x5B }, { 1664, 0x64 }, { 1728, 0x65 } }; - private readonly MemoryAllocator memoryAllocator; + /// + /// The modified huffman is basically the same as CCITT T4, but without EOL markers and padding at the end of the rows. + /// + private readonly bool useModifiedHuffman; - private readonly Configuration configuration; + private IMemoryOwner compressedDataBuffer; private int bytePosition; private byte bitPosition; /// - /// The modified huffman is basically the same as CCITT T4, but without EOL markers and padding at the end of the rows. - /// - private readonly bool useModifiedHuffman; - - /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// The memory allocator. - /// The configuration. + /// The output. + /// The allocator. + /// The width. + /// The bits per pixel. /// Indicates if the modified huffman RLE should be used. - public T4BitWriter(MemoryAllocator memoryAllocator, Configuration configuration, bool useModifiedHuffman = false) + public T4BitCompressor(Stream output, MemoryAllocator allocator, int width, int bitsPerPixel, bool useModifiedHuffman = false) + : base(output, allocator, width, bitsPerPixel) { - this.memoryAllocator = memoryAllocator; - this.configuration = configuration; this.bytePosition = 0; this.bitPosition = 0; this.useModifiedHuffman = useModifiedHuffman; } - /// - /// Writes a image compressed with CCITT T4 to the stream. - /// - /// The pixel data. - /// The image to write to the stream. This has to be a bi-color image. - /// A span for converting a pixel row to gray. - /// The stream to write to. - /// The number of bytes written to the stream. - public int CompressImage(Image image, Span pixelRowAsGray, Stream stream) - where TPixel : unmanaged, IPixel + public override TiffEncoderCompression Method => this.useModifiedHuffman ? TiffEncoderCompression.ModifiedHuffman : TiffEncoderCompression.CcittGroup3Fax; + + public override void Initialize(int rowsPerStrip) { // This is too much memory allocated, but just 1 bit per pixel will not do, if the compression rate is not good. - int maxNeededBytes = image.Width * image.Height; - IMemoryOwner compressedDataBuffer = this.memoryAllocator.Allocate(maxNeededBytes, AllocationOptions.Clean); - Span compressedData = compressedDataBuffer.GetSpan(); + int maxNeededBytes = this.Width * rowsPerStrip; + this.compressedDataBuffer = this.Allocator.Allocate(maxNeededBytes); + } + + /// Writes a image compressed with CCITT T4 to the stream. + /// The pixels as 8-bit gray array. + /// The strip height. + public override void CompressStrip(Span pixelsAsGray, int height) + { + DebugGuard.Equals(pixelsAsGray.Length / height, this.Width); + DebugGuard.Equals(pixelsAsGray.Length % height, 0); + + this.compressedDataBuffer.Clear(); + Span compressedData = this.compressedDataBuffer.GetSpan(); this.bytePosition = 0; this.bitPosition = 0; @@ -229,36 +230,35 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors this.WriteCode(12, 1, compressedData); } - uint pixelsWritten = 0; - for (int y = 0; y < image.Height; y++) + for (int y = 0; y < height; y++) { bool isWhiteRun = true; bool isStartOrRow = true; - Span pixelRow = image.GetPixelRowSpan(y); - PixelOperations.Instance.ToL8(this.configuration, pixelRow, pixelRowAsGray); int x = 0; - while (x < image.Width) + + Span row = pixelsAsGray.Slice(y * this.Width, this.Width); + while (x < this.Width) { uint runLength = 0; - for (int i = x; i < image.Width; i++) + for (int i = x; i < this.Width; i++) { - if (isWhiteRun && pixelRowAsGray[i].PackedValue != 255) + if (isWhiteRun && row[i] != 255) { break; } - if (isWhiteRun && pixelRowAsGray[i].PackedValue == 255) + if (isWhiteRun && row[i] == 255) { runLength++; continue; } - if (!isWhiteRun && pixelRowAsGray[i].PackedValue != 0) + if (!isWhiteRun && row[i] != 0) { break; } - if (!isWhiteRun && pixelRowAsGray[i].PackedValue == 0) + if (!isWhiteRun && row[i] == 0) { runLength++; } @@ -280,7 +280,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors code = this.GetTermCode(runLength, out codeLength, isWhiteRun); this.WriteCode(codeLength, code, compressedData); x += (int)runLength; - pixelsWritten += runLength; } else { @@ -288,10 +287,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors code = this.GetMakeupCode(runLength, out codeLength, isWhiteRun); this.WriteCode(codeLength, code, compressedData); x += (int)runLength; - pixelsWritten += runLength; // If we are at the end of the line with a makeup code, we need to write a final term code with a length of zero. - if (x == image.Width) + if (x == this.Width) { if (isWhiteRun) { @@ -315,11 +313,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors // Write the compressed data to the stream. int bytesToWrite = this.bitPosition != 0 ? this.bytePosition + 1 : this.bytePosition; - stream.Write(compressedData.Slice(0, bytesToWrite)); - - return bytesToWrite; + this.Output.Write(compressedData.Slice(0, bytesToWrite)); } + protected override void Dispose(bool disposing) => this.compressedDataBuffer?.Dispose(); + private void WriteEndOfLine(Span compressedData) { if (this.useModifiedHuffman) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffLzwEncoder.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffLzwEncoder.cs index db7d18a41..f6a74c166 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffLzwEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffLzwEncoder.cs @@ -7,7 +7,7 @@ using System.IO; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors { /* This implementation is a port of a java tiff encoder by Harald Kuhr: https://github.com/haraldk/TwelveMonkeys @@ -71,8 +71,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils private static readonly int TableSize = 1 << MaxBits; - private readonly IMemoryOwner data; - // A child is made up of a parent (or prefix) code plus a suffix byte // and siblings are strings with a common parent(or prefix) and different suffix bytes. private readonly IMemoryOwner children; @@ -95,31 +93,27 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// /// Initializes a new instance of the class. /// - /// The data to compress. /// The memory allocator. - public TiffLzwEncoder(MemoryAllocator memoryAllocator, IMemoryOwner data) + public TiffLzwEncoder(MemoryAllocator memoryAllocator) { - this.data = data; - this.children = memoryAllocator.Allocate(TableSize, AllocationOptions.Clean); - this.siblings = memoryAllocator.Allocate(TableSize, AllocationOptions.Clean); - this.suffixes = memoryAllocator.Allocate(TableSize, AllocationOptions.Clean); - - this.parent = -1; - this.bitsPerCode = MinBits; - this.nextValidCode = EoiCode + 1; - this.maxCode = (1 << this.bitsPerCode) - 1; + this.children = memoryAllocator.Allocate(TableSize); + this.siblings = memoryAllocator.Allocate(TableSize); + this.suffixes = memoryAllocator.Allocate(TableSize); } /// /// Encodes and compresses the indexed pixels to the stream. /// + /// The data to compress. /// The stream to write to. - public void Encode(Stream stream) + public void Encode(Span data, Stream stream) { + this.Reset(); + Span childrenSpan = this.children.GetSpan(); Span suffixesSpan = this.suffixes.GetSpan(); Span siblingsSpan = this.siblings.GetSpan(); - int length = this.data.Length(); + int length = data.Length; if (length == 0) { @@ -130,12 +124,12 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils { // Init stream. this.WriteCode(stream, ClearCode); - this.parent = this.ReadNextByte() & 0xff; + this.parent = this.ReadNextByte(data); } - while (this.bufferPosition < this.data.Length()) + while (this.bufferPosition < data.Length) { - int value = this.ReadNextByte() & 0xff; + int value = this.ReadNextByte(data); int child = childrenSpan[this.parent]; if (child > 0) @@ -206,14 +200,24 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils this.suffixes.Dispose(); } - private byte ReadNextByte() + private void Reset() { - Span dataSpan = this.data.GetSpan(); - var nextByte = dataSpan[this.bufferPosition]; - this.bufferPosition++; - return nextByte; + this.children.Clear(); + this.siblings.Clear(); + this.suffixes.Clear(); + + this.parent = -1; + this.bitsPerCode = MinBits; + this.nextValidCode = EoiCode + 1; + this.maxCode = (1 << this.bitsPerCode) - 1; + + this.bits = 0; + this.bitPos = 0; + this.bufferPosition = 0; } + private byte ReadNextByte(Span data) => data[this.bufferPosition++]; + private void IncreaseCodeSizeOrResetIfNeeded(Stream stream) { if (this.nextValidCode > this.maxCode) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs index 8c0dbee95..a53d69027 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs @@ -6,7 +6,6 @@ using System.IO.Compression; using SixLabors.ImageSharp.Compression.Zlib; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; -using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; @@ -18,7 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso /// /// Note that the 'OldDeflate' compression type is identical to the 'Deflate' compression type. /// - internal class DeflateTiffCompression : TiffBaseCompression + internal class DeflateTiffCompression : TiffBaseDecompresor { /// /// Initializes a new instance of the class. @@ -54,5 +53,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso HorizontalPredictor.Undo(buffer, this.Width, this.BitsPerPixel); } } + + protected override void Dispose(bool disposing) + { + } } } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs index 98aecd173..82640dfed 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs @@ -4,7 +4,6 @@ using System; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; -using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; @@ -13,7 +12,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso /// /// Class to handle cases where TIFF image data is compressed using LZW compression. /// - internal class LzwTiffCompression : TiffBaseCompression + internal class LzwTiffCompression : TiffBaseDecompresor { /// /// Initializes a new instance of the class. @@ -38,5 +37,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso HorizontalPredictor.Undo(buffer, this.Width, this.BitsPerPixel); } } + + protected override void Dispose(bool disposing) + { + } } } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs index 1664cbebb..7a7cd20f7 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs @@ -14,6 +14,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso /// internal class ModifiedHuffmanTiffCompression : T4TiffCompression { + private readonly byte whiteValue; + + private readonly byte blackValue; + /// /// Initializes a new instance of the class. /// @@ -23,15 +27,14 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso public ModifiedHuffmanTiffCompression(MemoryAllocator allocator, TiffPhotometricInterpretation photometricInterpretation, int width) : base(allocator, FaxCompressionOptions.None, photometricInterpretation, width) { + bool isWhiteZero = photometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero; + this.whiteValue = (byte)(isWhiteZero ? 0 : 1); + this.blackValue = (byte)(isWhiteZero ? 1 : 0); } /// protected override void Decompress(BufferedReadStream stream, int byteCount, Span buffer) { - bool isWhiteZero = this.PhotometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero; - byte whiteValue = (byte)(isWhiteZero ? 0 : 1); - byte blackValue = (byte)(isWhiteZero ? 1 : 0); - using var bitReader = new T4BitReader(stream, byteCount, this.Allocator, eolPadding: false, isModifiedHuffman: true); buffer.Clear(); @@ -45,13 +48,13 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso { if (bitReader.IsWhiteRun) { - BitWriterUtils.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, whiteValue); + BitWriterUtils.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, this.whiteValue); bitsWritten += bitReader.RunLength; pixelsWritten += bitReader.RunLength; } else { - BitWriterUtils.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, blackValue); + BitWriterUtils.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, this.blackValue); bitsWritten += bitReader.RunLength; pixelsWritten += bitReader.RunLength; } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/NoneTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/NoneTiffCompression.cs index f7271fd03..a30997deb 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/NoneTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/NoneTiffCompression.cs @@ -4,25 +4,27 @@ using System; using SixLabors.ImageSharp.IO; -using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors { /// /// Class to handle cases where TIFF image data is not compressed. /// - internal class NoneTiffCompression : TiffBaseCompression + internal class NoneTiffCompression : TiffBaseDecompresor { /// /// Initializes a new instance of the class. /// - /// The memoryAllocator to use for buffer allocations. - public NoneTiffCompression(MemoryAllocator memoryAllocator) - : base(memoryAllocator) + public NoneTiffCompression() + : base(default, default, default) { } /// protected override void Decompress(BufferedReadStream stream, int byteCount, Span buffer) => _ = stream.Read(buffer, 0, Math.Min(buffer.Length, byteCount)); + + protected override void Dispose(bool disposing) + { + } } } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/PackBitsTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/PackBitsTiffCompression.cs index 9786ef5ff..ab67d818d 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/PackBitsTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/PackBitsTiffCompression.cs @@ -12,23 +12,33 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso /// /// Class to handle cases where TIFF image data is compressed using PackBits compression. /// - internal class PackBitsTiffCompression : TiffBaseCompression + internal class PackBitsTiffCompression : TiffBaseDecompresor { + private IMemoryOwner compressedDataMemory; + /// /// Initializes a new instance of the class. /// /// The memoryAllocator to use for buffer allocations. public PackBitsTiffCompression(MemoryAllocator memoryAllocator) - : base(memoryAllocator) + : base(memoryAllocator, default, default) { } /// protected override void Decompress(BufferedReadStream stream, int byteCount, Span buffer) { - using IMemoryOwner compressedDataMemory = this.Allocator.Allocate(byteCount); + if (this.compressedDataMemory == null) + { + this.compressedDataMemory = this.Allocator.Allocate(byteCount); + } + else if (this.compressedDataMemory.Length() < byteCount) + { + this.compressedDataMemory.Dispose(); + this.compressedDataMemory = this.Allocator.Allocate(byteCount); + } - Span compressedData = compressedDataMemory.GetSpan(); + Span compressedData = this.compressedDataMemory.GetSpan(); stream.Read(compressedData, 0, byteCount); int compressedOffset = 0; @@ -77,5 +87,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso destinationArray[i + destinationIndex] = value; } } + + protected override void Dispose(bool disposing) => this.compressedDataMemory?.Dispose(); } } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs index 005b5132a..fe4641fb2 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs @@ -12,10 +12,14 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso /// /// Class to handle cases where TIFF image data is compressed using CCITT T4 compression. /// - internal class T4TiffCompression : TiffBaseCompression + internal class T4TiffCompression : TiffBaseDecompresor { private readonly FaxCompressionOptions faxCompressionOptions; + private readonly byte whiteValue; + + private readonly byte blackValue; + /// /// Initializes a new instance of the class. /// @@ -24,7 +28,14 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso /// The photometric interpretation. /// The image width. public T4TiffCompression(MemoryAllocator allocator, FaxCompressionOptions faxOptions, TiffPhotometricInterpretation photometricInterpretation, int width) - : base(allocator, photometricInterpretation, width) => this.faxCompressionOptions = faxOptions; + : base(allocator, width, default) + { + this.faxCompressionOptions = faxOptions; + + bool isWhiteZero = photometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero; + this.whiteValue = (byte)(isWhiteZero ? 0 : 1); + this.blackValue = (byte)(isWhiteZero ? 1 : 0); + } /// protected override void Decompress(BufferedReadStream stream, int byteCount, Span buffer) @@ -34,10 +45,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso TiffThrowHelper.ThrowNotSupported("TIFF CCITT 2D compression is not yet supported"); } - bool isWhiteZero = this.PhotometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero; - byte whiteValue = (byte)(isWhiteZero ? 0 : 1); - byte blackValue = (byte)(isWhiteZero ? 1 : 0); - var eolPadding = this.faxCompressionOptions.HasFlag(FaxCompressionOptions.EolPadding); using var bitReader = new T4BitReader(stream, byteCount, this.Allocator, eolPadding); @@ -51,12 +58,12 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso { if (bitReader.IsWhiteRun) { - BitWriterUtils.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, whiteValue); + BitWriterUtils.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, this.whiteValue); bitsWritten += bitReader.RunLength; } else { - BitWriterUtils.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, blackValue); + BitWriterUtils.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, this.blackValue); bitsWritten += bitReader.RunLength; } } @@ -73,5 +80,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso } } } + + protected override void Dispose(bool disposing) + { + } } } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/FaxCompressionOptions.cs b/src/ImageSharp/Formats/Tiff/Compression/FaxCompressionOptions.cs similarity index 98% rename from src/ImageSharp/Formats/Tiff/Compression/Decompressors/FaxCompressionOptions.cs rename to src/ImageSharp/Formats/Tiff/Compression/FaxCompressionOptions.cs index c98ad0387..d5171db65 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/FaxCompressionOptions.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/FaxCompressionOptions.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression { /// /// Fax compression options, see TIFF spec page 51f (T4Options). diff --git a/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs b/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs index 10ac39747..0e394d26a 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs @@ -7,7 +7,7 @@ using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Tiff.Compression +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression { /// /// Methods for undoing the horizontal prediction used in combination with deflate and LZW compressed TIFF images. @@ -32,38 +32,64 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression } } + public static void ApplyHorizontalPrediction(Span rows, int width, int bitsPerPixel) + { + if (bitsPerPixel == 8) + { + ApplyHorizontalPrediction8Bit(rows, width); + } + else if (bitsPerPixel == 24) + { + ApplyHorizontalPrediction24Bit(rows, width); + } + } + /// /// Applies a horizontal predictor to the rgb row. /// Make use of the fact that many continuous-tone images rarely vary much in pixel value from one pixel to the next. /// In such images, if we replace the pixel values by differences between consecutive pixels, many of the differences should be 0, plus /// or minus 1, and so on.This reduces the apparent information content and allows LZW to encode the data more compactly. /// - /// The rgb pixel row. + /// The rgb pixel rows. + /// The width. [MethodImpl(InliningOptions.ShortMethod)] - public static void ApplyHorizontalPrediction24Bit(Span rowSpan) + private static void ApplyHorizontalPrediction24Bit(Span rows, int width) { - Span rowRgb = MemoryMarshal.Cast(rowSpan); - - for (int x = rowRgb.Length - 1; x >= 1; x--) + DebugGuard.Equals(rows.Length % width, 0); + int height = rows.Length / width; + for (int y = 0; y < height; y++) { - byte r = (byte)(rowRgb[x].R - rowRgb[x - 1].R); - byte g = (byte)(rowRgb[x].G - rowRgb[x - 1].G); - byte b = (byte)(rowRgb[x].B - rowRgb[x - 1].B); - var rgb = new Rgb24(r, g, b); - rowRgb[x].FromRgb24(rgb); + Span rowSpan = rows.Slice(y * width, width); + Span rowRgb = MemoryMarshal.Cast(rowSpan); + + for (int x = rowRgb.Length - 1; x >= 1; x--) + { + byte r = (byte)(rowRgb[x].R - rowRgb[x - 1].R); + byte g = (byte)(rowRgb[x].G - rowRgb[x - 1].G); + byte b = (byte)(rowRgb[x].B - rowRgb[x - 1].B); + var rgb = new Rgb24(r, g, b); + rowRgb[x].FromRgb24(rgb); + } } } /// /// Applies a horizontal predictor to a gray pixel row. /// - /// The gray pixel row. + /// The gray pixel rows. + /// The width. [MethodImpl(InliningOptions.ShortMethod)] - public static void ApplyHorizontalPrediction8Bit(Span rowSpan) + private static void ApplyHorizontalPrediction8Bit(Span rows, int width) { - for (int x = rowSpan.Length - 1; x >= 1; x--) + DebugGuard.Equals(rows.Length % width, 0); + int height = rows.Length / width; + for (int y = 0; y < height; y++) { - rowSpan[x] -= rowSpan[x - 1]; + Span rowSpan = rows.Slice(y * width, width); + for (int x = rowSpan.Length - 1; x >= 1; x--) + { + rowSpan[x] -= rowSpan[x - 1]; + } } } diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs new file mode 100644 index 000000000..e47b65c99 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs @@ -0,0 +1,47 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression +{ + internal abstract class TiffBaseCompression : IDisposable + { + private bool isDisposed; + + protected TiffBaseCompression(MemoryAllocator allocator, int width, int bitsPerPixel, TiffPredictor predictor = TiffPredictor.None) + { + this.Allocator = allocator; + this.Width = width; + this.BitsPerPixel = bitsPerPixel; + this.Predictor = predictor; + + this.BytesPerRow = ((width * bitsPerPixel) + 7) / 8; + } + + public int Width { get; } + + public int BitsPerPixel { get; } + + public int BytesPerRow { get; } + + public TiffPredictor Predictor { get; } + + protected MemoryAllocator Allocator { get; } + + public void Dispose() + { + if (this.isDisposed) + { + return; + } + + this.isDisposed = true; + this.Dispose(true); + } + + protected abstract void Dispose(bool disposing); + } +} diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompressor.cs new file mode 100644 index 000000000..71190c7c4 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompressor.cs @@ -0,0 +1,25 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression +{ + internal abstract class TiffBaseCompressor : TiffBaseCompression + { + protected TiffBaseCompressor(Stream output, MemoryAllocator allocator, int width, int bitsPerPixel, TiffPredictor predictor = TiffPredictor.None) + : base(allocator, width, bitsPerPixel, predictor) + => this.Output = output; + + public abstract TiffEncoderCompression Method { get; } + + public Stream Output { get; } + + public abstract void Initialize(int rowsPerStrip); + + public abstract void CompressStrip(Span rows, int height); + } +} diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffBaseCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseDecompresor.cs similarity index 65% rename from src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffBaseCompression.cs rename to src/ImageSharp/Formats/Tiff/Compression/TiffBaseDecompresor.cs index 7262b483b..5f981911d 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffBaseCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseDecompresor.cs @@ -8,40 +8,18 @@ using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression { /// - /// Base tiff decompressor class. + /// The base tiff decompressor class. /// - internal abstract class TiffBaseCompression + internal abstract class TiffBaseDecompresor : TiffBaseCompression { - protected TiffBaseCompression(MemoryAllocator allocator) => this.Allocator = allocator; - - protected TiffBaseCompression(MemoryAllocator allocator, TiffPhotometricInterpretation photometricInterpretation, int width) - : this(allocator) + protected TiffBaseDecompresor(MemoryAllocator allocator, int width, int bitsPerPixel, TiffPredictor predictor = TiffPredictor.None) + : base(allocator, width, bitsPerPixel, predictor) { - this.PhotometricInterpretation = photometricInterpretation; - this.Width = width; } - protected TiffBaseCompression(MemoryAllocator allocator, int width, int bitsPerPixel, TiffPredictor predictor) - : this(allocator) - { - this.Width = width; - this.BitsPerPixel = bitsPerPixel; - this.Predictor = predictor; - } - - protected MemoryAllocator Allocator { get; } - - protected TiffPhotometricInterpretation PhotometricInterpretation { get; } - - protected int Width { get; } - - protected int BitsPerPixel { get; } - - protected TiffPredictor Predictor { get; } - /// /// Decompresses image data into the supplied buffer. /// diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressorFactory.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressorFactory.cs new file mode 100644 index 000000000..d964fbb14 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressorFactory.cs @@ -0,0 +1,61 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; +using System.IO; +using SixLabors.ImageSharp.Compression.Zlib; +using SixLabors.ImageSharp.Formats.Experimental.Tiff; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression +{ + internal static class TiffCompressorFactory + { + public static TiffBaseCompressor Create( + TiffEncoderCompression method, + Stream output, + MemoryAllocator allocator, + int width, + int bitsPerPixel, + DeflateCompressionLevel compressionLevel, + TiffPredictor predictor) + { + switch (method) + { + case TiffEncoderCompression.None: + DebugGuard.Equals(compressionLevel, default(DeflateCompressionLevel)); + DebugGuard.Equals(predictor, TiffPredictor.None); + + return new NoCompressor(output); + + case TiffEncoderCompression.PackBits: + DebugGuard.Equals(compressionLevel, default(DeflateCompressionLevel)); + DebugGuard.Equals(predictor, TiffPredictor.None); + return new PackBitsCompressor(output, allocator, width, bitsPerPixel); + + case TiffEncoderCompression.Deflate: + return new DeflateCompressor(output, allocator, width, bitsPerPixel, predictor, compressionLevel); + + case TiffEncoderCompression.Lzw: + DebugGuard.Equals(compressionLevel, default(DeflateCompressionLevel)); + return new LzwCompressor(output, allocator, width, bitsPerPixel, predictor); + + case TiffEncoderCompression.CcittGroup3Fax: + DebugGuard.Equals(compressionLevel, default(DeflateCompressionLevel)); + DebugGuard.Equals(predictor, TiffPredictor.None); + return new T4BitCompressor(output, allocator, width, bitsPerPixel, false); + + case TiffEncoderCompression.ModifiedHuffman: + DebugGuard.Equals(compressionLevel, default(DeflateCompressionLevel)); + DebugGuard.Equals(predictor, TiffPredictor.None); + return new T4BitCompressor(output, allocator, width, bitsPerPixel, true); + + default: + throw TiffThrowHelper.NotSupportedCompressor(nameof(method)); + } + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffDecoderCompressionType.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffDecoderCompressionType.cs similarity index 98% rename from src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffDecoderCompressionType.cs rename to src/ImageSharp/Formats/Tiff/Compression/TiffDecoderCompressionType.cs index 8ec11c360..247d91e63 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffDecoderCompressionType.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffDecoderCompressionType.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression { /// /// Provides enumeration of the various TIFF compression types the decoder can handle. diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffDecompressorsFactory.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs similarity index 56% rename from src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffDecompressorsFactory.cs rename to src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs index 2f78405bb..e219f0b93 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffDecompressorsFactory.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs @@ -1,15 +1,16 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression { internal static class TiffDecompressorsFactory { - public static TiffBaseCompression Create( - TiffDecoderCompressionType compressionType, + public static TiffBaseDecompresor Create( + TiffDecoderCompressionType method, MemoryAllocator allocator, TiffPhotometricInterpretation photometricInterpretation, int width, @@ -17,32 +18,38 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso TiffPredictor predictor, FaxCompressionOptions faxOptions) { - switch (compressionType) + switch (method) { case TiffDecoderCompressionType.None: - DebugGuard.IsTrue(predictor == TiffPredictor.None, "predictor"); - return new NoneTiffCompression(allocator); + DebugGuard.Equals(predictor, TiffPredictor.None); + DebugGuard.Equals(faxOptions, FaxCompressionOptions.None); + return new NoneTiffCompression(); case TiffDecoderCompressionType.PackBits: - DebugGuard.IsTrue(predictor == TiffPredictor.None, "predictor"); + DebugGuard.Equals(predictor, TiffPredictor.None); + DebugGuard.Equals(faxOptions, FaxCompressionOptions.None); return new PackBitsTiffCompression(allocator); case TiffDecoderCompressionType.Deflate: + DebugGuard.Equals(faxOptions, FaxCompressionOptions.None); return new DeflateTiffCompression(allocator, width, bitsPerPixel, predictor); case TiffDecoderCompressionType.Lzw: + DebugGuard.Equals(faxOptions, FaxCompressionOptions.None); return new LzwTiffCompression(allocator, width, bitsPerPixel, predictor); case TiffDecoderCompressionType.T4: - DebugGuard.IsTrue(predictor == TiffPredictor.None, "predictor"); + DebugGuard.Equals(predictor, TiffPredictor.None); + DebugGuard.Equals(faxOptions, FaxCompressionOptions.None); return new T4TiffCompression(allocator, faxOptions, photometricInterpretation, width); case TiffDecoderCompressionType.HuffmanRle: - DebugGuard.IsTrue(predictor == TiffPredictor.None, "predictor"); + DebugGuard.Equals(predictor, TiffPredictor.None); + DebugGuard.Equals(faxOptions, FaxCompressionOptions.None); return new ModifiedHuffmanTiffCompression(allocator, photometricInterpretation, width); default: - throw TiffThrowHelper.NotSupportedCompression(nameof(compressionType)); + throw TiffThrowHelper.NotSupportedDecompressor(nameof(method)); } } } diff --git a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs index d9ede337a..8d0a15ffe 100644 --- a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs +++ b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs @@ -37,5 +37,15 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// Gets the quantizer for creating a color palette image. /// IQuantizer Quantizer { get; } + + /// + /// Gets the pixel storage method. + /// + TiffEncoderPixelStorageMethod PixelStorageMethod { get; } + + /// + /// Gets the maximum size of strip (bytes). + /// + int MaxStripBytes { get; } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 26c4d0038..fe81d2edb 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Threading; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; @@ -249,7 +249,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff stripBuffers[stripIndex] = this.memoryAllocator.AllocateManagedByteBuffer(uncompressedStripSize); } - TiffBaseCompression decompressor = TiffDecompressorsFactory.Create(this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, frame.Width, bitsPerPixel, this.Predictor, this.FaxCompressionOptions); + using TiffBaseDecompresor decompressor = TiffDecompressorsFactory.Create(this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, frame.Width, bitsPerPixel, this.Predictor, this.FaxCompressionOptions); RgbPlanarTiffColor colorDecoder = TiffColorDecoderFactory.CreatePlanar(this.ColorType, this.BitsPerSample, this.ColorMap); @@ -292,7 +292,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff Buffer2D pixels = frame.PixelBuffer; - TiffBaseCompression decompressor = TiffDecompressorsFactory.Create(this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, frame.Width, bitsPerPixel, this.Predictor, this.FaxCompressionOptions); + using TiffBaseDecompresor decompressor = TiffDecompressorsFactory.Create(this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, frame.Width, bitsPerPixel, this.Predictor, this.FaxCompressionOptions); TiffBaseColorDecoder colorDecoder = TiffColorDecoderFactory.Create(this.ColorType, this.BitsPerSample, this.ColorMap); diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs index 625af123d..2c632b36e 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Metadata.Profiles.Exif; diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs index 0f333679e..86091a5c4 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs @@ -32,6 +32,12 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// public IQuantizer Quantizer { get; set; } + /// + public TiffEncoderPixelStorageMethod PixelStorageMethod { get; set; } + + /// + public int MaxStripBytes { get; set; } + /// public void Encode(Image image, Stream stream) where TPixel : unmanaged, IPixel diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 063da629f..f378e2fe8 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -8,6 +8,7 @@ using System.Threading; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Compression.Zlib; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers; using SixLabors.ImageSharp.Memory; @@ -30,6 +31,8 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff ? TiffConstants.ByteOrderLittleEndianShort : TiffConstants.ByteOrderBigEndianShort; + private const int DefaultStripSize = 8 * 1024; + /// /// Used for allocating memory during processing operations. /// @@ -43,7 +46,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// /// The color depth, in number of bits per pixel. /// - private TiffBitsPerPixel? bitsPerPixel; + private TiffBitsPerPixel bitsPerPixel; /// /// The quantizer for creating color palette image. @@ -55,6 +58,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// private readonly DeflateCompressionLevel compressionLevel; + private readonly TiffEncoderPixelStorageMethod storageMode; + + private readonly int maxStripBytes; + /// /// Initializes a new instance of the class. /// @@ -68,6 +75,8 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff this.quantizer = options.Quantizer ?? KnownQuantizers.Octree; this.UseHorizontalPredictor = options.UseHorizontalPredictor; this.compressionLevel = options.CompressionLevel; + this.storageMode = options.PixelStorageMethod; + this.maxStripBytes = options.MaxStripBytes; } /// @@ -105,26 +114,8 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff Guard.NotNull(stream, nameof(stream)); this.configuration = image.GetConfiguration(); - ImageMetadata metadata = image.Metadata; - TiffMetadata tiffMetadata = metadata.GetTiffMetadata(); - this.bitsPerPixel ??= tiffMetadata.BitsPerPixel; - if (this.Mode == TiffEncodingMode.Default) - { - // Preserve input bits per pixel, if no mode was specified. - if (this.bitsPerPixel == TiffBitsPerPixel.Pixel8) - { - this.Mode = TiffEncodingMode.Gray; - } - else if (this.bitsPerPixel == TiffBitsPerPixel.Pixel1) - { - this.Mode = TiffEncodingMode.BiColor; - } - else - { - this.Mode = TiffEncodingMode.Rgb; - } - } + this.SetMode(image); this.SetPhotometricInterpretation(); using (var writer = new TiffStreamWriter(stream)) @@ -132,7 +123,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff long firstIfdMarker = this.WriteHeader(writer); // TODO: multiframing is not support - long nextIfdMarker = this.WriteImage(writer, image, firstIfdMarker); + this.WriteImage(writer, image, firstIfdMarker); } } @@ -159,8 +150,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// The to write data to. /// The to encode from. /// The marker to write this IFD offset. - /// The marker to write the next IFD offset (if present). - public long WriteImage(TiffStreamWriter writer, Image image, long ifdOffset) + private void WriteImage(TiffStreamWriter writer, Image image, long ifdOffset) where TPixel : unmanaged, IPixel { var entriesCollector = new TiffEncoderEntriesCollector(); @@ -168,18 +158,43 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff // Write the image bytes to the steam. var imageDataStart = (uint)writer.Position; - TiffBaseColorWriter colorWriter = TiffColorWriterFactory.Create(this.Mode, writer, this.memoryAllocator, this.configuration, entriesCollector); + using TiffBaseCompressor compressor = TiffCompressorFactory.Create( + this.CompressionType, + writer.BaseStream, + this.memoryAllocator, + image.Width, + (int)this.bitsPerPixel, + this.compressionLevel, + this.UseHorizontalPredictor ? TiffPredictor.Horizontal : TiffPredictor.None); + + using TiffBaseColorWriter colorWriter = TiffColorWriterFactory.Create(this.Mode, image.Frames.RootFrame, this.quantizer, this.memoryAllocator, this.configuration, entriesCollector); + + int rowsPerStrip = this.CalcRowsPerStrip(image.Frames.RootFrame, colorWriter.BytesPerRow); - int imageDataBytes = colorWriter.Write(image, this.quantizer, this.CompressionType, this.compressionLevel, this.UseHorizontalPredictor); + colorWriter.Write(compressor, rowsPerStrip); - this.AddStripTags(image, entriesCollector, imageDataStart, imageDataBytes); entriesCollector.ProcessImageFormat(this); entriesCollector.ProcessGeneral(image); writer.WriteMarker(ifdOffset, (uint)writer.Position); long nextIfdMarker = this.WriteIfd(writer, entriesCollector.Entries); + } + + private int CalcRowsPerStrip(ImageFrame image, int bytesPerRow) + { + switch (this.storageMode) + { + default: + case TiffEncoderPixelStorageMethod.Auto: + case TiffEncoderPixelStorageMethod.MultiStrip: + int sz = this.maxStripBytes > 0 ? this.maxStripBytes : DefaultStripSize; + int height = sz / bytesPerRow; + + return height > 0 ? (height < image.Height ? height : image.Height) : 1; - return nextIfdMarker + imageDataBytes; + case TiffEncoderPixelStorageMethod.SingleStrip: + return image.Height; + } } /// @@ -188,7 +203,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// The to write data to. /// The IFD entries to write to the file. /// The marker to write the next IFD offset (if present). - public long WriteIfd(TiffStreamWriter writer, List entries) + private long WriteIfd(TiffStreamWriter writer, List entries) { if (entries.Count == 0) { @@ -239,37 +254,51 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff return nextIfdMarker; } - /// - /// Adds image format information to the specified IFD. - /// - /// The pixel format. - /// The to encode from. - /// The entries collector. - /// The start of the image data in the stream. - /// The image data in bytes to write. - public void AddStripTags(Image image, TiffEncoderEntriesCollector entriesCollector, uint imageDataStartOffset, int imageDataBytes) - where TPixel : unmanaged, IPixel + private void SetMode(Image image) { - var stripOffsets = new ExifLongArray(ExifTagValue.StripOffsets) + if (this.CompressionType == TiffEncoderCompression.CcittGroup3Fax || this.CompressionType == TiffEncoderCompression.ModifiedHuffman) { - // TODO: we only write one image strip for the start. - Value = new[] { imageDataStartOffset } - }; + this.Mode = TiffEncodingMode.BiColor; + this.bitsPerPixel = TiffBitsPerPixel.Pixel1; + return; + } - var rowsPerStrip = new ExifLong(ExifTagValue.RowsPerStrip) + if (this.Mode == TiffEncodingMode.Default) { - // All rows in one strip. - Value = (uint)image.Height - }; + // Preserve input bits per pixel, if no mode was specified. + TiffMetadata tiffMetadata = image.Metadata.GetTiffMetadata(); + switch (tiffMetadata.BitsPerPixel) + { + case TiffBitsPerPixel.Pixel1: + this.Mode = TiffEncodingMode.BiColor; + break; + case TiffBitsPerPixel.Pixel8: + // todo: can gray or palette + this.Mode = TiffEncodingMode.Gray; + break; + default: + this.Mode = TiffEncodingMode.Rgb; + break; + } + } - var stripByteCounts = new ExifLongArray(ExifTagValue.StripByteCounts) + switch (this.Mode) { - Value = new[] { (uint)imageDataBytes } - }; - - entriesCollector.Add(stripOffsets); - entriesCollector.Add(rowsPerStrip); - entriesCollector.Add(stripByteCounts); + case TiffEncodingMode.BiColor: + this.bitsPerPixel = TiffBitsPerPixel.Pixel1; + break; + case TiffEncodingMode.ColorPalette: + case TiffEncodingMode.Gray: + this.bitsPerPixel = TiffBitsPerPixel.Pixel8; + break; + case TiffEncodingMode.Rgb: + this.bitsPerPixel = TiffBitsPerPixel.Pixel24; + break; + default: + this.Mode = TiffEncodingMode.Rgb; + this.bitsPerPixel = TiffBitsPerPixel.Pixel24; + break; + } } private void SetPhotometricInterpretation() diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderPixelStorageMethod.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderPixelStorageMethod.cs new file mode 100644 index 000000000..e1e12c08d --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderPixelStorageMethod.cs @@ -0,0 +1,26 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff +{ + /// + /// The tiff encoder pixel storage method. + /// + public enum TiffEncoderPixelStorageMethod + { + /// + /// The auto mode. + /// + Auto, + + /// + /// The single strip mode. + /// + SingleStrip, + + /// + /// The multi strip mode. + /// + MultiStrip, + } +} diff --git a/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs b/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs index d853836b5..db91fcbcb 100644 --- a/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs +++ b/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs @@ -19,7 +19,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff public static void ThrowImageFormatException(string errorMessage) => throw new ImageFormatException(errorMessage); [MethodImpl(InliningOptions.ColdPath)] - public static Exception NotSupportedCompression(string compressionType) => throw new NotSupportedException($"Not supported compression: {compressionType}"); + public static Exception NotSupportedDecompressor(string compressionType) => throw new NotSupportedException($"Not supported decoder compression method: {compressionType}"); + + [MethodImpl(InliningOptions.ColdPath)] + public static Exception NotSupportedCompressor(string compressionType) => throw new NotSupportedException($"Not supported encoder compression method: {compressionType}"); [MethodImpl(InliningOptions.ColdPath)] public static Exception InvalidColorType(string colorType) => throw new NotSupportedException($"Invalid color type: {colorType}"); diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs index 23b4e329d..191c051d6 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs @@ -2,35 +2,33 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.IO; -using SixLabors.ImageSharp.Compression.Zlib; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing.Processors.Quantization; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers { - /// - /// Utility class for writing TIFF data to a . - /// - internal abstract class TiffBaseColorWriter + internal abstract class TiffBaseColorWriter : IDisposable + where TPixel : unmanaged, IPixel { - /// - /// Initializes a new instance of the class. - /// - /// The output stream. - /// The memory allocator. - /// The configuration. - /// The entries collector. - protected TiffBaseColorWriter(TiffStreamWriter output, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector) + private bool isDisposed; + + protected TiffBaseColorWriter(ImageFrame image, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector) { - this.Output = output; + this.Image = image; this.MemoryAllocator = memoryAllocator; this.Configuration = configuration; this.EntriesCollector = entriesCollector; + + this.BytesPerRow = ((image.Width * this.BitsPerPixel) + 7) / 8; } - protected TiffStreamWriter Output { get; } + public abstract int BitsPerPixel { get; } + + public int BytesPerRow { get; } + + protected ImageFrame Image { get; } protected MemoryAllocator MemoryAllocator { get; } @@ -38,7 +36,74 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers protected TiffEncoderEntriesCollector EntriesCollector { get; } - public abstract int Write(Image image, IQuantizer quantizer, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) - where TPixel : unmanaged, IPixel; + public virtual void Write(TiffBaseCompressor compressor, int rowsPerStrip) + { + DebugGuard.Equals(this.BytesPerRow, compressor.BytesPerRow); + int stripsCount = (this.Image.Height + rowsPerStrip - 1) / rowsPerStrip; + + uint[] stripOffsets = new uint[stripsCount]; + uint[] stripByteCounts = new uint[stripsCount]; + + int stripIndex = 0; + compressor.Initialize(rowsPerStrip); + for (int y = 0; y < this.Image.Height; y += rowsPerStrip) + { + long offset = compressor.Output.Position; + + int height = Math.Min(rowsPerStrip, this.Image.Height - y); + this.EncodeStrip(y, height, compressor); + + long endOffset = compressor.Output.Position; + stripOffsets[stripIndex] = (uint)offset; + stripByteCounts[stripIndex] = (uint)(endOffset - offset); + stripIndex++; + } + + DebugGuard.Equals(stripIndex, stripsCount); + this.AddStripTags(rowsPerStrip, stripOffsets, stripByteCounts); + } + + public void Dispose() + { + if (this.isDisposed) + { + return; + } + + this.isDisposed = true; + this.Dispose(true); + } + + protected static Span GetStripPixels(Buffer2D buffer2D, int y, int height) + where T : struct + => buffer2D.GetSingleSpan().Slice(y * buffer2D.Width, height * buffer2D.Width); + + protected abstract void EncodeStrip(int y, int height, TiffBaseCompressor compressor); + + /// + /// Adds image format information to the specified IFD. + /// + /// The rows per strip. + /// The strip offsets. + /// The strip byte counts. + private void AddStripTags(int rowsPerStrip, uint[] stripOffsets, uint[] stripByteCounts) + { + this.EntriesCollector.Add(new ExifLong(ExifTagValue.RowsPerStrip) + { + Value = (uint)rowsPerStrip + }); + + this.EntriesCollector.Add(new ExifLongArray(ExifTagValue.StripOffsets) + { + Value = stripOffsets + }); + + this.EntriesCollector.Add(new ExifLongArray(ExifTagValue.StripByteCounts) + { + Value = stripByteCounts + }); + } + + protected abstract void Dispose(bool disposing); } } diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs index 24c2f08da..76d5cbaa0 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs @@ -3,208 +3,97 @@ using System; using System.Buffers; -using System.IO; - -using SixLabors.ImageSharp.Compression.Zlib; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Dithering; -using SixLabors.ImageSharp.Processing.Processors.Quantization; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers { - /// - /// Utility class for writing TIFF data to a . - /// - internal class TiffBiColorWriter : TiffBaseColorWriter + internal class TiffBiColorWriter : TiffBaseColorWriter + where TPixel : unmanaged, IPixel { - public TiffBiColorWriter(TiffStreamWriter output, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector) - : base(output, memoryAllocator, configuration, entriesCollector) - { - } + private readonly Image imageBlackWhite; - /// - /// Writes the image data as 1 bit black and white to the stream. - /// - /// The pixel data. - /// The image to write to the stream. - /// The quantizer. - /// The compression to use. - /// The compression level for deflate compression. - /// if set to true [use horizontal predictor]. - /// - /// The number of bytes written. - /// - public override int Write(Image image, IQuantizer quantizer, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) - { - int padding = image.Width % 8 == 0 ? 0 : 1; - int bytesPerRow = (image.Width / 8) + padding; - using IMemoryOwner pixelRowAsGray = this.MemoryAllocator.Allocate(image.Width); - using IManagedByteBuffer row = this.MemoryAllocator.AllocateManagedByteBuffer(bytesPerRow, AllocationOptions.Clean); - Span outputRow = row.GetSpan(); - Span pixelRowAsGraySpan = pixelRowAsGray.GetSpan(); + private IMemoryOwner pixelsAsGray; + + private IMemoryOwner bitStrip; + public TiffBiColorWriter(ImageFrame image, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector) + : base(image, memoryAllocator, configuration, entriesCollector) + { // Convert image to black and white. // TODO: Should we allow to skip this by the user, if its known to be black and white already? - using Image imageBlackWhite = image.Clone(); - imageBlackWhite.Mutate(img => img.BinaryDither(default(ErrorDither))); - - if (compression == TiffEncoderCompression.Deflate) - { - return this.WriteBiColorDeflate(imageBlackWhite, pixelRowAsGraySpan, outputRow, compressionLevel); - } + this.imageBlackWhite = new Image(configuration, new ImageMetadata(), new[] { image.Clone() }); + this.imageBlackWhite.Mutate(img => img.BinaryDither(default(ErrorDither))); + } - if (compression == TiffEncoderCompression.PackBits) - { - return this.WriteBiColorPackBits(imageBlackWhite, pixelRowAsGraySpan, outputRow); - } + public override int BitsPerPixel => 1; - if (compression == TiffEncoderCompression.CcittGroup3Fax) + protected override void EncodeStrip(int y, int height, TiffBaseCompressor compressor) + { + if (this.pixelsAsGray == null) { - var bitWriter = new T4BitWriter(this.MemoryAllocator, this.Configuration); - return bitWriter.CompressImage(imageBlackWhite, pixelRowAsGraySpan, this.Output.BaseStream); + this.pixelsAsGray = this.MemoryAllocator.Allocate(height * this.Image.Width); } - if (compression == TiffEncoderCompression.ModifiedHuffman) - { - var bitWriter = new T4BitWriter(this.MemoryAllocator, this.Configuration, useModifiedHuffman: true); - return bitWriter.CompressImage(imageBlackWhite, pixelRowAsGraySpan, this.Output.BaseStream); - } + this.pixelsAsGray.Clear(); - // Write image uncompressed. - int bytesWritten = 0; - for (int y = 0; y < image.Height; y++) - { - int bitIndex = 0; - int byteIndex = 0; - Span pixelRow = imageBlackWhite.GetPixelRowSpan(y); - PixelOperations.Instance.ToL8(this.Configuration, pixelRow, pixelRowAsGraySpan); - for (int x = 0; x < pixelRow.Length; x++) - { - int shift = 7 - bitIndex; - if (pixelRowAsGraySpan[x].PackedValue == 255) - { - outputRow[byteIndex] |= (byte)(1 << shift); - } + Span pixelAsGraySpan = this.pixelsAsGray.Slice(0, height * this.Image.Width); - bitIndex++; - if (bitIndex == 8) - { - byteIndex++; - bitIndex = 0; - } - } + Span pixels = GetStripPixels(this.imageBlackWhite.GetRootFramePixelBuffer(), y, height); - this.Output.Write(outputRow); - bytesWritten += outputRow.Length; + PixelOperations.Instance.ToL8Bytes(this.Configuration, pixels, pixelAsGraySpan, pixels.Length); - outputRow.Clear(); + if (compressor.Method == TiffEncoderCompression.CcittGroup3Fax || compressor.Method == TiffEncoderCompression.ModifiedHuffman) + { + compressor.CompressStrip(pixelAsGraySpan, height); } - - return bytesWritten; - } - - /// - /// Writes the image data as 1 bit black and white with deflate compression to the stream. - /// - /// The pixel data. - /// The image to write to the stream. - /// A span for converting a pixel row to gray. - /// A span which will be used to store the output pixels. - /// The compression level for deflate compression. - /// The number of bytes written. - public int WriteBiColorDeflate(Image image, Span pixelRowAsGraySpan, Span outputRow, DeflateCompressionLevel compressionLevel) - where TPixel : unmanaged, IPixel - { - using var memoryStream = new MemoryStream(); - using var deflateStream = new ZlibDeflateStream(this.MemoryAllocator, memoryStream, compressionLevel); - - int bytesWritten = 0; - for (int y = 0; y < image.Height; y++) + else { - int bitIndex = 0; - int byteIndex = 0; - Span pixelRow = image.GetPixelRowSpan(y); - PixelOperations.Instance.ToL8(this.Configuration, pixelRow, pixelRowAsGraySpan); - for (int x = 0; x < pixelRow.Length; x++) + if (this.bitStrip == null) { - int shift = 7 - bitIndex; - if (pixelRowAsGraySpan[x].PackedValue == 255) - { - outputRow[byteIndex] |= (byte)(1 << shift); - } - - bitIndex++; - if (bitIndex == 8) - { - byteIndex++; - bitIndex = 0; - } + int bytesPerRow = this.BytesPerRow * height; + this.bitStrip = this.MemoryAllocator.AllocateManagedByteBuffer(bytesPerRow); } - deflateStream.Write(outputRow); - - outputRow.Clear(); - } + this.bitStrip.Clear(); + Span rows = this.bitStrip.GetSpan(); - deflateStream.Flush(); - byte[] buffer = memoryStream.ToArray(); - this.Output.Write(buffer); - bytesWritten += buffer.Length; - - return bytesWritten; - } - - /// - /// Writes the image data as 1 bit black and white with pack bits compression to the stream. - /// - /// The pixel data. - /// The image to write to the stream. - /// A span for converting a pixel row to gray. - /// A span which will be used to store the output pixels. - /// The number of bytes written. - public int WriteBiColorPackBits(Image image, Span pixelRowAsGraySpan, Span outputRow) - where TPixel : unmanaged, IPixel - { - // Worst case is that the actual compressed data is larger then the input data. In this case we need 1 additional byte per 127 bits. - int additionalBytes = (image.Width / 127) + 2; - int compressedRowBytes = (image.Width / 8) + additionalBytes; - using IManagedByteBuffer compressedRow = this.MemoryAllocator.AllocateManagedByteBuffer(compressedRowBytes, AllocationOptions.Clean); - Span compressedRowSpan = compressedRow.GetSpan(); - - int bytesWritten = 0; - for (int y = 0; y < image.Height; y++) - { - int bitIndex = 0; - int byteIndex = 0; - Span pixelRow = image.GetPixelRowSpan(y); - PixelOperations.Instance.ToL8(this.Configuration, pixelRow, pixelRowAsGraySpan); - for (int x = 0; x < pixelRow.Length; x++) + int xx = 0; + for (int s = 0; s < height; s++) { - int shift = 7 - bitIndex; - if (pixelRowAsGraySpan[x].PackedValue == 255) + int bitIndex = 0; + int byteIndex = 0; + Span outputRow = rows.Slice(s * this.BytesPerRow); + for (int x = 0; x < this.Image.Width; x++) { - outputRow[byteIndex] |= (byte)(1 << shift); - } - - bitIndex++; - if (bitIndex == 8) - { - byteIndex++; - bitIndex = 0; + int shift = 7 - bitIndex; + if (pixelAsGraySpan[xx++] == 255) + { + outputRow[byteIndex] |= (byte)(1 << shift); + } + + bitIndex++; + if (bitIndex == 8) + { + byteIndex++; + bitIndex = 0; + } } } - var size = PackBitsWriter.PackBits(outputRow, compressedRowSpan); - this.Output.Write(compressedRowSpan.Slice(0, size)); - bytesWritten += size; - - outputRow.Clear(); + compressor.CompressStrip(rows, height); } + } - return bytesWritten; + protected override void Dispose(bool disposing) + { + this.imageBlackWhite?.Dispose(); + this.pixelsAsGray?.Dispose(); + this.bitStrip?.Dispose(); } } } diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffColorWriterFactory.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffColorWriterFactory.cs index 6c378f28d..10bb9b96e 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffColorWriterFactory.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffColorWriterFactory.cs @@ -2,23 +2,32 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors.Quantization; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers { internal static class TiffColorWriterFactory { - public static TiffBaseColorWriter Create(TiffEncodingMode mode, TiffStreamWriter output, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector) + public static TiffBaseColorWriter Create( + TiffEncodingMode mode, + ImageFrame image, + IQuantizer quantizer, + MemoryAllocator memoryAllocator, + Configuration configuration, + TiffEncoderEntriesCollector entriesCollector) + where TPixel : unmanaged, IPixel { switch (mode) { case TiffEncodingMode.ColorPalette: - return new TiffPaletteWriter(output, memoryAllocator, configuration, entriesCollector); + return new TiffPaletteWriter(image, quantizer, memoryAllocator, configuration, entriesCollector); case TiffEncodingMode.Gray: - return new TiffGrayWriter(output, memoryAllocator, configuration, entriesCollector); + return new TiffGrayWriter(image, memoryAllocator, configuration, entriesCollector); case TiffEncodingMode.BiColor: - return new TiffBiColorWriter(output, memoryAllocator, configuration, entriesCollector); + return new TiffBiColorWriter(image, memoryAllocator, configuration, entriesCollector); default: - return new TiffRgbWriter(output, memoryAllocator, configuration, entriesCollector); + return new TiffRgbWriter(image, memoryAllocator, configuration, entriesCollector); } } } diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffCompositeColorWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffCompositeColorWriter.cs new file mode 100644 index 000000000..1b2bd4ab6 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffCompositeColorWriter.cs @@ -0,0 +1,45 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers +{ + /// + /// The base class for composite color types: 8-bit gray, 24-bit RGB (4-bit gray, 16-bit (565/555) RGB, 32-bit RGB, CMYK, YCbCr). + /// + internal abstract class TiffCompositeColorWriter : TiffBaseColorWriter + where TPixel : unmanaged, IPixel + { + private IManagedByteBuffer rowBuffer; + + public TiffCompositeColorWriter(ImageFrame image, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector) + : base(image, memoryAllocator, configuration, entriesCollector) + { + } + + protected override void EncodeStrip(int y, int height, TiffBaseCompressor compressor) + { + if (this.rowBuffer == null) + { + this.rowBuffer = this.MemoryAllocator.AllocateManagedByteBuffer(this.BytesPerRow * height); + } + + this.rowBuffer.Clear(); + + Span rowSpan = this.rowBuffer.GetSpan().Slice(0, this.BytesPerRow * height); + + Span pixels = GetStripPixels(this.Image.PixelBuffer, y, height); + + this.EncodePixels(pixels, rowSpan); + compressor.CompressStrip(rowSpan, height); + } + + protected abstract void EncodePixels(Span pixels, Span buffer); + + protected override void Dispose(bool disposing) => this.rowBuffer?.Dispose(); + } +} diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffGrayWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffGrayWriter.cs index 7161254f8..f2b06d872 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffGrayWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffGrayWriter.cs @@ -2,172 +2,22 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Buffers; using System.IO; - -using SixLabors.ImageSharp.Compression.Zlib; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; -using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing.Processors.Quantization; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers { - /// - /// Utility class for writing TIFF data to a . - /// - internal class TiffGrayWriter : TiffBaseColorWriter + internal class TiffGrayWriter : TiffCompositeColorWriter + where TPixel : unmanaged, IPixel { - public TiffGrayWriter(TiffStreamWriter output, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector) - : base(output, memoryAllocator, configuration, entriesCollector) - { - } - - /// - /// Writes the image data as 8 bit gray to the stream. - /// - /// The pixel data. - /// The image to write to the stream. - /// The quantizer. - /// The compression to use. - /// The compression level for deflate compression. - /// Indicates if horizontal prediction should be used. Should only be used with deflate or lzw compression. - /// - /// The number of bytes written. - /// - public override int Write(Image image, IQuantizer quantizer, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) + public TiffGrayWriter(ImageFrame image, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector) + : base(image, memoryAllocator, configuration, entriesCollector) { - using IManagedByteBuffer row = this.MemoryAllocator.AllocateManagedByteBuffer(image.Width); - Span rowSpan = row.GetSpan(); - - if (compression == TiffEncoderCompression.Deflate) - { - return this.WriteGrayDeflateCompressed(image, rowSpan, compressionLevel, useHorizontalPredictor); - } - - if (compression == TiffEncoderCompression.Lzw) - { - return this.WriteGrayLzwCompressed(image, rowSpan, useHorizontalPredictor); - } - - if (compression == TiffEncoderCompression.PackBits) - { - return this.WriteGrayPackBitsCompressed(image, rowSpan); - } - - int bytesWritten = 0; - for (int y = 0; y < image.Height; y++) - { - Span pixelRow = image.GetPixelRowSpan(y); - PixelOperations.Instance.ToL8Bytes(this.Configuration, pixelRow, rowSpan, pixelRow.Length); - this.Output.Write(rowSpan); - bytesWritten += rowSpan.Length; - } - - return bytesWritten; } - /// - /// Writes the image data as 8 bit gray with deflate compression to the stream. - /// - /// The image to write to the stream. - /// A span of a row of pixels. - /// The compression level for deflate compression. - /// Indicates if horizontal prediction should be used. - /// The number of bytes written. - private int WriteGrayDeflateCompressed(Image image, Span rowSpan, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) - where TPixel : unmanaged, IPixel - { - int bytesWritten = 0; - using var memoryStream = new MemoryStream(); - using var deflateStream = new ZlibDeflateStream(this.MemoryAllocator, memoryStream, compressionLevel); - - for (int y = 0; y < image.Height; y++) - { - Span pixelRow = image.GetPixelRowSpan(y); - PixelOperations.Instance.ToL8Bytes(this.Configuration, pixelRow, rowSpan, pixelRow.Length); - - if (useHorizontalPredictor) - { - HorizontalPredictor.ApplyHorizontalPrediction8Bit(rowSpan); - } - - deflateStream.Write(rowSpan); - } - - deflateStream.Flush(); + public override int BitsPerPixel => 8; - byte[] buffer = memoryStream.ToArray(); - this.Output.Write(buffer); - bytesWritten += buffer.Length; - return bytesWritten; - } - - /// - /// Writes the image data as 8 bit gray with lzw compression to the stream. - /// - /// The image to write to the stream. - /// A span of a row of pixels. - /// Indicates if horizontal prediction should be used. - /// The number of bytes written. - private int WriteGrayLzwCompressed(Image image, Span rowSpan, bool useHorizontalPredictor) - where TPixel : unmanaged, IPixel - { - int bytesWritten = 0; - using var memoryStream = new MemoryStream(); - - IMemoryOwner pixelData = this.MemoryAllocator.Allocate(image.Width * image.Height); - Span pixels = pixelData.GetSpan(); - for (int y = 0; y < image.Height; y++) - { - Span pixelRow = image.GetPixelRowSpan(y); - PixelOperations.Instance.ToL8Bytes(this.Configuration, pixelRow, rowSpan, pixelRow.Length); - if (useHorizontalPredictor) - { - HorizontalPredictor.ApplyHorizontalPrediction8Bit(rowSpan); - } - - rowSpan.CopyTo(pixels.Slice(y * image.Width)); - } - - using var lzwEncoder = new TiffLzwEncoder(this.MemoryAllocator, pixelData); - lzwEncoder.Encode(memoryStream); - - byte[] buffer = memoryStream.ToArray(); - this.Output.Write(buffer); - bytesWritten += buffer.Length; - return bytesWritten; - } - - /// - /// Writes the image data as 8 bit gray to the stream. - /// - /// The pixel data. - /// The image to write to the stream. - /// A span of a row of pixels. - /// The number of bytes written. - private int WriteGrayPackBitsCompressed(Image image, Span rowSpan) - where TPixel : unmanaged, IPixel - { - // Worst case is that the actual compressed data is larger then the input data. In this case we need 1 additional byte per 127 bytes. - int additionalBytes = (image.Width / 127) + 1; - using IManagedByteBuffer compressedRow = this.MemoryAllocator.AllocateManagedByteBuffer(image.Width + additionalBytes, AllocationOptions.Clean); - Span compressedRowSpan = compressedRow.GetSpan(); - - int bytesWritten = 0; - for (int y = 0; y < image.Height; y++) - { - Span pixelRow = image.GetPixelRowSpan(y); - PixelOperations.Instance.ToL8Bytes(this.Configuration, pixelRow, rowSpan, pixelRow.Length); - int size = PackBitsWriter.PackBits(rowSpan, compressedRowSpan); - this.Output.Write(compressedRow.Slice(0, size)); - bytesWritten += size; - compressedRowSpan.Clear(); - } - - return bytesWritten; - } + protected override void EncodePixels(Span pixels, Span buffer) => PixelOperations.Instance.ToL8Bytes(this.Configuration, pixels, buffer, pixels.Length); } } diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs index 55a2efc9a..286663706 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs @@ -3,13 +3,9 @@ using System; using System.Buffers; -using System.IO; using System.Runtime.InteropServices; - -using SixLabors.ImageSharp.Compression.Zlib; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; -using SixLabors.ImageSharp.Formats.Tiff.Compression; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; @@ -17,44 +13,37 @@ using SixLabors.ImageSharp.Processing.Processors.Quantization; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers { - /// - /// Utility class for writing TIFF data to a . - /// - internal class TiffPaletteWriter : TiffBaseColorWriter + internal class TiffPaletteWriter : TiffBaseColorWriter + where TPixel : unmanaged, IPixel { - /// - /// Initializes a new instance of the class. - /// - /// The output stream. - /// The memory allocator. - /// The configuration. - /// The entries collector. - public TiffPaletteWriter(TiffStreamWriter output, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector) - : base(output, memoryAllocator, configuration, entriesCollector) + private const int ColorsPerChannel = 256; + private const int ColorPaletteSize = ColorsPerChannel * 3; + private const int ColorPaletteBytes = ColorPaletteSize * 2; + + private readonly IndexedImageFrame quantized; + + public TiffPaletteWriter(ImageFrame image, IQuantizer quantizer, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector) + : base(image, memoryAllocator, configuration, entriesCollector) { + using IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(this.Configuration); + this.quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(image, image.Bounds()); + + this.AddTag(this.quantized); } - /// - /// Writes the image data as indices into a color map to the stream. - /// - /// The pixel data. - /// The image to write to the stream. - /// The quantizer to use. - /// The compression to use. - /// The compression level for deflate compression. - /// Indicates if horizontal prediction should be used. Should only be used in combination with deflate or LZW compression. - /// - /// The number of bytes written. - /// - public override int Write(Image image, IQuantizer quantizer, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) + public override int BitsPerPixel => 8; + + protected override void EncodeStrip(int y, int height, TiffBaseCompressor compressor) { - int colorsPerChannel = 256; - int colorPaletteSize = colorsPerChannel * 3; - int colorPaletteBytes = colorPaletteSize * 2; - using IManagedByteBuffer row = this.MemoryAllocator.AllocateManagedByteBuffer(image.Width); - using IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(this.Configuration); - using IndexedImageFrame quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(image.Frames.RootFrame, image.Bounds()); - using IMemoryOwner colorPaletteBuffer = this.MemoryAllocator.AllocateManagedByteBuffer(colorPaletteBytes); + Span pixels = GetStripPixels(((IPixelSource)this.quantized).PixelBuffer, y, height); + compressor.CompressStrip(pixels, height); + } + + protected override void Dispose(bool disposing) => this.quantized?.Dispose(); + + private void AddTag(IndexedImageFrame quantized) + { + using IMemoryOwner colorPaletteBuffer = this.MemoryAllocator.AllocateManagedByteBuffer(ColorPaletteBytes); Span colorPalette = colorPaletteBuffer.GetSpan(); ReadOnlySpan quantizedColors = quantized.Palette.Span; @@ -65,11 +54,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers PixelOperations.Instance.ToRgb48(this.Configuration, quantizedColors, quantizedColorRgb48); // It can happen that the quantized colors are less than the expected 256 per channel. - var diffToMaxColors = colorsPerChannel - quantizedColors.Length; + var diffToMaxColors = ColorsPerChannel - quantizedColors.Length; // In a TIFF ColorMap, all the Red values come first, followed by the Green values, // then the Blue values. Convert the quantized palette to this format. - var palette = new ushort[colorPaletteSize]; + var palette = new ushort[ColorPaletteSize]; int paletteIdx = 0; for (int i = 0; i < quantizedColors.Length; i++) { @@ -96,147 +85,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers }; this.EntriesCollector.Add(colorMap); - - if (compression == TiffEncoderCompression.Deflate) - { - return this.WriteDeflateCompressedPalettedRgb(image, quantized, compressionLevel, useHorizontalPredictor); - } - - if (compression == TiffEncoderCompression.Lzw) - { - return this.WriteLzwCompressedPalettedRgb(image, quantized, useHorizontalPredictor); - } - - if (compression == TiffEncoderCompression.PackBits) - { - return this.WritePackBitsCompressedPalettedRgb(image, quantized); - } - - // No compression. - int bytesWritten = 0; - for (int y = 0; y < image.Height; y++) - { - ReadOnlySpan pixelSpan = quantized.GetPixelRowSpan(y); - this.Output.Write(pixelSpan); - bytesWritten += pixelSpan.Length; - } - - return bytesWritten; - } - - /// - /// Writes the image data as indices into a color map compressed with deflate compression to the stream. - /// - /// The pixel data. - /// The image to write to the stream. - /// The quantized frame. - /// The compression level for deflate compression. - /// Indicates if horizontal prediction should be used. - /// The number of bytes written. - private int WriteDeflateCompressedPalettedRgb(Image image, IndexedImageFrame quantized, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) - where TPixel : unmanaged, IPixel - { - using IManagedByteBuffer tmpBuffer = this.MemoryAllocator.AllocateManagedByteBuffer(image.Width); - using var memoryStream = new MemoryStream(); - using var deflateStream = new ZlibDeflateStream(this.MemoryAllocator, memoryStream, compressionLevel); - - int bytesWritten = 0; - for (int y = 0; y < image.Height; y++) - { - ReadOnlySpan pixelRow = quantized.GetPixelRowSpan(y); - if (useHorizontalPredictor) - { - // We need a writable Span here. - Span pixelRowCopy = tmpBuffer.GetSpan(); - pixelRow.CopyTo(pixelRowCopy); - HorizontalPredictor.ApplyHorizontalPrediction8Bit(pixelRowCopy); - deflateStream.Write(pixelRowCopy); - } - else - { - deflateStream.Write(pixelRow); - } - } - - deflateStream.Flush(); - byte[] buffer = memoryStream.ToArray(); - this.Output.Write(buffer); - bytesWritten += buffer.Length; - - return bytesWritten; - } - - /// - /// Writes the image data as indices into a color map compressed with lzw compression to the stream. - /// - /// The pixel data. - /// The image to write to the stream. - /// The quantized frame. - /// Indicates if horizontal prediction should be used. - /// The number of bytes written. - private int WriteLzwCompressedPalettedRgb(Image image, IndexedImageFrame quantized, bool useHorizontalPredictor) - where TPixel : unmanaged, IPixel - { - IMemoryOwner pixelData = this.MemoryAllocator.Allocate(image.Width * image.Height); - using IManagedByteBuffer tmpBuffer = this.MemoryAllocator.AllocateManagedByteBuffer(image.Width); - using var memoryStream = new MemoryStream(); - - int bytesWritten = 0; - Span pixels = pixelData.GetSpan(); - for (int y = 0; y < image.Height; y++) - { - ReadOnlySpan indexedPixelRow = quantized.GetPixelRowSpan(y); - - if (useHorizontalPredictor) - { - // We need a writable Span here. - Span pixelRowCopy = tmpBuffer.GetSpan(); - indexedPixelRow.CopyTo(pixelRowCopy); - HorizontalPredictor.ApplyHorizontalPrediction8Bit(pixelRowCopy); - pixelRowCopy.CopyTo(pixels.Slice(y * image.Width)); - } - else - { - indexedPixelRow.CopyTo(pixels.Slice(y * image.Width)); - } - } - - using var lzwEncoder = new TiffLzwEncoder(this.MemoryAllocator, pixelData); - lzwEncoder.Encode(memoryStream); - - byte[] buffer = memoryStream.ToArray(); - this.Output.Write(buffer); - bytesWritten += buffer.Length; - - return bytesWritten; - } - - /// - /// Writes the image data as indices into a color map compressed with deflate compression to the stream. - /// - /// The pixel data. - /// The image to write to the stream. - /// The quantized frame. - /// The number of bytes written. - private int WritePackBitsCompressedPalettedRgb(Image image, IndexedImageFrame quantized) - where TPixel : unmanaged, IPixel - { - // Worst case is that the actual compressed data is larger then the input data. In this case we need 1 additional byte per 127 bytes. - int additionalBytes = (image.Width * 3 / 127) + 1; - using IManagedByteBuffer compressedRow = this.MemoryAllocator.AllocateManagedByteBuffer((image.Width * 3) + additionalBytes, AllocationOptions.Clean); - Span compressedRowSpan = compressedRow.GetSpan(); - - int bytesWritten = 0; - for (int y = 0; y < image.Height; y++) - { - ReadOnlySpan pixelSpan = quantized.GetPixelRowSpan(y); - - int size = PackBitsWriter.PackBits(pixelSpan, compressedRowSpan); - this.Output.Write(compressedRowSpan.Slice(0, size)); - bytesWritten += size; - } - - return bytesWritten; } } } diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffRgbWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffRgbWriter.cs index 26f2d82d8..174a67727 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffRgbWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffRgbWriter.cs @@ -2,175 +2,22 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Buffers; using System.IO; - -using SixLabors.ImageSharp.Compression.Zlib; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; -using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing.Processors.Quantization; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers { - /// - /// Utility class for writing TIFF data to a . - /// - internal class TiffRgbWriter : TiffBaseColorWriter + internal class TiffRgbWriter : TiffCompositeColorWriter + where TPixel : unmanaged, IPixel { - public TiffRgbWriter(TiffStreamWriter output, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector) - : base(output, memoryAllocator, configuration, entriesCollector) - { - } - - /// - /// Writes the image data as RGB to the stream. - /// - /// The pixel data. - /// The image to write to the stream. - /// The quantizer. - /// The compression to use. - /// The compression level for deflate compression. - /// Indicates if horizontal prediction should be used. Should only be used with deflate compression. - /// - /// The number of bytes written. - /// - public override int Write(Image image, IQuantizer quantizer, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) + public TiffRgbWriter(ImageFrame image, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector) + : base(image, memoryAllocator, configuration, entriesCollector) { - using IManagedByteBuffer row = this.MemoryAllocator.AllocateManagedByteBuffer(image.Width * 3); - Span rowSpan = row.GetSpan(); - if (compression == TiffEncoderCompression.Deflate) - { - return this.WriteDeflateCompressedRgb(image, rowSpan, compressionLevel, useHorizontalPredictor); - } - - if (compression == TiffEncoderCompression.Lzw) - { - return this.WriteLzwCompressedRgb(image, rowSpan, useHorizontalPredictor); - } - - if (compression == TiffEncoderCompression.PackBits) - { - return this.WriteRgbPackBitsCompressed(image, rowSpan); - } - - // No compression. - int bytesWritten = 0; - for (int y = 0; y < image.Height; y++) - { - Span pixelRow = image.GetPixelRowSpan(y); - PixelOperations.Instance.ToRgb24Bytes(this.Configuration, pixelRow, rowSpan, pixelRow.Length); - this.Output.Write(rowSpan); - bytesWritten += rowSpan.Length; - } - - return bytesWritten; } - /// - /// Writes the image data as RGB compressed with zlib to the stream. - /// - /// The pixel data. - /// The image to write to the stream. - /// A Span for a pixel row. - /// The compression level for deflate compression. - /// Indicates if horizontal prediction should be used. Should only be used with deflate compression. - /// The number of bytes written. - private int WriteDeflateCompressedRgb(Image image, Span rowSpan, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) - where TPixel : unmanaged, IPixel - { - int bytesWritten = 0; - using var memoryStream = new MemoryStream(); - using var deflateStream = new ZlibDeflateStream(this.MemoryAllocator, memoryStream, compressionLevel); - - for (int y = 0; y < image.Height; y++) - { - Span pixelRow = image.GetPixelRowSpan(y); - PixelOperations.Instance.ToRgb24Bytes(this.Configuration, pixelRow, rowSpan, pixelRow.Length); - - if (useHorizontalPredictor) - { - HorizontalPredictor.ApplyHorizontalPrediction24Bit(rowSpan); - } + public override int BitsPerPixel => 24; - deflateStream.Write(rowSpan); - } - - deflateStream.Flush(); - - byte[] buffer = memoryStream.ToArray(); - this.Output.Write(buffer); - bytesWritten += buffer.Length; - return bytesWritten; - } - - /// - /// Writes the image data as RGB compressed with lzw to the stream. - /// - /// The pixel data. - /// The image to write to the stream. - /// A Span for a pixel row. - /// Indicates if horizontal prediction should be used. - /// The number of bytes written. - private int WriteLzwCompressedRgb(Image image, Span rowSpan, bool useHorizontalPredictor) - where TPixel : unmanaged, IPixel - { - int bytesWritten = 0; - using var memoryStream = new MemoryStream(); - - IMemoryOwner pixelData = this.MemoryAllocator.Allocate(image.Width * image.Height * 3); - Span pixels = pixelData.GetSpan(); - for (int y = 0; y < image.Height; y++) - { - Span pixelRow = image.GetPixelRowSpan(y); - PixelOperations.Instance.ToRgb24Bytes(this.Configuration, pixelRow, rowSpan, pixelRow.Length); - - if (useHorizontalPredictor) - { - HorizontalPredictor.ApplyHorizontalPrediction24Bit(rowSpan); - } - - rowSpan.CopyTo(pixels.Slice(y * image.Width * 3)); - } - - using var lzwEncoder = new TiffLzwEncoder(this.MemoryAllocator, pixelData); - lzwEncoder.Encode(memoryStream); - - byte[] buffer = memoryStream.ToArray(); - this.Output.Write(buffer); - bytesWritten += buffer.Length; - return bytesWritten; - } - - /// - /// Writes the image data as RGB with packed bits compression to the stream. - /// - /// The pixel data. - /// The image to write to the stream. - /// A Span for a pixel row. - /// The number of bytes written. - private int WriteRgbPackBitsCompressed(Image image, Span rowSpan) - where TPixel : unmanaged, IPixel - { - // Worst case is that the actual compressed data is larger then the input data. In this case we need 1 additional byte per 127 bytes. - int additionalBytes = (image.Width * 3 / 127) + 1; - using IManagedByteBuffer compressedRow = this.MemoryAllocator.AllocateManagedByteBuffer((image.Width * 3) + additionalBytes, AllocationOptions.Clean); - Span compressedRowSpan = compressedRow.GetSpan(); - int bytesWritten = 0; - - for (int y = 0; y < image.Height; y++) - { - Span pixelRow = image.GetPixelRowSpan(y); - PixelOperations.Instance.ToRgb24Bytes(this.Configuration, pixelRow, rowSpan, pixelRow.Length); - int size = PackBitsWriter.PackBits(rowSpan, compressedRowSpan); - this.Output.Write(compressedRow.Slice(0, size)); - bytesWritten += size; - compressedRowSpan.Clear(); - } - - return bytesWritten; - } + protected override void EncodePixels(Span pixels, Span buffer) => PixelOperations.Instance.ToRgb24Bytes(this.Configuration, pixels, buffer, pixels.Length); } } diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs index 5b971962a..b7749e0f6 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs @@ -3,7 +3,6 @@ using System; using System.IO; -using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers { diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs index f1de0c971..cdf0f68f6 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs @@ -26,7 +26,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression { var buffer = new byte[data.Length]; - new DeflateTiffCompression(Configuration.Default.MemoryAllocator, 10, 8, TiffPredictor.None).Decompress(stream, 0, (uint)stream.Length, buffer); + using var decompressor = new DeflateTiffCompression(Configuration.Default.MemoryAllocator, 10, 8, TiffPredictor.None); + + decompressor.Decompress(stream, 0, (uint)stream.Length, buffer); Assert.Equal(data, buffer); } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs index 94835962d..fcce507d8 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs @@ -1,13 +1,11 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using System; using System.IO; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; using SixLabors.ImageSharp.IO; -using SixLabors.ImageSharp.Memory; using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression @@ -39,7 +37,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression using BufferedReadStream stream = CreateCompressedStream(data); var buffer = new byte[data.Length]; - new LzwTiffCompression(Configuration.Default.MemoryAllocator, 10, 8, TiffPredictor.None).Decompress(stream, 0, (uint)stream.Length, buffer); + using var decompressor = new LzwTiffCompression(Configuration.Default.MemoryAllocator, 10, 8, TiffPredictor.None); + decompressor.Decompress(stream, 0, (uint)stream.Length, buffer); Assert.Equal(data, buffer); } @@ -47,12 +46,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression private static BufferedReadStream CreateCompressedStream(byte[] inputData) { Stream compressedStream = new MemoryStream(); - using System.Buffers.IMemoryOwner data = Configuration.Default.MemoryAllocator.Allocate(inputData.Length); - inputData.AsSpan().CopyTo(data.GetSpan()); - using (var encoder = new TiffLzwEncoder(Configuration.Default.MemoryAllocator, data)) + using (var encoder = new TiffLzwEncoder(Configuration.Default.MemoryAllocator)) { - encoder.Encode(compressedStream); + encoder.Encode(inputData, compressedStream); } compressedStream.Seek(0, SeekOrigin.Begin); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs index b36669457..466027bee 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs @@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression var stream = new BufferedReadStream(Configuration.Default, new MemoryStream(inputData)); var buffer = new byte[expectedResult.Length]; - new NoneTiffCompression(null).Decompress(stream, 0, byteCount, buffer); + new NoneTiffCompression().Decompress(stream, 0, byteCount, buffer); Assert.Equal(expectedResult, buffer); } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs index 6fcaa24d2..a211bde53 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs @@ -29,7 +29,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression var stream = new BufferedReadStream(Configuration.Default, new MemoryStream(inputData)); var buffer = new byte[expectedResult.Length]; - new PackBitsTiffCompression(new ArrayPoolMemoryAllocator()).Decompress(stream, 0, (uint)inputData.Length, buffer); + using var decompressor = new PackBitsTiffCompression(new ArrayPoolMemoryAllocator()); + decompressor.Decompress(stream, 0, (uint)inputData.Length, buffer); Assert.Equal(expectedResult, buffer); } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs index d7c066ec2..107fd6079 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs @@ -4,7 +4,6 @@ using System.IO; using SixLabors.ImageSharp.Formats.Experimental.Tiff; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers; using SixLabors.ImageSharp.Memory; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 7eaf735c9..a79d84e10 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -95,7 +95,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffEncoderCompression.ModifiedHuffman, TiffCompression.Ccitt1D)] [WithFile(GrayscaleUncompressed, PixelTypes.L8, TiffEncoderCompression.CcittGroup3Fax, TiffCompression.CcittGroup3Fax)] [WithFile(PaletteDeflateMultistrip, PixelTypes.L8, TiffEncoderCompression.ModifiedHuffman, TiffCompression.Ccitt1D)] - public void TiffEncoder_CorrectBiMode_Bug(TestImageProvider provider, TiffEncoderCompression compression, TiffCompression expectedCompression) + public void TiffEncoder_CorrectBiMode(TestImageProvider provider, TiffEncoderCompression compression, TiffCompression expectedCompression) where TPixel : unmanaged, IPixel { // arrange @@ -111,17 +111,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff using var output = Image.Load(this.configuration, memStream); TiffMetadata meta = output.Metadata.GetTiffMetadata(); - // This is bug! - // BitsPerPixel must be 1, and compression must be eqals which was setted in encoder - Assert.NotEqual(TiffBitsPerPixel.Pixel1, meta.BitsPerPixel); - Assert.NotEqual(expectedCompression, meta.Compression); - - Assert.Equal(input.Metadata.GetTiffMetadata().BitsPerPixel, meta.BitsPerPixel); - Assert.Equal(TiffCompression.None, meta.Compression); - - // expected values - //// Assert.Equal(TiffBitsPerPixel.Pixel1, meta.BitsPerPixel); - //// Assert.Equal(expectedCompression, meta.Compression); + Assert.Equal(TiffBitsPerPixel.Pixel1, meta.BitsPerPixel); + Assert.Equal(expectedCompression, meta.Compression); } [Theory] @@ -286,6 +277,62 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public void TiffEncoder_EncodeBiColor_WithModifiedHuffmanCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel1, TiffEncodingMode.BiColor, TiffEncoderCompression.ModifiedHuffman); + [Theory] + [WithFile(Calliphora_BiColorUncompressed, PixelTypes.L8, TiffEncodingMode.BiColor, TiffEncoderPixelStorageMethod.SingleStrip)] + [WithFile(Calliphora_BiColorUncompressed, PixelTypes.L8, TiffEncodingMode.BiColor, TiffEncoderPixelStorageMethod.MultiStrip, 9 * 1024)] + [WithFile(GrayscaleUncompressed, PixelTypes.L8, TiffEncodingMode.Gray, TiffEncoderPixelStorageMethod.SingleStrip)] + [WithFile(GrayscaleUncompressed, PixelTypes.L8, TiffEncodingMode.Gray, TiffEncoderPixelStorageMethod.MultiStrip, 16 * 1024)] + [WithFile(PaletteDeflateMultistrip, PixelTypes.L8, TiffEncodingMode.ColorPalette, TiffEncoderPixelStorageMethod.SingleStrip)] + [WithFile(PaletteDeflateMultistrip, PixelTypes.L8, TiffEncodingMode.ColorPalette, TiffEncoderPixelStorageMethod.MultiStrip, 32 * 1024)] + [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffEncodingMode.Rgb, TiffEncoderPixelStorageMethod.SingleStrip)] + [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffEncodingMode.Rgb, TiffEncoderPixelStorageMethod.MultiStrip, 64 * 1024)] + public void TiffEncoder_StorageMethods(TestImageProvider provider, TiffEncodingMode mode, TiffEncoderPixelStorageMethod storageMethod, int maxSize = 0) + where TPixel : unmanaged, IPixel + { + // arrange + var tiffEncoder = new TiffEncoder() { PixelStorageMethod = storageMethod, MaxStripBytes = maxSize }; + using Image input = provider.GetImage(); + using var memStream = new MemoryStream(); + + TiffFrameMetadata inputMeta = input.Frames.RootFrame.Metadata.GetTiffMetadata(); + + // act + input.Save(memStream, tiffEncoder); + + // assert + memStream.Position = 0; + using var output = Image.Load(this.configuration, memStream); + TiffFrameMetadata meta = output.Frames.RootFrame.Metadata.GetTiffMetadata(); + + if (storageMethod == TiffEncoderPixelStorageMethod.SingleStrip) + { + Assert.Equal(output.Height, (int)meta.RowsPerStrip); + Assert.Equal(1, meta.StripOffsets.Length); + Assert.Equal(1, meta.StripByteCounts.Length); + } + else + { + Assert.True(output.Height > (int)meta.RowsPerStrip); + Assert.True(meta.StripOffsets.Length > 1); + Assert.True(meta.StripByteCounts.Length > 1); + + foreach (Number sz in meta.StripByteCounts) + { + Assert.True((int)sz <= maxSize); + } + } + + // compare with reference + TestTiffEncoderCore( + provider, + (TiffBitsPerPixel)inputMeta.BitsPerPixel, + mode, + Convert(inputMeta.Compression), + maxStripSize: maxSize, + storageMethod: storageMethod + ); + } + private static void TestTiffEncoderCore( TestImageProvider provider, TiffBitsPerPixel bitsPerPixel, @@ -293,14 +340,43 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff TiffEncoderCompression compression = TiffEncoderCompression.None, bool usePredictor = false, bool useExactComparer = true, + int maxStripSize = 0, + TiffEncoderPixelStorageMethod? storageMethod = null, float compareTolerance = 0.01f) where TPixel : unmanaged, IPixel { using Image image = provider.GetImage(); - var encoder = new TiffEncoder { Mode = mode, Compression = compression, UseHorizontalPredictor = usePredictor }; + var encoder = new TiffEncoder + { + Mode = mode, + Compression = compression, + UseHorizontalPredictor = usePredictor, + PixelStorageMethod = storageMethod ?? TiffEncoderPixelStorageMethod.Auto, + MaxStripBytes = maxStripSize + }; // Does DebugSave & load reference CompareToReferenceInput(): image.VerifyEncoder(provider, "tiff", bitsPerPixel, encoder, useExactComparer ? ImageComparer.Exact : ImageComparer.Tolerant(compareTolerance), referenceDecoder: ReferenceDecoder); } + + private static TiffEncoderCompression Convert(TiffCompression compression) + { + switch (compression) + { + default: + case TiffCompression.None: + return TiffEncoderCompression.None; + case TiffCompression.Deflate: + return TiffEncoderCompression.Deflate; + case TiffCompression.Lzw: + return TiffEncoderCompression.Lzw; + case TiffCompression.PackBits: + return TiffEncoderCompression.PackBits; + case TiffCompression.CcittGroup3Fax: + return TiffEncoderCompression.CcittGroup3Fax; + case TiffCompression.CcittGroup4Fax: + return TiffEncoderCompression.ModifiedHuffman; + } + } } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs index 8d86482ec..7ea2e4cc4 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs @@ -2,8 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System.IO; - -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers; using SixLabors.ImageSharp.Memory; From 74dacb66018db765274661f4ca7902d90af634da Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sat, 6 Feb 2021 23:14:38 +0300 Subject: [PATCH 192/275] Report palette lzw bug --- .../Formats/Tiff/TiffEncoderTests.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index a79d84e10..774454259 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -210,7 +210,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public void TiffEncoder_EncodeColorPalette_WithLzwCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel { - var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Lzw }; + var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Lzw, PixelStorageMethod = TiffEncoderPixelStorageMethod.SingleStrip }; this.TiffEncoderPaletteTest(provider, encoder); } @@ -220,11 +220,21 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public void TiffEncoder_EncodeColorPalette_WithLzwCompressionAndPredictor_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel { - var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Lzw, UseHorizontalPredictor = true }; + var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Lzw, UseHorizontalPredictor = true, PixelStorageMethod = TiffEncoderPixelStorageMethod.SingleStrip }; this.TiffEncoderPaletteTest(provider, encoder); } + [Theory] + [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeColorPalette_WithLzwCompressionAndPredictor_Bug(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Lzw, PixelStorageMethod = TiffEncoderPixelStorageMethod.MultiStrip, UseHorizontalPredictor = true }; + + Assert.Throws(() => this.TiffEncoderPaletteTest(provider, encoder)); + } + [Theory] [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_WithPackBitsCompression_Works(TestImageProvider provider) From ace0d184b0c01558834a8e0546291e789c9aa1f4 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sun, 7 Feb 2021 12:16:27 +0300 Subject: [PATCH 193/275] Update benchmark results --- ...hmarks.Codecs.DecodeTiff-report-default.md | 102 +++++++------- ...chmarks.Codecs.DecodeTiff-report-github.md | 102 +++++++------- ...p.Benchmarks.Codecs.DecodeTiff-report.html | 104 +++++++------- ...hmarks.Codecs.EncodeTiff-report-default.md | 128 +++++++++--------- ...chmarks.Codecs.EncodeTiff-report-github.md | 128 +++++++++--------- ...p.Benchmarks.Codecs.EncodeTiff-report.html | 80 +++++------ 6 files changed, 322 insertions(+), 322 deletions(-) diff --git a/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiff-report-default.md b/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiff-report-default.md index 6b35c6fe8..78128aff5 100644 --- a/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiff-report-default.md +++ b/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiff-report-default.md @@ -3,83 +3,83 @@ BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19042 Intel Core i7-3610QM CPU 2.30GHz (Ivy Bridge), 1 CPU, 8 logical and 4 physical cores .NET Core SDK=5.0.101 [Host] : .NET Core 5.0.1 (CoreCLR 5.0.120.57516, CoreFX 5.0.120.57516), X64 RyuJIT - Job-EMDSBW : .NET Framework 4.8 (4.8.4300.0), X64 RyuJIT - Job-KCUIVJ : .NET Core 2.1.23 (CoreCLR 4.6.29321.03, CoreFX 4.6.29321.01), X64 RyuJIT - Job-NIWDJE : .NET Core 3.1.10 (CoreCLR 4.700.20.51601, CoreFX 4.700.20.51901), X64 RyuJIT + Job-MVPLTM : .NET Framework 4.8 (4.8.4300.0), X64 RyuJIT + Job-ZMKWLH : .NET Core 2.1.23 (CoreCLR 4.6.29321.03, CoreFX 4.6.29321.01), X64 RyuJIT + Job-DYSEOC : .NET Core 3.1.10 (CoreCLR 4.700.20.51601, CoreFX 4.700.20.51901), X64 RyuJIT InvocationCount=1 IterationCount=3 LaunchCount=1 UnrollFactor=1 WarmupCount=3 Method | Job | Runtime | TestImage | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | ---------------------- |----------- |-------------- |----------------------------------------------- |------------:|--------------:|-------------:|------:|--------:|------:|------:|------:|----------:| - **'System.Drawing Tiff'** | **Job-EMDSBW** | **.NET 4.7.2** | **Tiff/Calliphora_grayscale_uncompressed.tiff** | **1,107.9 μs** | **260.10 μs** | **14.26 μs** | **1.00** | **0.00** | **-** | **-** | **-** | **974848 B** | - 'ImageSharp Tiff' | Job-EMDSBW | .NET 4.7.2 | Tiff/Calliphora_grayscale_uncompressed.tiff | 29,794.8 μs | 3,103.68 μs | 170.12 μs | 26.90 | 0.49 | - | - | - | 32768 B | + **'System.Drawing Tiff'** | **Job-MVPLTM** | **.NET 4.7.2** | **Tiff/Calliphora_grayscale_uncompressed.tiff** | **1,513.5 μs** | **6,982.54 μs** | **382.74 μs** | **1.00** | **0.00** | **-** | **-** | **-** | **974848 B** | + 'ImageSharp Tiff' | Job-MVPLTM | .NET 4.7.2 | Tiff/Calliphora_grayscale_uncompressed.tiff | 29,504.9 μs | 2,030.88 μs | 111.32 μs | 20.46 | 5.74 | - | - | - | 32768 B | | | | | | | | | | | | | | - 'System.Drawing Tiff' | Job-KCUIVJ | .NET Core 2.1 | Tiff/Calliphora_grayscale_uncompressed.tiff | 1,020.4 μs | 641.11 μs | 35.14 μs | 1.00 | 0.00 | - | - | - | 968832 B | - 'ImageSharp Tiff' | Job-KCUIVJ | .NET Core 2.1 | Tiff/Calliphora_grayscale_uncompressed.tiff | 12,593.4 μs | 4,807.87 μs | 263.54 μs | 12.36 | 0.67 | - | - | - | 29976 B | + 'System.Drawing Tiff' | Job-ZMKWLH | .NET Core 2.1 | Tiff/Calliphora_grayscale_uncompressed.tiff | 1,441.5 μs | 5,692.62 μs | 312.03 μs | 1.00 | 0.00 | - | - | - | 968832 B | + 'ImageSharp Tiff' | Job-ZMKWLH | .NET Core 2.1 | Tiff/Calliphora_grayscale_uncompressed.tiff | 12,512.9 μs | 669.83 μs | 36.72 μs | 8.98 | 2.11 | - | - | - | 30072 B | | | | | | | | | | | | | | - 'System.Drawing Tiff' | Job-NIWDJE | .NET Core 3.1 | Tiff/Calliphora_grayscale_uncompressed.tiff | 987.2 μs | 2,211.93 μs | 121.24 μs | 1.00 | 0.00 | - | - | - | 176 B | - 'ImageSharp Tiff' | Job-NIWDJE | .NET Core 3.1 | Tiff/Calliphora_grayscale_uncompressed.tiff | 44,255.5 μs | 13,031.10 μs | 714.28 μs | 45.23 | 4.88 | - | - | - | 29896 B | + 'System.Drawing Tiff' | Job-DYSEOC | .NET Core 3.1 | Tiff/Calliphora_grayscale_uncompressed.tiff | 989.7 μs | 1,621.23 μs | 88.87 μs | 1.00 | 0.00 | - | - | - | 176 B | + 'ImageSharp Tiff' | Job-DYSEOC | .NET Core 3.1 | Tiff/Calliphora_grayscale_uncompressed.tiff | 44,038.5 μs | 7,702.48 μs | 422.20 μs | 44.75 | 4.23 | - | - | - | 29992 B | | | | | | | | | | | | | | - **'System.Drawing Tiff'** | **Job-EMDSBW** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_deflate_predictor.tiff** | **16,118.9 μs** | **2,095.51 μs** | **114.86 μs** | **1.00** | **0.00** | **-** | **-** | **-** | **1483440 B** | - 'ImageSharp Tiff' | Job-EMDSBW | .NET 4.7.2 | Tiff/Calliphora_rgb_deflate_predictor.tiff | 25,967.5 μs | 4,545.04 μs | 249.13 μs | 1.61 | 0.01 | - | - | - | 848240 B | + **'System.Drawing Tiff'** | **Job-MVPLTM** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_deflate_predictor.tiff** | **16,368.6 μs** | **3,342.63 μs** | **183.22 μs** | **1.00** | **0.00** | **-** | **-** | **-** | **1483440 B** | + 'ImageSharp Tiff' | Job-MVPLTM | .NET 4.7.2 | Tiff/Calliphora_rgb_deflate_predictor.tiff | 27,334.9 μs | 5,327.53 μs | 292.02 μs | 1.67 | 0.04 | - | - | - | 848240 B | | | | | | | | | | | | | | - 'System.Drawing Tiff' | Job-KCUIVJ | .NET Core 2.1 | Tiff/Calliphora_rgb_deflate_predictor.tiff | 16,465.6 μs | 7,761.65 μs | 425.44 μs | 1.00 | 0.00 | - | - | - | 1480344 B | - 'ImageSharp Tiff' | Job-KCUIVJ | .NET Core 2.1 | Tiff/Calliphora_rgb_deflate_predictor.tiff | 18,536.9 μs | 3,415.62 μs | 187.22 μs | 1.13 | 0.02 | - | - | - | 68176 B | + 'System.Drawing Tiff' | Job-ZMKWLH | .NET Core 2.1 | Tiff/Calliphora_rgb_deflate_predictor.tiff | 16,192.2 μs | 4,200.11 μs | 230.22 μs | 1.00 | 0.00 | - | - | - | 1480344 B | + 'ImageSharp Tiff' | Job-ZMKWLH | .NET Core 2.1 | Tiff/Calliphora_rgb_deflate_predictor.tiff | 18,448.9 μs | 3,957.52 μs | 216.93 μs | 1.14 | 0.00 | - | - | - | 68224 B | | | | | | | | | | | | | | - 'System.Drawing Tiff' | Job-NIWDJE | .NET Core 3.1 | Tiff/Calliphora_rgb_deflate_predictor.tiff | 16,216.2 μs | 3,288.12 μs | 180.23 μs | 1.00 | 0.00 | - | - | - | 176 B | - 'ImageSharp Tiff' | Job-NIWDJE | .NET Core 3.1 | Tiff/Calliphora_rgb_deflate_predictor.tiff | 20,740.6 μs | 54,608.55 μs | 2,993.28 μs | 1.28 | 0.17 | - | - | - | 65120 B | + 'System.Drawing Tiff' | Job-DYSEOC | .NET Core 3.1 | Tiff/Calliphora_rgb_deflate_predictor.tiff | 15,936.7 μs | 3,145.57 μs | 172.42 μs | 1.00 | 0.00 | - | - | - | 176 B | + 'ImageSharp Tiff' | Job-DYSEOC | .NET Core 3.1 | Tiff/Calliphora_rgb_deflate_predictor.tiff | 18,973.7 μs | 16,625.54 μs | 911.30 μs | 1.19 | 0.06 | - | - | - | 65168 B | | | | | | | | | | | | | | - **'System.Drawing Tiff'** | **Job-EMDSBW** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_lzw_predictor.tiff** | **83,012.1 μs** | **14,786.35 μs** | **810.49 μs** | **1.00** | **0.00** | **-** | **-** | **-** | **2545736 B** | - 'ImageSharp Tiff' | Job-EMDSBW | .NET 4.7.2 | Tiff/Calliphora_rgb_lzw_predictor.tiff | 64,895.5 μs | 11,397.89 μs | 624.76 μs | 0.78 | 0.01 | - | - | - | 24576 B | + **'System.Drawing Tiff'** | **Job-MVPLTM** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_lzw_predictor.tiff** | **81,687.4 μs** | **6,229.79 μs** | **341.48 μs** | **1.00** | **0.00** | **-** | **-** | **-** | **2545736 B** | + 'ImageSharp Tiff' | Job-MVPLTM | .NET 4.7.2 | Tiff/Calliphora_rgb_lzw_predictor.tiff | 67,259.5 μs | 4,315.22 μs | 236.53 μs | 0.82 | 0.01 | - | - | - | 24576 B | | | | | | | | | | | | | | - 'System.Drawing Tiff' | Job-KCUIVJ | .NET Core 2.1 | Tiff/Calliphora_rgb_lzw_predictor.tiff | 82,854.1 μs | 45,495.28 μs | 2,493.75 μs | 1.00 | 0.00 | - | - | - | 2541376 B | - 'ImageSharp Tiff' | Job-KCUIVJ | .NET Core 2.1 | Tiff/Calliphora_rgb_lzw_predictor.tiff | 44,307.1 μs | 15,595.85 μs | 854.86 μs | 0.53 | 0.01 | - | - | - | 23832 B | + 'System.Drawing Tiff' | Job-ZMKWLH | .NET Core 2.1 | Tiff/Calliphora_rgb_lzw_predictor.tiff | 81,554.2 μs | 9,082.88 μs | 497.86 μs | 1.00 | 0.00 | - | - | - | 2541376 B | + 'ImageSharp Tiff' | Job-ZMKWLH | .NET Core 2.1 | Tiff/Calliphora_rgb_lzw_predictor.tiff | 43,966.2 μs | 3,806.49 μs | 208.65 μs | 0.54 | 0.00 | - | - | - | 23880 B | | | | | | | | | | | | | | - 'System.Drawing Tiff' | Job-NIWDJE | .NET Core 3.1 | Tiff/Calliphora_rgb_lzw_predictor.tiff | 83,297.5 μs | 15,796.71 μs | 865.87 μs | 1.00 | 0.00 | - | - | - | 176 B | - 'ImageSharp Tiff' | Job-NIWDJE | .NET Core 3.1 | Tiff/Calliphora_rgb_lzw_predictor.tiff | 59,464.0 μs | 13,870.15 μs | 760.27 μs | 0.71 | 0.01 | - | - | - | 23760 B | + 'System.Drawing Tiff' | Job-DYSEOC | .NET Core 3.1 | Tiff/Calliphora_rgb_lzw_predictor.tiff | 80,333.6 μs | 24,190.59 μs | 1,325.97 μs | 1.00 | 0.00 | - | - | - | 176 B | + 'ImageSharp Tiff' | Job-DYSEOC | .NET Core 3.1 | Tiff/Calliphora_rgb_lzw_predictor.tiff | 54,418.0 μs | 122,629.27 μs | 6,721.72 μs | 0.68 | 0.09 | - | - | - | 23848 B | | | | | | | | | | | | | | - **'System.Drawing Tiff'** | **Job-EMDSBW** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_packbits.tiff** | **3,707.2 μs** | **6,293.27 μs** | **344.96 μs** | **1.00** | **0.00** | **-** | **-** | **-** | **2916008 B** | - 'ImageSharp Tiff' | Job-EMDSBW | .NET 4.7.2 | Tiff/Calliphora_rgb_packbits.tiff | 7,526.9 μs | 5,965.86 μs | 327.01 μs | 2.04 | 0.24 | - | - | - | 81920 B | + **'System.Drawing Tiff'** | **Job-MVPLTM** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_packbits.tiff** | **3,554.1 μs** | **2,577.75 μs** | **141.30 μs** | **1.00** | **0.00** | **-** | **-** | **-** | **2916000 B** | + 'ImageSharp Tiff' | Job-MVPLTM | .NET 4.7.2 | Tiff/Calliphora_rgb_packbits.tiff | 7,231.9 μs | 3,934.62 μs | 215.67 μs | 2.04 | 0.08 | - | - | - | 57344 B | | | | | | | | | | | | | | - 'System.Drawing Tiff' | Job-KCUIVJ | .NET Core 2.1 | Tiff/Calliphora_rgb_packbits.tiff | 4,037.7 μs | 9,243.97 μs | 506.69 μs | 1.00 | 0.00 | - | - | - | 2903544 B | - 'ImageSharp Tiff' | Job-KCUIVJ | .NET Core 2.1 | Tiff/Calliphora_rgb_packbits.tiff | 4,395.7 μs | 1,394.13 μs | 76.42 μs | 1.10 | 0.15 | - | - | - | 80256 B | + 'System.Drawing Tiff' | Job-ZMKWLH | .NET Core 2.1 | Tiff/Calliphora_rgb_packbits.tiff | 3,815.4 μs | 11,074.41 μs | 607.03 μs | 1.00 | 0.00 | - | - | - | 2903544 B | + 'ImageSharp Tiff' | Job-ZMKWLH | .NET Core 2.1 | Tiff/Calliphora_rgb_packbits.tiff | 4,415.6 μs | 7,272.82 μs | 398.65 μs | 1.17 | 0.08 | - | - | - | 51920 B | | | | | | | | | | | | | | - 'System.Drawing Tiff' | Job-NIWDJE | .NET Core 3.1 | Tiff/Calliphora_rgb_packbits.tiff | 3,456.3 μs | 4,443.73 μs | 243.58 μs | 1.00 | 0.00 | - | - | - | 176 B | - 'ImageSharp Tiff' | Job-NIWDJE | .NET Core 3.1 | Tiff/Calliphora_rgb_packbits.tiff | 4,542.9 μs | 3,820.61 μs | 209.42 μs | 1.32 | 0.13 | - | - | - | 80184 B | + 'System.Drawing Tiff' | Job-DYSEOC | .NET Core 3.1 | Tiff/Calliphora_rgb_packbits.tiff | 3,297.6 μs | 5,129.08 μs | 281.14 μs | 1.00 | 0.00 | - | - | - | 176 B | + 'ImageSharp Tiff' | Job-DYSEOC | .NET Core 3.1 | Tiff/Calliphora_rgb_packbits.tiff | 4,421.7 μs | 3,349.14 μs | 183.58 μs | 1.34 | 0.07 | - | - | - | 51848 B | | | | | | | | | | | | | | - **'System.Drawing Tiff'** | **Job-EMDSBW** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_palette_lzw_predictor.tiff** | **60,298.5 μs** | **24,263.76 μs** | **1,329.98 μs** | **1.00** | **0.00** | **-** | **-** | **-** | **827416 B** | - 'ImageSharp Tiff' | Job-EMDSBW | .NET 4.7.2 | Tiff/Calliphora_rgb_palette_lzw_predictor.tiff | 76,021.3 μs | 4,206.79 μs | 230.59 μs | 1.26 | 0.02 | - | - | - | 49152 B | + **'System.Drawing Tiff'** | **Job-MVPLTM** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_palette_lzw_predictor.tiff** | **60,458.2 μs** | **21,405.09 μs** | **1,173.29 μs** | **1.00** | **0.00** | **-** | **-** | **-** | **827416 B** | + 'ImageSharp Tiff' | Job-MVPLTM | .NET 4.7.2 | Tiff/Calliphora_rgb_palette_lzw_predictor.tiff | 76,324.5 μs | 12,909.45 μs | 707.61 μs | 1.26 | 0.04 | - | - | - | 49152 B | | | | | | | | | | | | | | - 'System.Drawing Tiff' | Job-KCUIVJ | .NET Core 2.1 | Tiff/Calliphora_rgb_palette_lzw_predictor.tiff | 59,122.1 μs | 9,681.07 μs | 530.65 μs | 1.00 | 0.00 | - | - | - | 825648 B | - 'ImageSharp Tiff' | Job-KCUIVJ | .NET Core 2.1 | Tiff/Calliphora_rgb_palette_lzw_predictor.tiff | 45,789.3 μs | 7,453.72 μs | 408.56 μs | 0.77 | 0.00 | - | - | - | 45936 B | + 'System.Drawing Tiff' | Job-ZMKWLH | .NET Core 2.1 | Tiff/Calliphora_rgb_palette_lzw_predictor.tiff | 61,210.5 μs | 17,165.24 μs | 940.88 μs | 1.00 | 0.00 | - | - | - | 825648 B | + 'ImageSharp Tiff' | Job-ZMKWLH | .NET Core 2.1 | Tiff/Calliphora_rgb_palette_lzw_predictor.tiff | 46,951.4 μs | 1,602.53 μs | 87.84 μs | 0.77 | 0.01 | - | - | - | 45984 B | | | | | | | | | | | | | | - 'System.Drawing Tiff' | Job-NIWDJE | .NET Core 3.1 | Tiff/Calliphora_rgb_palette_lzw_predictor.tiff | 61,361.5 μs | 25,759.90 μs | 1,411.99 μs | 1.00 | 0.00 | - | - | - | 176 B | - 'ImageSharp Tiff' | Job-NIWDJE | .NET Core 3.1 | Tiff/Calliphora_rgb_palette_lzw_predictor.tiff | 68,134.6 μs | 303,212.80 μs | 16,620.12 μs | 1.11 | 0.25 | - | - | - | 45864 B | + 'System.Drawing Tiff' | Job-DYSEOC | .NET Core 3.1 | Tiff/Calliphora_rgb_palette_lzw_predictor.tiff | 59,056.7 μs | 6,187.79 μs | 339.17 μs | 1.00 | 0.00 | - | - | - | 176 B | + 'ImageSharp Tiff' | Job-DYSEOC | .NET Core 3.1 | Tiff/Calliphora_rgb_palette_lzw_predictor.tiff | 66,042.9 μs | 291,880.02 μs | 15,998.93 μs | 1.12 | 0.27 | - | - | - | 45912 B | | | | | | | | | | | | | | - **'System.Drawing Tiff'** | **Job-EMDSBW** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_uncompressed.tiff** | **3,431.7 μs** | **7,649.10 μs** | **419.27 μs** | **1.00** | **0.00** | **-** | **-** | **-** | **2915944 B** | - 'ImageSharp Tiff' | Job-EMDSBW | .NET 4.7.2 | Tiff/Calliphora_rgb_uncompressed.tiff | 6,382.4 μs | 2,573.27 μs | 141.05 μs | 1.87 | 0.18 | - | - | - | 57344 B | + **'System.Drawing Tiff'** | **Job-MVPLTM** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_uncompressed.tiff** | **3,385.5 μs** | **6,266.60 μs** | **343.49 μs** | **1.00** | **0.00** | **-** | **-** | **-** | **2915968 B** | + 'ImageSharp Tiff' | Job-MVPLTM | .NET 4.7.2 | Tiff/Calliphora_rgb_uncompressed.tiff | 7,584.8 μs | 358.27 μs | 19.64 μs | 2.25 | 0.21 | - | - | - | 57344 B | | | | | | | | | | | | | | - 'System.Drawing Tiff' | Job-KCUIVJ | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | 3,636.1 μs | 8,607.66 μs | 471.81 μs | 1.00 | 0.00 | - | - | - | 2905840 B | - 'ImageSharp Tiff' | Job-KCUIVJ | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | 4,018.7 μs | 1,662.68 μs | 91.14 μs | 1.12 | 0.16 | - | - | - | 51472 B | + 'System.Drawing Tiff' | Job-ZMKWLH | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | 3,405.8 μs | 4,765.81 μs | 261.23 μs | 1.00 | 0.00 | - | - | - | 2905840 B | + 'ImageSharp Tiff' | Job-ZMKWLH | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | 3,930.3 μs | 3,250.19 μs | 178.15 μs | 1.16 | 0.05 | - | - | - | 51568 B | | | | | | | | | | | | | | - 'System.Drawing Tiff' | Job-NIWDJE | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | 2,970.8 μs | 5,028.62 μs | 275.64 μs | 1.00 | 0.00 | - | - | - | 176 B | - 'ImageSharp Tiff' | Job-NIWDJE | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | 4,009.6 μs | 3,007.19 μs | 164.83 μs | 1.36 | 0.17 | - | - | - | 51400 B | + 'System.Drawing Tiff' | Job-DYSEOC | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | 3,087.8 μs | 5,556.58 μs | 304.58 μs | 1.00 | 0.00 | - | - | - | 176 B | + 'ImageSharp Tiff' | Job-DYSEOC | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | 3,909.1 μs | 2,519.27 μs | 138.09 μs | 1.27 | 0.14 | - | - | - | 51496 B | | | | | | | | | | | | | | - **'System.Drawing Tiff'** | **Job-EMDSBW** | **.NET 4.7.2** | **Tiff/ccitt_fax3_all_terminating_codes.tiff** | **178.4 μs** | **375.89 μs** | **20.60 μs** | **1.00** | **0.00** | **-** | **-** | **-** | **8192 B** | - 'ImageSharp Tiff' | Job-EMDSBW | .NET 4.7.2 | Tiff/ccitt_fax3_all_terminating_codes.tiff | 634.5 μs | 251.14 μs | 13.77 μs | 3.58 | 0.37 | - | - | - | 24576 B | + **'System.Drawing Tiff'** | **Job-MVPLTM** | **.NET 4.7.2** | **Tiff/ccitt_fax3_all_terminating_codes.tiff** | **151.9 μs** | **73.26 μs** | **4.02 μs** | **1.00** | **0.00** | **-** | **-** | **-** | **8192 B** | + 'ImageSharp Tiff' | Job-MVPLTM | .NET 4.7.2 | Tiff/ccitt_fax3_all_terminating_codes.tiff | 648.7 μs | 165.54 μs | 9.07 μs | 4.27 | 0.08 | - | - | - | 24576 B | | | | | | | | | | | | | | - 'System.Drawing Tiff' | Job-KCUIVJ | .NET Core 2.1 | Tiff/ccitt_fax3_all_terminating_codes.tiff | 171.7 μs | 606.95 μs | 33.27 μs | 1.00 | 0.00 | - | - | - | 2032 B | - 'ImageSharp Tiff' | Job-KCUIVJ | .NET Core 2.1 | Tiff/ccitt_fax3_all_terminating_codes.tiff | 421.0 μs | 31.60 μs | 1.73 μs | 2.51 | 0.49 | - | - | - | 17848 B | + 'System.Drawing Tiff' | Job-ZMKWLH | .NET Core 2.1 | Tiff/ccitt_fax3_all_terminating_codes.tiff | 160.6 μs | 80.44 μs | 4.41 μs | 1.00 | 0.00 | - | - | - | 2032 B | + 'ImageSharp Tiff' | Job-ZMKWLH | .NET Core 2.1 | Tiff/ccitt_fax3_all_terminating_codes.tiff | 427.7 μs | 431.89 μs | 23.67 μs | 2.66 | 0.17 | - | - | - | 17952 B | | | | | | | | | | | | | | - 'System.Drawing Tiff' | Job-NIWDJE | .NET Core 3.1 | Tiff/ccitt_fax3_all_terminating_codes.tiff | 137.2 μs | 78.18 μs | 4.29 μs | 1.00 | 0.00 | - | - | - | 176 B | - 'ImageSharp Tiff' | Job-NIWDJE | .NET Core 3.1 | Tiff/ccitt_fax3_all_terminating_codes.tiff | 888.5 μs | 495.11 μs | 27.14 μs | 6.47 | 0.05 | - | - | - | 17768 B | + 'System.Drawing Tiff' | Job-DYSEOC | .NET Core 3.1 | Tiff/ccitt_fax3_all_terminating_codes.tiff | 230.1 μs | 952.83 μs | 52.23 μs | 1.00 | 0.00 | - | - | - | 176 B | + 'ImageSharp Tiff' | Job-DYSEOC | .NET Core 3.1 | Tiff/ccitt_fax3_all_terminating_codes.tiff | 945.4 μs | 511.67 μs | 28.05 μs | 4.26 | 1.00 | - | - | - | 17872 B | | | | | | | | | | | | | | - **'System.Drawing Tiff'** | **Job-EMDSBW** | **.NET 4.7.2** | **Tiff/huffman_rle_all_makeup_codes.tiff** | **189.8 μs** | **818.95 μs** | **44.89 μs** | **1.00** | **0.00** | **-** | **-** | **-** | **8192 B** | - 'ImageSharp Tiff' | Job-EMDSBW | .NET 4.7.2 | Tiff/huffman_rle_all_makeup_codes.tiff | 9,137.1 μs | 1,178.82 μs | 64.62 μs | 49.85 | 10.86 | - | - | - | 24576 B | + **'System.Drawing Tiff'** | **Job-MVPLTM** | **.NET 4.7.2** | **Tiff/huffman_rle_all_makeup_codes.tiff** | **188.4 μs** | **609.38 μs** | **33.40 μs** | **1.00** | **0.00** | **-** | **-** | **-** | **8192 B** | + 'ImageSharp Tiff' | Job-MVPLTM | .NET 4.7.2 | Tiff/huffman_rle_all_makeup_codes.tiff | 8,870.6 μs | 2,847.17 μs | 156.06 μs | 48.06 | 8.32 | - | - | - | 24576 B | | | | | | | | | | | | | | - 'System.Drawing Tiff' | Job-KCUIVJ | .NET Core 2.1 | Tiff/huffman_rle_all_makeup_codes.tiff | 298.5 μs | 1,361.33 μs | 74.62 μs | 1.00 | 0.00 | - | - | - | 2088 B | - 'ImageSharp Tiff' | Job-KCUIVJ | .NET Core 2.1 | Tiff/huffman_rle_all_makeup_codes.tiff | 5,717.5 μs | 2,533.21 μs | 138.85 μs | 19.89 | 4.51 | - | - | - | 18328 B | + 'System.Drawing Tiff' | Job-ZMKWLH | .NET Core 2.1 | Tiff/huffman_rle_all_makeup_codes.tiff | 222.0 μs | 620.73 μs | 34.02 μs | 1.00 | 0.00 | - | - | - | 2088 B | + 'ImageSharp Tiff' | Job-ZMKWLH | .NET Core 2.1 | Tiff/huffman_rle_all_makeup_codes.tiff | 5,660.9 μs | 3,414.81 μs | 187.18 μs | 25.92 | 4.17 | - | - | - | 18432 B | | | | | | | | | | | | | | - 'System.Drawing Tiff' | Job-NIWDJE | .NET Core 3.1 | Tiff/huffman_rle_all_makeup_codes.tiff | 159.5 μs | 140.52 μs | 7.70 μs | 1.00 | 0.00 | - | - | - | 176 B | - 'ImageSharp Tiff' | Job-NIWDJE | .NET Core 3.1 | Tiff/huffman_rle_all_makeup_codes.tiff | 15,047.7 μs | 2,686.03 μs | 147.23 μs | 94.47 | 4.56 | - | - | - | 18248 B | + 'System.Drawing Tiff' | Job-DYSEOC | .NET Core 3.1 | Tiff/huffman_rle_all_makeup_codes.tiff | 176.5 μs | 227.25 μs | 12.46 μs | 1.00 | 0.00 | - | - | - | 176 B | + 'ImageSharp Tiff' | Job-DYSEOC | .NET Core 3.1 | Tiff/huffman_rle_all_makeup_codes.tiff | 14,251.6 μs | 597.28 μs | 32.74 μs | 81.00 | 5.71 | - | - | - | 18352 B | diff --git a/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiff-report-github.md b/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiff-report-github.md index a07b5d1f8..a42466907 100644 --- a/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiff-report-github.md +++ b/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiff-report-github.md @@ -4,9 +4,9 @@ BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19042 Intel Core i7-3610QM CPU 2.30GHz (Ivy Bridge), 1 CPU, 8 logical and 4 physical cores .NET Core SDK=5.0.101 [Host] : .NET Core 5.0.1 (CoreCLR 5.0.120.57516, CoreFX 5.0.120.57516), X64 RyuJIT - Job-EMDSBW : .NET Framework 4.8 (4.8.4300.0), X64 RyuJIT - Job-KCUIVJ : .NET Core 2.1.23 (CoreCLR 4.6.29321.03, CoreFX 4.6.29321.01), X64 RyuJIT - Job-NIWDJE : .NET Core 3.1.10 (CoreCLR 4.700.20.51601, CoreFX 4.700.20.51901), X64 RyuJIT + Job-MVPLTM : .NET Framework 4.8 (4.8.4300.0), X64 RyuJIT + Job-ZMKWLH : .NET Core 2.1.23 (CoreCLR 4.6.29321.03, CoreFX 4.6.29321.01), X64 RyuJIT + Job-DYSEOC : .NET Core 3.1.10 (CoreCLR 4.700.20.51601, CoreFX 4.700.20.51901), X64 RyuJIT InvocationCount=1 IterationCount=3 LaunchCount=1 UnrollFactor=1 WarmupCount=3 @@ -14,74 +14,74 @@ UnrollFactor=1 WarmupCount=3 ``` | Method | Job | Runtime | TestImage | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | |---------------------- |----------- |-------------- |----------------------------------------------- |------------:|--------------:|-------------:|------:|--------:|------:|------:|------:|----------:| -| **'System.Drawing Tiff'** | **Job-EMDSBW** | **.NET 4.7.2** | **Tiff/Calliphora_grayscale_uncompressed.tiff** | **1,107.9 μs** | **260.10 μs** | **14.26 μs** | **1.00** | **0.00** | **-** | **-** | **-** | **974848 B** | -| 'ImageSharp Tiff' | Job-EMDSBW | .NET 4.7.2 | Tiff/Calliphora_grayscale_uncompressed.tiff | 29,794.8 μs | 3,103.68 μs | 170.12 μs | 26.90 | 0.49 | - | - | - | 32768 B | +| **'System.Drawing Tiff'** | **Job-MVPLTM** | **.NET 4.7.2** | **Tiff/Calliphora_grayscale_uncompressed.tiff** | **1,513.5 μs** | **6,982.54 μs** | **382.74 μs** | **1.00** | **0.00** | **-** | **-** | **-** | **974848 B** | +| 'ImageSharp Tiff' | Job-MVPLTM | .NET 4.7.2 | Tiff/Calliphora_grayscale_uncompressed.tiff | 29,504.9 μs | 2,030.88 μs | 111.32 μs | 20.46 | 5.74 | - | - | - | 32768 B | | | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-KCUIVJ | .NET Core 2.1 | Tiff/Calliphora_grayscale_uncompressed.tiff | 1,020.4 μs | 641.11 μs | 35.14 μs | 1.00 | 0.00 | - | - | - | 968832 B | -| 'ImageSharp Tiff' | Job-KCUIVJ | .NET Core 2.1 | Tiff/Calliphora_grayscale_uncompressed.tiff | 12,593.4 μs | 4,807.87 μs | 263.54 μs | 12.36 | 0.67 | - | - | - | 29976 B | +| 'System.Drawing Tiff' | Job-ZMKWLH | .NET Core 2.1 | Tiff/Calliphora_grayscale_uncompressed.tiff | 1,441.5 μs | 5,692.62 μs | 312.03 μs | 1.00 | 0.00 | - | - | - | 968832 B | +| 'ImageSharp Tiff' | Job-ZMKWLH | .NET Core 2.1 | Tiff/Calliphora_grayscale_uncompressed.tiff | 12,512.9 μs | 669.83 μs | 36.72 μs | 8.98 | 2.11 | - | - | - | 30072 B | | | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-NIWDJE | .NET Core 3.1 | Tiff/Calliphora_grayscale_uncompressed.tiff | 987.2 μs | 2,211.93 μs | 121.24 μs | 1.00 | 0.00 | - | - | - | 176 B | -| 'ImageSharp Tiff' | Job-NIWDJE | .NET Core 3.1 | Tiff/Calliphora_grayscale_uncompressed.tiff | 44,255.5 μs | 13,031.10 μs | 714.28 μs | 45.23 | 4.88 | - | - | - | 29896 B | +| 'System.Drawing Tiff' | Job-DYSEOC | .NET Core 3.1 | Tiff/Calliphora_grayscale_uncompressed.tiff | 989.7 μs | 1,621.23 μs | 88.87 μs | 1.00 | 0.00 | - | - | - | 176 B | +| 'ImageSharp Tiff' | Job-DYSEOC | .NET Core 3.1 | Tiff/Calliphora_grayscale_uncompressed.tiff | 44,038.5 μs | 7,702.48 μs | 422.20 μs | 44.75 | 4.23 | - | - | - | 29992 B | | | | | | | | | | | | | | | -| **'System.Drawing Tiff'** | **Job-EMDSBW** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_deflate_predictor.tiff** | **16,118.9 μs** | **2,095.51 μs** | **114.86 μs** | **1.00** | **0.00** | **-** | **-** | **-** | **1483440 B** | -| 'ImageSharp Tiff' | Job-EMDSBW | .NET 4.7.2 | Tiff/Calliphora_rgb_deflate_predictor.tiff | 25,967.5 μs | 4,545.04 μs | 249.13 μs | 1.61 | 0.01 | - | - | - | 848240 B | +| **'System.Drawing Tiff'** | **Job-MVPLTM** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_deflate_predictor.tiff** | **16,368.6 μs** | **3,342.63 μs** | **183.22 μs** | **1.00** | **0.00** | **-** | **-** | **-** | **1483440 B** | +| 'ImageSharp Tiff' | Job-MVPLTM | .NET 4.7.2 | Tiff/Calliphora_rgb_deflate_predictor.tiff | 27,334.9 μs | 5,327.53 μs | 292.02 μs | 1.67 | 0.04 | - | - | - | 848240 B | | | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-KCUIVJ | .NET Core 2.1 | Tiff/Calliphora_rgb_deflate_predictor.tiff | 16,465.6 μs | 7,761.65 μs | 425.44 μs | 1.00 | 0.00 | - | - | - | 1480344 B | -| 'ImageSharp Tiff' | Job-KCUIVJ | .NET Core 2.1 | Tiff/Calliphora_rgb_deflate_predictor.tiff | 18,536.9 μs | 3,415.62 μs | 187.22 μs | 1.13 | 0.02 | - | - | - | 68176 B | +| 'System.Drawing Tiff' | Job-ZMKWLH | .NET Core 2.1 | Tiff/Calliphora_rgb_deflate_predictor.tiff | 16,192.2 μs | 4,200.11 μs | 230.22 μs | 1.00 | 0.00 | - | - | - | 1480344 B | +| 'ImageSharp Tiff' | Job-ZMKWLH | .NET Core 2.1 | Tiff/Calliphora_rgb_deflate_predictor.tiff | 18,448.9 μs | 3,957.52 μs | 216.93 μs | 1.14 | 0.00 | - | - | - | 68224 B | | | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-NIWDJE | .NET Core 3.1 | Tiff/Calliphora_rgb_deflate_predictor.tiff | 16,216.2 μs | 3,288.12 μs | 180.23 μs | 1.00 | 0.00 | - | - | - | 176 B | -| 'ImageSharp Tiff' | Job-NIWDJE | .NET Core 3.1 | Tiff/Calliphora_rgb_deflate_predictor.tiff | 20,740.6 μs | 54,608.55 μs | 2,993.28 μs | 1.28 | 0.17 | - | - | - | 65120 B | +| 'System.Drawing Tiff' | Job-DYSEOC | .NET Core 3.1 | Tiff/Calliphora_rgb_deflate_predictor.tiff | 15,936.7 μs | 3,145.57 μs | 172.42 μs | 1.00 | 0.00 | - | - | - | 176 B | +| 'ImageSharp Tiff' | Job-DYSEOC | .NET Core 3.1 | Tiff/Calliphora_rgb_deflate_predictor.tiff | 18,973.7 μs | 16,625.54 μs | 911.30 μs | 1.19 | 0.06 | - | - | - | 65168 B | | | | | | | | | | | | | | | -| **'System.Drawing Tiff'** | **Job-EMDSBW** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_lzw_predictor.tiff** | **83,012.1 μs** | **14,786.35 μs** | **810.49 μs** | **1.00** | **0.00** | **-** | **-** | **-** | **2545736 B** | -| 'ImageSharp Tiff' | Job-EMDSBW | .NET 4.7.2 | Tiff/Calliphora_rgb_lzw_predictor.tiff | 64,895.5 μs | 11,397.89 μs | 624.76 μs | 0.78 | 0.01 | - | - | - | 24576 B | +| **'System.Drawing Tiff'** | **Job-MVPLTM** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_lzw_predictor.tiff** | **81,687.4 μs** | **6,229.79 μs** | **341.48 μs** | **1.00** | **0.00** | **-** | **-** | **-** | **2545736 B** | +| 'ImageSharp Tiff' | Job-MVPLTM | .NET 4.7.2 | Tiff/Calliphora_rgb_lzw_predictor.tiff | 67,259.5 μs | 4,315.22 μs | 236.53 μs | 0.82 | 0.01 | - | - | - | 24576 B | | | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-KCUIVJ | .NET Core 2.1 | Tiff/Calliphora_rgb_lzw_predictor.tiff | 82,854.1 μs | 45,495.28 μs | 2,493.75 μs | 1.00 | 0.00 | - | - | - | 2541376 B | -| 'ImageSharp Tiff' | Job-KCUIVJ | .NET Core 2.1 | Tiff/Calliphora_rgb_lzw_predictor.tiff | 44,307.1 μs | 15,595.85 μs | 854.86 μs | 0.53 | 0.01 | - | - | - | 23832 B | +| 'System.Drawing Tiff' | Job-ZMKWLH | .NET Core 2.1 | Tiff/Calliphora_rgb_lzw_predictor.tiff | 81,554.2 μs | 9,082.88 μs | 497.86 μs | 1.00 | 0.00 | - | - | - | 2541376 B | +| 'ImageSharp Tiff' | Job-ZMKWLH | .NET Core 2.1 | Tiff/Calliphora_rgb_lzw_predictor.tiff | 43,966.2 μs | 3,806.49 μs | 208.65 μs | 0.54 | 0.00 | - | - | - | 23880 B | | | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-NIWDJE | .NET Core 3.1 | Tiff/Calliphora_rgb_lzw_predictor.tiff | 83,297.5 μs | 15,796.71 μs | 865.87 μs | 1.00 | 0.00 | - | - | - | 176 B | -| 'ImageSharp Tiff' | Job-NIWDJE | .NET Core 3.1 | Tiff/Calliphora_rgb_lzw_predictor.tiff | 59,464.0 μs | 13,870.15 μs | 760.27 μs | 0.71 | 0.01 | - | - | - | 23760 B | +| 'System.Drawing Tiff' | Job-DYSEOC | .NET Core 3.1 | Tiff/Calliphora_rgb_lzw_predictor.tiff | 80,333.6 μs | 24,190.59 μs | 1,325.97 μs | 1.00 | 0.00 | - | - | - | 176 B | +| 'ImageSharp Tiff' | Job-DYSEOC | .NET Core 3.1 | Tiff/Calliphora_rgb_lzw_predictor.tiff | 54,418.0 μs | 122,629.27 μs | 6,721.72 μs | 0.68 | 0.09 | - | - | - | 23848 B | | | | | | | | | | | | | | | -| **'System.Drawing Tiff'** | **Job-EMDSBW** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_packbits.tiff** | **3,707.2 μs** | **6,293.27 μs** | **344.96 μs** | **1.00** | **0.00** | **-** | **-** | **-** | **2916008 B** | -| 'ImageSharp Tiff' | Job-EMDSBW | .NET 4.7.2 | Tiff/Calliphora_rgb_packbits.tiff | 7,526.9 μs | 5,965.86 μs | 327.01 μs | 2.04 | 0.24 | - | - | - | 81920 B | +| **'System.Drawing Tiff'** | **Job-MVPLTM** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_packbits.tiff** | **3,554.1 μs** | **2,577.75 μs** | **141.30 μs** | **1.00** | **0.00** | **-** | **-** | **-** | **2916000 B** | +| 'ImageSharp Tiff' | Job-MVPLTM | .NET 4.7.2 | Tiff/Calliphora_rgb_packbits.tiff | 7,231.9 μs | 3,934.62 μs | 215.67 μs | 2.04 | 0.08 | - | - | - | 57344 B | | | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-KCUIVJ | .NET Core 2.1 | Tiff/Calliphora_rgb_packbits.tiff | 4,037.7 μs | 9,243.97 μs | 506.69 μs | 1.00 | 0.00 | - | - | - | 2903544 B | -| 'ImageSharp Tiff' | Job-KCUIVJ | .NET Core 2.1 | Tiff/Calliphora_rgb_packbits.tiff | 4,395.7 μs | 1,394.13 μs | 76.42 μs | 1.10 | 0.15 | - | - | - | 80256 B | +| 'System.Drawing Tiff' | Job-ZMKWLH | .NET Core 2.1 | Tiff/Calliphora_rgb_packbits.tiff | 3,815.4 μs | 11,074.41 μs | 607.03 μs | 1.00 | 0.00 | - | - | - | 2903544 B | +| 'ImageSharp Tiff' | Job-ZMKWLH | .NET Core 2.1 | Tiff/Calliphora_rgb_packbits.tiff | 4,415.6 μs | 7,272.82 μs | 398.65 μs | 1.17 | 0.08 | - | - | - | 51920 B | | | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-NIWDJE | .NET Core 3.1 | Tiff/Calliphora_rgb_packbits.tiff | 3,456.3 μs | 4,443.73 μs | 243.58 μs | 1.00 | 0.00 | - | - | - | 176 B | -| 'ImageSharp Tiff' | Job-NIWDJE | .NET Core 3.1 | Tiff/Calliphora_rgb_packbits.tiff | 4,542.9 μs | 3,820.61 μs | 209.42 μs | 1.32 | 0.13 | - | - | - | 80184 B | +| 'System.Drawing Tiff' | Job-DYSEOC | .NET Core 3.1 | Tiff/Calliphora_rgb_packbits.tiff | 3,297.6 μs | 5,129.08 μs | 281.14 μs | 1.00 | 0.00 | - | - | - | 176 B | +| 'ImageSharp Tiff' | Job-DYSEOC | .NET Core 3.1 | Tiff/Calliphora_rgb_packbits.tiff | 4,421.7 μs | 3,349.14 μs | 183.58 μs | 1.34 | 0.07 | - | - | - | 51848 B | | | | | | | | | | | | | | | -| **'System.Drawing Tiff'** | **Job-EMDSBW** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_palette_lzw_predictor.tiff** | **60,298.5 μs** | **24,263.76 μs** | **1,329.98 μs** | **1.00** | **0.00** | **-** | **-** | **-** | **827416 B** | -| 'ImageSharp Tiff' | Job-EMDSBW | .NET 4.7.2 | Tiff/Calliphora_rgb_palette_lzw_predictor.tiff | 76,021.3 μs | 4,206.79 μs | 230.59 μs | 1.26 | 0.02 | - | - | - | 49152 B | +| **'System.Drawing Tiff'** | **Job-MVPLTM** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_palette_lzw_predictor.tiff** | **60,458.2 μs** | **21,405.09 μs** | **1,173.29 μs** | **1.00** | **0.00** | **-** | **-** | **-** | **827416 B** | +| 'ImageSharp Tiff' | Job-MVPLTM | .NET 4.7.2 | Tiff/Calliphora_rgb_palette_lzw_predictor.tiff | 76,324.5 μs | 12,909.45 μs | 707.61 μs | 1.26 | 0.04 | - | - | - | 49152 B | | | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-KCUIVJ | .NET Core 2.1 | Tiff/Calliphora_rgb_palette_lzw_predictor.tiff | 59,122.1 μs | 9,681.07 μs | 530.65 μs | 1.00 | 0.00 | - | - | - | 825648 B | -| 'ImageSharp Tiff' | Job-KCUIVJ | .NET Core 2.1 | Tiff/Calliphora_rgb_palette_lzw_predictor.tiff | 45,789.3 μs | 7,453.72 μs | 408.56 μs | 0.77 | 0.00 | - | - | - | 45936 B | +| 'System.Drawing Tiff' | Job-ZMKWLH | .NET Core 2.1 | Tiff/Calliphora_rgb_palette_lzw_predictor.tiff | 61,210.5 μs | 17,165.24 μs | 940.88 μs | 1.00 | 0.00 | - | - | - | 825648 B | +| 'ImageSharp Tiff' | Job-ZMKWLH | .NET Core 2.1 | Tiff/Calliphora_rgb_palette_lzw_predictor.tiff | 46,951.4 μs | 1,602.53 μs | 87.84 μs | 0.77 | 0.01 | - | - | - | 45984 B | | | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-NIWDJE | .NET Core 3.1 | Tiff/Calliphora_rgb_palette_lzw_predictor.tiff | 61,361.5 μs | 25,759.90 μs | 1,411.99 μs | 1.00 | 0.00 | - | - | - | 176 B | -| 'ImageSharp Tiff' | Job-NIWDJE | .NET Core 3.1 | Tiff/Calliphora_rgb_palette_lzw_predictor.tiff | 68,134.6 μs | 303,212.80 μs | 16,620.12 μs | 1.11 | 0.25 | - | - | - | 45864 B | +| 'System.Drawing Tiff' | Job-DYSEOC | .NET Core 3.1 | Tiff/Calliphora_rgb_palette_lzw_predictor.tiff | 59,056.7 μs | 6,187.79 μs | 339.17 μs | 1.00 | 0.00 | - | - | - | 176 B | +| 'ImageSharp Tiff' | Job-DYSEOC | .NET Core 3.1 | Tiff/Calliphora_rgb_palette_lzw_predictor.tiff | 66,042.9 μs | 291,880.02 μs | 15,998.93 μs | 1.12 | 0.27 | - | - | - | 45912 B | | | | | | | | | | | | | | | -| **'System.Drawing Tiff'** | **Job-EMDSBW** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_uncompressed.tiff** | **3,431.7 μs** | **7,649.10 μs** | **419.27 μs** | **1.00** | **0.00** | **-** | **-** | **-** | **2915944 B** | -| 'ImageSharp Tiff' | Job-EMDSBW | .NET 4.7.2 | Tiff/Calliphora_rgb_uncompressed.tiff | 6,382.4 μs | 2,573.27 μs | 141.05 μs | 1.87 | 0.18 | - | - | - | 57344 B | +| **'System.Drawing Tiff'** | **Job-MVPLTM** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_uncompressed.tiff** | **3,385.5 μs** | **6,266.60 μs** | **343.49 μs** | **1.00** | **0.00** | **-** | **-** | **-** | **2915968 B** | +| 'ImageSharp Tiff' | Job-MVPLTM | .NET 4.7.2 | Tiff/Calliphora_rgb_uncompressed.tiff | 7,584.8 μs | 358.27 μs | 19.64 μs | 2.25 | 0.21 | - | - | - | 57344 B | | | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-KCUIVJ | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | 3,636.1 μs | 8,607.66 μs | 471.81 μs | 1.00 | 0.00 | - | - | - | 2905840 B | -| 'ImageSharp Tiff' | Job-KCUIVJ | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | 4,018.7 μs | 1,662.68 μs | 91.14 μs | 1.12 | 0.16 | - | - | - | 51472 B | +| 'System.Drawing Tiff' | Job-ZMKWLH | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | 3,405.8 μs | 4,765.81 μs | 261.23 μs | 1.00 | 0.00 | - | - | - | 2905840 B | +| 'ImageSharp Tiff' | Job-ZMKWLH | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | 3,930.3 μs | 3,250.19 μs | 178.15 μs | 1.16 | 0.05 | - | - | - | 51568 B | | | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-NIWDJE | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | 2,970.8 μs | 5,028.62 μs | 275.64 μs | 1.00 | 0.00 | - | - | - | 176 B | -| 'ImageSharp Tiff' | Job-NIWDJE | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | 4,009.6 μs | 3,007.19 μs | 164.83 μs | 1.36 | 0.17 | - | - | - | 51400 B | +| 'System.Drawing Tiff' | Job-DYSEOC | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | 3,087.8 μs | 5,556.58 μs | 304.58 μs | 1.00 | 0.00 | - | - | - | 176 B | +| 'ImageSharp Tiff' | Job-DYSEOC | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | 3,909.1 μs | 2,519.27 μs | 138.09 μs | 1.27 | 0.14 | - | - | - | 51496 B | | | | | | | | | | | | | | | -| **'System.Drawing Tiff'** | **Job-EMDSBW** | **.NET 4.7.2** | **Tiff/ccitt_fax3_all_terminating_codes.tiff** | **178.4 μs** | **375.89 μs** | **20.60 μs** | **1.00** | **0.00** | **-** | **-** | **-** | **8192 B** | -| 'ImageSharp Tiff' | Job-EMDSBW | .NET 4.7.2 | Tiff/ccitt_fax3_all_terminating_codes.tiff | 634.5 μs | 251.14 μs | 13.77 μs | 3.58 | 0.37 | - | - | - | 24576 B | +| **'System.Drawing Tiff'** | **Job-MVPLTM** | **.NET 4.7.2** | **Tiff/ccitt_fax3_all_terminating_codes.tiff** | **151.9 μs** | **73.26 μs** | **4.02 μs** | **1.00** | **0.00** | **-** | **-** | **-** | **8192 B** | +| 'ImageSharp Tiff' | Job-MVPLTM | .NET 4.7.2 | Tiff/ccitt_fax3_all_terminating_codes.tiff | 648.7 μs | 165.54 μs | 9.07 μs | 4.27 | 0.08 | - | - | - | 24576 B | | | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-KCUIVJ | .NET Core 2.1 | Tiff/ccitt_fax3_all_terminating_codes.tiff | 171.7 μs | 606.95 μs | 33.27 μs | 1.00 | 0.00 | - | - | - | 2032 B | -| 'ImageSharp Tiff' | Job-KCUIVJ | .NET Core 2.1 | Tiff/ccitt_fax3_all_terminating_codes.tiff | 421.0 μs | 31.60 μs | 1.73 μs | 2.51 | 0.49 | - | - | - | 17848 B | +| 'System.Drawing Tiff' | Job-ZMKWLH | .NET Core 2.1 | Tiff/ccitt_fax3_all_terminating_codes.tiff | 160.6 μs | 80.44 μs | 4.41 μs | 1.00 | 0.00 | - | - | - | 2032 B | +| 'ImageSharp Tiff' | Job-ZMKWLH | .NET Core 2.1 | Tiff/ccitt_fax3_all_terminating_codes.tiff | 427.7 μs | 431.89 μs | 23.67 μs | 2.66 | 0.17 | - | - | - | 17952 B | | | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-NIWDJE | .NET Core 3.1 | Tiff/ccitt_fax3_all_terminating_codes.tiff | 137.2 μs | 78.18 μs | 4.29 μs | 1.00 | 0.00 | - | - | - | 176 B | -| 'ImageSharp Tiff' | Job-NIWDJE | .NET Core 3.1 | Tiff/ccitt_fax3_all_terminating_codes.tiff | 888.5 μs | 495.11 μs | 27.14 μs | 6.47 | 0.05 | - | - | - | 17768 B | +| 'System.Drawing Tiff' | Job-DYSEOC | .NET Core 3.1 | Tiff/ccitt_fax3_all_terminating_codes.tiff | 230.1 μs | 952.83 μs | 52.23 μs | 1.00 | 0.00 | - | - | - | 176 B | +| 'ImageSharp Tiff' | Job-DYSEOC | .NET Core 3.1 | Tiff/ccitt_fax3_all_terminating_codes.tiff | 945.4 μs | 511.67 μs | 28.05 μs | 4.26 | 1.00 | - | - | - | 17872 B | | | | | | | | | | | | | | | -| **'System.Drawing Tiff'** | **Job-EMDSBW** | **.NET 4.7.2** | **Tiff/huffman_rle_all_makeup_codes.tiff** | **189.8 μs** | **818.95 μs** | **44.89 μs** | **1.00** | **0.00** | **-** | **-** | **-** | **8192 B** | -| 'ImageSharp Tiff' | Job-EMDSBW | .NET 4.7.2 | Tiff/huffman_rle_all_makeup_codes.tiff | 9,137.1 μs | 1,178.82 μs | 64.62 μs | 49.85 | 10.86 | - | - | - | 24576 B | +| **'System.Drawing Tiff'** | **Job-MVPLTM** | **.NET 4.7.2** | **Tiff/huffman_rle_all_makeup_codes.tiff** | **188.4 μs** | **609.38 μs** | **33.40 μs** | **1.00** | **0.00** | **-** | **-** | **-** | **8192 B** | +| 'ImageSharp Tiff' | Job-MVPLTM | .NET 4.7.2 | Tiff/huffman_rle_all_makeup_codes.tiff | 8,870.6 μs | 2,847.17 μs | 156.06 μs | 48.06 | 8.32 | - | - | - | 24576 B | | | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-KCUIVJ | .NET Core 2.1 | Tiff/huffman_rle_all_makeup_codes.tiff | 298.5 μs | 1,361.33 μs | 74.62 μs | 1.00 | 0.00 | - | - | - | 2088 B | -| 'ImageSharp Tiff' | Job-KCUIVJ | .NET Core 2.1 | Tiff/huffman_rle_all_makeup_codes.tiff | 5,717.5 μs | 2,533.21 μs | 138.85 μs | 19.89 | 4.51 | - | - | - | 18328 B | +| 'System.Drawing Tiff' | Job-ZMKWLH | .NET Core 2.1 | Tiff/huffman_rle_all_makeup_codes.tiff | 222.0 μs | 620.73 μs | 34.02 μs | 1.00 | 0.00 | - | - | - | 2088 B | +| 'ImageSharp Tiff' | Job-ZMKWLH | .NET Core 2.1 | Tiff/huffman_rle_all_makeup_codes.tiff | 5,660.9 μs | 3,414.81 μs | 187.18 μs | 25.92 | 4.17 | - | - | - | 18432 B | | | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-NIWDJE | .NET Core 3.1 | Tiff/huffman_rle_all_makeup_codes.tiff | 159.5 μs | 140.52 μs | 7.70 μs | 1.00 | 0.00 | - | - | - | 176 B | -| 'ImageSharp Tiff' | Job-NIWDJE | .NET Core 3.1 | Tiff/huffman_rle_all_makeup_codes.tiff | 15,047.7 μs | 2,686.03 μs | 147.23 μs | 94.47 | 4.56 | - | - | - | 18248 B | +| 'System.Drawing Tiff' | Job-DYSEOC | .NET Core 3.1 | Tiff/huffman_rle_all_makeup_codes.tiff | 176.5 μs | 227.25 μs | 12.46 μs | 1.00 | 0.00 | - | - | - | 176 B | +| 'ImageSharp Tiff' | Job-DYSEOC | .NET Core 3.1 | Tiff/huffman_rle_all_makeup_codes.tiff | 14,251.6 μs | 597.28 μs | 32.74 μs | 81.00 | 5.71 | - | - | - | 18352 B | diff --git a/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiff-report.html b/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiff-report.html index 86292073b..c29e35d57 100644 --- a/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiff-report.html +++ b/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiff-report.html @@ -2,7 +2,7 @@ -SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiff-20210207-115335 +SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiff-20210207-174953 - - -

-BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19042
-Intel Core i7-3610QM CPU 2.30GHz (Ivy Bridge), 1 CPU, 8 logical and 4 physical cores
-.NET Core SDK=5.0.101
-  [Host]     : .NET Core 5.0.1 (CoreCLR 5.0.120.57516, CoreFX 5.0.120.57516), X64 RyuJIT
-  Job-MVPLTM : .NET Framework 4.8 (4.8.4300.0), X64 RyuJIT
-  Job-ZMKWLH : .NET Core 2.1.23 (CoreCLR 4.6.29321.03, CoreFX 4.6.29321.01), X64 RyuJIT
-  Job-DYSEOC : .NET Core 3.1.10 (CoreCLR 4.700.20.51601, CoreFX 4.700.20.51901), X64 RyuJIT
-
-
InvocationCount=1  IterationCount=3  LaunchCount=1  
-UnrollFactor=1  WarmupCount=3  
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Method JobRuntime TestImage Mean ErrorStdDevRatioRatioSDGen 0Gen 1Gen 2Allocated
'System.Drawing Tiff'Job-MVPLTM.NET 4.7.2Tiff/Calliphora_grayscale_uncompressed.tiff1,513.5 μs6,982.54 μs382.74 μs1.000.00---974848 B
'ImageSharp Tiff'Job-MVPLTM.NET 4.7.2Tiff/Calliphora_grayscale_uncompressed.tiff29,504.9 μs2,030.88 μs111.32 μs20.465.74---32768 B
'System.Drawing Tiff'Job-ZMKWLH.NET Core 2.1Tiff/Calliphora_grayscale_uncompressed.tiff1,441.5 μs5,692.62 μs312.03 μs1.000.00---968832 B
'ImageSharp Tiff'Job-ZMKWLH.NET Core 2.1Tiff/Calliphora_grayscale_uncompressed.tiff12,512.9 μs669.83 μs36.72 μs8.982.11---30072 B
'System.Drawing Tiff'Job-DYSEOC.NET Core 3.1Tiff/Calliphora_grayscale_uncompressed.tiff989.7 μs1,621.23 μs88.87 μs1.000.00---176 B
'ImageSharp Tiff'Job-DYSEOC.NET Core 3.1Tiff/Calliphora_grayscale_uncompressed.tiff44,038.5 μs7,702.48 μs422.20 μs44.754.23---29992 B
'System.Drawing Tiff'Job-MVPLTM.NET 4.7.2Tiff/Calliphora_rgb_deflate_predictor.tiff16,368.6 μs3,342.63 μs183.22 μs1.000.00---1483440 B
'ImageSharp Tiff'Job-MVPLTM.NET 4.7.2Tiff/Calliphora_rgb_deflate_predictor.tiff27,334.9 μs5,327.53 μs292.02 μs1.670.04---848240 B
'System.Drawing Tiff'Job-ZMKWLH.NET Core 2.1Tiff/Calliphora_rgb_deflate_predictor.tiff16,192.2 μs4,200.11 μs230.22 μs1.000.00---1480344 B
'ImageSharp Tiff'Job-ZMKWLH.NET Core 2.1Tiff/Calliphora_rgb_deflate_predictor.tiff18,448.9 μs3,957.52 μs216.93 μs1.140.00---68224 B
'System.Drawing Tiff'Job-DYSEOC.NET Core 3.1Tiff/Calliphora_rgb_deflate_predictor.tiff15,936.7 μs3,145.57 μs172.42 μs1.000.00---176 B
'ImageSharp Tiff'Job-DYSEOC.NET Core 3.1Tiff/Calliphora_rgb_deflate_predictor.tiff18,973.7 μs16,625.54 μs911.30 μs1.190.06---65168 B
'System.Drawing Tiff'Job-MVPLTM.NET 4.7.2Tiff/Calliphora_rgb_lzw_predictor.tiff81,687.4 μs6,229.79 μs341.48 μs1.000.00---2545736 B
'ImageSharp Tiff'Job-MVPLTM.NET 4.7.2Tiff/Calliphora_rgb_lzw_predictor.tiff67,259.5 μs4,315.22 μs236.53 μs0.820.01---24576 B
'System.Drawing Tiff'Job-ZMKWLH.NET Core 2.1Tiff/Calliphora_rgb_lzw_predictor.tiff81,554.2 μs9,082.88 μs497.86 μs1.000.00---2541376 B
'ImageSharp Tiff'Job-ZMKWLH.NET Core 2.1Tiff/Calliphora_rgb_lzw_predictor.tiff43,966.2 μs3,806.49 μs208.65 μs0.540.00---23880 B
'System.Drawing Tiff'Job-DYSEOC.NET Core 3.1Tiff/Calliphora_rgb_lzw_predictor.tiff80,333.6 μs24,190.59 μs1,325.97 μs1.000.00---176 B
'ImageSharp Tiff'Job-DYSEOC.NET Core 3.1Tiff/Calliphora_rgb_lzw_predictor.tiff54,418.0 μs122,629.27 μs6,721.72 μs0.680.09---23848 B
'System.Drawing Tiff'Job-MVPLTM.NET 4.7.2Tiff/Calliphora_rgb_packbits.tiff3,554.1 μs2,577.75 μs141.30 μs1.000.00---2916000 B
'ImageSharp Tiff'Job-MVPLTM.NET 4.7.2Tiff/Calliphora_rgb_packbits.tiff7,231.9 μs3,934.62 μs215.67 μs2.040.08---57344 B
'System.Drawing Tiff'Job-ZMKWLH.NET Core 2.1Tiff/Calliphora_rgb_packbits.tiff3,815.4 μs11,074.41 μs607.03 μs1.000.00---2903544 B
'ImageSharp Tiff'Job-ZMKWLH.NET Core 2.1Tiff/Calliphora_rgb_packbits.tiff4,415.6 μs7,272.82 μs398.65 μs1.170.08---51920 B
'System.Drawing Tiff'Job-DYSEOC.NET Core 3.1Tiff/Calliphora_rgb_packbits.tiff3,297.6 μs5,129.08 μs281.14 μs1.000.00---176 B
'ImageSharp Tiff'Job-DYSEOC.NET Core 3.1Tiff/Calliphora_rgb_packbits.tiff4,421.7 μs3,349.14 μs183.58 μs1.340.07---51848 B
'System.Drawing Tiff'Job-MVPLTM.NET 4.7.2Tiff/Calliphora_rgb_palette_lzw_predictor.tiff60,458.2 μs21,405.09 μs1,173.29 μs1.000.00---827416 B
'ImageSharp Tiff'Job-MVPLTM.NET 4.7.2Tiff/Calliphora_rgb_palette_lzw_predictor.tiff76,324.5 μs12,909.45 μs707.61 μs1.260.04---49152 B
'System.Drawing Tiff'Job-ZMKWLH.NET Core 2.1Tiff/Calliphora_rgb_palette_lzw_predictor.tiff61,210.5 μs17,165.24 μs940.88 μs1.000.00---825648 B
'ImageSharp Tiff'Job-ZMKWLH.NET Core 2.1Tiff/Calliphora_rgb_palette_lzw_predictor.tiff46,951.4 μs1,602.53 μs87.84 μs0.770.01---45984 B
'System.Drawing Tiff'Job-DYSEOC.NET Core 3.1Tiff/Calliphora_rgb_palette_lzw_predictor.tiff59,056.7 μs6,187.79 μs339.17 μs1.000.00---176 B
'ImageSharp Tiff'Job-DYSEOC.NET Core 3.1Tiff/Calliphora_rgb_palette_lzw_predictor.tiff66,042.9 μs291,880.02 μs15,998.93 μs1.120.27---45912 B
'System.Drawing Tiff'Job-MVPLTM.NET 4.7.2Tiff/Calliphora_rgb_uncompressed.tiff3,385.5 μs6,266.60 μs343.49 μs1.000.00---2915968 B
'ImageSharp Tiff'Job-MVPLTM.NET 4.7.2Tiff/Calliphora_rgb_uncompressed.tiff7,584.8 μs358.27 μs19.64 μs2.250.21---57344 B
'System.Drawing Tiff'Job-ZMKWLH.NET Core 2.1Tiff/Calliphora_rgb_uncompressed.tiff3,405.8 μs4,765.81 μs261.23 μs1.000.00---2905840 B
'ImageSharp Tiff'Job-ZMKWLH.NET Core 2.1Tiff/Calliphora_rgb_uncompressed.tiff3,930.3 μs3,250.19 μs178.15 μs1.160.05---51568 B
'System.Drawing Tiff'Job-DYSEOC.NET Core 3.1Tiff/Calliphora_rgb_uncompressed.tiff3,087.8 μs5,556.58 μs304.58 μs1.000.00---176 B
'ImageSharp Tiff'Job-DYSEOC.NET Core 3.1Tiff/Calliphora_rgb_uncompressed.tiff3,909.1 μs2,519.27 μs138.09 μs1.270.14---51496 B
'System.Drawing Tiff'Job-MVPLTM.NET 4.7.2Tiff/ccitt_fax3_all_terminating_codes.tiff151.9 μs73.26 μs4.02 μs1.000.00---8192 B
'ImageSharp Tiff'Job-MVPLTM.NET 4.7.2Tiff/ccitt_fax3_all_terminating_codes.tiff648.7 μs165.54 μs9.07 μs4.270.08---24576 B
'System.Drawing Tiff'Job-ZMKWLH.NET Core 2.1Tiff/ccitt_fax3_all_terminating_codes.tiff160.6 μs80.44 μs4.41 μs1.000.00---2032 B
'ImageSharp Tiff'Job-ZMKWLH.NET Core 2.1Tiff/ccitt_fax3_all_terminating_codes.tiff427.7 μs431.89 μs23.67 μs2.660.17---17952 B
'System.Drawing Tiff'Job-DYSEOC.NET Core 3.1Tiff/ccitt_fax3_all_terminating_codes.tiff230.1 μs952.83 μs52.23 μs1.000.00---176 B
'ImageSharp Tiff'Job-DYSEOC.NET Core 3.1Tiff/ccitt_fax3_all_terminating_codes.tiff945.4 μs511.67 μs28.05 μs4.261.00---17872 B
'System.Drawing Tiff'Job-MVPLTM.NET 4.7.2Tiff/huffman_rle_all_makeup_codes.tiff188.4 μs609.38 μs33.40 μs1.000.00---8192 B
'ImageSharp Tiff'Job-MVPLTM.NET 4.7.2Tiff/huffman_rle_all_makeup_codes.tiff8,870.6 μs2,847.17 μs156.06 μs48.068.32---24576 B
'System.Drawing Tiff'Job-ZMKWLH.NET Core 2.1Tiff/huffman_rle_all_makeup_codes.tiff222.0 μs620.73 μs34.02 μs1.000.00---2088 B
'ImageSharp Tiff'Job-ZMKWLH.NET Core 2.1Tiff/huffman_rle_all_makeup_codes.tiff5,660.9 μs3,414.81 μs187.18 μs25.924.17---18432 B
'System.Drawing Tiff'Job-DYSEOC.NET Core 3.1Tiff/huffman_rle_all_makeup_codes.tiff176.5 μs227.25 μs12.46 μs1.000.00---176 B
'ImageSharp Tiff'Job-DYSEOC.NET Core 3.1Tiff/huffman_rle_all_makeup_codes.tiff14,251.6 μs597.28 μs32.74 μs81.005.71---18352 B
- - diff --git a/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiffBig-report-github.md b/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiffBig-report-github.md deleted file mode 100644 index 366c1480a..000000000 --- a/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiffBig-report-github.md +++ /dev/null @@ -1,87 +0,0 @@ -``` ini - -BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19042 -Intel Core i7-3610QM CPU 2.30GHz (Ivy Bridge), 1 CPU, 8 logical and 4 physical cores -.NET Core SDK=5.0.100 - [Host] : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT - Job-KSIANY : .NET Framework 4.8 (4.8.4250.0), X64 RyuJIT - Job-VMCLSF : .NET Core 2.1.23 (CoreCLR 4.6.29321.03, CoreFX 4.6.29321.01), X64 RyuJIT - Job-UHENIY : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT - -InvocationCount=1 IterationCount=5 LaunchCount=1 -UnrollFactor=1 WarmupCount=3 - -``` -| Method | Job | Runtime | TestImage | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | -|---------------------- |----------- |-------------- |----------------------------------- |-----------:|----------:|----------:|------:|--------:|-----------:|----------:|----------:|------------:| -| **'System.Drawing Tiff'** | **Job-KSIANY** | **.NET 4.7.2** | **medium_bw_Fax3.tiff** | **491.6 ms** | **20.40 ms** | **5.30 ms** | **1.00** | **0.00** | **1000.0000** | **-** | **-** | **5768128 B** | -| 'ImageSharp Tiff' | Job-KSIANY | .NET 4.7.2 | medium_bw_Fax3.tiff | 6,970.2 ms | 70.64 ms | 10.93 ms | 14.23 | 0.12 | 1000.0000 | 1000.0000 | 1000.0000 | 241518600 B | -| | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-VMCLSF | .NET Core 2.1 | medium_bw_Fax3.tiff | 486.2 ms | 23.15 ms | 3.58 ms | 1.00 | 0.00 | 1000.0000 | - | - | 5751016 B | -| 'ImageSharp Tiff' | Job-VMCLSF | .NET Core 2.1 | medium_bw_Fax3.tiff | 4,150.2 ms | 322.16 ms | 83.66 ms | 8.47 | 0.16 | - | - | - | 235961088 B | -| | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-UHENIY | .NET Core 3.1 | medium_bw_Fax3.tiff | 490.1 ms | 12.76 ms | 3.31 ms | 1.00 | 0.00 | - | - | - | 176 B | -| 'ImageSharp Tiff' | Job-UHENIY | .NET Core 3.1 | medium_bw_Fax3.tiff | 3,582.9 ms | 61.89 ms | 16.07 ms | 7.31 | 0.06 | - | - | - | 235961496 B | -| | | | | | | | | | | | | | -| **'System.Drawing Tiff'** | **Job-KSIANY** | **.NET 4.7.2** | **medium_bw_Rle.tiff** | **499.1 ms** | **26.71 ms** | **6.94 ms** | **1.00** | **0.00** | **1000.0000** | **-** | **-** | **8494472 B** | -| 'ImageSharp Tiff' | Job-KSIANY | .NET 4.7.2 | medium_bw_Rle.tiff | 7,290.4 ms | 938.28 ms | 243.67 ms | 14.61 | 0.33 | 1000.0000 | 1000.0000 | 1000.0000 | 237020384 B | -| | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-VMCLSF | .NET Core 2.1 | medium_bw_Rle.tiff | 490.6 ms | 30.19 ms | 4.67 ms | 1.00 | 0.00 | 1000.0000 | - | - | 8475688 B | -| 'ImageSharp Tiff' | Job-VMCLSF | .NET Core 2.1 | medium_bw_Rle.tiff | 4,230.2 ms | 35.59 ms | 5.51 ms | 8.62 | 0.08 | - | - | - | 235961944 B | -| | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-UHENIY | .NET Core 3.1 | medium_bw_Rle.tiff | 487.6 ms | 12.07 ms | 1.87 ms | 1.00 | 0.00 | - | - | - | 176 B | -| 'ImageSharp Tiff' | Job-UHENIY | .NET Core 3.1 | medium_bw_Rle.tiff | 3,647.4 ms | 42.62 ms | 11.07 ms | 7.48 | 0.04 | - | - | - | 235962184 B | -| | | | | | | | | | | | | | -| **'System.Drawing Tiff'** | **Job-KSIANY** | **.NET 4.7.2** | **medium_grayscale_uncompressed.tiff** | **606.7 ms** | **20.45 ms** | **5.31 ms** | **1.00** | **0.00** | **18000.0000** | **-** | **-** | **90301696 B** | -| 'ImageSharp Tiff' | Job-KSIANY | .NET 4.7.2 | medium_grayscale_uncompressed.tiff | 1,852.9 ms | 6.74 ms | 1.75 ms | 3.05 | 0.03 | - | - | - | 235970584 B | -| | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-VMCLSF | .NET Core 2.1 | medium_grayscale_uncompressed.tiff | 606.6 ms | 36.58 ms | 9.50 ms | 1.00 | 0.00 | 18000.0000 | - | - | 90104048 B | -| 'ImageSharp Tiff' | Job-VMCLSF | .NET Core 2.1 | medium_grayscale_uncompressed.tiff | 764.3 ms | 15.69 ms | 4.08 ms | 1.26 | 0.02 | - | - | - | 235965376 B | -| | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-UHENIY | .NET Core 3.1 | medium_grayscale_uncompressed.tiff | 569.6 ms | 17.44 ms | 4.53 ms | 1.00 | 0.00 | - | - | - | 176 B | -| 'ImageSharp Tiff' | Job-UHENIY | .NET Core 3.1 | medium_grayscale_uncompressed.tiff | 655.2 ms | 17.48 ms | 4.54 ms | 1.15 | 0.01 | - | - | - | 235965488 B | -| | | | | | | | | | | | | | -| **'System.Drawing Tiff'** | **Job-KSIANY** | **.NET 4.7.2** | **medium_palette_uncompressed.tiff** | **578.0 ms** | **22.32 ms** | **5.80 ms** | **1.00** | **0.00** | **18000.0000** | **-** | **-** | **90301696 B** | -| 'ImageSharp Tiff' | Job-KSIANY | .NET 4.7.2 | medium_palette_uncompressed.tiff | 3,336.9 ms | 21.42 ms | 5.56 ms | 5.77 | 0.07 | - | - | - | 236003608 B | -| | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-VMCLSF | .NET Core 2.1 | medium_palette_uncompressed.tiff | 601.9 ms | 40.85 ms | 6.32 ms | 1.00 | 0.00 | 18000.0000 | - | - | 90107368 B | -| 'ImageSharp Tiff' | Job-VMCLSF | .NET Core 2.1 | medium_palette_uncompressed.tiff | 1,971.9 ms | 15.69 ms | 4.07 ms | 3.28 | 0.04 | - | - | - | 235996096 B | -| | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-UHENIY | .NET Core 3.1 | medium_palette_uncompressed.tiff | 566.1 ms | 28.06 ms | 4.34 ms | 1.00 | 0.00 | - | - | - | 176 B | -| 'ImageSharp Tiff' | Job-UHENIY | .NET Core 3.1 | medium_palette_uncompressed.tiff | 1,664.1 ms | 11.59 ms | 1.79 ms | 2.94 | 0.02 | - | - | - | 235996208 B | -| | | | | | | | | | | | | | -| **'System.Drawing Tiff'** | **Job-KSIANY** | **.NET 4.7.2** | **medium_rgb_deflate.tiff** | **357.4 ms** | **15.54 ms** | **2.40 ms** | **1.00** | **0.00** | **3000.0000** | **-** | **-** | **9662560 B** | -| 'ImageSharp Tiff' | Job-KSIANY | .NET 4.7.2 | medium_rgb_deflate.tiff | 776.1 ms | 14.51 ms | 3.77 ms | 2.17 | 0.01 | 22000.0000 | 1000.0000 | 1000.0000 | 303476856 B | -| | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-VMCLSF | .NET Core 2.1 | medium_rgb_deflate.tiff | 359.7 ms | 12.29 ms | 3.19 ms | 1.00 | 0.00 | 3000.0000 | - | - | 9629400 B | -| 'ImageSharp Tiff' | Job-VMCLSF | .NET Core 2.1 | medium_rgb_deflate.tiff | 554.5 ms | 16.78 ms | 4.36 ms | 1.54 | 0.02 | 2000.0000 | 1000.0000 | 1000.0000 | 239716144 B | -| | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-UHENIY | .NET Core 3.1 | medium_rgb_deflate.tiff | 353.2 ms | 7.22 ms | 1.12 ms | 1.00 | 0.00 | - | - | - | 176 B | -| 'ImageSharp Tiff' | Job-UHENIY | .NET Core 3.1 | medium_rgb_deflate.tiff | 557.1 ms | 10.79 ms | 2.80 ms | 1.58 | 0.00 | 2000.0000 | 1000.0000 | 1000.0000 | 239470552 B | -| | | | | | | | | | | | | | -| **'System.Drawing Tiff'** | **Job-KSIANY** | **.NET 4.7.2** | **medium_rgb_lzw.tiff** | **511.0 ms** | **6.43 ms** | **1.67 ms** | **1.00** | **0.00** | **3000.0000** | **-** | **-** | **11600840 B** | -| 'ImageSharp Tiff' | Job-KSIANY | .NET 4.7.2 | medium_rgb_lzw.tiff | 2,691.6 ms | 16.81 ms | 2.60 ms | 5.27 | 0.02 | - | - | - | 236044312 B | -| | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-VMCLSF | .NET Core 2.1 | medium_rgb_lzw.tiff | 511.4 ms | 11.44 ms | 1.77 ms | 1.00 | 0.00 | 3000.0000 | - | - | 11569776 B | -| 'ImageSharp Tiff' | Job-VMCLSF | .NET Core 2.1 | medium_rgb_lzw.tiff | 1,654.1 ms | 12.42 ms | 1.92 ms | 3.23 | 0.01 | - | - | - | 236041592 B | -| | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-UHENIY | .NET Core 3.1 | medium_rgb_lzw.tiff | 507.7 ms | 8.89 ms | 2.31 ms | 1.00 | 0.00 | - | - | - | 176 B | -| 'ImageSharp Tiff' | Job-UHENIY | .NET Core 3.1 | medium_rgb_lzw.tiff | 1,689.5 ms | 40.41 ms | 6.25 ms | 3.33 | 0.03 | - | - | - | 236041656 B | -| | | | | | | | | | | | | | -| **'System.Drawing Tiff'** | **Job-KSIANY** | **.NET 4.7.2** | **medium_rgb_packbits.tiff** | **776.8 ms** | **31.69 ms** | **8.23 ms** | **1.00** | **0.00** | **56000.0000** | **-** | **-** | **304057016 B** | -| 'ImageSharp Tiff' | Job-KSIANY | .NET 4.7.2 | medium_rgb_packbits.tiff | 531.2 ms | 23.17 ms | 6.02 ms | 0.68 | 0.01 | - | - | - | 236003352 B | -| | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-VMCLSF | .NET Core 2.1 | medium_rgb_packbits.tiff | 764.2 ms | 41.43 ms | 6.41 ms | 1.00 | 0.00 | 56000.0000 | - | - | 303861120 B | -| 'ImageSharp Tiff' | Job-VMCLSF | .NET Core 2.1 | medium_rgb_packbits.tiff | 300.0 ms | 4.39 ms | 0.68 ms | 0.39 | 0.00 | - | - | - | 235998408 B | -| | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-UHENIY | .NET Core 3.1 | medium_rgb_packbits.tiff | 659.1 ms | 34.59 ms | 8.98 ms | 1.00 | 0.00 | - | - | - | 176 B | -| 'ImageSharp Tiff' | Job-UHENIY | .NET Core 3.1 | medium_rgb_packbits.tiff | 297.5 ms | 21.13 ms | 5.49 ms | 0.45 | 0.00 | - | - | - | 235998520 B | -| | | | | | | | | | | | | | -| **'System.Drawing Tiff'** | **Job-KSIANY** | **.NET 4.7.2** | **medium_rgb_uncompressed.tiff** | **742.5 ms** | **50.45 ms** | **13.10 ms** | **1.00** | **0.00** | **55000.0000** | **-** | **-** | **302644272 B** | -| 'ImageSharp Tiff' | Job-KSIANY | .NET 4.7.2 | medium_rgb_uncompressed.tiff | 414.3 ms | 15.37 ms | 3.99 ms | 0.56 | 0.01 | - | - | - | 235986968 B | -| | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-VMCLSF | .NET Core 2.1 | medium_rgb_uncompressed.tiff | 750.2 ms | 74.13 ms | 19.25 ms | 1.00 | 0.00 | 55000.0000 | - | - | 302448096 B | -| 'ImageSharp Tiff' | Job-VMCLSF | .NET Core 2.1 | medium_rgb_uncompressed.tiff | 283.6 ms | 21.56 ms | 5.60 ms | 0.38 | 0.01 | - | - | - | 235981128 B | -| | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-UHENIY | .NET Core 3.1 | medium_rgb_uncompressed.tiff | 662.6 ms | 49.79 ms | 12.93 ms | 1.00 | 0.00 | - | - | - | 176 B | -| 'ImageSharp Tiff' | Job-UHENIY | .NET Core 3.1 | medium_rgb_uncompressed.tiff | 278.6 ms | 9.48 ms | 2.46 ms | 0.42 | 0.01 | - | - | - | 235981352 B | diff --git a/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiffBig-report.html b/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiffBig-report.html deleted file mode 100644 index 10fd01fd4..000000000 --- a/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiffBig-report.html +++ /dev/null @@ -1,81 +0,0 @@ - - - - -SixLabors.ImageSharp.Benchmarks.Codecs.DecodeTiffBig-20201209-175548 - - - - -

-BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19042
-Intel Core i7-3610QM CPU 2.30GHz (Ivy Bridge), 1 CPU, 8 logical and 4 physical cores
-.NET Core SDK=5.0.100
-  [Host]     : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT
-  Job-KSIANY : .NET Framework 4.8 (4.8.4250.0), X64 RyuJIT
-  Job-VMCLSF : .NET Core 2.1.23 (CoreCLR 4.6.29321.03, CoreFX 4.6.29321.01), X64 RyuJIT
-  Job-UHENIY : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT
-
-
InvocationCount=1  IterationCount=5  LaunchCount=1  
-UnrollFactor=1  WarmupCount=3  
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Method JobRuntime TestImageMeanErrorStdDevRatioRatioSDGen 0Gen 1Gen 2Allocated
'System.Drawing Tiff'Job-KSIANY.NET 4.7.2medium_bw_Fax3.tiff491.6 ms20.40 ms5.30 ms1.000.001000.0000--5768128 B
'ImageSharp Tiff'Job-KSIANY.NET 4.7.2medium_bw_Fax3.tiff6,970.2 ms70.64 ms10.93 ms14.230.121000.00001000.00001000.0000241518600 B
'System.Drawing Tiff'Job-VMCLSF.NET Core 2.1medium_bw_Fax3.tiff486.2 ms23.15 ms3.58 ms1.000.001000.0000--5751016 B
'ImageSharp Tiff'Job-VMCLSF.NET Core 2.1medium_bw_Fax3.tiff4,150.2 ms322.16 ms83.66 ms8.470.16---235961088 B
'System.Drawing Tiff'Job-UHENIY.NET Core 3.1medium_bw_Fax3.tiff490.1 ms12.76 ms3.31 ms1.000.00---176 B
'ImageSharp Tiff'Job-UHENIY.NET Core 3.1medium_bw_Fax3.tiff3,582.9 ms61.89 ms16.07 ms7.310.06---235961496 B
'System.Drawing Tiff'Job-KSIANY.NET 4.7.2medium_bw_Rle.tiff499.1 ms26.71 ms6.94 ms1.000.001000.0000--8494472 B
'ImageSharp Tiff'Job-KSIANY.NET 4.7.2medium_bw_Rle.tiff7,290.4 ms938.28 ms243.67 ms14.610.331000.00001000.00001000.0000237020384 B
'System.Drawing Tiff'Job-VMCLSF.NET Core 2.1medium_bw_Rle.tiff490.6 ms30.19 ms4.67 ms1.000.001000.0000--8475688 B
'ImageSharp Tiff'Job-VMCLSF.NET Core 2.1medium_bw_Rle.tiff4,230.2 ms35.59 ms5.51 ms8.620.08---235961944 B
'System.Drawing Tiff'Job-UHENIY.NET Core 3.1medium_bw_Rle.tiff487.6 ms12.07 ms1.87 ms1.000.00---176 B
'ImageSharp Tiff'Job-UHENIY.NET Core 3.1medium_bw_Rle.tiff3,647.4 ms42.62 ms11.07 ms7.480.04---235962184 B
'System.Drawing Tiff'Job-KSIANY.NET 4.7.2medium_grayscale_uncompressed.tiff606.7 ms20.45 ms5.31 ms1.000.0018000.0000--90301696 B
'ImageSharp Tiff'Job-KSIANY.NET 4.7.2medium_grayscale_uncompressed.tiff1,852.9 ms6.74 ms1.75 ms3.050.03---235970584 B
'System.Drawing Tiff'Job-VMCLSF.NET Core 2.1medium_grayscale_uncompressed.tiff606.6 ms36.58 ms9.50 ms1.000.0018000.0000--90104048 B
'ImageSharp Tiff'Job-VMCLSF.NET Core 2.1medium_grayscale_uncompressed.tiff764.3 ms15.69 ms4.08 ms1.260.02---235965376 B
'System.Drawing Tiff'Job-UHENIY.NET Core 3.1medium_grayscale_uncompressed.tiff569.6 ms17.44 ms4.53 ms1.000.00---176 B
'ImageSharp Tiff'Job-UHENIY.NET Core 3.1medium_grayscale_uncompressed.tiff655.2 ms17.48 ms4.54 ms1.150.01---235965488 B
'System.Drawing Tiff'Job-KSIANY.NET 4.7.2medium_palette_uncompressed.tiff578.0 ms22.32 ms5.80 ms1.000.0018000.0000--90301696 B
'ImageSharp Tiff'Job-KSIANY.NET 4.7.2medium_palette_uncompressed.tiff3,336.9 ms21.42 ms5.56 ms5.770.07---236003608 B
'System.Drawing Tiff'Job-VMCLSF.NET Core 2.1medium_palette_uncompressed.tiff601.9 ms40.85 ms6.32 ms1.000.0018000.0000--90107368 B
'ImageSharp Tiff'Job-VMCLSF.NET Core 2.1medium_palette_uncompressed.tiff1,971.9 ms15.69 ms4.07 ms3.280.04---235996096 B
'System.Drawing Tiff'Job-UHENIY.NET Core 3.1medium_palette_uncompressed.tiff566.1 ms28.06 ms4.34 ms1.000.00---176 B
'ImageSharp Tiff'Job-UHENIY.NET Core 3.1medium_palette_uncompressed.tiff1,664.1 ms11.59 ms1.79 ms2.940.02---235996208 B
'System.Drawing Tiff'Job-KSIANY.NET 4.7.2medium_rgb_deflate.tiff357.4 ms15.54 ms2.40 ms1.000.003000.0000--9662560 B
'ImageSharp Tiff'Job-KSIANY.NET 4.7.2medium_rgb_deflate.tiff776.1 ms14.51 ms3.77 ms2.170.0122000.00001000.00001000.0000303476856 B
'System.Drawing Tiff'Job-VMCLSF.NET Core 2.1medium_rgb_deflate.tiff359.7 ms12.29 ms3.19 ms1.000.003000.0000--9629400 B
'ImageSharp Tiff'Job-VMCLSF.NET Core 2.1medium_rgb_deflate.tiff554.5 ms16.78 ms4.36 ms1.540.022000.00001000.00001000.0000239716144 B
'System.Drawing Tiff'Job-UHENIY.NET Core 3.1medium_rgb_deflate.tiff353.2 ms7.22 ms1.12 ms1.000.00---176 B
'ImageSharp Tiff'Job-UHENIY.NET Core 3.1medium_rgb_deflate.tiff557.1 ms10.79 ms2.80 ms1.580.002000.00001000.00001000.0000239470552 B
'System.Drawing Tiff'Job-KSIANY.NET 4.7.2medium_rgb_lzw.tiff511.0 ms6.43 ms1.67 ms1.000.003000.0000--11600840 B
'ImageSharp Tiff'Job-KSIANY.NET 4.7.2medium_rgb_lzw.tiff2,691.6 ms16.81 ms2.60 ms5.270.02---236044312 B
'System.Drawing Tiff'Job-VMCLSF.NET Core 2.1medium_rgb_lzw.tiff511.4 ms11.44 ms1.77 ms1.000.003000.0000--11569776 B
'ImageSharp Tiff'Job-VMCLSF.NET Core 2.1medium_rgb_lzw.tiff1,654.1 ms12.42 ms1.92 ms3.230.01---236041592 B
'System.Drawing Tiff'Job-UHENIY.NET Core 3.1medium_rgb_lzw.tiff507.7 ms8.89 ms2.31 ms1.000.00---176 B
'ImageSharp Tiff'Job-UHENIY.NET Core 3.1medium_rgb_lzw.tiff1,689.5 ms40.41 ms6.25 ms3.330.03---236041656 B
'System.Drawing Tiff'Job-KSIANY.NET 4.7.2medium_rgb_packbits.tiff776.8 ms31.69 ms8.23 ms1.000.0056000.0000--304057016 B
'ImageSharp Tiff'Job-KSIANY.NET 4.7.2medium_rgb_packbits.tiff531.2 ms23.17 ms6.02 ms0.680.01---236003352 B
'System.Drawing Tiff'Job-VMCLSF.NET Core 2.1medium_rgb_packbits.tiff764.2 ms41.43 ms6.41 ms1.000.0056000.0000--303861120 B
'ImageSharp Tiff'Job-VMCLSF.NET Core 2.1medium_rgb_packbits.tiff300.0 ms4.39 ms0.68 ms0.390.00---235998408 B
'System.Drawing Tiff'Job-UHENIY.NET Core 3.1medium_rgb_packbits.tiff659.1 ms34.59 ms8.98 ms1.000.00---176 B
'ImageSharp Tiff'Job-UHENIY.NET Core 3.1medium_rgb_packbits.tiff297.5 ms21.13 ms5.49 ms0.450.00---235998520 B
'System.Drawing Tiff'Job-KSIANY.NET 4.7.2medium_rgb_uncompressed.tiff742.5 ms50.45 ms13.10 ms1.000.0055000.0000--302644272 B
'ImageSharp Tiff'Job-KSIANY.NET 4.7.2medium_rgb_uncompressed.tiff414.3 ms15.37 ms3.99 ms0.560.01---235986968 B
'System.Drawing Tiff'Job-VMCLSF.NET Core 2.1medium_rgb_uncompressed.tiff750.2 ms74.13 ms19.25 ms1.000.0055000.0000--302448096 B
'ImageSharp Tiff'Job-VMCLSF.NET Core 2.1medium_rgb_uncompressed.tiff283.6 ms21.56 ms5.60 ms0.380.01---235981128 B
'System.Drawing Tiff'Job-UHENIY.NET Core 3.1medium_rgb_uncompressed.tiff662.6 ms49.79 ms12.93 ms1.000.00---176 B
'ImageSharp Tiff'Job-UHENIY.NET Core 3.1medium_rgb_uncompressed.tiff278.6 ms9.48 ms2.46 ms0.420.01---235981352 B
- - diff --git a/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.EncodeTiff-report-default.md b/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.EncodeTiff-report-default.md deleted file mode 100644 index 886e2bb3e..000000000 --- a/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.EncodeTiff-report-default.md +++ /dev/null @@ -1,74 +0,0 @@ - -BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19042 -Intel Core i7-3610QM CPU 2.30GHz (Ivy Bridge), 1 CPU, 8 logical and 4 physical cores -.NET Core SDK=5.0.101 - [Host] : .NET Core 5.0.1 (CoreCLR 5.0.120.57516, CoreFX 5.0.120.57516), X64 RyuJIT - Job-KBSVFT : .NET Framework 4.8 (4.8.4300.0), X64 RyuJIT - Job-SLIUCH : .NET Core 2.1.23 (CoreCLR 4.6.29321.03, CoreFX 4.6.29321.01), X64 RyuJIT - Job-EFFLUU : .NET Core 3.1.10 (CoreCLR 4.700.20.51601, CoreFX 4.700.20.51901), X64 RyuJIT - -IterationCount=3 LaunchCount=1 WarmupCount=3 - - Method | Job | Runtime | TestImage | Compression | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | ----------------------- |----------- |-------------- |-------------------------------------- |---------------- |-----------:|------------:|----------:|------:|--------:|----------:|----------:|---------:|-----------:| - **'System.Drawing Tiff'** | **Job-KBSVFT** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_uncompressed.tiff** | **None** | **6.614 ms** | **0.2900 ms** | **0.0159 ms** | **1.00** | **0.00** | **984.3750** | **984.3750** | **984.3750** | **11570062 B** | - 'ImageSharp Tiff' | Job-KBSVFT | .NET 4.7.2 | Tiff/Calliphora_rgb_uncompressed.tiff | None | 4.844 ms | 0.8879 ms | 0.0487 ms | 0.73 | 0.01 | 375.0000 | 335.9375 | 335.9375 | 7445922 B | - | | | | | | | | | | | | | | - 'System.Drawing Tiff' | Job-SLIUCH | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | None | 6.953 ms | 0.2917 ms | 0.0160 ms | 1.00 | 0.00 | 984.3750 | 984.3750 | 984.3750 | 11562768 B | - 'ImageSharp Tiff' | Job-SLIUCH | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | None | 3.189 ms | 15.5206 ms | 0.8507 ms | 0.46 | 0.12 | 925.7813 | 886.7188 | 882.8125 | 7444718 B | - | | | | | | | | | | | | | | - 'System.Drawing Tiff' | Job-EFFLUU | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | None | 5.884 ms | 0.7275 ms | 0.0399 ms | 1.00 | 0.00 | 984.3750 | 984.3750 | 984.3750 | 8672224 B | - 'ImageSharp Tiff' | Job-EFFLUU | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | None | 3.342 ms | 18.8082 ms | 1.0309 ms | 0.57 | 0.18 | 796.8750 | 765.6250 | 757.8125 | 7444631 B | - | | | | | | | | | | | | | | - **'System.Drawing Tiff'** | **Job-KBSVFT** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_uncompressed.tiff** | **Deflate** | **NA** | **NA** | **NA** | **?** | **?** | **-** | **-** | **-** | **-** | - 'ImageSharp Tiff' | Job-KBSVFT | .NET 4.7.2 | Tiff/Calliphora_rgb_uncompressed.tiff | Deflate | 87.815 ms | 11.2070 ms | 0.6143 ms | ? | ? | 833.3333 | 333.3333 | 333.3333 | 6617521 B | - | | | | | | | | | | | | | | - 'System.Drawing Tiff' | Job-SLIUCH | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | Deflate | NA | NA | NA | ? | ? | - | - | - | - | - 'ImageSharp Tiff' | Job-SLIUCH | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | Deflate | 84.005 ms | 3.1221 ms | 0.1711 ms | ? | ? | 1000.0000 | 500.0000 | 500.0000 | 6605507 B | - | | | | | | | | | | | | | | - 'System.Drawing Tiff' | Job-EFFLUU | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | Deflate | NA | NA | NA | ? | ? | - | - | - | - | - 'ImageSharp Tiff' | Job-EFFLUU | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | Deflate | 81.102 ms | 6.5299 ms | 0.3579 ms | ? | ? | 1000.0000 | 428.5714 | 428.5714 | 6604792 B | - | | | | | | | | | | | | | | - **'System.Drawing Tiff'** | **Job-KBSVFT** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_uncompressed.tiff** | **Lzw** | **47.121 ms** | **7.2057 ms** | **0.3950 ms** | **1.00** | **0.00** | **818.1818** | **818.1818** | **818.1818** | **10673499 B** | - 'ImageSharp Tiff' | Job-KBSVFT | .NET 4.7.2 | Tiff/Calliphora_rgb_uncompressed.tiff | Lzw | 125.569 ms | 5.9762 ms | 0.3276 ms | 2.66 | 0.03 | 500.0000 | 500.0000 | 500.0000 | 8423760 B | - | | | | | | | | | | | | | | - 'System.Drawing Tiff' | Job-SLIUCH | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | Lzw | 47.311 ms | 4.2582 ms | 0.2334 ms | 1.00 | 0.00 | 818.1818 | 818.1818 | 818.1818 | 10668688 B | - 'ImageSharp Tiff' | Job-SLIUCH | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | Lzw | 96.217 ms | 10.7439 ms | 0.5889 ms | 2.03 | 0.02 | 333.3333 | 333.3333 | 333.3333 | 8422488 B | - | | | | | | | | | | | | | | - 'System.Drawing Tiff' | Job-EFFLUU | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | Lzw | 46.347 ms | 3.7463 ms | 0.2053 ms | 1.00 | 0.00 | 818.1818 | 818.1818 | 818.1818 | 8001750 B | - 'ImageSharp Tiff' | Job-EFFLUU | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | Lzw | 93.635 ms | 11.9328 ms | 0.6541 ms | 2.02 | 0.01 | 333.3333 | 333.3333 | 333.3333 | 8422504 B | - | | | | | | | | | | | | | | - **'System.Drawing Tiff'** | **Job-KBSVFT** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_uncompressed.tiff** | **PackBits** | **NA** | **NA** | **NA** | **?** | **?** | **-** | **-** | **-** | **-** | - 'ImageSharp Tiff' | Job-KBSVFT | .NET 4.7.2 | Tiff/Calliphora_rgb_uncompressed.tiff | PackBits | 27.449 ms | 2.1924 ms | 0.1202 ms | ? | ? | 375.0000 | 343.7500 | 343.7500 | 7453052 B | - | | | | | | | | | | | | | | - 'System.Drawing Tiff' | Job-SLIUCH | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | PackBits | NA | NA | NA | ? | ? | - | - | - | - | - 'ImageSharp Tiff' | Job-SLIUCH | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | PackBits | 19.935 ms | 1.6746 ms | 0.0918 ms | ? | ? | 375.0000 | 343.7500 | 343.7500 | 7451912 B | - | | | | | | | | | | | | | | - 'System.Drawing Tiff' | Job-EFFLUU | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | PackBits | NA | NA | NA | ? | ? | - | - | - | - | - 'ImageSharp Tiff' | Job-EFFLUU | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | PackBits | 19.664 ms | 9.2973 ms | 0.5096 ms | ? | ? | 375.0000 | 343.7500 | 343.7500 | 7451974 B | - | | | | | | | | | | | | | | - **'System.Drawing Tiff'** | **Job-KBSVFT** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_uncompressed.tiff** | **CcittGroup3Fax** | **43.335 ms** | **2.7418 ms** | **0.1503 ms** | **1.00** | **0.00** | **-** | **-** | **-** | **1169683 B** | - 'ImageSharp Tiff' | Job-KBSVFT | .NET 4.7.2 | Tiff/Calliphora_rgb_uncompressed.tiff | CcittGroup3Fax | 191.413 ms | 55.3579 ms | 3.0344 ms | 4.42 | 0.07 | 3333.3333 | 1333.3333 | 333.3333 | 22714336 B | - | | | | | | | | | | | | | | - 'System.Drawing Tiff' | Job-SLIUCH | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | CcittGroup3Fax | 43.559 ms | 4.3644 ms | 0.2392 ms | 1.00 | 0.00 | - | - | - | 1169200 B | - 'ImageSharp Tiff' | Job-SLIUCH | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | CcittGroup3Fax | 180.059 ms | 38.0202 ms | 2.0840 ms | 4.13 | 0.03 | 3666.6667 | 2000.0000 | 666.6667 | 22658509 B | - | | | | | | | | | | | | | | - 'System.Drawing Tiff' | Job-EFFLUU | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | CcittGroup3Fax | 43.437 ms | 3.9436 ms | 0.2162 ms | 1.00 | 0.00 | - | - | - | 850187 B | - 'ImageSharp Tiff' | Job-EFFLUU | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | CcittGroup3Fax | 171.370 ms | 129.4719 ms | 7.0968 ms | 3.94 | 0.14 | 3333.3333 | 1333.3333 | 333.3333 | 22658261 B | - | | | | | | | | | | | | | | - **'System.Drawing Tiff'** | **Job-KBSVFT** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_uncompressed.tiff** | **ModifiedHuffman** | **17.099 ms** | **9.2464 ms** | **0.5068 ms** | **1.00** | **0.00** | **937.5000** | **937.5000** | **937.5000** | **11561706 B** | - 'ImageSharp Tiff' | Job-KBSVFT | .NET 4.7.2 | Tiff/Calliphora_rgb_uncompressed.tiff | ModifiedHuffman | 191.066 ms | 16.8580 ms | 0.9240 ms | 11.18 | 0.36 | 3333.3333 | 1333.3333 | 333.3333 | 22710384 B | - | | | | | | | | | | | | | | - 'System.Drawing Tiff' | Job-SLIUCH | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | ModifiedHuffman | 17.035 ms | 1.8390 ms | 0.1008 ms | 1.00 | 0.00 | 937.5000 | 937.5000 | 937.5000 | 11555088 B | - 'ImageSharp Tiff' | Job-SLIUCH | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | ModifiedHuffman | 177.379 ms | 33.9255 ms | 1.8596 ms | 10.41 | 0.06 | 3666.6667 | 2000.0000 | 666.6667 | 22656395 B | - | | | | | | | | | | | | | | - 'System.Drawing Tiff' | Job-EFFLUU | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | ModifiedHuffman | 15.948 ms | 3.3609 ms | 0.1842 ms | 1.00 | 0.00 | 937.5000 | 937.5000 | 937.5000 | 8666468 B | - 'ImageSharp Tiff' | Job-EFFLUU | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | ModifiedHuffman | 167.231 ms | 21.2228 ms | 1.1633 ms | 10.49 | 0.09 | 3333.3333 | 1333.3333 | 333.3333 | 22659275 B | - -Benchmarks with issues: - EncodeTiff.'System.Drawing Tiff': Job-KBSVFT(Runtime=.NET 4.7.2, IterationCount=3, LaunchCount=1, WarmupCount=3) [TestImage=Tiff/Calliphora_rgb_uncompressed.tiff, Compression=Deflate] - EncodeTiff.'System.Drawing Tiff': Job-SLIUCH(Runtime=.NET Core 2.1, IterationCount=3, LaunchCount=1, WarmupCount=3) [TestImage=Tiff/Calliphora_rgb_uncompressed.tiff, Compression=Deflate] - EncodeTiff.'System.Drawing Tiff': Job-EFFLUU(Runtime=.NET Core 3.1, IterationCount=3, LaunchCount=1, WarmupCount=3) [TestImage=Tiff/Calliphora_rgb_uncompressed.tiff, Compression=Deflate] - EncodeTiff.'System.Drawing Tiff': Job-KBSVFT(Runtime=.NET 4.7.2, IterationCount=3, LaunchCount=1, WarmupCount=3) [TestImage=Tiff/Calliphora_rgb_uncompressed.tiff, Compression=PackBits] - EncodeTiff.'System.Drawing Tiff': Job-SLIUCH(Runtime=.NET Core 2.1, IterationCount=3, LaunchCount=1, WarmupCount=3) [TestImage=Tiff/Calliphora_rgb_uncompressed.tiff, Compression=PackBits] - EncodeTiff.'System.Drawing Tiff': Job-EFFLUU(Runtime=.NET Core 3.1, IterationCount=3, LaunchCount=1, WarmupCount=3) [TestImage=Tiff/Calliphora_rgb_uncompressed.tiff, Compression=PackBits] diff --git a/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.EncodeTiff-report-github.md b/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.EncodeTiff-report-github.md deleted file mode 100644 index ac9aebf61..000000000 --- a/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.EncodeTiff-report-github.md +++ /dev/null @@ -1,76 +0,0 @@ -``` ini - -BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19042 -Intel Core i7-3610QM CPU 2.30GHz (Ivy Bridge), 1 CPU, 8 logical and 4 physical cores -.NET Core SDK=5.0.101 - [Host] : .NET Core 5.0.1 (CoreCLR 5.0.120.57516, CoreFX 5.0.120.57516), X64 RyuJIT - Job-KBSVFT : .NET Framework 4.8 (4.8.4300.0), X64 RyuJIT - Job-SLIUCH : .NET Core 2.1.23 (CoreCLR 4.6.29321.03, CoreFX 4.6.29321.01), X64 RyuJIT - Job-EFFLUU : .NET Core 3.1.10 (CoreCLR 4.700.20.51601, CoreFX 4.700.20.51901), X64 RyuJIT - -IterationCount=3 LaunchCount=1 WarmupCount=3 - -``` -| Method | Job | Runtime | TestImage | Compression | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | -|---------------------- |----------- |-------------- |-------------------------------------- |---------------- |-----------:|------------:|----------:|------:|--------:|----------:|----------:|---------:|-----------:| -| **'System.Drawing Tiff'** | **Job-KBSVFT** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_uncompressed.tiff** | **None** | **6.614 ms** | **0.2900 ms** | **0.0159 ms** | **1.00** | **0.00** | **984.3750** | **984.3750** | **984.3750** | **11570062 B** | -| 'ImageSharp Tiff' | Job-KBSVFT | .NET 4.7.2 | Tiff/Calliphora_rgb_uncompressed.tiff | None | 4.844 ms | 0.8879 ms | 0.0487 ms | 0.73 | 0.01 | 375.0000 | 335.9375 | 335.9375 | 7445922 B | -| | | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-SLIUCH | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | None | 6.953 ms | 0.2917 ms | 0.0160 ms | 1.00 | 0.00 | 984.3750 | 984.3750 | 984.3750 | 11562768 B | -| 'ImageSharp Tiff' | Job-SLIUCH | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | None | 3.189 ms | 15.5206 ms | 0.8507 ms | 0.46 | 0.12 | 925.7813 | 886.7188 | 882.8125 | 7444718 B | -| | | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-EFFLUU | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | None | 5.884 ms | 0.7275 ms | 0.0399 ms | 1.00 | 0.00 | 984.3750 | 984.3750 | 984.3750 | 8672224 B | -| 'ImageSharp Tiff' | Job-EFFLUU | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | None | 3.342 ms | 18.8082 ms | 1.0309 ms | 0.57 | 0.18 | 796.8750 | 765.6250 | 757.8125 | 7444631 B | -| | | | | | | | | | | | | | | -| **'System.Drawing Tiff'** | **Job-KBSVFT** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_uncompressed.tiff** | **Deflate** | **NA** | **NA** | **NA** | **?** | **?** | **-** | **-** | **-** | **-** | -| 'ImageSharp Tiff' | Job-KBSVFT | .NET 4.7.2 | Tiff/Calliphora_rgb_uncompressed.tiff | Deflate | 87.815 ms | 11.2070 ms | 0.6143 ms | ? | ? | 833.3333 | 333.3333 | 333.3333 | 6617521 B | -| | | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-SLIUCH | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | Deflate | NA | NA | NA | ? | ? | - | - | - | - | -| 'ImageSharp Tiff' | Job-SLIUCH | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | Deflate | 84.005 ms | 3.1221 ms | 0.1711 ms | ? | ? | 1000.0000 | 500.0000 | 500.0000 | 6605507 B | -| | | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-EFFLUU | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | Deflate | NA | NA | NA | ? | ? | - | - | - | - | -| 'ImageSharp Tiff' | Job-EFFLUU | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | Deflate | 81.102 ms | 6.5299 ms | 0.3579 ms | ? | ? | 1000.0000 | 428.5714 | 428.5714 | 6604792 B | -| | | | | | | | | | | | | | | -| **'System.Drawing Tiff'** | **Job-KBSVFT** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_uncompressed.tiff** | **Lzw** | **47.121 ms** | **7.2057 ms** | **0.3950 ms** | **1.00** | **0.00** | **818.1818** | **818.1818** | **818.1818** | **10673499 B** | -| 'ImageSharp Tiff' | Job-KBSVFT | .NET 4.7.2 | Tiff/Calliphora_rgb_uncompressed.tiff | Lzw | 125.569 ms | 5.9762 ms | 0.3276 ms | 2.66 | 0.03 | 500.0000 | 500.0000 | 500.0000 | 8423760 B | -| | | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-SLIUCH | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | Lzw | 47.311 ms | 4.2582 ms | 0.2334 ms | 1.00 | 0.00 | 818.1818 | 818.1818 | 818.1818 | 10668688 B | -| 'ImageSharp Tiff' | Job-SLIUCH | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | Lzw | 96.217 ms | 10.7439 ms | 0.5889 ms | 2.03 | 0.02 | 333.3333 | 333.3333 | 333.3333 | 8422488 B | -| | | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-EFFLUU | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | Lzw | 46.347 ms | 3.7463 ms | 0.2053 ms | 1.00 | 0.00 | 818.1818 | 818.1818 | 818.1818 | 8001750 B | -| 'ImageSharp Tiff' | Job-EFFLUU | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | Lzw | 93.635 ms | 11.9328 ms | 0.6541 ms | 2.02 | 0.01 | 333.3333 | 333.3333 | 333.3333 | 8422504 B | -| | | | | | | | | | | | | | | -| **'System.Drawing Tiff'** | **Job-KBSVFT** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_uncompressed.tiff** | **PackBits** | **NA** | **NA** | **NA** | **?** | **?** | **-** | **-** | **-** | **-** | -| 'ImageSharp Tiff' | Job-KBSVFT | .NET 4.7.2 | Tiff/Calliphora_rgb_uncompressed.tiff | PackBits | 27.449 ms | 2.1924 ms | 0.1202 ms | ? | ? | 375.0000 | 343.7500 | 343.7500 | 7453052 B | -| | | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-SLIUCH | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | PackBits | NA | NA | NA | ? | ? | - | - | - | - | -| 'ImageSharp Tiff' | Job-SLIUCH | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | PackBits | 19.935 ms | 1.6746 ms | 0.0918 ms | ? | ? | 375.0000 | 343.7500 | 343.7500 | 7451912 B | -| | | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-EFFLUU | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | PackBits | NA | NA | NA | ? | ? | - | - | - | - | -| 'ImageSharp Tiff' | Job-EFFLUU | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | PackBits | 19.664 ms | 9.2973 ms | 0.5096 ms | ? | ? | 375.0000 | 343.7500 | 343.7500 | 7451974 B | -| | | | | | | | | | | | | | | -| **'System.Drawing Tiff'** | **Job-KBSVFT** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_uncompressed.tiff** | **CcittGroup3Fax** | **43.335 ms** | **2.7418 ms** | **0.1503 ms** | **1.00** | **0.00** | **-** | **-** | **-** | **1169683 B** | -| 'ImageSharp Tiff' | Job-KBSVFT | .NET 4.7.2 | Tiff/Calliphora_rgb_uncompressed.tiff | CcittGroup3Fax | 191.413 ms | 55.3579 ms | 3.0344 ms | 4.42 | 0.07 | 3333.3333 | 1333.3333 | 333.3333 | 22714336 B | -| | | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-SLIUCH | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | CcittGroup3Fax | 43.559 ms | 4.3644 ms | 0.2392 ms | 1.00 | 0.00 | - | - | - | 1169200 B | -| 'ImageSharp Tiff' | Job-SLIUCH | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | CcittGroup3Fax | 180.059 ms | 38.0202 ms | 2.0840 ms | 4.13 | 0.03 | 3666.6667 | 2000.0000 | 666.6667 | 22658509 B | -| | | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-EFFLUU | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | CcittGroup3Fax | 43.437 ms | 3.9436 ms | 0.2162 ms | 1.00 | 0.00 | - | - | - | 850187 B | -| 'ImageSharp Tiff' | Job-EFFLUU | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | CcittGroup3Fax | 171.370 ms | 129.4719 ms | 7.0968 ms | 3.94 | 0.14 | 3333.3333 | 1333.3333 | 333.3333 | 22658261 B | -| | | | | | | | | | | | | | | -| **'System.Drawing Tiff'** | **Job-KBSVFT** | **.NET 4.7.2** | **Tiff/Calliphora_rgb_uncompressed.tiff** | **ModifiedHuffman** | **17.099 ms** | **9.2464 ms** | **0.5068 ms** | **1.00** | **0.00** | **937.5000** | **937.5000** | **937.5000** | **11561706 B** | -| 'ImageSharp Tiff' | Job-KBSVFT | .NET 4.7.2 | Tiff/Calliphora_rgb_uncompressed.tiff | ModifiedHuffman | 191.066 ms | 16.8580 ms | 0.9240 ms | 11.18 | 0.36 | 3333.3333 | 1333.3333 | 333.3333 | 22710384 B | -| | | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-SLIUCH | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | ModifiedHuffman | 17.035 ms | 1.8390 ms | 0.1008 ms | 1.00 | 0.00 | 937.5000 | 937.5000 | 937.5000 | 11555088 B | -| 'ImageSharp Tiff' | Job-SLIUCH | .NET Core 2.1 | Tiff/Calliphora_rgb_uncompressed.tiff | ModifiedHuffman | 177.379 ms | 33.9255 ms | 1.8596 ms | 10.41 | 0.06 | 3666.6667 | 2000.0000 | 666.6667 | 22656395 B | -| | | | | | | | | | | | | | | -| 'System.Drawing Tiff' | Job-EFFLUU | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | ModifiedHuffman | 15.948 ms | 3.3609 ms | 0.1842 ms | 1.00 | 0.00 | 937.5000 | 937.5000 | 937.5000 | 8666468 B | -| 'ImageSharp Tiff' | Job-EFFLUU | .NET Core 3.1 | Tiff/Calliphora_rgb_uncompressed.tiff | ModifiedHuffman | 167.231 ms | 21.2228 ms | 1.1633 ms | 10.49 | 0.09 | 3333.3333 | 1333.3333 | 333.3333 | 22659275 B | - -Benchmarks with issues: - EncodeTiff.'System.Drawing Tiff': Job-KBSVFT(Runtime=.NET 4.7.2, IterationCount=3, LaunchCount=1, WarmupCount=3) [TestImage=Tiff/Calliphora_rgb_uncompressed.tiff, Compression=Deflate] - EncodeTiff.'System.Drawing Tiff': Job-SLIUCH(Runtime=.NET Core 2.1, IterationCount=3, LaunchCount=1, WarmupCount=3) [TestImage=Tiff/Calliphora_rgb_uncompressed.tiff, Compression=Deflate] - EncodeTiff.'System.Drawing Tiff': Job-EFFLUU(Runtime=.NET Core 3.1, IterationCount=3, LaunchCount=1, WarmupCount=3) [TestImage=Tiff/Calliphora_rgb_uncompressed.tiff, Compression=Deflate] - EncodeTiff.'System.Drawing Tiff': Job-KBSVFT(Runtime=.NET 4.7.2, IterationCount=3, LaunchCount=1, WarmupCount=3) [TestImage=Tiff/Calliphora_rgb_uncompressed.tiff, Compression=PackBits] - EncodeTiff.'System.Drawing Tiff': Job-SLIUCH(Runtime=.NET Core 2.1, IterationCount=3, LaunchCount=1, WarmupCount=3) [TestImage=Tiff/Calliphora_rgb_uncompressed.tiff, Compression=PackBits] - EncodeTiff.'System.Drawing Tiff': Job-EFFLUU(Runtime=.NET Core 3.1, IterationCount=3, LaunchCount=1, WarmupCount=3) [TestImage=Tiff/Calliphora_rgb_uncompressed.tiff, Compression=PackBits] diff --git a/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.EncodeTiff-report.html b/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.EncodeTiff-report.html deleted file mode 100644 index 660964955..000000000 --- a/tests/Images/Input/Tiff/Benchmarks/SixLabors.ImageSharp.Benchmarks.Codecs.EncodeTiff-report.html +++ /dev/null @@ -1,68 +0,0 @@ - - - - -SixLabors.ImageSharp.Benchmarks.Codecs.EncodeTiff-20210207-120859 - - - - -

-BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19042
-Intel Core i7-3610QM CPU 2.30GHz (Ivy Bridge), 1 CPU, 8 logical and 4 physical cores
-.NET Core SDK=5.0.101
-  [Host]     : .NET Core 5.0.1 (CoreCLR 5.0.120.57516, CoreFX 5.0.120.57516), X64 RyuJIT
-  Job-KBSVFT : .NET Framework 4.8 (4.8.4300.0), X64 RyuJIT
-  Job-SLIUCH : .NET Core 2.1.23 (CoreCLR 4.6.29321.03, CoreFX 4.6.29321.01), X64 RyuJIT
-  Job-EFFLUU : .NET Core 3.1.10 (CoreCLR 4.700.20.51601, CoreFX 4.700.20.51901), X64 RyuJIT
-
-
IterationCount=3  LaunchCount=1  WarmupCount=3  
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Method JobRuntime TestImageCompressionMeanErrorStdDevRatioRatioSDGen 0Gen 1Gen 2Allocated
'System.Drawing Tiff'Job-KBSVFT.NET 4.7.2Tiff/Calliphora_rgb_uncompressed.tiffNone6.614 ms0.2900 ms0.0159 ms1.000.00984.3750984.3750984.375011570062 B
'ImageSharp Tiff'Job-KBSVFT.NET 4.7.2Tiff/Calliphora_rgb_uncompressed.tiffNone4.844 ms0.8879 ms0.0487 ms0.730.01375.0000335.9375335.93757445922 B
'System.Drawing Tiff'Job-SLIUCH.NET Core 2.1Tiff/Calliphora_rgb_uncompressed.tiffNone6.953 ms0.2917 ms0.0160 ms1.000.00984.3750984.3750984.375011562768 B
'ImageSharp Tiff'Job-SLIUCH.NET Core 2.1Tiff/Calliphora_rgb_uncompressed.tiffNone3.189 ms15.5206 ms0.8507 ms0.460.12925.7813886.7188882.81257444718 B
'System.Drawing Tiff'Job-EFFLUU.NET Core 3.1Tiff/Calliphora_rgb_uncompressed.tiffNone5.884 ms0.7275 ms0.0399 ms1.000.00984.3750984.3750984.37508672224 B
'ImageSharp Tiff'Job-EFFLUU.NET Core 3.1Tiff/Calliphora_rgb_uncompressed.tiffNone3.342 ms18.8082 ms1.0309 ms0.570.18796.8750765.6250757.81257444631 B
'System.Drawing Tiff'Job-KBSVFT.NET 4.7.2Tiff/Calliphora_rgb_uncompressed.tiffDeflateNANANA??----
'ImageSharp Tiff'Job-KBSVFT.NET 4.7.2Tiff/Calliphora_rgb_uncompressed.tiffDeflate87.815 ms11.2070 ms0.6143 ms??833.3333333.3333333.33336617521 B
'System.Drawing Tiff'Job-SLIUCH.NET Core 2.1Tiff/Calliphora_rgb_uncompressed.tiffDeflateNANANA??----
'ImageSharp Tiff'Job-SLIUCH.NET Core 2.1Tiff/Calliphora_rgb_uncompressed.tiffDeflate84.005 ms3.1221 ms0.1711 ms??1000.0000500.0000500.00006605507 B
'System.Drawing Tiff'Job-EFFLUU.NET Core 3.1Tiff/Calliphora_rgb_uncompressed.tiffDeflateNANANA??----
'ImageSharp Tiff'Job-EFFLUU.NET Core 3.1Tiff/Calliphora_rgb_uncompressed.tiffDeflate81.102 ms6.5299 ms0.3579 ms??1000.0000428.5714428.57146604792 B
'System.Drawing Tiff'Job-KBSVFT.NET 4.7.2Tiff/Calliphora_rgb_uncompressed.tiffLzw47.121 ms7.2057 ms0.3950 ms1.000.00818.1818818.1818818.181810673499 B
'ImageSharp Tiff'Job-KBSVFT.NET 4.7.2Tiff/Calliphora_rgb_uncompressed.tiffLzw125.569 ms5.9762 ms0.3276 ms2.660.03500.0000500.0000500.00008423760 B
'System.Drawing Tiff'Job-SLIUCH.NET Core 2.1Tiff/Calliphora_rgb_uncompressed.tiffLzw47.311 ms4.2582 ms0.2334 ms1.000.00818.1818818.1818818.181810668688 B
'ImageSharp Tiff'Job-SLIUCH.NET Core 2.1Tiff/Calliphora_rgb_uncompressed.tiffLzw96.217 ms10.7439 ms0.5889 ms2.030.02333.3333333.3333333.33338422488 B
'System.Drawing Tiff'Job-EFFLUU.NET Core 3.1Tiff/Calliphora_rgb_uncompressed.tiffLzw46.347 ms3.7463 ms0.2053 ms1.000.00818.1818818.1818818.18188001750 B
'ImageSharp Tiff'Job-EFFLUU.NET Core 3.1Tiff/Calliphora_rgb_uncompressed.tiffLzw93.635 ms11.9328 ms0.6541 ms2.020.01333.3333333.3333333.33338422504 B
'System.Drawing Tiff'Job-KBSVFT.NET 4.7.2Tiff/Calliphora_rgb_uncompressed.tiffPackBitsNANANA??----
'ImageSharp Tiff'Job-KBSVFT.NET 4.7.2Tiff/Calliphora_rgb_uncompressed.tiffPackBits27.449 ms2.1924 ms0.1202 ms??375.0000343.7500343.75007453052 B
'System.Drawing Tiff'Job-SLIUCH.NET Core 2.1Tiff/Calliphora_rgb_uncompressed.tiffPackBitsNANANA??----
'ImageSharp Tiff'Job-SLIUCH.NET Core 2.1Tiff/Calliphora_rgb_uncompressed.tiffPackBits19.935 ms1.6746 ms0.0918 ms??375.0000343.7500343.75007451912 B
'System.Drawing Tiff'Job-EFFLUU.NET Core 3.1Tiff/Calliphora_rgb_uncompressed.tiffPackBitsNANANA??----
'ImageSharp Tiff'Job-EFFLUU.NET Core 3.1Tiff/Calliphora_rgb_uncompressed.tiffPackBits19.664 ms9.2973 ms0.5096 ms??375.0000343.7500343.75007451974 B
'System.Drawing Tiff'Job-KBSVFT.NET 4.7.2Tiff/Calliphora_rgb_uncompressed.tiffCcittGroup3Fax43.335 ms2.7418 ms0.1503 ms1.000.00---1169683 B
'ImageSharp Tiff'Job-KBSVFT.NET 4.7.2Tiff/Calliphora_rgb_uncompressed.tiffCcittGroup3Fax191.413 ms55.3579 ms3.0344 ms4.420.073333.33331333.3333333.333322714336 B
'System.Drawing Tiff'Job-SLIUCH.NET Core 2.1Tiff/Calliphora_rgb_uncompressed.tiffCcittGroup3Fax43.559 ms4.3644 ms0.2392 ms1.000.00---1169200 B
'ImageSharp Tiff'Job-SLIUCH.NET Core 2.1Tiff/Calliphora_rgb_uncompressed.tiffCcittGroup3Fax180.059 ms38.0202 ms2.0840 ms4.130.033666.66672000.0000666.666722658509 B
'System.Drawing Tiff'Job-EFFLUU.NET Core 3.1Tiff/Calliphora_rgb_uncompressed.tiffCcittGroup3Fax43.437 ms3.9436 ms0.2162 ms1.000.00---850187 B
'ImageSharp Tiff'Job-EFFLUU.NET Core 3.1Tiff/Calliphora_rgb_uncompressed.tiffCcittGroup3Fax171.370 ms129.4719 ms7.0968 ms3.940.143333.33331333.3333333.333322658261 B
'System.Drawing Tiff'Job-KBSVFT.NET 4.7.2Tiff/Calliphora_rgb_uncompressed.tiffModifiedHuffman17.099 ms9.2464 ms0.5068 ms1.000.00937.5000937.5000937.500011561706 B
'ImageSharp Tiff'Job-KBSVFT.NET 4.7.2Tiff/Calliphora_rgb_uncompressed.tiffModifiedHuffman191.066 ms16.8580 ms0.9240 ms11.180.363333.33331333.3333333.333322710384 B
'System.Drawing Tiff'Job-SLIUCH.NET Core 2.1Tiff/Calliphora_rgb_uncompressed.tiffModifiedHuffman17.035 ms1.8390 ms0.1008 ms1.000.00937.5000937.5000937.500011555088 B
'ImageSharp Tiff'Job-SLIUCH.NET Core 2.1Tiff/Calliphora_rgb_uncompressed.tiffModifiedHuffman177.379 ms33.9255 ms1.8596 ms10.410.063666.66672000.0000666.666722656395 B
'System.Drawing Tiff'Job-EFFLUU.NET Core 3.1Tiff/Calliphora_rgb_uncompressed.tiffModifiedHuffman15.948 ms3.3609 ms0.1842 ms1.000.00937.5000937.5000937.50008666468 B
'ImageSharp Tiff'Job-EFFLUU.NET Core 3.1Tiff/Calliphora_rgb_uncompressed.tiffModifiedHuffman167.231 ms21.2228 ms1.1633 ms10.490.093333.33331333.3333333.333322659275 B
- - From 729220647ec927e39adb3356fb55bf0460a29bf7 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Mon, 8 Feb 2021 08:41:02 +0300 Subject: [PATCH 196/275] Update readme --- src/ImageSharp/Formats/Tiff/README.md | 14 +++++++------- .../Formats/Tiff/TiffDecoderOptionsParser.cs | 5 +++++ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/README.md b/src/ImageSharp/Formats/Tiff/README.md index 32c2b1d40..565ecb6cd 100644 --- a/src/ImageSharp/Formats/Tiff/README.md +++ b/src/ImageSharp/Formats/Tiff/README.md @@ -79,19 +79,19 @@ |CellWidth | | | | |CellLength | | | | |FillOrder | | - | Ignore. In practice is very uncommon, and is not recommended. | -|ImageDescription | | Y | | -|Make | | Y | | -|Model | | Y | | +|ImageDescription | Y | Y | | +|Make | Y | Y | | +|Model | Y | Y | | |StripOffsets | Y | Y | | |Orientation | | - | Ignore. Many readers ignore this tag. | |SamplesPerPixel | Y | - | Currently ignored, as can be inferred from count of BitsPerSample | -|RowsPerStrip | | Y | | +|RowsPerStrip | Y | Y | | |StripByteCounts | Y | Y | | |MinSampleValue | | | | |MaxSampleValue | | | | |XResolution | Y | Y | | |YResolution | Y | Y | | -|PlanarConfiguration | | Y | | +|PlanarConfiguration | | Y | Encoding support only chunky. | |FreeOffsets | | | | |FreeByteCounts | | | | |GrayResponseUnit | | | | @@ -110,7 +110,7 @@ | |Encoder|Decoder|Comments | |---------------------------|:-----:|:-----:|--------------------------| |NewSubfileType | | | | -|DocumentName | | | | +|DocumentName | Y | Y | | |PageName | | | | |XPosition | | | | |YPosition | | | | @@ -166,7 +166,7 @@ |YCbCrSubSampling | | | | |YCbCrPositioning | | | | |ReferenceBlackWhite | | | | -|StripRowCounts | | - | | +|StripRowCounts | - | - | See RFC 2301 (File Format for Internet Fax). | |XMP | Y | Y | | |ImageID | | | | |ImageLayer | | | | diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs index 2c632b36e..123b8494e 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs @@ -50,6 +50,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff } } + if (entries.ExifProfile.GetValue(ExifTag.StripRowCounts) != null) + { + TiffThrowHelper.ThrowNotSupported("Variable-sized strips are not supported."); + } + options.PlanarConfiguration = entries.PlanarConfiguration; options.Predictor = entries.Predictor; options.PhotometricInterpretation = entries.PhotometricInterpretation; From 7c97634bcbd5812b514e39a325f548a2fc131ebf Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 12 Feb 2021 12:54:16 +0100 Subject: [PATCH 197/275] Change BinaryDither to FloydSteinberg --- src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs index 76d5cbaa0..5db0eff73 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs @@ -3,12 +3,12 @@ using System; using System.Buffers; + using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Dithering; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers { @@ -27,11 +27,13 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers // Convert image to black and white. // TODO: Should we allow to skip this by the user, if its known to be black and white already? this.imageBlackWhite = new Image(configuration, new ImageMetadata(), new[] { image.Clone() }); - this.imageBlackWhite.Mutate(img => img.BinaryDither(default(ErrorDither))); + this.imageBlackWhite.Mutate(img => img.BinaryDither(KnownDitherings.FloydSteinberg)); } + /// public override int BitsPerPixel => 1; + /// protected override void EncodeStrip(int y, int height, TiffBaseCompressor compressor) { if (this.pixelsAsGray == null) @@ -89,6 +91,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers } } + /// protected override void Dispose(bool disposing) { this.imageBlackWhite?.Dispose(); From 3b4bc1de2384239add75ae5ad33b2ee068ba79f1 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sat, 13 Feb 2021 12:47:29 +0300 Subject: [PATCH 198/275] Remove TiffEncoderPixelStorageMethod, add CRC writing for deflate. Correct tests. --- .../Compressors/DeflateCompressor.cs | 2 +- .../Formats/Tiff/ITiffEncoderOptions.cs | 5 - src/ImageSharp/Formats/Tiff/TiffEncoder.cs | 5 +- .../Formats/Tiff/TiffEncoderCore.cs | 35 +++-- .../Tiff/TiffEncoderPixelStorageMethod.cs | 26 ---- .../Formats/Tiff/TiffEncoderTests.cs | 122 ++++++++++-------- 6 files changed, 86 insertions(+), 109 deletions(-) delete mode 100644 src/ImageSharp/Formats/Tiff/TiffEncoderPixelStorageMethod.cs diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/DeflateCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/DeflateCompressor.cs index f4b6c6ad7..64d3b1ea3 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/DeflateCompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/DeflateCompressor.cs @@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors stream.Write(rows); stream.Flush(); - //// stream.Dispose(); // todo: dispose write crc + stream.Dispose(); int size = (int)this.memoryStream.Position; diff --git a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs index 8d0a15ffe..03a8328ec 100644 --- a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs +++ b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs @@ -38,11 +38,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff ///
IQuantizer Quantizer { get; } - /// - /// Gets the pixel storage method. - /// - TiffEncoderPixelStorageMethod PixelStorageMethod { get; } - /// /// Gets the maximum size of strip (bytes). /// diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs index 86091a5c4..5f747a685 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs @@ -33,10 +33,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff public IQuantizer Quantizer { get; set; } /// - public TiffEncoderPixelStorageMethod PixelStorageMethod { get; set; } - - /// - public int MaxStripBytes { get; set; } + public int MaxStripBytes { get; set; } = TiffEncoderCore.DefaultStripSize; /// public void Encode(Image image, Stream stream) diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index f378e2fe8..ec9c761aa 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -25,14 +25,14 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff ///
internal sealed class TiffEncoderCore : IImageEncoderInternals { + public const int DefaultStripSize = 8 * 1024; + public static readonly ByteOrder ByteOrder = BitConverter.IsLittleEndian ? ByteOrder.LittleEndian : ByteOrder.BigEndian; private static readonly ushort ByteOrderMarker = BitConverter.IsLittleEndian ? TiffConstants.ByteOrderLittleEndianShort : TiffConstants.ByteOrderBigEndianShort; - private const int DefaultStripSize = 8 * 1024; - /// /// Used for allocating memory during processing operations. /// @@ -58,8 +58,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff ///
private readonly DeflateCompressionLevel compressionLevel; - private readonly TiffEncoderPixelStorageMethod storageMode; - private readonly int maxStripBytes; /// @@ -75,7 +73,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff this.quantizer = options.Quantizer ?? KnownQuantizers.Octree; this.UseHorizontalPredictor = options.UseHorizontalPredictor; this.compressionLevel = options.CompressionLevel; - this.storageMode = options.PixelStorageMethod; this.maxStripBytes = options.MaxStripBytes; } @@ -182,19 +179,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff private int CalcRowsPerStrip(ImageFrame image, int bytesPerRow) { - switch (this.storageMode) - { - default: - case TiffEncoderPixelStorageMethod.Auto: - case TiffEncoderPixelStorageMethod.MultiStrip: - int sz = this.maxStripBytes > 0 ? this.maxStripBytes : DefaultStripSize; - int height = sz / bytesPerRow; - - return height > 0 ? (height < image.Height ? height : image.Height) : 1; + int sz = this.maxStripBytes > 0 ? this.maxStripBytes : DefaultStripSize; + int height = sz / bytesPerRow; - case TiffEncoderPixelStorageMethod.SingleStrip: - return image.Height; - } + return height > 0 ? (height < image.Height ? height : image.Height) : 1; } /// @@ -258,9 +246,16 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { if (this.CompressionType == TiffEncoderCompression.CcittGroup3Fax || this.CompressionType == TiffEncoderCompression.ModifiedHuffman) { - this.Mode = TiffEncodingMode.BiColor; - this.bitsPerPixel = TiffBitsPerPixel.Pixel1; - return; + if (this.Mode == TiffEncodingMode.Default) + { + this.Mode = TiffEncodingMode.BiColor; + this.bitsPerPixel = TiffBitsPerPixel.Pixel1; + return; + } + else if (this.Mode != TiffEncodingMode.BiColor) + { + TiffThrowHelper.ThrowImageFormatException($"The {this.CompressionType} compression and {this.Mode} aren't compatible. Please use {this.CompressionType} only with {TiffEncodingMode.BiColor} or {TiffEncodingMode.Default} mode."); + } } if (this.Mode == TiffEncodingMode.Default) diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderPixelStorageMethod.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderPixelStorageMethod.cs deleted file mode 100644 index e1e12c08d..000000000 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderPixelStorageMethod.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff -{ - /// - /// The tiff encoder pixel storage method. - /// - public enum TiffEncoderPixelStorageMethod - { - /// - /// The auto mode. - /// - Auto, - - /// - /// The single strip mode. - /// - SingleStrip, - - /// - /// The multi strip mode. - /// - MultiStrip, - } -} diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 774454259..b9368e4ca 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; using System.IO; using SixLabors.ImageSharp.Formats; @@ -21,12 +22,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { private static readonly IImageDecoder ReferenceDecoder = new MagickReferenceDecoder(); - private readonly Configuration configuration; + private static readonly Configuration Configuration; - public TiffEncoderTests() + static TiffEncoderTests() { - this.configuration = new Configuration(); - this.configuration.AddTiff(); + Configuration = new Configuration(); + Configuration.AddTiff(); } [Theory] @@ -62,7 +63,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // assert memStream.Position = 0; - using var output = Image.Load(this.configuration, memStream); + using var output = Image.Load(Configuration, memStream); TiffMetadata meta = output.Metadata.GetTiffMetadata(); Assert.Equal(expectedBitsPerPixel, meta.BitsPerPixel); Assert.Equal(expectedCompression, meta.Compression); @@ -85,7 +86,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // assert memStream.Position = 0; - using var output = Image.Load(this.configuration, memStream); + using var output = Image.Load(Configuration, memStream); TiffMetadata meta = output.Metadata.GetTiffMetadata(); Assert.Equal(expectedBitsPerPixel, meta.BitsPerPixel); } @@ -108,13 +109,30 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // assert memStream.Position = 0; - using var output = Image.Load(this.configuration, memStream); + using var output = Image.Load(Configuration, memStream); TiffMetadata meta = output.Metadata.GetTiffMetadata(); Assert.Equal(TiffBitsPerPixel.Pixel1, meta.BitsPerPixel); Assert.Equal(expectedCompression, meta.Compression); } + [Theory] + [InlineData(TiffEncodingMode.ColorPalette, TiffEncoderCompression.CcittGroup3Fax)] + [InlineData(TiffEncodingMode.ColorPalette, TiffEncoderCompression.ModifiedHuffman)] + [InlineData(TiffEncodingMode.Gray, TiffEncoderCompression.ModifiedHuffman)] + [InlineData(TiffEncodingMode.Rgb, TiffEncoderCompression.ModifiedHuffman)] + public void TiffEncoder_IncompatibilityOptions(TiffEncodingMode mode, TiffEncoderCompression compression) + where TPixel : unmanaged, IPixel + { + // arrange + using var input = new Image(10, 10); + var encoder = new TiffEncoder() { Mode = mode, Compression = compression }; + using var memStream = new MemoryStream(); + + // act + Assert.Throws(() => input.Save(memStream, encoder)); + } + [Theory] [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeRgb_Works(TestImageProvider provider) @@ -207,30 +225,20 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Theory] [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] - public void TiffEncoder_EncodeColorPalette_WithLzwCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel - { - var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Lzw, PixelStorageMethod = TiffEncoderPixelStorageMethod.SingleStrip }; - - this.TiffEncoderPaletteTest(provider, encoder); - } - - [Theory] - [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] - public void TiffEncoder_EncodeColorPalette_WithLzwCompressionAndPredictor_Works(TestImageProvider provider) + public void TiffEncoder_EncodeColorPalette_WithLzwCompression_Works_Bug(TestImageProvider provider) where TPixel : unmanaged, IPixel { - var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Lzw, UseHorizontalPredictor = true, PixelStorageMethod = TiffEncoderPixelStorageMethod.SingleStrip }; + var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Lzw }; this.TiffEncoderPaletteTest(provider, encoder); } [Theory] [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] - public void TiffEncoder_EncodeColorPalette_WithLzwCompressionAndPredictor_Bug(TestImageProvider provider) + public void TiffEncoder_EncodeColorPalette_WithLzwCompressionAndPredictor_Works_Bug(TestImageProvider provider) where TPixel : unmanaged, IPixel { - var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Lzw, PixelStorageMethod = TiffEncoderPixelStorageMethod.MultiStrip, UseHorizontalPredictor = true }; + var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Lzw, UseHorizontalPredictor = true }; Assert.Throws(() => this.TiffEncoderPaletteTest(provider, encoder)); } @@ -257,7 +265,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff image.Save(memStream, encoder); memStream.Position = 0; - using var encodedImage = (Image)Image.Load(this.configuration, memStream); + using var encodedImage = (Image)Image.Load(Configuration, memStream); var encodedImagePath = provider.Utility.SaveTestOutputFile(encodedImage, "tiff", encoder); TiffTestUtils.CompareWithReferenceDecoder(encodedImagePath, encodedImage); } @@ -288,22 +296,30 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel1, TiffEncodingMode.BiColor, TiffEncoderCompression.ModifiedHuffman); [Theory] - [WithFile(Calliphora_BiColorUncompressed, PixelTypes.L8, TiffEncodingMode.BiColor, TiffEncoderPixelStorageMethod.SingleStrip)] - [WithFile(Calliphora_BiColorUncompressed, PixelTypes.L8, TiffEncodingMode.BiColor, TiffEncoderPixelStorageMethod.MultiStrip, 9 * 1024)] - [WithFile(GrayscaleUncompressed, PixelTypes.L8, TiffEncodingMode.Gray, TiffEncoderPixelStorageMethod.SingleStrip)] - [WithFile(GrayscaleUncompressed, PixelTypes.L8, TiffEncodingMode.Gray, TiffEncoderPixelStorageMethod.MultiStrip, 16 * 1024)] - [WithFile(PaletteDeflateMultistrip, PixelTypes.L8, TiffEncodingMode.ColorPalette, TiffEncoderPixelStorageMethod.SingleStrip)] - [WithFile(PaletteDeflateMultistrip, PixelTypes.L8, TiffEncodingMode.ColorPalette, TiffEncoderPixelStorageMethod.MultiStrip, 32 * 1024)] - [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffEncodingMode.Rgb, TiffEncoderPixelStorageMethod.SingleStrip)] - [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffEncodingMode.Rgb, TiffEncoderPixelStorageMethod.MultiStrip, 64 * 1024)] - public void TiffEncoder_StorageMethods(TestImageProvider provider, TiffEncodingMode mode, TiffEncoderPixelStorageMethod storageMethod, int maxSize = 0) + [WithFile(GrayscaleUncompressed, PixelTypes.L8, TiffEncodingMode.Gray, TiffEncoderCompression.PackBits, 16 * 1024)] + [WithFile(PaletteDeflateMultistrip, PixelTypes.L8, TiffEncodingMode.ColorPalette, TiffEncoderCompression.Lzw, 32 * 1024)] + [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffEncodingMode.Rgb, TiffEncoderCompression.Deflate, 64 * 1024)] + [WithFile(RgbUncompressed, PixelTypes.Rgb24, TiffEncodingMode.Rgb, TiffEncoderCompression.None, 10 * 1024)] + [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffEncodingMode.Rgb, TiffEncoderCompression.None, 30 * 1024)] + [WithFile(RgbUncompressed, PixelTypes.Rgb48, TiffEncodingMode.Rgb, TiffEncoderCompression.None, 70 * 1024)] + public void TiffEncoder_StripLength(TestImageProvider provider, TiffEncodingMode mode, TiffEncoderCompression compression, int maxSize) + where TPixel : unmanaged, IPixel => + TestStripLength(provider, mode, compression, maxSize); + + [Theory] + [WithFile(Calliphora_BiColorUncompressed, PixelTypes.L8, TiffEncodingMode.BiColor, TiffEncoderCompression.CcittGroup3Fax, 9 * 1024)] + public void TiffEncoder_StripLength_OutOfBounds(TestImageProvider provider, TiffEncodingMode mode, TiffEncoderCompression compression, int maxSize) + where TPixel : unmanaged, IPixel => + //// CcittGroup3Fax compressed data length can be larger than the original length + Assert.Throws(() => TestStripLength(provider, mode, compression, maxSize)); + + private static void TestStripLength(TestImageProvider provider, TiffEncodingMode mode, TiffEncoderCompression compression, int maxSize) where TPixel : unmanaged, IPixel { // arrange - var tiffEncoder = new TiffEncoder() { PixelStorageMethod = storageMethod, MaxStripBytes = maxSize }; - using Image input = provider.GetImage(); + var tiffEncoder = new TiffEncoder() { Mode = mode, Compression = compression, MaxStripBytes = maxSize }; + Image input = provider.GetImage(); using var memStream = new MemoryStream(); - TiffFrameMetadata inputMeta = input.Frames.RootFrame.Metadata.GetTiffMetadata(); // act @@ -311,24 +327,28 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // assert memStream.Position = 0; - using var output = Image.Load(this.configuration, memStream); + var output = Image.Load(Configuration, memStream); TiffFrameMetadata meta = output.Frames.RootFrame.Metadata.GetTiffMetadata(); - if (storageMethod == TiffEncoderPixelStorageMethod.SingleStrip) + Assert.True(output.Height > (int)meta.RowsPerStrip); + Assert.True(meta.StripOffsets.Length > 1); + Assert.True(meta.StripByteCounts.Length > 1); + + foreach (Number sz in meta.StripByteCounts) { - Assert.Equal(output.Height, (int)meta.RowsPerStrip); - Assert.Equal(1, meta.StripOffsets.Length); - Assert.Equal(1, meta.StripByteCounts.Length); + Assert.True((uint)sz <= maxSize); } - else - { - Assert.True(output.Height > (int)meta.RowsPerStrip); - Assert.True(meta.StripOffsets.Length > 1); - Assert.True(meta.StripByteCounts.Length > 1); - foreach (Number sz in meta.StripByteCounts) + // for uncompressed more accurate test + if (compression == TiffEncoderCompression.None) + { + for (int i = 0; i < meta.StripByteCounts.Length - 1; i++) { - Assert.True((int)sz <= maxSize); + // the difference must be less than one row + int stripBytes = (int)meta.StripByteCounts[i]; + var widthBytes = (meta.BitsPerPixel + 7) / 8 * (int)meta.Width; + + Assert.True((maxSize - stripBytes) < widthBytes); } } @@ -338,9 +358,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff (TiffBitsPerPixel)inputMeta.BitsPerPixel, mode, Convert(inputMeta.Compression), - maxStripSize: maxSize, - storageMethod: storageMethod - ); + maxStripSize: maxSize); } private static void TestTiffEncoderCore( @@ -351,7 +369,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff bool usePredictor = false, bool useExactComparer = true, int maxStripSize = 0, - TiffEncoderPixelStorageMethod? storageMethod = null, float compareTolerance = 0.01f) where TPixel : unmanaged, IPixel { @@ -361,7 +378,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Mode = mode, Compression = compression, UseHorizontalPredictor = usePredictor, - PixelStorageMethod = storageMethod ?? TiffEncoderPixelStorageMethod.Auto, MaxStripBytes = maxStripSize }; @@ -382,10 +398,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff return TiffEncoderCompression.Lzw; case TiffCompression.PackBits: return TiffEncoderCompression.PackBits; + case TiffCompression.Ccitt1D: + return TiffEncoderCompression.ModifiedHuffman; case TiffCompression.CcittGroup3Fax: return TiffEncoderCompression.CcittGroup3Fax; - case TiffCompression.CcittGroup4Fax: - return TiffEncoderCompression.ModifiedHuffman; } } } From d3033b351c6e7a2798b90b1fe93c8265d4769b25 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 14 Feb 2021 11:41:01 +0100 Subject: [PATCH 199/275] Replace lzw decompression implementation --- .../Compression/Compressors/TiffLzwEncoder.cs | 1 - .../Compression/Decompressors/LzwString.cs | 95 ++++++ .../Decompressors/LzwTiffCompression.cs | 4 +- .../Decompressors/TiffLzwDecoder.cs | 305 +++++++++++------- .../Compression/LzwTiffCompressionTests.cs | 1 + 5 files changed, 281 insertions(+), 125 deletions(-) create mode 100644 src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwString.cs diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffLzwEncoder.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffLzwEncoder.cs index db7d18a41..93876c071 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffLzwEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffLzwEncoder.cs @@ -254,7 +254,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils { this.children.GetSpan().Fill(0); this.siblings.GetSpan().Fill(0); - this.bitsPerCode = MinBits; this.maxCode = MaxValue(this.bitsPerCode); this.nextValidCode = EoiCode + 1; diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwString.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwString.cs new file mode 100644 index 000000000..ebe731941 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwString.cs @@ -0,0 +1,95 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors +{ + /// + /// Represents a lzw string with a code word and a code length. + /// + public class LzwString + { + private static readonly LzwString Empty = new LzwString(0, 0, 0, null); + + private readonly LzwString previous; + private readonly byte value; + + /// + /// Initializes a new instance of the class. + /// + /// The code word. + public LzwString(byte code) + : this(code, code, 1, null) + { + } + + private LzwString(byte value, byte firstChar, int length, LzwString previous) + { + this.value = value; + this.FirstChar = firstChar; + this.Length = length; + this.previous = previous; + } + + /// + /// Gets the code length; + /// + public int Length { get; } + + /// + /// Gets the first character of the codeword. + /// + public byte FirstChar { get; } + + /// + /// Concatenates two code words. + /// + /// The code word to concatenate. + /// A concatenated lzw string. + public LzwString Concatenate(byte other) + { + if (this == Empty) + { + return new LzwString(other); + } + + return new LzwString(other, this.FirstChar, this.Length + 1, this); + } + + /// + /// Writes decoded pixel to buffer at a given position. + /// + /// The buffer to write to. + /// The position to write to. + /// The number of bytes written. + public int WriteTo(Span buffer, int offset) + { + if (this.Length == 0) + { + return 0; + } + + if (this.Length == 1) + { + buffer[offset] = this.value; + return 1; + } + + LzwString e = this; + var endIdx = this.Length - 1; + if (endIdx >= buffer.Length) + { + TiffThrowHelper.ThrowImageFormatException("Error reading lzw compressed stream. Either pixel buffer to write to is to small or code length is invalid!"); + } + + for (int i = endIdx; i >= 0; i--) + { + buffer[offset + i] = e.value; + e = e.previous; + } + + return this.Length; + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs index 98aecd173..f0439fb7e 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs @@ -30,8 +30,8 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso /// protected override void Decompress(BufferedReadStream stream, int byteCount, Span buffer) { - var decoder = new TiffLzwDecoder(stream, this.Allocator); - decoder.DecodePixels(buffer.Length, 8, buffer); + var decoder = new TiffLzwDecoder(stream); + decoder.DecodePixels(buffer); if (this.Predictor == TiffPredictor.Horizontal) { diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffLzwDecoder.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffLzwDecoder.cs index d8150bea7..2f7ff0ee3 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffLzwDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffLzwDecoder.cs @@ -2,193 +2,254 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Buffers; using System.IO; -using SixLabors.ImageSharp.Formats.Gif; -using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors { + /* + This implementation is based on a port of a java tiff decoder by Harald Kuhr: https://github.com/haraldk/TwelveMonkeys + + Original licence: + + BSD 3-Clause License + + * Copyright (c) 2015, Harald Kuhr + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + ** Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + /// - /// Decompresses and decodes data using the dynamic LZW algorithms. + /// Decompresses and decodes data using the dynamic LZW algorithms, see TIFF spec Section 13. /// - /// - /// This code is based on the used for GIF decoding. There is potential - /// for a shared implementation. Differences between the GIF and TIFF implementations of the LZW - /// encoding are: (i) The GIF implementation includes an initial 'data size' byte, whilst this is - /// always 8 for TIFF. (ii) The GIF implementation writes a number of sub-blocks with an initial - /// byte indicating the length of the sub-block. In TIFF the data is written as a single block - /// with no length indicator (this can be determined from the 'StripByteCounts' entry). - /// internal sealed class TiffLzwDecoder { /// - /// The max decoder pixel stack size. + /// The stream to decode. /// - private const int MaxStackSize = 4096; + private readonly Stream stream; /// - /// The null code. + /// As soon as we use entry 4094 of the table (maxTableSize - 2), the lzw compressor write out a (12-bit) ClearCode. + /// At this point, the compressor reinitializes the string table and then writes out 9-bit codes again. /// - private const int NullCode = -1; + private const int ClearCode = 256; /// - /// The stream to decode. + /// End of Information. /// - private readonly Stream stream; + private const int EoiCode = 257; /// - /// The memory allocator. + /// Minimum code length of 9 bits. /// - private readonly MemoryAllocator allocator; + private const int MinBits = 9; + + /// + /// Maximum code length of 12 bits. + /// + private const int MaxBits = 12; + + /// + /// Maximum table size of 4096. + /// + private const int TableSize = 1 << MaxBits; + + private readonly LzwString[] table; + + private int tableLength; + private int bitsPerCode; + private int oldCode = ClearCode; + private int maxCode; + private int bitMask; + private int maxString; + private bool eofReached; + private int nextData; + private int nextBits; /// /// Initializes a new instance of the class /// and sets the stream, where the compressed data should be read from. /// /// The stream to read from. - /// The memory allocator. /// is null. - public TiffLzwDecoder(Stream stream, MemoryAllocator allocator) + public TiffLzwDecoder(Stream stream) { Guard.NotNull(stream, nameof(stream)); this.stream = stream; - this.allocator = allocator; + this.table = new LzwString[TableSize]; + for (int i = 0; i < 256; i++) + { + this.table[i] = new LzwString((byte)i); + } + + this.Init(); + } + + private void Init() + { + // Table length is 256 + 2, because of special clear code and end of information code. + this.tableLength = 258; + this.bitsPerCode = MinBits; + this.bitMask = BitmaskFor(this.bitsPerCode); + this.maxCode = this.MaxCode(); + this.maxString = 1; } /// /// Decodes and decompresses all pixel indices from the stream. /// - /// The length of the compressed data. - /// Size of the data. /// The pixel array to decode to. - public void DecodePixels(int length, int dataSize, Span pixels) + public void DecodePixels(Span pixels) { - Guard.MustBeLessThan(dataSize, int.MaxValue, nameof(dataSize)); - - // Initialize buffers - using IMemoryOwner prefixMemory = this.allocator.Allocate(MaxStackSize, AllocationOptions.Clean); - using IMemoryOwner suffixMemory = this.allocator.Allocate(MaxStackSize, AllocationOptions.Clean); - using IMemoryOwner pixelStackMemory = this.allocator.Allocate(MaxStackSize + 1, AllocationOptions.Clean); - - Span prefix = prefixMemory.GetSpan(); - Span suffix = suffixMemory.GetSpan(); - Span pixelStack = pixelStackMemory.GetSpan(); - - // Calculate the clear code. The value of the clear code is 2 ^ dataSize - int clearCode = 1 << dataSize; - - int codeSize = dataSize + 1; - - // Calculate the end code - int endCode = clearCode + 1; - - // Calculate the available code. - int availableCode = clearCode + 2; - - // Jillzhangs Code see: http://giflib.codeplex.com/ - // Adapted from John Cristy's ImageMagick. + // Adapted from the pseudo-code example found in the TIFF 6.0 Specification, 1992. + // See Section 13: "LZW Compression"/"LZW Decoding", page 61+ int code; - int oldCode = NullCode; - int codeMask = (1 << codeSize) - 1; - - int inputByte = 0; - int bits = 0; - - int top = 0; - int xyz = 0; - - int first = 0; + int offset = 0; - for (code = 0; code < clearCode; code++) + while ((code = this.GetNextCode()) != EoiCode) { - prefix[code] = 0; - suffix[code] = (byte)code; - } - - // Decoding process - while (xyz < length) - { - if (top == 0) + if (code == ClearCode) { - // Get the next code - int data = inputByte & ((1 << bits) - 1); + this.Init(); + code = this.GetNextCode(); - while (bits < codeSize) - { - inputByte = this.stream.ReadByte(); - data = (data << 8) | inputByte; - bits += 8; - } - - data >>= bits - codeSize; - bits -= codeSize; - code = data & codeMask; - - // Interpret the code - if (code > availableCode || code == endCode) + if (code == EoiCode) { break; } - if (code == clearCode) + if (this.table[code] == null) { - // Reset the decoder - codeSize = dataSize + 1; - codeMask = (1 << codeSize) - 1; - availableCode = clearCode + 2; - oldCode = NullCode; - continue; + TiffThrowHelper.ThrowImageFormatException($"Corrupted TIFF LZW: code {code} (table size: {this.tableLength})"); } - if (oldCode == NullCode) + offset += this.table[code].WriteTo(pixels, offset); + } + else + { + if (this.table[this.oldCode] == null) { - pixelStack[top++] = suffix[code]; - oldCode = code; - first = code; - continue; + TiffThrowHelper.ThrowImageFormatException($"Corrupted TIFF LZW: code {this.oldCode} (table size: {this.tableLength})"); } - int inCode = code; - if (code == availableCode) + if (this.IsInTable(code)) { - pixelStack[top++] = (byte)first; + offset += this.table[code].WriteTo(pixels, offset); - code = oldCode; + this.AddStringToTable(this.table[this.oldCode].Concatenate(this.table[code].FirstChar)); } - - while (code > clearCode) + else { - pixelStack[top++] = suffix[code]; - code = prefix[code]; + LzwString outString = this.table[this.oldCode].Concatenate(this.table[this.oldCode].FirstChar); + + offset += outString.WriteTo(pixels, offset); + this.AddStringToTable(outString); } + } - first = suffix[code]; + this.oldCode = code; - pixelStack[top++] = suffix[code]; + if (offset >= pixels.Length) + { + break; + } + } + } - if (availableCode < MaxStackSize) - { - prefix[availableCode] = oldCode; - suffix[availableCode] = first; - availableCode++; - if (availableCode > codeMask - 1 && availableCode < MaxStackSize) - { - codeSize++; - codeMask = (1 << codeSize) - 1; - } - } + private void AddStringToTable(LzwString lzwString) + { + if (this.tableLength > this.table.Length) + { + TiffThrowHelper.ThrowImageFormatException($"TIFF LZW with more than {MaxBits} bits per code encountered (table overflow)"); + } + + this.table[this.tableLength++] = lzwString; + + if (this.tableLength > this.maxCode) + { + this.bitsPerCode++; - oldCode = inCode; + if (this.bitsPerCode > MaxBits) + { + // Continue reading MaxBits (12 bit) length codes. + this.bitsPerCode = MaxBits; } - // Pop a pixel off the pixel stack. - top--; + this.bitMask = BitmaskFor(this.bitsPerCode); + this.maxCode = this.MaxCode(); + } + + if (lzwString.Length > this.maxString) + { + this.maxString = lzwString.Length; + } + } + + private int GetNextCode() + { + if (this.eofReached) + { + return EoiCode; + } - // Clear missing pixels - pixels[xyz++] = (byte)pixelStack[top]; + int read = this.stream.ReadByte(); + if (read < 0) + { + this.eofReached = true; + return EoiCode; } + + this.nextData = (this.nextData << 8) | read; + this.nextBits += 8; + + if (this.nextBits < this.bitsPerCode) + { + read = this.stream.ReadByte(); + if (read < 0) + { + this.eofReached = true; + return EoiCode; + } + + this.nextData = (this.nextData << 8) | read; + this.nextBits += 8; + } + + var code = (this.nextData >> (this.nextBits - this.bitsPerCode)) & this.bitMask; + this.nextBits -= this.bitsPerCode; + + return code; } + + private bool IsInTable(int code) => code < this.tableLength; + + private int MaxCode() => this.bitMask - 1; + + private static int BitmaskFor(int bits) => (1 << bits) - 1; } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs index 94835962d..410ead84d 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs @@ -30,6 +30,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression [Theory] [InlineData(new byte[] { })] [InlineData(new byte[] { 42 })] // One byte + [InlineData(new byte[] { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 })] [InlineData(new byte[] { 42, 16, 128, 53, 96, 218, 7, 64, 3, 4, 97 })] // Random bytes [InlineData(new byte[] { 1, 2, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 3, 4 })] // Repeated bytes [InlineData(new byte[] { 1, 2, 42, 53, 42, 53, 42, 53, 42, 53, 42, 53, 3, 4 })] // Repeated sequence From 0e1e8fe531eaf711a1cd734f1eba6d71fc2eef9e Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 14 Feb 2021 15:17:52 +0100 Subject: [PATCH 200/275] Use tolerant comparer for color palette tests --- .../Formats/Tiff/TiffEncoderTests.cs | 65 ++++--------------- 1 file changed, 12 insertions(+), 53 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index fd4432c5e..3af0608df 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -153,79 +153,38 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Theory] [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel - { - var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.None }; - - this.TiffEncoderPaletteTest(provider, encoder); - } + where TPixel : unmanaged, IPixel => + TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, useExactComparer: false, compareTolerance: 0.001f); [Theory] [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_WithDeflateCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel - { - var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Deflate }; - - this.TiffEncoderPaletteTest(provider, encoder); - } + where TPixel : unmanaged, IPixel => + TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, TiffEncoderCompression.Deflate, useExactComparer: false, compareTolerance: 0.001f); [Theory] [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_WithDeflateCompressionAndPredictor_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel - { - var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Deflate, UseHorizontalPredictor = true }; - - this.TiffEncoderPaletteTest(provider, encoder); - } + where TPixel : unmanaged, IPixel => + TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, TiffEncoderCompression.Deflate, usePredictor: true, useExactComparer: false, compareTolerance: 0.001f); [Theory] [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_WithLzwCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel - { - var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Lzw }; - - this.TiffEncoderPaletteTest(provider, encoder); - } + where TPixel : unmanaged, IPixel => + TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, TiffEncoderCompression.Lzw, useExactComparer: false, compareTolerance: 0.001f); [Theory] [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_WithLzwCompressionAndPredictor_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel - { - var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Lzw, UseHorizontalPredictor = true }; - - this.TiffEncoderPaletteTest(provider, encoder); - } + where TPixel : unmanaged, IPixel => + TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, TiffEncoderCompression.Lzw, usePredictor: true, useExactComparer: false, compareTolerance: 0.001f); [Theory] [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_WithPackBitsCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel - { - var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.PackBits }; - - this.TiffEncoderPaletteTest(provider, encoder); - } - - private void TiffEncoderPaletteTest(TestImageProvider provider, TiffEncoder encoder) - where TPixel : unmanaged, IPixel - { - // Because a quantizer is used to create the palette (and therefore changes to the original are expected), - // we do not compare the encoded image against the original: - // Instead we load the encoded image with a reference decoder and compare against that image. - using Image image = provider.GetImage(); - using var memStream = new MemoryStream(); - - image.Save(memStream, encoder); - memStream.Position = 0; - - using var encodedImage = (Image)Image.Load(this.configuration, memStream); - var encodedImagePath = provider.Utility.SaveTestOutputFile(encodedImage, "tiff", encoder); - TiffTestUtils.CompareWithReferenceDecoder(encodedImagePath, encodedImage); - } + where TPixel : unmanaged, IPixel => + TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, TiffEncoderCompression.PackBits, useExactComparer: false, compareTolerance: 0.001f); [Theory] [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] From 4b210bbfe01ea14673b7785a2c6366c64f0bd4a7 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sun, 14 Feb 2021 18:03:15 +0300 Subject: [PATCH 201/275] Rename tests --- tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 6efe92b23..c2dccd2a7 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -213,13 +213,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Theory] [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] - public void TiffEncoder_EncodeColorPalette_WithLzwCompression_Works_Bug(TestImageProvider provider) + public void TiffEncoder_EncodeColorPalette_WithLzwCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, TiffEncoderCompression.Lzw, useExactComparer: false, compareTolerance: 0.001f); [Theory] [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] - public void TiffEncoder_EncodeColorPalette_WithLzwCompressionAndPredictor_Works_Bug(TestImageProvider provider) + public void TiffEncoder_EncodeColorPalette_WithLzwCompressionAndPredictor_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, TiffEncoderCompression.Lzw, usePredictor: true, useExactComparer: false, compareTolerance: 0.001f); From 8cc2ce38f81faa0c33a0b1e34cc72dd4cf3cdfc2 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sun, 14 Feb 2021 19:22:47 +0300 Subject: [PATCH 202/275] Correct test --- tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index c2dccd2a7..0041d05ce 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -121,11 +121,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [InlineData(TiffEncodingMode.ColorPalette, TiffEncoderCompression.ModifiedHuffman)] [InlineData(TiffEncodingMode.Gray, TiffEncoderCompression.ModifiedHuffman)] [InlineData(TiffEncodingMode.Rgb, TiffEncoderCompression.ModifiedHuffman)] - public void TiffEncoder_IncompatibilityOptions(TiffEncodingMode mode, TiffEncoderCompression compression) - where TPixel : unmanaged, IPixel + public void TiffEncoder_IncompatibilityOptions(TiffEncodingMode mode, TiffEncoderCompression compression) { // arrange - using var input = new Image(10, 10); + using var input = new Image(10, 10); var encoder = new TiffEncoder() { Mode = mode, Compression = compression }; using var memStream = new MemoryStream(); From 677800f3267ed7b41a99a9723f492b62b4e96518 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sun, 14 Feb 2021 20:44:17 +0300 Subject: [PATCH 203/275] PackBits bug fix --- .../Compressors/PackBitsCompressor.cs | 17 +++++++++++------ .../Formats/Tiff/Writers/TiffBiColorWriter.cs | 9 +++++---- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsCompressor.cs index 627ca6cbb..ce5d8a769 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsCompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsCompressor.cs @@ -3,8 +3,6 @@ using System; using System.IO; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors @@ -22,16 +20,23 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors public override void Initialize(int rowsPerStrip) { - int additionalBytes = (this.BytesPerRow / 127) + 1; - this.pixelData = this.Allocator.AllocateManagedByteBuffer((this.BytesPerRow + additionalBytes) * rowsPerStrip); + int additionalBytes = ((this.BytesPerRow + 126) / 127) + 1; + this.pixelData = this.Allocator.AllocateManagedByteBuffer(this.BytesPerRow + additionalBytes); } public override void CompressStrip(Span rows, int height) { + DebugGuard.IsTrue(rows.Length % height == 0, "Invalid height"); + DebugGuard.IsTrue(this.BytesPerRow == rows.Length / height, "The widths must match"); + this.pixelData.Clear(); Span span = this.pixelData.GetSpan(); - int size = PackBitsWriter.PackBits(rows, span); - this.Output.Write(span.Slice(0, size)); + for (int i = 0; i < height; i++) + { + Span row = rows.Slice(i * this.BytesPerRow, this.BytesPerRow); + int size = PackBitsWriter.PackBits(row, span); + this.Output.Write(span.Slice(0, size)); + } } protected override void Dispose(bool disposing) => this.pixelData?.Dispose(); diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs index 5db0eff73..cd17f1665 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs @@ -51,18 +51,19 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers if (compressor.Method == TiffEncoderCompression.CcittGroup3Fax || compressor.Method == TiffEncoderCompression.ModifiedHuffman) { + // special case for T4BitCompressor compressor.CompressStrip(pixelAsGraySpan, height); } else { + int bytesPerStrip = this.BytesPerRow * height; if (this.bitStrip == null) { - int bytesPerRow = this.BytesPerRow * height; - this.bitStrip = this.MemoryAllocator.AllocateManagedByteBuffer(bytesPerRow); + this.bitStrip = this.MemoryAllocator.AllocateManagedByteBuffer(bytesPerStrip); } - this.bitStrip.Clear(); - Span rows = this.bitStrip.GetSpan(); + Span rows = this.bitStrip.Slice(0, bytesPerStrip); + rows.Clear(); int xx = 0; for (int s = 0; s < height; s++) From 60dcaac07632c836da2a0618c07752a09b4dfd9a Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sun, 14 Feb 2021 20:53:49 +0300 Subject: [PATCH 204/275] Remove excess clearing --- .../Formats/Tiff/Compression/Compressors/PackBitsCompressor.cs | 1 - src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs | 2 -- 2 files changed, 3 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsCompressor.cs index ce5d8a769..93c37d25d 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsCompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsCompressor.cs @@ -29,7 +29,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors DebugGuard.IsTrue(rows.Length % height == 0, "Invalid height"); DebugGuard.IsTrue(this.BytesPerRow == rows.Length / height, "The widths must match"); - this.pixelData.Clear(); Span span = this.pixelData.GetSpan(); for (int i = 0; i < height; i++) { diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs index cd17f1665..91cc9ddfc 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs @@ -41,8 +41,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers this.pixelsAsGray = this.MemoryAllocator.Allocate(height * this.Image.Width); } - this.pixelsAsGray.Clear(); - Span pixelAsGraySpan = this.pixelsAsGray.Slice(0, height * this.Image.Width); Span pixels = GetStripPixels(this.imageBlackWhite.GetRootFramePixelBuffer(), y, height); From 20726c3d074d54a689ea34452f5907a608dce3fb Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 15 Feb 2021 13:30:20 +0100 Subject: [PATCH 205/275] Clarify some DebugGuard messages and a little cleanup --- .../Compressors/DeflateCompressor.cs | 4 ++++ .../Compression/Compressors/LzwCompressor.cs | 4 ++++ .../Compression/Compressors/NoCompressor.cs | 4 ++++ .../Compressors/PackBitsCompressor.cs | 4 ++++ .../Decompressors/DeflateTiffCompression.cs | 1 + .../Decompressors/LzwTiffCompression.cs | 1 + .../Decompressors/NoneTiffCompression.cs | 1 + .../Decompressors/PackBitsTiffCompression.cs | 1 + .../Decompressors/T4TiffCompression.cs | 1 + .../Tiff/Compression/TiffBaseCompression.cs | 17 +++++++++++++- .../Tiff/Compression/TiffBaseCompressor.cs | 23 +++++++++++++++++++ .../Tiff/Compression/TiffCompressorFactory.cs | 21 ++++++++--------- .../Compression/TiffDecompressorsFactory.cs | 16 ++++++------- .../Formats/Tiff/TiffEncoderCore.cs | 7 ++++-- .../Tiff/Writers/TiffBaseColorWriter.cs | 1 + .../Formats/Tiff/Writers/TiffBiColorWriter.cs | 6 ++--- .../Tiff/Writers/TiffCompositeColorWriter.cs | 3 ++- .../Formats/Tiff/Writers/TiffGrayWriter.cs | 3 ++- .../Formats/Tiff/Writers/TiffPaletteWriter.cs | 3 +++ .../Formats/Tiff/Writers/TiffRgbWriter.cs | 3 ++- 20 files changed, 95 insertions(+), 29 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/DeflateCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/DeflateCompressor.cs index 64d3b1ea3..46cb63a86 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/DeflateCompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/DeflateCompressor.cs @@ -19,12 +19,15 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors : base(output, allocator, width, bitsPerPixel, predictor) => this.compressionLevel = compressionLevel; + /// public override TiffEncoderCompression Method => TiffEncoderCompression.Deflate; + /// public override void Initialize(int rowsPerStrip) { } + /// public override void CompressStrip(Span rows, int height) { this.memoryStream.Seek(0, SeekOrigin.Begin); @@ -52,6 +55,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors #endif } + /// protected override void Dispose(bool disposing) { } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/LzwCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/LzwCompressor.cs index 84dc95b5f..d29cf6157 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/LzwCompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/LzwCompressor.cs @@ -17,10 +17,13 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors { } + /// public override TiffEncoderCompression Method => TiffEncoderCompression.Lzw; + /// public override void Initialize(int rowsPerStrip) => this.lzwEncoder = new TiffLzwEncoder(this.Allocator); + /// public override void CompressStrip(Span rows, int height) { if (this.Predictor == TiffPredictor.Horizontal) @@ -31,6 +34,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors this.lzwEncoder.Encode(rows, this.Output); } + /// protected override void Dispose(bool disposing) => this.lzwEncoder?.Dispose(); } } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/NoCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/NoCompressor.cs index 6c6b9ef34..f4a902b46 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/NoCompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/NoCompressor.cs @@ -13,14 +13,18 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors { } + /// public override TiffEncoderCompression Method => TiffEncoderCompression.None; + /// public override void Initialize(int rowsPerStrip) { } + /// public override void CompressStrip(Span rows, int height) => this.Output.Write(rows); + /// protected override void Dispose(bool disposing) { } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsCompressor.cs index 93c37d25d..5353b17c3 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsCompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsCompressor.cs @@ -16,14 +16,17 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors { } + /// public override TiffEncoderCompression Method => TiffEncoderCompression.PackBits; + /// public override void Initialize(int rowsPerStrip) { int additionalBytes = ((this.BytesPerRow + 126) / 127) + 1; this.pixelData = this.Allocator.AllocateManagedByteBuffer(this.BytesPerRow + additionalBytes); } + /// public override void CompressStrip(Span rows, int height) { DebugGuard.IsTrue(rows.Length % height == 0, "Invalid height"); @@ -38,6 +41,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors } } + /// protected override void Dispose(bool disposing) => this.pixelData?.Dispose(); } } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs index a53d69027..be63bdad8 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs @@ -54,6 +54,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso } } + /// protected override void Dispose(bool disposing) { } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs index b85aa3a22..a14738e44 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs @@ -38,6 +38,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso } } + /// protected override void Dispose(bool disposing) { } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/NoneTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/NoneTiffCompression.cs index a30997deb..f041a00e9 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/NoneTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/NoneTiffCompression.cs @@ -23,6 +23,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso /// protected override void Decompress(BufferedReadStream stream, int byteCount, Span buffer) => _ = stream.Read(buffer, 0, Math.Min(buffer.Length, byteCount)); + /// protected override void Dispose(bool disposing) { } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/PackBitsTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/PackBitsTiffCompression.cs index ab67d818d..c7461a807 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/PackBitsTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/PackBitsTiffCompression.cs @@ -88,6 +88,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso } } + /// protected override void Dispose(bool disposing) => this.compressedDataMemory?.Dispose(); } } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs index fe4641fb2..275e3d598 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs @@ -81,6 +81,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso } } + /// protected override void Dispose(bool disposing) { } diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs index e47b65c99..a403ca064 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs @@ -17,20 +17,35 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression this.Width = width; this.BitsPerPixel = bitsPerPixel; this.Predictor = predictor; - this.BytesPerRow = ((width * bitsPerPixel) + 7) / 8; } + /// + /// Gets the image width. + /// public int Width { get; } + /// + /// Gets the bits per pixel. + /// public int BitsPerPixel { get; } + /// + /// Gets the bytes per row. + /// public int BytesPerRow { get; } + /// + /// Gets the predictor to use. Should only be used with deflate or lzw compression. + /// public TiffPredictor Predictor { get; } + /// + /// Gets the memory allocator. + /// protected MemoryAllocator Allocator { get; } + /// public void Dispose() { if (this.isDisposed) diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompressor.cs index 71190c7c4..6514fbe83 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompressor.cs @@ -10,16 +10,39 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression { internal abstract class TiffBaseCompressor : TiffBaseCompression { + /// + /// Initializes a new instance of the class. + /// + /// The output stream to write the compressed image to. + /// The memory allocator. + /// The image width. + /// Bits per pixel. + /// The predictor to use (should only be used with deflate or lzw compression). Defaults to none. protected TiffBaseCompressor(Stream output, MemoryAllocator allocator, int width, int bitsPerPixel, TiffPredictor predictor = TiffPredictor.None) : base(allocator, width, bitsPerPixel, predictor) => this.Output = output; + /// + /// Gets the compression method to use. + /// public abstract TiffEncoderCompression Method { get; } + /// + /// Gets the output stream to write the compressed image to. + /// public Stream Output { get; } + /// + /// Does any initialization required for the compression. + /// + /// The number of rows per strip. public abstract void Initialize(int rowsPerStrip); + /// + /// Compresses a strip of the image. + /// + /// Image rows to compress. + /// Image height. public abstract void CompressStrip(Span rows, int height); } } diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressorFactory.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressorFactory.cs index c954de215..eeb14fb74 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressorFactory.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressorFactory.cs @@ -1,11 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Buffers; using System.IO; using SixLabors.ImageSharp.Compression.Zlib; -using SixLabors.ImageSharp.Formats.Experimental.Tiff; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Memory; @@ -26,31 +23,31 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression switch (method) { case TiffEncoderCompression.None: - DebugGuard.IsTrue(compressionLevel == DeflateCompressionLevel.DefaultCompression, "Values must be equals"); - DebugGuard.IsTrue(predictor == TiffPredictor.None, "Values must be equals"); + DebugGuard.IsTrue(compressionLevel == DeflateCompressionLevel.DefaultCompression, "No deflate compression level is expected to be set"); + DebugGuard.IsTrue(predictor == TiffPredictor.None, "Predictor should only be used with lzw or deflate compression"); return new NoCompressor(output); case TiffEncoderCompression.PackBits: - DebugGuard.IsTrue(compressionLevel == DeflateCompressionLevel.DefaultCompression, "Values must be equals"); - DebugGuard.IsTrue(predictor == TiffPredictor.None, "Values must be equals"); + DebugGuard.IsTrue(compressionLevel == DeflateCompressionLevel.DefaultCompression, "No deflate compression level is expected to be set"); + DebugGuard.IsTrue(predictor == TiffPredictor.None, "Predictor should only be used with lzw or deflate compression"); return new PackBitsCompressor(output, allocator, width, bitsPerPixel); case TiffEncoderCompression.Deflate: return new DeflateCompressor(output, allocator, width, bitsPerPixel, predictor, compressionLevel); case TiffEncoderCompression.Lzw: - DebugGuard.IsTrue(compressionLevel == DeflateCompressionLevel.DefaultCompression, "Values must be equals"); + DebugGuard.IsTrue(compressionLevel == DeflateCompressionLevel.DefaultCompression, "No deflate compression level is expected to be set"); return new LzwCompressor(output, allocator, width, bitsPerPixel, predictor); case TiffEncoderCompression.CcittGroup3Fax: - DebugGuard.IsTrue(compressionLevel == DeflateCompressionLevel.DefaultCompression, "Values must be equals"); - DebugGuard.IsTrue(predictor == TiffPredictor.None, "Values must be equals"); + DebugGuard.IsTrue(compressionLevel == DeflateCompressionLevel.DefaultCompression, "No deflate compression level is expected to be set"); + DebugGuard.IsTrue(predictor == TiffPredictor.None, "Predictor should only be used with lzw or deflate compression"); return new T4BitCompressor(output, allocator, width, bitsPerPixel, false); case TiffEncoderCompression.ModifiedHuffman: - DebugGuard.IsTrue(compressionLevel == DeflateCompressionLevel.DefaultCompression, "Values must be equals"); - DebugGuard.IsTrue(predictor == TiffPredictor.None, "Values must be equals"); + DebugGuard.IsTrue(compressionLevel == DeflateCompressionLevel.DefaultCompression, "No deflate compression level is expected to be set"); + DebugGuard.IsTrue(predictor == TiffPredictor.None, "Predictor should only be used with lzw or deflate compression"); return new T4BitCompressor(output, allocator, width, bitsPerPixel, true); default: diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs index 50e77f4f1..130205b5b 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs @@ -21,29 +21,29 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression switch (method) { case TiffDecoderCompressionType.None: - DebugGuard.IsTrue(predictor == TiffPredictor.None, "Values must be equals"); - DebugGuard.IsTrue(faxOptions == FaxCompressionOptions.None, "Values must be equals"); + DebugGuard.IsTrue(predictor == TiffPredictor.None, "Predictor should only be used with lzw or deflate compression"); + DebugGuard.IsTrue(faxOptions == FaxCompressionOptions.None, "No fax compression options are expected"); return new NoneTiffCompression(); case TiffDecoderCompressionType.PackBits: - DebugGuard.IsTrue(predictor == TiffPredictor.None, "Values must be equals"); - DebugGuard.IsTrue(faxOptions == FaxCompressionOptions.None, "Values must be equals"); + DebugGuard.IsTrue(predictor == TiffPredictor.None, "Predictor should only be used with lzw or deflate compression"); + DebugGuard.IsTrue(faxOptions == FaxCompressionOptions.None, "No fax compression options are expected"); return new PackBitsTiffCompression(allocator); case TiffDecoderCompressionType.Deflate: - DebugGuard.IsTrue(faxOptions == FaxCompressionOptions.None, "Values must be equals"); + DebugGuard.IsTrue(faxOptions == FaxCompressionOptions.None, "No fax compression options are expected"); return new DeflateTiffCompression(allocator, width, bitsPerPixel, predictor); case TiffDecoderCompressionType.Lzw: - DebugGuard.IsTrue(faxOptions == FaxCompressionOptions.None, "Values must be equals"); + DebugGuard.IsTrue(faxOptions == FaxCompressionOptions.None, "No fax compression options are expected"); return new LzwTiffCompression(allocator, width, bitsPerPixel, predictor); case TiffDecoderCompressionType.T4: - DebugGuard.IsTrue(predictor == TiffPredictor.None, "Values must be equals"); + DebugGuard.IsTrue(predictor == TiffPredictor.None, "Predictor should only be used with lzw or deflate compression"); return new T4TiffCompression(allocator, faxOptions, photometricInterpretation, width); case TiffDecoderCompressionType.HuffmanRle: - DebugGuard.IsTrue(predictor == TiffPredictor.None, "Values must be equals"); + DebugGuard.IsTrue(predictor == TiffPredictor.None, "Predictor should only be used with lzw or deflate compression"); return new ModifiedHuffmanTiffCompression(allocator, photometricInterpretation, width); default: diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index ec9c761aa..d9997a28d 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -12,7 +12,6 @@ using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; @@ -58,6 +57,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// private readonly DeflateCompressionLevel compressionLevel; + /// + /// The maximum number of bytes for a strip. + /// private readonly int maxStripBytes; /// @@ -252,7 +254,8 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff this.bitsPerPixel = TiffBitsPerPixel.Pixel1; return; } - else if (this.Mode != TiffEncodingMode.BiColor) + + if (this.Mode != TiffEncodingMode.BiColor) { TiffThrowHelper.ThrowImageFormatException($"The {this.CompressionType} compression and {this.Mode} aren't compatible. Please use {this.CompressionType} only with {TiffEncodingMode.BiColor} or {TiffEncodingMode.Default} mode."); } diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs index 7d5d64f16..f61b29436 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs @@ -63,6 +63,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers this.AddStripTags(rowsPerStrip, stripOffsets, stripByteCounts); } + /// public void Dispose() { if (this.isDisposed) diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs index 91cc9ddfc..ca366f515 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs @@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers if (compressor.Method == TiffEncoderCompression.CcittGroup3Fax || compressor.Method == TiffEncoderCompression.ModifiedHuffman) { - // special case for T4BitCompressor + // Special case for T4BitCompressor. compressor.CompressStrip(pixelAsGraySpan, height); } else @@ -63,7 +63,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers Span rows = this.bitStrip.Slice(0, bytesPerStrip); rows.Clear(); - int xx = 0; + int grayPixelIndex = 0; for (int s = 0; s < height; s++) { int bitIndex = 0; @@ -72,7 +72,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers for (int x = 0; x < this.Image.Width; x++) { int shift = 7 - bitIndex; - if (pixelAsGraySpan[xx++] == 255) + if (pixelAsGraySpan[grayPixelIndex++] == 255) { outputRow[byteIndex] |= (byte)(1 << shift); } diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffCompositeColorWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffCompositeColorWriter.cs index 1b2bd4ab6..018ac6fa0 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffCompositeColorWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffCompositeColorWriter.cs @@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers { private IManagedByteBuffer rowBuffer; - public TiffCompositeColorWriter(ImageFrame image, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector) + protected TiffCompositeColorWriter(ImageFrame image, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector) : base(image, memoryAllocator, configuration, entriesCollector) { } @@ -40,6 +40,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers protected abstract void EncodePixels(Span pixels, Span buffer); + /// protected override void Dispose(bool disposing) => this.rowBuffer?.Dispose(); } } diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffGrayWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffGrayWriter.cs index f2b06d872..7e2e4e304 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffGrayWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffGrayWriter.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.IO; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -16,8 +15,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers { } + /// public override int BitsPerPixel => 8; + /// protected override void EncodePixels(Span pixels, Span buffer) => PixelOperations.Instance.ToL8Bytes(this.Configuration, pixels, buffer, pixels.Length); } } diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs index 286663706..dc7dcf589 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs @@ -31,14 +31,17 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers this.AddTag(this.quantized); } + /// public override int BitsPerPixel => 8; + /// protected override void EncodeStrip(int y, int height, TiffBaseCompressor compressor) { Span pixels = GetStripPixels(((IPixelSource)this.quantized).PixelBuffer, y, height); compressor.CompressStrip(pixels, height); } + /// protected override void Dispose(bool disposing) => this.quantized?.Dispose(); private void AddTag(IndexedImageFrame quantized) diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffRgbWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffRgbWriter.cs index 174a67727..b32b5ed90 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffRgbWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffRgbWriter.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.IO; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -16,8 +15,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers { } + /// public override int BitsPerPixel => 24; + /// protected override void EncodePixels(Span pixels, Span buffer) => PixelOperations.Instance.ToRgb24Bytes(this.Configuration, pixels, buffer, pixels.Length); } } From 572f616ae3c39e38fefee71e10cf8972a96f4a68 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 15 Feb 2021 18:28:20 +0100 Subject: [PATCH 206/275] Add PhotometricInterpretation to the tiff metadata --- .../Formats/Tiff/ITiffEncoderOptions.cs | 2 +- .../Tiff/TiffDecoderMetadataCreator.cs | 4 +-- .../Formats/Tiff/TiffEncoderCore.cs | 5 ++-- src/ImageSharp/Formats/Tiff/TiffMetadata.cs | 10 ++++++- .../Formats/Tiff/TiffEncoderTests.cs | 1 + .../Formats/Tiff/TiffMetadataTests.cs | 27 ++++++++++++++++--- 6 files changed, 39 insertions(+), 10 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs index 03a8328ec..de1095eee 100644 --- a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs +++ b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff DeflateCompressionLevel CompressionLevel { get; } /// - /// Gets the encoding mode to use. RGB, RGB with color palette or gray. + /// Gets the encoding mode to use. Possible options are RGB, RGB with a color palette, gray or BiColor. /// If no mode is specified in the options, RGB will be used. /// TiffEncodingMode Mode { get; } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs index 3cd67d2ab..de17ada5d 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs @@ -2,10 +2,9 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Buffers.Binary; using System.Collections.Generic; using System.Linq; -using System.Runtime.InteropServices; + using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Icc; @@ -43,6 +42,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff tiffMetadata.ByteOrder = byteOrder; tiffMetadata.BitsPerPixel = GetBitsPerPixel(rootFrameMetadata); tiffMetadata.Compression = rootFrameMetadata.Compression; + tiffMetadata.PhotometricInterpretation = rootFrameMetadata.PhotometricInterpretation; if (!ignoreMetadata) { diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index d9997a28d..d18ba7dc6 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -70,10 +70,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff public TiffEncoderCore(ITiffEncoderOptions options, MemoryAllocator memoryAllocator) { this.memoryAllocator = memoryAllocator; - this.CompressionType = options.Compression; this.Mode = options.Mode; this.quantizer = options.Quantizer ?? KnownQuantizers.Octree; this.UseHorizontalPredictor = options.UseHorizontalPredictor; + this.CompressionType = options.Compression; this.compressionLevel = options.CompressionLevel; this.maxStripBytes = options.MaxStripBytes; } @@ -271,8 +271,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff this.Mode = TiffEncodingMode.BiColor; break; case TiffBitsPerPixel.Pixel8: - // todo: can gray or palette - this.Mode = TiffEncodingMode.Gray; + this.Mode = tiffMetadata.PhotometricInterpretation != TiffPhotometricInterpretation.PaletteColor ? TiffEncodingMode.Gray : TiffEncodingMode.Rgb; break; default: this.Mode = TiffEncodingMode.Rgb; diff --git a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs index f8df21c1e..b1d4fff0d 100644 --- a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff @@ -24,9 +25,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff private TiffMetadata(TiffMetadata other) { this.ByteOrder = other.ByteOrder; - this.XmpProfile = other.XmpProfile; this.BitsPerPixel = other.BitsPerPixel; this.Compression = other.Compression; + this.PhotometricInterpretation = other.PhotometricInterpretation; + this.XmpProfile = other.XmpProfile != null ? new byte[other.XmpProfile.Length] : null; + other.XmpProfile?.AsSpan().CopyTo(this.XmpProfile.AsSpan()); } /// @@ -44,6 +47,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// public TiffCompression Compression { get; internal set; } = TiffCompression.None; + /// + /// Gets the photometric interpretation which indicates how the pixels are to be interpreted, e.g. if the image is bicolor, RGB, color paletted etc. + /// + public TiffPhotometricInterpretation PhotometricInterpretation { get; internal set; } + /// /// Gets or sets the XMP profile. /// For internal use only. ImageSharp not support XMP profile. diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 0041d05ce..d4f68ff66 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -73,6 +73,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32, TiffBitsPerPixel.Pixel1)] [WithFile(GrayscaleUncompressed, PixelTypes.Rgba32, TiffBitsPerPixel.Pixel8)] [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffBitsPerPixel.Pixel24)] + [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32, TiffBitsPerPixel.Pixel24)] public void TiffEncoder_PreserveBitsPerPixel(TestImageProvider provider, TiffBitsPerPixel expectedBitsPerPixel) where TPixel : unmanaged, IPixel { diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index 2329baf76..9acd44608 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -36,12 +36,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Fact] public void CloneIsDeep() { + byte[] xmpData = { 1, 1, 1 }; var meta = new TiffMetadata { Compression = TiffCompression.Deflate, BitsPerPixel = TiffBitsPerPixel.Pixel8, ByteOrder = ByteOrder.BigEndian, - XmpProfile = new byte[3] + XmpProfile = xmpData, + PhotometricInterpretation = TiffPhotometricInterpretation.Rgb }; var clone = (TiffMetadata)meta.DeepClone(); @@ -49,12 +51,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff clone.Compression = TiffCompression.None; clone.BitsPerPixel = TiffBitsPerPixel.Pixel24; clone.ByteOrder = ByteOrder.LittleEndian; - clone.XmpProfile = new byte[1]; + clone.PhotometricInterpretation = TiffPhotometricInterpretation.YCbCr; Assert.False(meta.Compression == clone.Compression); Assert.False(meta.BitsPerPixel == clone.BitsPerPixel); Assert.False(meta.ByteOrder == clone.ByteOrder); - Assert.False(meta.XmpProfile.SequenceEqual(clone.XmpProfile)); + Assert.False(meta.PhotometricInterpretation == clone.PhotometricInterpretation); + Assert.False(meta.XmpProfile.Equals(clone.XmpProfile)); + Assert.True(meta.XmpProfile.SequenceEqual(clone.XmpProfile)); } [Theory] @@ -95,6 +99,23 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal(expectedCompression, tiffMetadata.Compression); } + [Theory] + [InlineData(Calliphora_RgbUncompressed, TiffPhotometricInterpretation.Rgb)] + [InlineData(Calliphora_BiColorUncompressed, TiffPhotometricInterpretation.BlackIsZero)] + [InlineData(Calliphora_PaletteUncompressed, TiffPhotometricInterpretation.PaletteColor)] + public void Identify_DetectsCorrectPhotometricInterpretation(string imagePath, TiffPhotometricInterpretation expectedPhotometricInterpretation) + { + var testFile = TestFile.Create(imagePath); + using var stream = new MemoryStream(testFile.Bytes, false); + + IImageInfo imageInfo = Image.Identify(this.configuration, stream); + + Assert.NotNull(imageInfo); + TiffMetadata tiffMetadata = imageInfo.Metadata.GetTiffMetadata(); + Assert.NotNull(tiffMetadata); + Assert.Equal(expectedPhotometricInterpretation, tiffMetadata.PhotometricInterpretation); + } + [Theory] [InlineData(GrayscaleUncompressed, ByteOrder.BigEndian)] [InlineData(LittleEndianByteOrder, ByteOrder.LittleEndian)] From 8cee9a4aa482741565b760d89aeffd8b2da2969e Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 15 Feb 2021 18:31:53 +0100 Subject: [PATCH 207/275] Add setter for DeflateCompressionLevel --- src/ImageSharp/Formats/Tiff/TiffEncoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs index 5f747a685..88204eb55 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs @@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff public TiffEncoderCompression Compression { get; set; } = TiffEncoderCompression.None; /// - public DeflateCompressionLevel CompressionLevel { get; } = DeflateCompressionLevel.DefaultCompression; + public DeflateCompressionLevel CompressionLevel { get; set; } = DeflateCompressionLevel.DefaultCompression; /// public TiffEncodingMode Mode { get; set; } From b0e965fdf95a8e93e26092e51913ebe3c9ad3441 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 23 Feb 2021 17:53:50 +0100 Subject: [PATCH 208/275] - Seal tiff writer - TiffStreamWriter Write byte uses WriteByte method from base stream - Deflate compress strip: remove duplicate Dispose --- src/ImageSharp/Common/ByteOrder.cs | 2 +- .../Compressors/DeflateCompressor.cs | 17 ++++++++--------- src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs | 2 -- .../Formats/Tiff/Writers/TiffBiColorWriter.cs | 3 +-- .../Formats/Tiff/Writers/TiffGrayWriter.cs | 2 +- .../Formats/Tiff/Writers/TiffPaletteWriter.cs | 2 +- .../Formats/Tiff/Writers/TiffRgbWriter.cs | 2 +- .../Formats/Tiff/Writers/TiffStreamWriter.cs | 2 +- 8 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/ImageSharp/Common/ByteOrder.cs b/src/ImageSharp/Common/ByteOrder.cs index 8daa35eb3..cc38f1cde 100644 --- a/src/ImageSharp/Common/ByteOrder.cs +++ b/src/ImageSharp/Common/ByteOrder.cs @@ -4,7 +4,7 @@ namespace SixLabors.ImageSharp { /// - /// The tiff data stream byte order enum. + /// The byte order of the data stream. /// public enum ByteOrder { diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/DeflateCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/DeflateCompressor.cs index 46cb63a86..f8fa2b89c 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/DeflateCompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/DeflateCompressor.cs @@ -31,17 +31,16 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors public override void CompressStrip(Span rows, int height) { this.memoryStream.Seek(0, SeekOrigin.Begin); - using var stream = new ZlibDeflateStream(this.Allocator, this.memoryStream, this.compressionLevel); - - if (this.Predictor == TiffPredictor.Horizontal) + using (var stream = new ZlibDeflateStream(this.Allocator, this.memoryStream, this.compressionLevel)) { - HorizontalPredictor.ApplyHorizontalPrediction(rows, this.BytesPerRow, this.BitsPerPixel); - } + if (this.Predictor == TiffPredictor.Horizontal) + { + HorizontalPredictor.ApplyHorizontalPrediction(rows, this.BytesPerRow, this.BitsPerPixel); + } - stream.Write(rows); - - stream.Flush(); - stream.Dispose(); + stream.Write(rows); + stream.Flush(); + } int size = (int)this.memoryStream.Position; diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index d18ba7dc6..6e80899e4 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -26,8 +26,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { public const int DefaultStripSize = 8 * 1024; - public static readonly ByteOrder ByteOrder = BitConverter.IsLittleEndian ? ByteOrder.LittleEndian : ByteOrder.BigEndian; - private static readonly ushort ByteOrderMarker = BitConverter.IsLittleEndian ? TiffConstants.ByteOrderLittleEndianShort : TiffConstants.ByteOrderBigEndianShort; diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs index ca366f515..edeee81a1 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs @@ -12,7 +12,7 @@ using SixLabors.ImageSharp.Processing; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers { - internal class TiffBiColorWriter : TiffBaseColorWriter + internal sealed class TiffBiColorWriter : TiffBaseColorWriter where TPixel : unmanaged, IPixel { private readonly Image imageBlackWhite; @@ -25,7 +25,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers : base(image, memoryAllocator, configuration, entriesCollector) { // Convert image to black and white. - // TODO: Should we allow to skip this by the user, if its known to be black and white already? this.imageBlackWhite = new Image(configuration, new ImageMetadata(), new[] { image.Clone() }); this.imageBlackWhite.Mutate(img => img.BinaryDither(KnownDitherings.FloydSteinberg)); } diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffGrayWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffGrayWriter.cs index 7e2e4e304..4da9a9b97 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffGrayWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffGrayWriter.cs @@ -7,7 +7,7 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers { - internal class TiffGrayWriter : TiffCompositeColorWriter + internal sealed class TiffGrayWriter : TiffCompositeColorWriter where TPixel : unmanaged, IPixel { public TiffGrayWriter(ImageFrame image, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector) diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs index dc7dcf589..d15bc5e17 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs @@ -13,7 +13,7 @@ using SixLabors.ImageSharp.Processing.Processors.Quantization; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers { - internal class TiffPaletteWriter : TiffBaseColorWriter + internal sealed class TiffPaletteWriter : TiffBaseColorWriter where TPixel : unmanaged, IPixel { private const int ColorsPerChannel = 256; diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffRgbWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffRgbWriter.cs index b32b5ed90..acb0030bb 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffRgbWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffRgbWriter.cs @@ -7,7 +7,7 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers { - internal class TiffRgbWriter : TiffCompositeColorWriter + internal sealed class TiffRgbWriter : TiffCompositeColorWriter where TPixel : unmanaged, IPixel { public TiffRgbWriter(ImageFrame image, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector) diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs index b7749e0f6..39d46c878 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs @@ -61,7 +61,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers /// Writes a byte to the current stream. /// /// The byte to write. - public void Write(byte value) => this.BaseStream.Write(new byte[] { value }, 0, 1); + public void Write(byte value) => this.BaseStream.WriteByte(value); /// /// Writes a two-byte unsigned integer to the current stream. From 8077172088f4c7516e221db1acace288aefec338 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 25 Feb 2021 15:39:07 +0100 Subject: [PATCH 209/275] Introduce TiffBitsPerSample enum --- .../Formats/Tiff/TiffBitsPerSample.cs | 36 ++++ .../Tiff/TiffBitsPerSampleExtensions.cs | 99 +++++++++++ .../Formats/Tiff/TiffDecoderCore.cs | 15 +- .../Tiff/TiffDecoderMetadataCreator.cs | 4 +- .../Formats/Tiff/TiffDecoderOptionsParser.cs | 157 ++++++++---------- .../Formats/Tiff/TiffFrameMetadata.cs | 30 ++-- .../Formats/Tiff/TiffMetadataTests.cs | 3 +- 7 files changed, 225 insertions(+), 119 deletions(-) create mode 100644 src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs create mode 100644 src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs diff --git a/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs b/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs new file mode 100644 index 000000000..b556e5b95 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs @@ -0,0 +1,36 @@ +// 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, + + /// + /// One bit per sample for bicolor images. + /// + One, + + /// + /// Four bits per sample for grayscale images with 16 different levels of gray or paletted images with a palette of 16 colors. + /// + Four, + + /// + /// Eight bits per sample for grayscale images with 256 different levels of gray or paletted images with a palette of 256 colors. + /// + Eight, + + /// + /// Each channel has 8 Bits. + /// + Rgb888, + } +} diff --git a/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs b/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs new file mode 100644 index 000000000..884481b98 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs @@ -0,0 +1,99 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.Formats.Experimental.Tiff; + +namespace SixLabors.ImageSharp.Formats.Tiff +{ + internal static class TiffBitsPerSampleExtensions + { + private static readonly ushort[] One = { 1 }; + + private static readonly ushort[] Four = { 4 }; + + private static readonly ushort[] Eight = { 8 }; + + private static readonly ushort[] Rgb888 = { 8, 8, 8 }; + + /// + /// 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[] Bits(this TiffBitsPerSample tiffBitsPerSample) + { + switch (tiffBitsPerSample) + { + case TiffBitsPerSample.One: + return One; + case TiffBitsPerSample.Four: + return Four; + case TiffBitsPerSample.Eight: + return Eight; + case TiffBitsPerSample.Rgb888: + return Rgb888; + + default: + TiffThrowHelper.ThrowNotSupported("The bits per pixels are not supported"); + 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[0] == Rgb888[0] && bitsPerSample[1] == Rgb888[1] && bitsPerSample[2] == Rgb888[2]) + { + return TiffBitsPerSample.Rgb888; + } + + break; + + case 1: + if (bitsPerSample[0] == One[0]) + { + return TiffBitsPerSample.One; + } + + if (bitsPerSample[0] == Four[0]) + { + return TiffBitsPerSample.Four; + } + + if (bitsPerSample[0] == Eight[0]) + { + return TiffBitsPerSample.Eight; + } + + break; + } + + return TiffBitsPerSample.Unknown; + } + + /// + /// Gets the bits per pixel for the given bits per sample. + /// + /// The tiff bits per sample. + /// Bits per pixel. + public static int BitsPerPixel(this TiffBitsPerSample tiffBitsPerSample) + { + var bitsPerSample = tiffBitsPerSample.Bits(); + int bitsPerPixel = 0; + foreach (var bits in bitsPerSample) + { + bitsPerPixel += bits; + } + + return bitsPerPixel; + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index fe81d2edb..876816a36 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Threading; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; @@ -48,9 +49,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff } /// - /// Gets or sets the number of bits for each sample of the pixel format used to encode the image. + /// Gets or sets the number of bits per component of the pixel format used to decode the image. /// - public ushort[] BitsPerSample { get; set; } + public TiffBitsPerSample BitsPerSample { get; set; } /// /// Gets or sets the bits per pixel. @@ -154,7 +155,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff ImageMetadata metadata = TiffDecoderMetadataCreator.Create(framesMetadata, this.ignoreMetadata, reader.ByteOrder); TiffFrameMetadata root = framesMetadata[0]; - return new ImageInfo(new PixelTypeInfo(root.BitsPerPixel), (int)root.Width, (int)root.Height, metadata); + return new ImageInfo(new PixelTypeInfo(root.BitsPerSample.BitsPerPixel()), (int)root.Width, (int)root.Height, metadata); } /// @@ -213,7 +214,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff } else { - bitsPerPixel = this.BitsPerSample[plane]; + bitsPerPixel = this.BitsPerSample.Bits()[plane]; } int bytesPerRow = ((width * bitsPerPixel) + 7) / 8; @@ -233,7 +234,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff private void DecodeStripsPlanar(ImageFrame frame, int rowsPerStrip, Number[] stripOffsets, Number[] stripByteCounts) where TPixel : unmanaged, IPixel { - int stripsPerPixel = this.BitsPerSample.Length; + int stripsPerPixel = this.BitsPerSample.Bits().Length; int stripsPerPlane = stripOffsets.Length / stripsPerPixel; int bitsPerPixel = this.BitsPerPixel; @@ -251,7 +252,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff using TiffBaseDecompresor decompressor = TiffDecompressorsFactory.Create(this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, frame.Width, bitsPerPixel, this.Predictor, this.FaxCompressionOptions); - RgbPlanarTiffColor colorDecoder = TiffColorDecoderFactory.CreatePlanar(this.ColorType, this.BitsPerSample, this.ColorMap); + RgbPlanarTiffColor colorDecoder = TiffColorDecoderFactory.CreatePlanar(this.ColorType, this.BitsPerSample.Bits(), this.ColorMap); for (int i = 0; i < stripsPerPlane; i++) { @@ -294,7 +295,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff using TiffBaseDecompresor decompressor = TiffDecompressorsFactory.Create(this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, frame.Width, bitsPerPixel, this.Predictor, this.FaxCompressionOptions); - TiffBaseColorDecoder colorDecoder = TiffColorDecoderFactory.Create(this.ColorType, this.BitsPerSample, this.ColorMap); + TiffBaseColorDecoder colorDecoder = TiffColorDecoderFactory.Create(this.ColorType, this.BitsPerSample.Bits(), this.ColorMap); for (int stripIndex = 0; stripIndex < stripOffsets.Length; stripIndex++) { diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs index de17ada5d..b1696dc86 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; - +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Icc; @@ -129,6 +129,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff } private static TiffBitsPerPixel GetBitsPerPixel(TiffFrameMetadata firstFrameMetaData) - => (TiffBitsPerPixel)firstFrameMetaData.BitsPerPixel; + => (TiffBitsPerPixel)firstFrameMetaData.BitsPerSample.BitsPerPixel(); } } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs index 123b8494e..fa90c4522 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs @@ -3,6 +3,7 @@ using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Metadata.Profiles.Exif; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff @@ -59,7 +60,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff options.Predictor = entries.Predictor; options.PhotometricInterpretation = entries.PhotometricInterpretation; options.BitsPerSample = entries.BitsPerSample; - options.BitsPerPixel = entries.BitsPerPixel; + options.BitsPerPixel = entries.BitsPerSample.BitsPerPixel(); ParseColorType(options, entries); ParseCompression(options, entries); @@ -71,38 +72,36 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { case TiffPhotometricInterpretation.WhiteIsZero: { - if (options.BitsPerSample.Length == 1) + if (options.BitsPerSample.Bits().Length != 1) { - switch (options.BitsPerSample[0]) - { - 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; - } - } + TiffThrowHelper.ThrowNotSupported("The number of samples in the TIFF BitsPerSample entry is not supported."); } - else + + switch (options.BitsPerSample) { - TiffThrowHelper.ThrowNotSupported("The number of samples in the TIFF BitsPerSample entry is not supported."); + case TiffBitsPerSample.Eight: + { + options.ColorType = TiffColorType.WhiteIsZero8; + break; + } + + case TiffBitsPerSample.Four: + { + options.ColorType = TiffColorType.WhiteIsZero4; + break; + } + + case TiffBitsPerSample.One: + { + options.ColorType = TiffColorType.WhiteIsZero1; + break; + } + + default: + { + options.ColorType = TiffColorType.WhiteIsZero; + break; + } } break; @@ -110,38 +109,36 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff case TiffPhotometricInterpretation.BlackIsZero: { - if (options.BitsPerSample.Length == 1) + if (options.BitsPerSample.Bits().Length != 1) { - switch (options.BitsPerSample[0]) - { - 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; - } - } + TiffThrowHelper.ThrowNotSupported("The number of samples in the TIFF BitsPerSample entry is not supported."); } - else + + switch (options.BitsPerSample) { - TiffThrowHelper.ThrowNotSupported("The number of samples in the TIFF BitsPerSample entry is not supported."); + case TiffBitsPerSample.Eight: + { + options.ColorType = TiffColorType.BlackIsZero8; + break; + } + + case TiffBitsPerSample.Four: + { + options.ColorType = TiffColorType.BlackIsZero4; + break; + } + + case TiffBitsPerSample.One: + { + options.ColorType = TiffColorType.BlackIsZero1; + break; + } + + default: + { + options.ColorType = TiffColorType.BlackIsZero; + break; + } } break; @@ -149,27 +146,18 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff case TiffPhotometricInterpretation.Rgb: { - if (options.BitsPerSample.Length == 3) + if (options.BitsPerSample.Bits().Length != 3) { - if (options.PlanarConfiguration == TiffPlanarConfiguration.Chunky) - { - if (options.BitsPerSample[0] == 8 && options.BitsPerSample[1] == 8 && options.BitsPerSample[2] == 8) - { - options.ColorType = TiffColorType.Rgb888; - } - else - { - options.ColorType = TiffColorType.Rgb; - } - } - else - { - options.ColorType = TiffColorType.RgbPlanar; - } + TiffThrowHelper.ThrowNotSupported("The number of samples in the TIFF BitsPerSample entry is not supported."); + } + + if (options.PlanarConfiguration == TiffPlanarConfiguration.Chunky) + { + options.ColorType = options.BitsPerSample == TiffBitsPerSample.Rgb888 ? TiffColorType.Rgb888 : TiffColorType.Rgb; } else { - TiffThrowHelper.ThrowNotSupported("The number of samples in the TIFF BitsPerSample entry is not supported."); + options.ColorType = TiffColorType.RgbPlanar; } break; @@ -180,21 +168,12 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff options.ColorMap = entries.ColorMap; if (options.ColorMap != null) { - if (options.BitsPerSample.Length == 1) - { - switch (options.BitsPerSample[0]) - { - default: - { - options.ColorType = TiffColorType.PaletteColor; - break; - } - } - } - else + 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 { @@ -206,7 +185,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff default: { - TiffThrowHelper.ThrowNotSupported("The specified TIFF photometric interpretation is not supported: " + options.PhotometricInterpretation); + TiffThrowHelper.ThrowNotSupported($"The specified TIFF photometric interpretation is not supported: {options.PhotometricInterpretation}"); } break; diff --git a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs index 363e68cee..21a24896d 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; @@ -88,7 +89,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// /// Gets the number of bits per component. /// - public ushort[] BitsPerSample + public TiffBitsPerSample BitsPerSample { get { @@ -98,32 +99,21 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff if (this.PhotometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero || this.PhotometricInterpretation == TiffPhotometricInterpretation.BlackIsZero) { - bits = new[] { (ushort)1 }; + return TiffBitsPerSample.One; } - else - { - TiffThrowHelper.ThrowNotSupported("The TIFF BitsPerSample entry is missing."); - } - } - return bits; - } - } - - internal int BitsPerPixel - { - get - { - int bitsPerPixel = 0; - foreach (var bits in this.BitsPerSample) - { - bitsPerPixel += bits; + TiffThrowHelper.ThrowNotSupported("The TIFF BitsPerSample entry is missing which is required to decode the image."); } - return bitsPerPixel; + return bits.GetBitsPerSample(); } } + /// + /// Gets the bits per pixel. + /// + public int BitsPerPixel => this.BitsPerSample.BitsPerPixel(); + /// /// Gets the compression scheme used on the image data. /// diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index 9acd44608..e4b935e0f 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -8,6 +8,7 @@ using System.Linq; using SixLabors.ImageSharp.Formats.Experimental.Tiff; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Icc; @@ -187,7 +188,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal(32u, frame.Width); Assert.Equal(32u, frame.Height); - Assert.Equal(new ushort[] { 4 }, frame.BitsPerSample); + Assert.Equal(TiffBitsPerSample.Four, frame.BitsPerSample); Assert.Equal(TiffCompression.Lzw, frame.Compression); Assert.Equal(TiffPhotometricInterpretation.PaletteColor, frame.PhotometricInterpretation); Assert.Equal("This is Название", frame.ImageDescription); From ffa38bc2736f768e41903eb5ef5af543537bde20 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 25 Feb 2021 16:34:39 +0100 Subject: [PATCH 210/275] Remove properties from TiffFrame meta data which can be received directly from the ExifProfile --- .../Formats/Tiff/TiffFrameMetadata.cs | 74 ------------------- .../Formats/Tiff/TiffMetadataTests.cs | 68 ++++++++--------- 2 files changed, 34 insertions(+), 108 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs index 21a24896d..5d3674422 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs @@ -43,13 +43,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// /// Gets a general indication of the kind of data contained in this subfile. /// - /// A general indication of the kind of data contained in this subfile. public TiffNewSubfileType SubfileType => (TiffNewSubfileType?)this.ExifProfile.GetValue(ExifTag.SubfileType)?.Value ?? TiffNewSubfileType.FullImage; /// /// Gets a general indication of the kind of data contained in this subfile. /// - /// A general indication of the kind of data contained in this subfile. public TiffSubfileType? OldSubfileType => (TiffSubfileType?)this.ExifProfile.GetValue(ExifTag.OldSubfileType)?.Value; /// @@ -154,33 +152,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// internal TiffFillOrder FillOrder => (TiffFillOrder?)this.ExifProfile.GetValue(ExifTag.FillOrder)?.Value ?? TiffFillOrder.MostSignificantBitFirst; - /// - /// Gets or sets the a string that describes the subject of the image. - /// - public string ImageDescription - { - get => this.ExifProfile.GetValue(ExifTag.ImageDescription)?.Value; - set => this.ExifProfile.SetValue(ExifTag.ImageDescription, value); - } - - /// - /// Gets or sets the scanner manufacturer. - /// - public string Make - { - get => this.ExifProfile.GetValue(ExifTag.Make)?.Value; - set => this.ExifProfile.SetValue(ExifTag.Make, value); - } - - /// - /// Gets or sets the scanner model name or number. - /// - public string Model - { - get => this.ExifProfile.GetValue(ExifTag.Model)?.Value; - set => this.ExifProfile.SetValue(ExifTag.Model, value); - } - /// /// Gets for each strip, the byte offset of that strip. /// @@ -259,42 +230,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// public PixelResolutionUnit ResolutionUnit => this.GetResolutionUnit(); - /// - /// Gets or sets the name and version number of the software package(s) used to create the image. - /// - public string Software - { - get => this.ExifProfile.GetValue(ExifTag.Software)?.Value; - set => this.ExifProfile.SetValue(ExifTag.Software, value); - } - - /// - /// Gets or sets the date and time of image creation. - /// - public string DateTime - { - get => this.ExifProfile.GetValue(ExifTag.DateTime)?.Value; - set => this.ExifProfile.SetValue(ExifTag.DateTime, value); - } - - /// - /// Gets or sets the person who created the image. - /// - public string Artist - { - get => this.ExifProfile.GetValue(ExifTag.Artist)?.Value; - set => this.ExifProfile.SetValue(ExifTag.Artist, value); - } - - /// - /// Gets or sets the computer and/or operating system in use at the time of image creation. - /// - public string HostComputer - { - get => this.ExifProfile.GetValue(ExifTag.HostComputer)?.Value; - set => this.ExifProfile.SetValue(ExifTag.HostComputer, value); - } - /// /// Gets a color map for palette color images. /// @@ -305,15 +240,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// public ushort[] ExtraSamples => this.ExifProfile.GetValue(ExifTag.ExtraSamples)?.Value; - /// - /// Gets or sets the copyright notice. - /// - public string Copyright - { - get => this.ExifProfile.GetValue(ExifTag.Copyright)?.Value; - set => this.ExifProfile.SetValue(ExifTag.Copyright, value); - } - /// /// Gets a mathematical operator that is applied to the image data before an encoding scheme is applied. /// diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index e4b935e0f..b2eb3add9 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -191,9 +191,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal(TiffBitsPerSample.Four, frame.BitsPerSample); Assert.Equal(TiffCompression.Lzw, frame.Compression); Assert.Equal(TiffPhotometricInterpretation.PaletteColor, frame.PhotometricInterpretation); - Assert.Equal("This is Название", frame.ImageDescription); - Assert.Equal("This is Изготовитель камеры", frame.Make); - Assert.Equal("This is Модель камеры", frame.Model); + Assert.Equal("This is Название", frame.ExifProfile.GetValue(ExifTag.ImageDescription).Value); + Assert.Equal("This is Изготовитель камеры", frame.ExifProfile.GetValue(ExifTag.Make).Value); + Assert.Equal("This is Модель камеры", frame.ExifProfile.GetValue(ExifTag.Model).Value); Assert.Equal(new Number[] { 8u }, frame.StripOffsets, new NumberComparer()); Assert.Equal(1, frame.SamplesPerPixel); Assert.Equal(32u, frame.RowsPerStrip); @@ -202,10 +202,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal(10, frame.VerticalResolution); Assert.Equal(TiffPlanarConfiguration.Chunky, frame.PlanarConfiguration); Assert.Equal(PixelResolutionUnit.PixelsPerInch, frame.ResolutionUnit); - Assert.Equal("IrfanView", frame.Software); - Assert.Null(frame.DateTime); - Assert.Equal("This is author1;Author2", frame.Artist); - Assert.Null(frame.HostComputer); + Assert.Equal("IrfanView", frame.ExifProfile.GetValue(ExifTag.Software).Value); + Assert.Null(frame.ExifProfile.GetValue(ExifTag.DateTime)?.Value); + Assert.Equal("This is author1;Author2", frame.ExifProfile.GetValue(ExifTag.Artist).Value); + Assert.Null(frame.ExifProfile.GetValue(ExifTag.HostComputer)?.Value); Assert.Equal(48, frame.ColorMap.Length); Assert.Equal(10537, frame.ColorMap[0]); Assert.Equal(14392, frame.ColorMap[1]); @@ -215,7 +215,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Null(frame.ExtraSamples); Assert.Equal(TiffPredictor.None, frame.Predictor); Assert.Null(frame.SampleFormat); - Assert.Equal("This is Авторские права", frame.Copyright); + Assert.Equal("This is Авторские права", frame.ExifProfile.GetValue(ExifTag.Copyright).Value); Assert.Equal(4, frame.ExifProfile.GetValue(ExifTag.Rating).Value); Assert.Equal(75, frame.ExifProfile.GetValue(ExifTag.RatingPercent).Value); } @@ -293,33 +293,33 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal(frameMeta.HorizontalResolution, frameMetaOut.HorizontalResolution); Assert.Equal(frameMeta.VerticalResolution, frameMetaOut.VerticalResolution); - Assert.Equal("ImageSharp", frameMetaOut.Software); + Assert.Equal("ImageSharp", frameMetaOut.ExifProfile.GetValue(ExifTag.Software).Value); if (preserveMetadata) { Assert.Equal(tiffMeta.XmpProfile, tiffMetaOut.XmpProfile); - Assert.Equal("IrfanView", frameMeta.Software); - Assert.Equal("This is Название", frameMeta.ImageDescription); - Assert.Equal("This is Изготовитель камеры", frameMeta.Make); - Assert.Equal("This is Авторские права", frameMeta.Copyright); + Assert.Equal("IrfanView", frameMeta.ExifProfile.GetValue(ExifTag.Software).Value); + Assert.Equal("This is Название", frameMeta.ExifProfile.GetValue(ExifTag.ImageDescription).Value); + Assert.Equal("This is Изготовитель камеры", frameMeta.ExifProfile.GetValue(ExifTag.Make).Value); + Assert.Equal("This is Авторские права", frameMeta.ExifProfile.GetValue(ExifTag.Copyright).Value); - Assert.Equal(frameMeta.ImageDescription, frameMetaOut.ImageDescription); - Assert.Equal(frameMeta.Make, frameMetaOut.Make); - Assert.Equal(frameMeta.Copyright, frameMetaOut.Copyright); + Assert.Equal(frameMeta.ExifProfile.GetValue(ExifTag.ImageDescription).Value, frameMetaOut.ExifProfile.GetValue(ExifTag.ImageDescription).Value); + Assert.Equal(frameMeta.ExifProfile.GetValue(ExifTag.Make).Value, frameMetaOut.ExifProfile.GetValue(ExifTag.Make).Value); + Assert.Equal(frameMeta.ExifProfile.GetValue(ExifTag.Copyright).Value, frameMetaOut.ExifProfile.GetValue(ExifTag.Copyright).Value); } else { Assert.Null(tiffMetaOut.XmpProfile); - Assert.Null(frameMeta.Software); - Assert.Null(frameMeta.ImageDescription); - Assert.Null(frameMeta.Make); - Assert.Null(frameMeta.Copyright); + Assert.Null(frameMeta.ExifProfile.GetValue(ExifTag.Software)?.Value); + Assert.Null(frameMeta.ExifProfile.GetValue(ExifTag.ImageDescription)?.Value); + Assert.Null(frameMeta.ExifProfile.GetValue(ExifTag.Make)?.Value); + Assert.Null(frameMeta.ExifProfile.GetValue(ExifTag.Copyright)?.Value); - Assert.Null(frameMetaOut.ImageDescription); - Assert.Null(frameMetaOut.Make); - Assert.Null(frameMetaOut.Copyright); + Assert.Null(frameMetaOut.ExifProfile.GetValue(ExifTag.ImageDescription)?.Value); + Assert.Null(frameMetaOut.ExifProfile.GetValue(ExifTag.Make)?.Value); + Assert.Null(frameMetaOut.ExifProfile.GetValue(ExifTag.Copyright)?.Value); } } @@ -350,8 +350,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff coreMeta.VerticalResolution = 5400; var datetime = DateTime.Now.ToString(CultureInfo.InvariantCulture); - frameMeta.ImageDescription = "test ImageDescription"; - frameMeta.DateTime = datetime; + frameMeta.ExifProfile.SetValue(ExifTag.ImageDescription, "test ImageDescription"); + frameMeta.ExifProfile.SetValue(ExifTag.DateTime, datetime); // Save to Tiff var tiffEncoder = new TiffEncoder { Mode = TiffEncodingMode.Default, Compression = TiffEncoderCompression.Deflate }; @@ -385,7 +385,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal(frameMeta.HorizontalResolution, frameMetaOut.HorizontalResolution); Assert.Equal(frameMeta.VerticalResolution, frameMetaOut.VerticalResolution); - Assert.Equal("ImageSharp", frameMetaOut.Software); + Assert.Equal("ImageSharp", frameMetaOut.ExifProfile.GetValue(ExifTag.Software)?.Value); if (preserveMetadata) { @@ -397,11 +397,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal(coreMeta.IptcProfile.Data, coreMetaOut.IptcProfile.Data); Assert.Equal(coreMeta.IccProfile.ToByteArray(), coreMetaOut.IccProfile.ToByteArray()); - Assert.Equal("test ImageDescription", frameMeta.ImageDescription); - Assert.Equal(datetime, frameMeta.DateTime); + Assert.Equal("test ImageDescription", frameMeta.ExifProfile.GetValue(ExifTag.ImageDescription).Value); + Assert.Equal(datetime, frameMeta.ExifProfile.GetValue(ExifTag.DateTime)?.Value); - Assert.Equal(frameMeta.ImageDescription, frameMetaOut.ImageDescription); - Assert.Equal(frameMeta.DateTime, frameMetaOut.DateTime); + Assert.Equal(frameMeta.ExifProfile.GetValue(ExifTag.ImageDescription).Value, frameMetaOut.ExifProfile.GetValue(ExifTag.ImageDescription).Value); + Assert.Equal(frameMeta.ExifProfile.GetValue(ExifTag.DateTime).Value, frameMetaOut.ExifProfile.GetValue(ExifTag.DateTime).Value); } else { @@ -409,11 +409,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Null(coreMetaOut.IptcProfile); Assert.Null(coreMetaOut.IccProfile); - Assert.Null(frameMeta.ImageDescription); - Assert.Null(frameMeta.DateTime); + Assert.Null(frameMeta.ExifProfile.GetValue(ExifTag.ImageDescription)?.Value); + Assert.Null(frameMeta.ExifProfile.GetValue(ExifTag.DateTime)?.Value); - Assert.Null(frameMetaOut.ImageDescription); - Assert.Null(frameMetaOut.DateTime); + Assert.Null(frameMetaOut.ExifProfile.GetValue(ExifTag.ImageDescription)?.Value); + Assert.Null(frameMetaOut.ExifProfile.GetValue(ExifTag.DateTime)?.Value); } } From 1dbe5838245b7855209d3ba3b75446740d6fa08c Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 1 Mar 2021 11:22:17 +0100 Subject: [PATCH 211/275] Allow encoding 4bit color palette images --- .../Compression/Compressors/NoCompressor.cs | 5 +- .../Compressors/T4BitCompressor.cs | 2 + .../Decompressors/DeflateTiffCompression.cs | 2 +- .../Decompressors/LzwTiffCompression.cs | 2 +- .../ModifiedHuffmanTiffCompression.cs | 7 +- .../Decompressors/NoneTiffCompression.cs | 10 +- .../Decompressors/PackBitsTiffCompression.cs | 8 +- .../Decompressors/T4TiffCompression.cs | 9 +- ...Decompresor.cs => TiffBaseDecompressor.cs} | 4 +- .../Tiff/Compression/TiffCompressorFactory.cs | 2 +- .../Compression/TiffDecompressorsFactory.cs | 10 +- .../Formats/Tiff/Constants/TiffConstants.cs | 20 ++++ .../Formats/Tiff/ITiffEncoderOptions.cs | 5 + .../Tiff/TiffBitsPerSampleExtensions.cs | 27 ++--- .../Formats/Tiff/TiffDecoderCore.cs | 4 +- src/ImageSharp/Formats/Tiff/TiffEncoder.cs | 3 + .../Formats/Tiff/TiffEncoderCore.cs | 107 ++++++++++++------ .../Tiff/TiffEncoderEntriesCollector.cs | 22 ++-- .../Tiff/Writers/TiffBaseColorWriter.cs | 8 +- .../Tiff/Writers/TiffColorWriterFactory.cs | 5 +- .../Formats/Tiff/Writers/TiffPaletteWriter.cs | 76 +++++++++---- .../Compression/NoneTiffCompressionTests.cs | 2 +- .../PackBitsTiffCompressionTests.cs | 2 +- .../Formats/Tiff/TiffEncoderTests.cs | 38 ++----- tests/ImageSharp.Tests/TestImages.cs | 1 + .../Input/Tiff/bike_colorpalette_4bit.tiff | 3 + 26 files changed, 242 insertions(+), 142 deletions(-) rename src/ImageSharp/Formats/Tiff/Compression/{TiffBaseDecompresor.cs => TiffBaseDecompressor.cs} (90%) create mode 100644 tests/Images/Input/Tiff/bike_colorpalette_4bit.tiff diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/NoCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/NoCompressor.cs index f4a902b46..79fbd29b4 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/NoCompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/NoCompressor.cs @@ -3,13 +3,14 @@ using System; using System.IO; +using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors { internal class NoCompressor : TiffBaseCompressor { - public NoCompressor(Stream output) - : base(output, default, default, default) + public NoCompressor(Stream output, MemoryAllocator memoryAllocator, int width, int bitsPerPixel) + : base(output, memoryAllocator, width, bitsPerPixel) { } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitCompressor.cs index 13d19f7ff..bb631c5a9 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitCompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitCompressor.cs @@ -201,8 +201,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors this.useModifiedHuffman = useModifiedHuffman; } + /// public override TiffEncoderCompression Method => this.useModifiedHuffman ? TiffEncoderCompression.ModifiedHuffman : TiffEncoderCompression.CcittGroup3Fax; + /// public override void Initialize(int rowsPerStrip) { // This is too much memory allocated, but just 1 bit per pixel will not do, if the compression rate is not good. diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs index be63bdad8..a6ca8c78d 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs @@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso /// /// Note that the 'OldDeflate' compression type is identical to the 'Deflate' compression type. /// - internal class DeflateTiffCompression : TiffBaseDecompresor + internal class DeflateTiffCompression : TiffBaseDecompressor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs index a14738e44..f3b37b09c 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso /// /// Class to handle cases where TIFF image data is compressed using LZW compression. /// - internal class LzwTiffCompression : TiffBaseDecompresor + internal class LzwTiffCompression : TiffBaseDecompressor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs index 7a7cd20f7..9c2495efc 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs @@ -22,10 +22,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso /// Initializes a new instance of the class. /// /// The memory allocator. - /// The photometric interpretation. /// The image width. - public ModifiedHuffmanTiffCompression(MemoryAllocator allocator, TiffPhotometricInterpretation photometricInterpretation, int width) - : base(allocator, FaxCompressionOptions.None, photometricInterpretation, width) + /// The number of bits per pixel. + /// The photometric interpretation. + public ModifiedHuffmanTiffCompression(MemoryAllocator allocator, int width, int bitsPerPixel, TiffPhotometricInterpretation photometricInterpretation) + : base(allocator, width, bitsPerPixel, FaxCompressionOptions.None, photometricInterpretation) { bool isWhiteZero = photometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero; this.whiteValue = (byte)(isWhiteZero ? 0 : 1); diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/NoneTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/NoneTiffCompression.cs index f041a00e9..3475b74ce 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/NoneTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/NoneTiffCompression.cs @@ -4,19 +4,23 @@ using System; using SixLabors.ImageSharp.IO; +using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors { /// /// Class to handle cases where TIFF image data is not compressed. /// - internal class NoneTiffCompression : TiffBaseDecompresor + internal class NoneTiffCompression : TiffBaseDecompressor { /// /// Initializes a new instance of the class. /// - public NoneTiffCompression() - : base(default, default, default) + /// The memory allocator. + /// The width of the image. + /// The bits per pixel. + public NoneTiffCompression(MemoryAllocator memoryAllocator, int width, int bitsPerPixel) + : base(memoryAllocator, width, bitsPerPixel) { } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/PackBitsTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/PackBitsTiffCompression.cs index c7461a807..727e0eeb7 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/PackBitsTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/PackBitsTiffCompression.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso /// /// Class to handle cases where TIFF image data is compressed using PackBits compression. /// - internal class PackBitsTiffCompression : TiffBaseDecompresor + internal class PackBitsTiffCompression : TiffBaseDecompressor { private IMemoryOwner compressedDataMemory; @@ -20,8 +20,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso /// Initializes a new instance of the class. /// /// The memoryAllocator to use for buffer allocations. - public PackBitsTiffCompression(MemoryAllocator memoryAllocator) - : base(memoryAllocator, default, default) + /// The width of the image. + /// The number of bits per pixel. + public PackBitsTiffCompression(MemoryAllocator memoryAllocator, int width, int bitsPerPixel) + : base(memoryAllocator, width, bitsPerPixel) { } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs index 275e3d598..549f75846 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso /// /// Class to handle cases where TIFF image data is compressed using CCITT T4 compression. /// - internal class T4TiffCompression : TiffBaseDecompresor + internal class T4TiffCompression : TiffBaseDecompressor { private readonly FaxCompressionOptions faxCompressionOptions; @@ -24,11 +24,12 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso /// Initializes a new instance of the class. /// /// The memory allocator. + /// The image width. + /// The number of bits per pixel. /// Fax compression options. /// The photometric interpretation. - /// The image width. - public T4TiffCompression(MemoryAllocator allocator, FaxCompressionOptions faxOptions, TiffPhotometricInterpretation photometricInterpretation, int width) - : base(allocator, width, default) + public T4TiffCompression(MemoryAllocator allocator, int width, int bitsPerPixel, FaxCompressionOptions faxOptions, TiffPhotometricInterpretation photometricInterpretation) + : base(allocator, width, bitsPerPixel) { this.faxCompressionOptions = faxOptions; diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseDecompresor.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseDecompressor.cs similarity index 90% rename from src/ImageSharp/Formats/Tiff/Compression/TiffBaseDecompresor.cs rename to src/ImageSharp/Formats/Tiff/Compression/TiffBaseDecompressor.cs index 5f981911d..2f40214eb 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseDecompresor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseDecompressor.cs @@ -13,9 +13,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression /// /// The base tiff decompressor class. /// - internal abstract class TiffBaseDecompresor : TiffBaseCompression + internal abstract class TiffBaseDecompressor : TiffBaseCompression { - protected TiffBaseDecompresor(MemoryAllocator allocator, int width, int bitsPerPixel, TiffPredictor predictor = TiffPredictor.None) + protected TiffBaseDecompressor(MemoryAllocator allocator, int width, int bitsPerPixel, TiffPredictor predictor = TiffPredictor.None) : base(allocator, width, bitsPerPixel, predictor) { } diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressorFactory.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressorFactory.cs index eeb14fb74..7cd39d8cb 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressorFactory.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressorFactory.cs @@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression DebugGuard.IsTrue(compressionLevel == DeflateCompressionLevel.DefaultCompression, "No deflate compression level is expected to be set"); DebugGuard.IsTrue(predictor == TiffPredictor.None, "Predictor should only be used with lzw or deflate compression"); - return new NoCompressor(output); + return new NoCompressor(output, allocator, width, bitsPerPixel); case TiffEncoderCompression.PackBits: DebugGuard.IsTrue(compressionLevel == DeflateCompressionLevel.DefaultCompression, "No deflate compression level is expected to be set"); diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs index 130205b5b..641142d4c 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression { internal static class TiffDecompressorsFactory { - public static TiffBaseDecompresor Create( + public static TiffBaseDecompressor Create( TiffDecoderCompressionType method, MemoryAllocator allocator, TiffPhotometricInterpretation photometricInterpretation, @@ -23,12 +23,12 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression case TiffDecoderCompressionType.None: DebugGuard.IsTrue(predictor == TiffPredictor.None, "Predictor should only be used with lzw or deflate compression"); DebugGuard.IsTrue(faxOptions == FaxCompressionOptions.None, "No fax compression options are expected"); - return new NoneTiffCompression(); + return new NoneTiffCompression(allocator, width, bitsPerPixel); case TiffDecoderCompressionType.PackBits: DebugGuard.IsTrue(predictor == TiffPredictor.None, "Predictor should only be used with lzw or deflate compression"); DebugGuard.IsTrue(faxOptions == FaxCompressionOptions.None, "No fax compression options are expected"); - return new PackBitsTiffCompression(allocator); + return new PackBitsTiffCompression(allocator, width, bitsPerPixel); case TiffDecoderCompressionType.Deflate: DebugGuard.IsTrue(faxOptions == FaxCompressionOptions.None, "No fax compression options are expected"); @@ -40,11 +40,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression case TiffDecoderCompressionType.T4: DebugGuard.IsTrue(predictor == TiffPredictor.None, "Predictor should only be used with lzw or deflate compression"); - return new T4TiffCompression(allocator, faxOptions, photometricInterpretation, width); + return new T4TiffCompression(allocator, width, bitsPerPixel, faxOptions, photometricInterpretation); case TiffDecoderCompressionType.HuffmanRle: DebugGuard.IsTrue(predictor == TiffPredictor.None, "Predictor should only be used with lzw or deflate compression"); - return new ModifiedHuffmanTiffCompression(allocator, photometricInterpretation, width); + return new ModifiedHuffmanTiffCompression(allocator, width, bitsPerPixel, photometricInterpretation); default: throw TiffThrowHelper.NotSupportedDecompressor(nameof(method)); diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs index 8a591fc83..894a6d348 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs @@ -75,6 +75,26 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants /// public const int SizeOfDouble = 8; + /// + /// The bits per sample for 1 bit bicolor images. + /// + public static readonly ushort[] BitsPerSample1Bit = { 1 }; + + /// + /// The bits per sample for images with a 4 color palette. + /// + public static readonly ushort[] BitsPerSample4Bit = { 4 }; + + /// + /// The bits per sample for 8 bit images. + /// + public static readonly ushort[] BitsPerSample8Bit = { 8 }; + + /// + /// The bits per sample for images with 8 bits for each color channel. + /// + public static readonly ushort[] BitsPerSampleRgb8Bit = { 8, 8, 8 }; + /// /// The list of mimetypes that equate to a tiff. /// diff --git a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs index de1095eee..efa5dcf85 100644 --- a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs +++ b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs @@ -11,6 +11,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// internal interface ITiffEncoderOptions { + /// + /// Gets or sets the number of bits per pixel. + /// + TiffBitsPerPixel? BitsPerPixel { get; set; } + /// /// Gets the compression type to use. /// diff --git a/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs b/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs index 884481b98..307ae4ee9 100644 --- a/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs +++ b/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs @@ -3,19 +3,12 @@ using System; using SixLabors.ImageSharp.Formats.Experimental.Tiff; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; namespace SixLabors.ImageSharp.Formats.Tiff { internal static class TiffBitsPerSampleExtensions { - private static readonly ushort[] One = { 1 }; - - private static readonly ushort[] Four = { 4 }; - - private static readonly ushort[] Eight = { 8 }; - - private static readonly ushort[] Rgb888 = { 8, 8, 8 }; - /// /// Gets the bits per channel array for a given BitsPerSample value, e,g, for RGB888: [8, 8, 8] /// @@ -26,13 +19,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff switch (tiffBitsPerSample) { case TiffBitsPerSample.One: - return One; + return TiffConstants.BitsPerSample1Bit; case TiffBitsPerSample.Four: - return Four; + return TiffConstants.BitsPerSample4Bit; case TiffBitsPerSample.Eight: - return Eight; + return TiffConstants.BitsPerSample8Bit; case TiffBitsPerSample.Rgb888: - return Rgb888; + return TiffConstants.BitsPerSampleRgb8Bit; default: TiffThrowHelper.ThrowNotSupported("The bits per pixels are not supported"); @@ -50,7 +43,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff switch (bitsPerSample.Length) { case 3: - if (bitsPerSample[0] == Rgb888[0] && bitsPerSample[1] == Rgb888[1] && bitsPerSample[2] == Rgb888[2]) + if (bitsPerSample[0] == TiffConstants.BitsPerSampleRgb8Bit[0] && + bitsPerSample[1] == TiffConstants.BitsPerSampleRgb8Bit[1] && + bitsPerSample[2] == TiffConstants.BitsPerSampleRgb8Bit[2]) { return TiffBitsPerSample.Rgb888; } @@ -58,17 +53,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff break; case 1: - if (bitsPerSample[0] == One[0]) + if (bitsPerSample[0] == TiffConstants.BitsPerSample1Bit[0]) { return TiffBitsPerSample.One; } - if (bitsPerSample[0] == Four[0]) + if (bitsPerSample[0] == TiffConstants.BitsPerSample4Bit[0]) { return TiffBitsPerSample.Four; } - if (bitsPerSample[0] == Eight[0]) + if (bitsPerSample[0] == TiffConstants.BitsPerSample8Bit[0]) { return TiffBitsPerSample.Eight; } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 876816a36..bedd132fa 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -250,7 +250,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff stripBuffers[stripIndex] = this.memoryAllocator.AllocateManagedByteBuffer(uncompressedStripSize); } - using TiffBaseDecompresor decompressor = TiffDecompressorsFactory.Create(this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, frame.Width, bitsPerPixel, this.Predictor, this.FaxCompressionOptions); + using TiffBaseDecompressor decompressor = TiffDecompressorsFactory.Create(this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, frame.Width, bitsPerPixel, this.Predictor, this.FaxCompressionOptions); RgbPlanarTiffColor colorDecoder = TiffColorDecoderFactory.CreatePlanar(this.ColorType, this.BitsPerSample.Bits(), this.ColorMap); @@ -293,7 +293,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff Buffer2D pixels = frame.PixelBuffer; - using TiffBaseDecompresor decompressor = TiffDecompressorsFactory.Create(this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, frame.Width, bitsPerPixel, this.Predictor, this.FaxCompressionOptions); + using TiffBaseDecompressor decompressor = TiffDecompressorsFactory.Create(this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, frame.Width, bitsPerPixel, this.Predictor, this.FaxCompressionOptions); TiffBaseColorDecoder colorDecoder = TiffColorDecoderFactory.Create(this.ColorType, this.BitsPerSample.Bits(), this.ColorMap); diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs index 88204eb55..7e075c2e6 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs @@ -17,6 +17,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff ///
public class TiffEncoder : IImageEncoder, ITiffEncoderOptions { + /// + public TiffBitsPerPixel? BitsPerPixel { get; set; } + /// public TiffEncoderCompression Compression { get; set; } = TiffEncoderCompression.None; diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 6e80899e4..a04b8a809 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -12,6 +12,7 @@ using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers; using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; @@ -40,11 +41,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff ///
private Configuration configuration; - /// - /// The color depth, in number of bits per pixel. - /// - private TiffBitsPerPixel bitsPerPixel; - /// /// The quantizer for creating color palette image. /// @@ -70,6 +66,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff this.memoryAllocator = memoryAllocator; this.Mode = options.Mode; this.quantizer = options.Quantizer ?? KnownQuantizers.Octree; + this.BitsPerPixel = options.BitsPerPixel; this.UseHorizontalPredictor = options.UseHorizontalPredictor; this.CompressionType = options.Compression; this.compressionLevel = options.CompressionLevel; @@ -82,9 +79,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff internal TiffPhotometricInterpretation PhotometricInterpretation { get; private set; } /// - /// Gets the compression implementation to use when encoding the image. + /// Gets or sets the compression implementation to use when encoding the image. /// - internal TiffEncoderCompression CompressionType { get; } + internal TiffEncoderCompression CompressionType { get; set; } /// /// Gets the encoding mode to use. RGB, RGB with color palette or gray. @@ -97,6 +94,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// internal bool UseHorizontalPredictor { get; } + /// + /// Gets the bits per pixel. + /// + internal TiffBitsPerPixel? BitsPerPixel { get; private set; } + /// /// Encodes the image to the specified stream from the . /// @@ -111,8 +113,12 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff Guard.NotNull(stream, nameof(stream)); this.configuration = image.GetConfiguration(); + ImageMetadata metadata = image.Metadata; + TiffMetadata tiffMetadata = metadata.GetTiffMetadata(); + this.BitsPerPixel ??= tiffMetadata.BitsPerPixel; - this.SetMode(image); + this.SetMode(tiffMetadata); + this.SetBitsPerPixel(); this.SetPhotometricInterpretation(); using (var writer = new TiffStreamWriter(stream)) @@ -155,20 +161,31 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff // Write the image bytes to the steam. var imageDataStart = (uint)writer.Position; - using TiffBaseCompressor compressor = TiffCompressorFactory.Create( - this.CompressionType, - writer.BaseStream, - this.memoryAllocator, - image.Width, - (int)this.bitsPerPixel, - this.compressionLevel, - this.UseHorizontalPredictor ? TiffPredictor.Horizontal : TiffPredictor.None); - - using TiffBaseColorWriter colorWriter = TiffColorWriterFactory.Create(this.Mode, image.Frames.RootFrame, this.quantizer, this.memoryAllocator, this.configuration, entriesCollector); - - int rowsPerStrip = this.CalcRowsPerStrip(image.Frames.RootFrame, colorWriter.BytesPerRow); - - colorWriter.Write(compressor, rowsPerStrip); + TiffBitsPerPixel? tiffBitsPerPixel = this.BitsPerPixel; + if (tiffBitsPerPixel != null) + { + using TiffBaseCompressor compressor = TiffCompressorFactory.Create( + this.CompressionType, + writer.BaseStream, + this.memoryAllocator, + image.Width, + (int)tiffBitsPerPixel, + this.compressionLevel, + this.UseHorizontalPredictor ? TiffPredictor.Horizontal : TiffPredictor.None); + + using TiffBaseColorWriter colorWriter = TiffColorWriterFactory.Create( + this.Mode, + image.Frames.RootFrame, + this.quantizer, + this.memoryAllocator, + this.configuration, + entriesCollector, + (int)tiffBitsPerPixel); + + int rowsPerStrip = this.CalcRowsPerStrip(image.Frames.RootFrame.Height, colorWriter.BytesPerRow); + + colorWriter.Write(compressor, rowsPerStrip); + } entriesCollector.ProcessImageFormat(this); entriesCollector.ProcessGeneral(image); @@ -177,12 +194,21 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff long nextIfdMarker = this.WriteIfd(writer, entriesCollector.Entries); } - private int CalcRowsPerStrip(ImageFrame image, int bytesPerRow) + /// + /// Calculates the number of rows written per strip. + /// + /// The height of the image. + /// The number of bytes per row. + /// Number of rows per strip. + private int CalcRowsPerStrip(int height, int bytesPerRow) { - int sz = this.maxStripBytes > 0 ? this.maxStripBytes : DefaultStripSize; - int height = sz / bytesPerRow; + DebugGuard.MustBeGreaterThan(height, 0, nameof(height)); + DebugGuard.MustBeGreaterThan(bytesPerRow, 0, nameof(bytesPerRow)); + + int stripBytes = this.maxStripBytes > 0 ? this.maxStripBytes : DefaultStripSize; + int rowsPerStrip = stripBytes / bytesPerRow; - return height > 0 ? (height < image.Height ? height : image.Height) : 1; + return rowsPerStrip > 0 ? (rowsPerStrip < height ? rowsPerStrip : height) : 1; } /// @@ -242,14 +268,15 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff return nextIfdMarker; } - private void SetMode(Image image) + private void SetMode(TiffMetadata tiffMetadata) { + // Make sure, that the fax compressions are only used together with the BiColor mode. if (this.CompressionType == TiffEncoderCompression.CcittGroup3Fax || this.CompressionType == TiffEncoderCompression.ModifiedHuffman) { + // Default means the user has not specified a preferred encoding mode. if (this.Mode == TiffEncodingMode.Default) { this.Mode = TiffEncodingMode.BiColor; - this.bitsPerPixel = TiffBitsPerPixel.Pixel1; return; } @@ -262,36 +289,48 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff if (this.Mode == TiffEncodingMode.Default) { // Preserve input bits per pixel, if no mode was specified. - TiffMetadata tiffMetadata = image.Metadata.GetTiffMetadata(); switch (tiffMetadata.BitsPerPixel) { case TiffBitsPerPixel.Pixel1: this.Mode = TiffEncodingMode.BiColor; break; + case TiffBitsPerPixel.Pixel4: + this.Mode = TiffEncodingMode.ColorPalette; + break; case TiffBitsPerPixel.Pixel8: - this.Mode = tiffMetadata.PhotometricInterpretation != TiffPhotometricInterpretation.PaletteColor ? TiffEncodingMode.Gray : TiffEncodingMode.Rgb; + this.Mode = tiffMetadata.PhotometricInterpretation == TiffPhotometricInterpretation.PaletteColor ? TiffEncodingMode.ColorPalette : TiffEncodingMode.Gray; + break; default: this.Mode = TiffEncodingMode.Rgb; break; } } + } + private void SetBitsPerPixel() + { switch (this.Mode) { case TiffEncodingMode.BiColor: - this.bitsPerPixel = TiffBitsPerPixel.Pixel1; + this.BitsPerPixel = TiffBitsPerPixel.Pixel1; break; case TiffEncodingMode.ColorPalette: + if (this.BitsPerPixel != TiffBitsPerPixel.Pixel8 && this.BitsPerPixel != TiffBitsPerPixel.Pixel4) + { + this.BitsPerPixel = TiffBitsPerPixel.Pixel8; + } + + break; case TiffEncodingMode.Gray: - this.bitsPerPixel = TiffBitsPerPixel.Pixel8; + this.BitsPerPixel = TiffBitsPerPixel.Pixel8; break; case TiffEncodingMode.Rgb: - this.bitsPerPixel = TiffBitsPerPixel.Pixel24; + this.BitsPerPixel = TiffBitsPerPixel.Pixel24; break; default: this.Mode = TiffEncodingMode.Rgb; - this.bitsPerPixel = TiffBitsPerPixel.Pixel24; + this.BitsPerPixel = TiffBitsPerPixel.Pixel24; break; } } diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs index f2e3d9faf..c8ad38db3 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs @@ -298,25 +298,33 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff switch (encoder.PhotometricInterpretation) { case TiffPhotometricInterpretation.PaletteColor: - return new ushort[] { 8 }; + if (encoder.BitsPerPixel == TiffBitsPerPixel.Pixel4) + { + return TiffConstants.BitsPerSample4Bit; + } + else + { + return TiffConstants.BitsPerSample8Bit; + } + case TiffPhotometricInterpretation.Rgb: - return new ushort[] { 8, 8, 8 }; + return TiffConstants.BitsPerSampleRgb8Bit; case TiffPhotometricInterpretation.WhiteIsZero: if (encoder.Mode == TiffEncodingMode.BiColor) { - return new ushort[] { 1 }; + return TiffConstants.BitsPerSample1Bit; } - return new ushort[] { 8 }; + return TiffConstants.BitsPerSample8Bit; case TiffPhotometricInterpretation.BlackIsZero: if (encoder.Mode == TiffEncodingMode.BiColor) { - return new ushort[] { 1 }; + return TiffConstants.BitsPerSample1Bit; } - return new ushort[] { 8 }; + return TiffConstants.BitsPerSample8Bit; default: - return new ushort[] { 8, 8, 8 }; + return TiffConstants.BitsPerSampleRgb8Bit; } } diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs index f61b29436..70c91fa89 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs @@ -20,13 +20,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers this.MemoryAllocator = memoryAllocator; this.Configuration = configuration; this.EntriesCollector = entriesCollector; - - this.BytesPerRow = ((image.Width * this.BitsPerPixel) + 7) / 8; } public abstract int BitsPerPixel { get; } - public int BytesPerRow { get; } + public int BytesPerRow => ((this.Image.Width * this.BitsPerPixel) + 7) / 8; protected ImageFrame Image { get; } @@ -38,7 +36,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers public virtual void Write(TiffBaseCompressor compressor, int rowsPerStrip) { - DebugGuard.IsTrue(this.BytesPerRow == compressor.BytesPerRow || compressor.BytesPerRow == 0, "Values must be equals"); + DebugGuard.IsTrue(this.BytesPerRow == compressor.BytesPerRow, "bytes per row of the compressor does not match tiff color writer"); int stripsCount = (this.Image.Height + rowsPerStrip - 1) / rowsPerStrip; uint[] stripOffsets = new uint[stripsCount]; @@ -59,7 +57,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers stripIndex++; } - DebugGuard.IsTrue(stripIndex == stripsCount, "Values must be equals"); + DebugGuard.IsTrue(stripIndex == stripsCount, "stripIndex and stripsCount should match"); this.AddStripTags(rowsPerStrip, stripOffsets, stripByteCounts); } diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffColorWriterFactory.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffColorWriterFactory.cs index 10bb9b96e..4dcb47b47 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffColorWriterFactory.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffColorWriterFactory.cs @@ -15,13 +15,14 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers IQuantizer quantizer, MemoryAllocator memoryAllocator, Configuration configuration, - TiffEncoderEntriesCollector entriesCollector) + TiffEncoderEntriesCollector entriesCollector, + int bitsPerPixel) where TPixel : unmanaged, IPixel { switch (mode) { case TiffEncodingMode.ColorPalette: - return new TiffPaletteWriter(image, quantizer, memoryAllocator, configuration, entriesCollector); + return new TiffPaletteWriter(image, quantizer, memoryAllocator, configuration, entriesCollector, bitsPerPixel); case TiffEncodingMode.Gray: return new TiffGrayWriter(image, memoryAllocator, configuration, entriesCollector); case TiffEncodingMode.BiColor: diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs index d15bc5e17..b094c22fc 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs @@ -4,6 +4,7 @@ using System; using System.Buffers; using System.Runtime.InteropServices; + using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; using SixLabors.ImageSharp.Memory; @@ -16,52 +17,85 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers internal sealed class TiffPaletteWriter : TiffBaseColorWriter where TPixel : unmanaged, IPixel { - private const int ColorsPerChannel = 256; - private const int ColorPaletteSize = ColorsPerChannel * 3; - private const int ColorPaletteBytes = ColorPaletteSize * 2; - - private readonly IndexedImageFrame quantized; - - public TiffPaletteWriter(ImageFrame image, IQuantizer quantizer, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector) + private readonly int maxColors; + private readonly int colorPaletteSize; + private readonly int colorPaletteBytes; + private readonly IndexedImageFrame quantizedImage; + + public TiffPaletteWriter( + ImageFrame image, + IQuantizer quantizer, + MemoryAllocator memoryAllocator, + Configuration configuration, + TiffEncoderEntriesCollector entriesCollector, + int bitsPerPixel) : base(image, memoryAllocator, configuration, entriesCollector) { - using IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(this.Configuration); - this.quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(image, image.Bounds()); + DebugGuard.NotNull(quantizer, nameof(quantizer)); + DebugGuard.NotNull(configuration, nameof(configuration)); + DebugGuard.NotNull(entriesCollector, nameof(entriesCollector)); + DebugGuard.MustBeBetweenOrEqualTo(bitsPerPixel, 4, 8, nameof(bitsPerPixel)); + + this.BitsPerPixel = bitsPerPixel; + this.maxColors = this.BitsPerPixel == 4 ? 16 : 256; + this.colorPaletteSize = this.maxColors * 3; + this.colorPaletteBytes = this.colorPaletteSize * 2; + using IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(this.Configuration, new QuantizerOptions() + { + MaxColors = this.maxColors + }); + this.quantizedImage = frameQuantizer.BuildPaletteAndQuantizeFrame(image, image.Bounds()); - this.AddTag(this.quantized); + this.AddColorMapTag(); } /// - public override int BitsPerPixel => 8; + public override int BitsPerPixel { get; } /// protected override void EncodeStrip(int y, int height, TiffBaseCompressor compressor) { - Span pixels = GetStripPixels(((IPixelSource)this.quantized).PixelBuffer, y, height); - compressor.CompressStrip(pixels, height); + Span pixels = GetStripPixels(((IPixelSource)this.quantizedImage).PixelBuffer, y, height); + if (this.BitsPerPixel == 4) + { + using IMemoryOwner rows4bitBuffer = this.MemoryAllocator.Allocate(pixels.Length / 2); + Span rows4bit = rows4bitBuffer.GetSpan(); + int idx = 0; + for (int i = 0; i < rows4bit.Length; i++) + { + rows4bit[i] = (byte)((pixels[idx] << 4) | (pixels[idx + 1] & 0xF)); + idx += 2; + } + + compressor.CompressStrip(rows4bit, height); + } + else + { + compressor.CompressStrip(pixels, height); + } } /// - protected override void Dispose(bool disposing) => this.quantized?.Dispose(); + protected override void Dispose(bool disposing) => this.quantizedImage?.Dispose(); - private void AddTag(IndexedImageFrame quantized) + private void AddColorMapTag() { - using IMemoryOwner colorPaletteBuffer = this.MemoryAllocator.AllocateManagedByteBuffer(ColorPaletteBytes); + using IMemoryOwner colorPaletteBuffer = this.MemoryAllocator.AllocateManagedByteBuffer(this.colorPaletteBytes); Span colorPalette = colorPaletteBuffer.GetSpan(); - ReadOnlySpan quantizedColors = quantized.Palette.Span; + ReadOnlySpan quantizedColors = this.quantizedImage.Palette.Span; int quantizedColorBytes = quantizedColors.Length * 3 * 2; - // In the ColorMap, black is represented by 0,0,0 and white is represented by 65535, 65535, 65535. + // In the ColorMap, black is represented by 0, 0, 0 and white is represented by 65535, 65535, 65535. Span quantizedColorRgb48 = MemoryMarshal.Cast(colorPalette.Slice(0, quantizedColorBytes)); PixelOperations.Instance.ToRgb48(this.Configuration, quantizedColors, quantizedColorRgb48); - // It can happen that the quantized colors are less than the expected 256 per channel. - var diffToMaxColors = ColorsPerChannel - quantizedColors.Length; + // It can happen that the quantized colors are less than the expected maximum per channel. + var diffToMaxColors = this.maxColors - quantizedColors.Length; // In a TIFF ColorMap, all the Red values come first, followed by the Green values, // then the Blue values. Convert the quantized palette to this format. - var palette = new ushort[ColorPaletteSize]; + var palette = new ushort[this.colorPaletteSize]; int paletteIdx = 0; for (int i = 0; i < quantizedColors.Length; i++) { diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs index 466027bee..d5435a8a4 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs @@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression var stream = new BufferedReadStream(Configuration.Default, new MemoryStream(inputData)); var buffer = new byte[expectedResult.Length]; - new NoneTiffCompression().Decompress(stream, 0, byteCount, buffer); + new NoneTiffCompression(default, default, default).Decompress(stream, 0, byteCount, buffer); Assert.Equal(expectedResult, buffer); } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs index a211bde53..d0649934d 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression var stream = new BufferedReadStream(Configuration.Default, new MemoryStream(inputData)); var buffer = new byte[expectedResult.Length]; - using var decompressor = new PackBitsTiffCompression(new ArrayPoolMemoryAllocator()); + using var decompressor = new PackBitsTiffCompression(new ArrayPoolMemoryAllocator(), default, default); decompressor.Decompress(stream, 0, (uint)inputData.Length, buffer); Assert.Equal(expectedResult, buffer); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index d4f68ff66..57a435629 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using System; using System.IO; using SixLabors.ImageSharp.Formats; @@ -38,7 +37,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [InlineData(TiffEncodingMode.BiColor, TiffEncoderCompression.None, TiffBitsPerPixel.Pixel1, TiffCompression.None)] [InlineData(TiffEncodingMode.Default, TiffEncoderCompression.Deflate, TiffBitsPerPixel.Pixel24, TiffCompression.Deflate)] [InlineData(TiffEncodingMode.Rgb, TiffEncoderCompression.Deflate, TiffBitsPerPixel.Pixel24, TiffCompression.Deflate)] - [InlineData(TiffEncodingMode.ColorPalette, TiffEncoderCompression.Deflate, TiffBitsPerPixel.Pixel8, TiffCompression.Deflate)] [InlineData(TiffEncodingMode.Gray, TiffEncoderCompression.Deflate, TiffBitsPerPixel.Pixel8, TiffCompression.Deflate)] [InlineData(TiffEncodingMode.BiColor, TiffEncoderCompression.Deflate, TiffBitsPerPixel.Pixel1, TiffCompression.Deflate)] [InlineData(TiffEncodingMode.Default, TiffEncoderCompression.PackBits, TiffBitsPerPixel.Pixel24, TiffCompression.PackBits)] @@ -47,7 +45,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [InlineData(TiffEncodingMode.Gray, TiffEncoderCompression.PackBits, TiffBitsPerPixel.Pixel8, TiffCompression.PackBits)] [InlineData(TiffEncodingMode.BiColor, TiffEncoderCompression.PackBits, TiffBitsPerPixel.Pixel1, TiffCompression.PackBits)] [InlineData(TiffEncodingMode.Rgb, TiffEncoderCompression.Lzw, TiffBitsPerPixel.Pixel24, TiffCompression.Lzw)] - [InlineData(TiffEncodingMode.ColorPalette, TiffEncoderCompression.Lzw, TiffBitsPerPixel.Pixel8, TiffCompression.Lzw)] [InlineData(TiffEncodingMode.Gray, TiffEncoderCompression.Lzw, TiffBitsPerPixel.Pixel8, TiffCompression.Lzw)] [InlineData(TiffEncodingMode.BiColor, TiffEncoderCompression.CcittGroup3Fax, TiffBitsPerPixel.Pixel1, TiffCompression.CcittGroup3Fax)] [InlineData(TiffEncodingMode.BiColor, TiffEncoderCompression.ModifiedHuffman, TiffBitsPerPixel.Pixel1, TiffCompression.Ccitt1D)] @@ -73,7 +70,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32, TiffBitsPerPixel.Pixel1)] [WithFile(GrayscaleUncompressed, PixelTypes.Rgba32, TiffBitsPerPixel.Pixel8)] [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffBitsPerPixel.Pixel24)] - [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32, TiffBitsPerPixel.Pixel24)] + [WithFile(Rgb4BitPalette, PixelTypes.Rgba32, TiffBitsPerPixel.Pixel4)] + [WithFile(RgbPalette, PixelTypes.Rgba32, TiffBitsPerPixel.Pixel8)] + [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32, TiffBitsPerPixel.Pixel8)] public void TiffEncoder_PreserveBitsPerPixel(TestImageProvider provider, TiffBitsPerPixel expectedBitsPerPixel) where TPixel : unmanaged, IPixel { @@ -97,7 +96,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffEncoderCompression.ModifiedHuffman, TiffCompression.Ccitt1D)] [WithFile(GrayscaleUncompressed, PixelTypes.L8, TiffEncoderCompression.CcittGroup3Fax, TiffCompression.CcittGroup3Fax)] [WithFile(PaletteDeflateMultistrip, PixelTypes.L8, TiffEncoderCompression.ModifiedHuffman, TiffCompression.Ccitt1D)] - public void TiffEncoder_CorrectBiMode(TestImageProvider provider, TiffEncoderCompression compression, TiffCompression expectedCompression) + public void TiffEncoder_EncodesWithCorrectBiColorModeCompression(TestImageProvider provider, TiffEncoderCompression compression, TiffCompression expectedCompression) where TPixel : unmanaged, IPixel { // arrange @@ -197,37 +196,19 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => - TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, useExactComparer: false, compareTolerance: 0.001f); + TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.ColorPalette, useExactComparer: false, compareTolerance: 0.001f); [Theory] - [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] - public void TiffEncoder_EncodeColorPalette_WithDeflateCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => - TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, TiffEncoderCompression.Deflate, useExactComparer: false, compareTolerance: 0.001f); - - [Theory] - [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] - public void TiffEncoder_EncodeColorPalette_WithDeflateCompressionAndPredictor_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => - TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, TiffEncoderCompression.Deflate, usePredictor: true, useExactComparer: false, compareTolerance: 0.001f); - - [Theory] - [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] - public void TiffEncoder_EncodeColorPalette_WithLzwCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => - TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, TiffEncoderCompression.Lzw, useExactComparer: false, compareTolerance: 0.001f); - - [Theory] - [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] - public void TiffEncoder_EncodeColorPalette_WithLzwCompressionAndPredictor_Works(TestImageProvider provider) + [WithFile(Rgb4BitPalette, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeColorPalette_With4Bit_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => - TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, TiffEncoderCompression.Lzw, usePredictor: true, useExactComparer: false, compareTolerance: 0.001f); + TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.ColorPalette, useExactComparer: false, compareTolerance: 0.001f); [Theory] [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_WithPackBitsCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => - TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, TiffEncoderCompression.PackBits, useExactComparer: false, compareTolerance: 0.001f); + TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.ColorPalette, TiffEncoderCompression.PackBits, useExactComparer: false, compareTolerance: 0.001f); [Theory] [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] @@ -335,6 +316,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff var encoder = new TiffEncoder { Mode = mode, + BitsPerPixel = bitsPerPixel, Compression = compression, UseHorizontalPredictor = usePredictor, MaxStripBytes = maxStripSize diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index aa9883e96..49d0e759c 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -556,6 +556,7 @@ namespace SixLabors.ImageSharp.Tests public const string RgbPackbitsMultistrip = "Tiff/rgb_packbits_multistrip.tiff"; public const string RgbUncompressed = "Tiff/rgb_uncompressed.tiff"; 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 SmallRgbDeflate = "Tiff/rgb_small_deflate.tiff"; diff --git a/tests/Images/Input/Tiff/bike_colorpalette_4bit.tiff b/tests/Images/Input/Tiff/bike_colorpalette_4bit.tiff new file mode 100644 index 000000000..d76966336 --- /dev/null +++ b/tests/Images/Input/Tiff/bike_colorpalette_4bit.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bc9047bc28ff5256530a7203bf73fa0a7ed1545736cd57fabdaff2d600cfa3f1 +size 132265 From 966d743d08d32ce9707aac12d3b4b2936b1cc4f4 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 1 Mar 2021 12:38:41 +0100 Subject: [PATCH 212/275] Rename tiff bits per pixels enum values --- .../Formats/Tiff/TiffBitsPerPixel.cs | 16 ++-- .../Formats/Tiff/TiffEncoderCore.cs | 18 ++-- .../Tiff/TiffEncoderEntriesCollector.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffMetadata.cs | 2 +- .../Formats/Tiff/TiffEncoderTests.cs | 90 +++++++++---------- .../Formats/Tiff/TiffMetadataTests.cs | 16 ++-- 6 files changed, 72 insertions(+), 72 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs b/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs index 2f816463c..77fd4f6f8 100644 --- a/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs +++ b/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs @@ -9,23 +9,23 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff public enum TiffBitsPerPixel { /// - /// 1 bits per pixel, bi-color image. Each pixel consists of 1 bit. + /// 1 bit per pixel, for bi-color image. /// - Pixel1 = 1, + Bit1 = 1, /// - /// 4 bits per pixel, grayscale or indexed image. Each pixel consists of 4 bit. + /// 4 bits per pixel, for images with a color palette. /// - Pixel4 = 4, + Bit4 = 4, /// - /// 8 bits per pixel, grayscale or indexed image. Each pixel consists of 1 byte. + /// 8 bits per pixel, grayscale or color palette images. /// - Pixel8 = 8, + Bit8 = 8, /// - /// 24 bits per pixel. Each pixel consists of 3 bytes. + /// 24 bits per pixel. One byte for each color channel. /// - Pixel24 = 24, + Bit24 = 24, } } diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index a04b8a809..300814421 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -291,13 +291,13 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff // Preserve input bits per pixel, if no mode was specified. switch (tiffMetadata.BitsPerPixel) { - case TiffBitsPerPixel.Pixel1: + case TiffBitsPerPixel.Bit1: this.Mode = TiffEncodingMode.BiColor; break; - case TiffBitsPerPixel.Pixel4: + case TiffBitsPerPixel.Bit4: this.Mode = TiffEncodingMode.ColorPalette; break; - case TiffBitsPerPixel.Pixel8: + case TiffBitsPerPixel.Bit8: this.Mode = tiffMetadata.PhotometricInterpretation == TiffPhotometricInterpretation.PaletteColor ? TiffEncodingMode.ColorPalette : TiffEncodingMode.Gray; break; @@ -313,24 +313,24 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff switch (this.Mode) { case TiffEncodingMode.BiColor: - this.BitsPerPixel = TiffBitsPerPixel.Pixel1; + this.BitsPerPixel = TiffBitsPerPixel.Bit1; break; case TiffEncodingMode.ColorPalette: - if (this.BitsPerPixel != TiffBitsPerPixel.Pixel8 && this.BitsPerPixel != TiffBitsPerPixel.Pixel4) + if (this.BitsPerPixel != TiffBitsPerPixel.Bit8 && this.BitsPerPixel != TiffBitsPerPixel.Bit4) { - this.BitsPerPixel = TiffBitsPerPixel.Pixel8; + this.BitsPerPixel = TiffBitsPerPixel.Bit8; } break; case TiffEncodingMode.Gray: - this.BitsPerPixel = TiffBitsPerPixel.Pixel8; + this.BitsPerPixel = TiffBitsPerPixel.Bit8; break; case TiffEncodingMode.Rgb: - this.BitsPerPixel = TiffBitsPerPixel.Pixel24; + this.BitsPerPixel = TiffBitsPerPixel.Bit24; break; default: this.Mode = TiffEncodingMode.Rgb; - this.BitsPerPixel = TiffBitsPerPixel.Pixel24; + this.BitsPerPixel = TiffBitsPerPixel.Bit24; break; } } diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs index c8ad38db3..0707ee321 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs @@ -298,7 +298,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff switch (encoder.PhotometricInterpretation) { case TiffPhotometricInterpretation.PaletteColor: - if (encoder.BitsPerPixel == TiffBitsPerPixel.Pixel4) + if (encoder.BitsPerPixel == TiffBitsPerPixel.Bit4) { return TiffConstants.BitsPerSample4Bit; } diff --git a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs index b1d4fff0d..e6b0bf868 100644 --- a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs @@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// /// Gets the number of bits per pixel. /// - public TiffBitsPerPixel BitsPerPixel { get; internal set; } = TiffBitsPerPixel.Pixel24; + public TiffBitsPerPixel BitsPerPixel { get; internal set; } = TiffBitsPerPixel.Bit24; /// /// Gets the compression used to create the TIFF file. diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 57a435629..c40ffa84c 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -30,24 +30,24 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff } [Theory] - [InlineData(TiffEncodingMode.Default, TiffEncoderCompression.None, TiffBitsPerPixel.Pixel24, TiffCompression.None)] - [InlineData(TiffEncodingMode.Rgb, TiffEncoderCompression.None, TiffBitsPerPixel.Pixel24, TiffCompression.None)] - [InlineData(TiffEncodingMode.ColorPalette, TiffEncoderCompression.None, TiffBitsPerPixel.Pixel8, TiffCompression.None)] - [InlineData(TiffEncodingMode.Gray, TiffEncoderCompression.None, TiffBitsPerPixel.Pixel8, TiffCompression.None)] - [InlineData(TiffEncodingMode.BiColor, TiffEncoderCompression.None, TiffBitsPerPixel.Pixel1, TiffCompression.None)] - [InlineData(TiffEncodingMode.Default, TiffEncoderCompression.Deflate, TiffBitsPerPixel.Pixel24, TiffCompression.Deflate)] - [InlineData(TiffEncodingMode.Rgb, TiffEncoderCompression.Deflate, TiffBitsPerPixel.Pixel24, TiffCompression.Deflate)] - [InlineData(TiffEncodingMode.Gray, TiffEncoderCompression.Deflate, TiffBitsPerPixel.Pixel8, TiffCompression.Deflate)] - [InlineData(TiffEncodingMode.BiColor, TiffEncoderCompression.Deflate, TiffBitsPerPixel.Pixel1, TiffCompression.Deflate)] - [InlineData(TiffEncodingMode.Default, TiffEncoderCompression.PackBits, TiffBitsPerPixel.Pixel24, TiffCompression.PackBits)] - [InlineData(TiffEncodingMode.Rgb, TiffEncoderCompression.PackBits, TiffBitsPerPixel.Pixel24, TiffCompression.PackBits)] - [InlineData(TiffEncodingMode.ColorPalette, TiffEncoderCompression.PackBits, TiffBitsPerPixel.Pixel8, TiffCompression.PackBits)] - [InlineData(TiffEncodingMode.Gray, TiffEncoderCompression.PackBits, TiffBitsPerPixel.Pixel8, TiffCompression.PackBits)] - [InlineData(TiffEncodingMode.BiColor, TiffEncoderCompression.PackBits, TiffBitsPerPixel.Pixel1, TiffCompression.PackBits)] - [InlineData(TiffEncodingMode.Rgb, TiffEncoderCompression.Lzw, TiffBitsPerPixel.Pixel24, TiffCompression.Lzw)] - [InlineData(TiffEncodingMode.Gray, TiffEncoderCompression.Lzw, TiffBitsPerPixel.Pixel8, TiffCompression.Lzw)] - [InlineData(TiffEncodingMode.BiColor, TiffEncoderCompression.CcittGroup3Fax, TiffBitsPerPixel.Pixel1, TiffCompression.CcittGroup3Fax)] - [InlineData(TiffEncodingMode.BiColor, TiffEncoderCompression.ModifiedHuffman, TiffBitsPerPixel.Pixel1, TiffCompression.Ccitt1D)] + [InlineData(TiffEncodingMode.Default, TiffEncoderCompression.None, TiffBitsPerPixel.Bit24, TiffCompression.None)] + [InlineData(TiffEncodingMode.Rgb, TiffEncoderCompression.None, TiffBitsPerPixel.Bit24, TiffCompression.None)] + [InlineData(TiffEncodingMode.ColorPalette, TiffEncoderCompression.None, TiffBitsPerPixel.Bit8, TiffCompression.None)] + [InlineData(TiffEncodingMode.Gray, TiffEncoderCompression.None, TiffBitsPerPixel.Bit8, TiffCompression.None)] + [InlineData(TiffEncodingMode.BiColor, TiffEncoderCompression.None, TiffBitsPerPixel.Bit1, TiffCompression.None)] + [InlineData(TiffEncodingMode.Default, TiffEncoderCompression.Deflate, TiffBitsPerPixel.Bit24, TiffCompression.Deflate)] + [InlineData(TiffEncodingMode.Rgb, TiffEncoderCompression.Deflate, TiffBitsPerPixel.Bit24, TiffCompression.Deflate)] + [InlineData(TiffEncodingMode.Gray, TiffEncoderCompression.Deflate, TiffBitsPerPixel.Bit8, TiffCompression.Deflate)] + [InlineData(TiffEncodingMode.BiColor, TiffEncoderCompression.Deflate, TiffBitsPerPixel.Bit1, TiffCompression.Deflate)] + [InlineData(TiffEncodingMode.Default, TiffEncoderCompression.PackBits, TiffBitsPerPixel.Bit24, TiffCompression.PackBits)] + [InlineData(TiffEncodingMode.Rgb, TiffEncoderCompression.PackBits, TiffBitsPerPixel.Bit24, TiffCompression.PackBits)] + [InlineData(TiffEncodingMode.ColorPalette, TiffEncoderCompression.PackBits, TiffBitsPerPixel.Bit8, TiffCompression.PackBits)] + [InlineData(TiffEncodingMode.Gray, TiffEncoderCompression.PackBits, TiffBitsPerPixel.Bit8, TiffCompression.PackBits)] + [InlineData(TiffEncodingMode.BiColor, TiffEncoderCompression.PackBits, TiffBitsPerPixel.Bit1, TiffCompression.PackBits)] + [InlineData(TiffEncodingMode.Rgb, TiffEncoderCompression.Lzw, TiffBitsPerPixel.Bit24, TiffCompression.Lzw)] + [InlineData(TiffEncodingMode.Gray, TiffEncoderCompression.Lzw, TiffBitsPerPixel.Bit8, TiffCompression.Lzw)] + [InlineData(TiffEncodingMode.BiColor, TiffEncoderCompression.CcittGroup3Fax, TiffBitsPerPixel.Bit1, TiffCompression.CcittGroup3Fax)] + [InlineData(TiffEncodingMode.BiColor, TiffEncoderCompression.ModifiedHuffman, TiffBitsPerPixel.Bit1, TiffCompression.Ccitt1D)] public void EncoderOptions_Work(TiffEncodingMode mode, TiffEncoderCompression compression, TiffBitsPerPixel expectedBitsPerPixel, TiffCompression expectedCompression) { // arrange @@ -67,12 +67,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff } [Theory] - [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32, TiffBitsPerPixel.Pixel1)] - [WithFile(GrayscaleUncompressed, PixelTypes.Rgba32, TiffBitsPerPixel.Pixel8)] - [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffBitsPerPixel.Pixel24)] - [WithFile(Rgb4BitPalette, PixelTypes.Rgba32, TiffBitsPerPixel.Pixel4)] - [WithFile(RgbPalette, PixelTypes.Rgba32, TiffBitsPerPixel.Pixel8)] - [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32, TiffBitsPerPixel.Pixel8)] + [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32, TiffBitsPerPixel.Bit1)] + [WithFile(GrayscaleUncompressed, PixelTypes.Rgba32, TiffBitsPerPixel.Bit8)] + [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffBitsPerPixel.Bit24)] + [WithFile(Rgb4BitPalette, PixelTypes.Rgba32, TiffBitsPerPixel.Bit4)] + [WithFile(RgbPalette, PixelTypes.Rgba32, TiffBitsPerPixel.Bit8)] + [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32, TiffBitsPerPixel.Bit8)] public void TiffEncoder_PreserveBitsPerPixel(TestImageProvider provider, TiffBitsPerPixel expectedBitsPerPixel) where TPixel : unmanaged, IPixel { @@ -112,7 +112,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff using var output = Image.Load(Configuration, memStream); TiffMetadata meta = output.Metadata.GetTiffMetadata(); - Assert.Equal(TiffBitsPerPixel.Pixel1, meta.BitsPerPixel); + Assert.Equal(TiffBitsPerPixel.Bit1, meta.BitsPerPixel); Assert.Equal(expectedCompression, meta.Compression); } @@ -135,105 +135,105 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Theory] [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeRgb_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.Rgb); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffEncodingMode.Rgb); [Theory] [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeRgb_WithDeflateCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.Rgb, TiffEncoderCompression.Deflate); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffEncodingMode.Rgb, TiffEncoderCompression.Deflate); [Theory] [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeRgb_WithDeflateCompressionAndPredictor_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.Rgb, TiffEncoderCompression.Deflate, usePredictor: true); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffEncodingMode.Rgb, TiffEncoderCompression.Deflate, usePredictor: true); [Theory] [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeRgb_WithLzwCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.Rgb, TiffEncoderCompression.Lzw); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffEncodingMode.Rgb, TiffEncoderCompression.Lzw); [Theory] [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeRgb_WithLzwCompressionAndPredictor_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.Rgb, TiffEncoderCompression.Lzw, usePredictor: true); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffEncodingMode.Rgb, TiffEncoderCompression.Lzw, usePredictor: true); [Theory] [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeRgb_WithPackBitsCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.Rgb, TiffEncoderCompression.PackBits); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffEncodingMode.Rgb, TiffEncoderCompression.PackBits); [Theory] [WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeGray_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.Gray); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.Gray); [Theory] [WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeGray_WithDeflateCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.Gray, TiffEncoderCompression.Deflate); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.Gray, TiffEncoderCompression.Deflate); [Theory] [WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeGray_WithDeflateCompressionAndPredictor_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.Gray, TiffEncoderCompression.Deflate, usePredictor: true); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.Gray, TiffEncoderCompression.Deflate, usePredictor: true); [Theory] [WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeGray_WithLzwCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.Gray, TiffEncoderCompression.Lzw); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.Gray, TiffEncoderCompression.Lzw); [Theory] [WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeGray_WithLzwCompressionAndPredictor_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.Gray, TiffEncoderCompression.Lzw, usePredictor: true); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.Gray, TiffEncoderCompression.Lzw, usePredictor: true); [Theory] [WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeGray_WithPackBitsCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.Gray, TiffEncoderCompression.PackBits); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.Gray, TiffEncoderCompression.PackBits); [Theory] [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => - TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.ColorPalette, useExactComparer: false, compareTolerance: 0.001f); + TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.ColorPalette, useExactComparer: false, compareTolerance: 0.001f); [Theory] [WithFile(Rgb4BitPalette, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_With4Bit_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => - TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.ColorPalette, useExactComparer: false, compareTolerance: 0.001f); + TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.ColorPalette, useExactComparer: false, compareTolerance: 0.001f); [Theory] [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_WithPackBitsCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => - TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.ColorPalette, TiffEncoderCompression.PackBits, useExactComparer: false, compareTolerance: 0.001f); + TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.ColorPalette, TiffEncoderCompression.PackBits, useExactComparer: false, compareTolerance: 0.001f); [Theory] [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeBiColor_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.BiColor); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffEncodingMode.BiColor); [Theory] [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeBiColor_WithDeflateCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel1, TiffEncodingMode.BiColor, TiffEncoderCompression.Deflate); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffEncodingMode.BiColor, TiffEncoderCompression.Deflate); [Theory] [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeBiColor_WithPackBitsCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel1, TiffEncodingMode.BiColor, TiffEncoderCompression.PackBits); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffEncodingMode.BiColor, TiffEncoderCompression.PackBits); [Theory] [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeBiColor_WithCcittGroup3FaxCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel1, TiffEncodingMode.BiColor, TiffEncoderCompression.CcittGroup3Fax); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffEncodingMode.BiColor, TiffEncoderCompression.CcittGroup3Fax); [Theory] [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeBiColor_WithModifiedHuffmanCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel1, TiffEncodingMode.BiColor, TiffEncoderCompression.ModifiedHuffman); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffEncodingMode.BiColor, TiffEncoderCompression.ModifiedHuffman); [Theory] [WithFile(GrayscaleUncompressed, PixelTypes.L8, TiffEncodingMode.Gray, TiffEncoderCompression.PackBits, 16 * 1024)] diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index b2eb3add9..a7279c942 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff var meta = new TiffMetadata { Compression = TiffCompression.Deflate, - BitsPerPixel = TiffBitsPerPixel.Pixel8, + BitsPerPixel = TiffBitsPerPixel.Bit8, ByteOrder = ByteOrder.BigEndian, XmpProfile = xmpData, PhotometricInterpretation = TiffPhotometricInterpretation.Rgb @@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff var clone = (TiffMetadata)meta.DeepClone(); clone.Compression = TiffCompression.None; - clone.BitsPerPixel = TiffBitsPerPixel.Pixel24; + clone.BitsPerPixel = TiffBitsPerPixel.Bit24; clone.ByteOrder = ByteOrder.LittleEndian; clone.PhotometricInterpretation = TiffPhotometricInterpretation.YCbCr; @@ -63,9 +63,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff } [Theory] - [InlineData(Calliphora_BiColorUncompressed, TiffBitsPerPixel.Pixel1)] - [InlineData(GrayscaleUncompressed, TiffBitsPerPixel.Pixel8)] - [InlineData(RgbUncompressed, TiffBitsPerPixel.Pixel24)] + [InlineData(Calliphora_BiColorUncompressed, TiffBitsPerPixel.Bit1)] + [InlineData(GrayscaleUncompressed, TiffBitsPerPixel.Bit8)] + [InlineData(RgbUncompressed, TiffBitsPerPixel.Bit24)] public void Identify_DetectsCorrectBitPerPixel(string imagePath, TiffBitsPerPixel expectedBitsPerPixel) { var testFile = TestFile.Create(imagePath); @@ -278,8 +278,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff TiffMetadata tiffMetaOut = output.Metadata.GetTiffMetadata(); TiffFrameMetadata frameMetaOut = output.Frames.RootFrame.Metadata.GetTiffMetadata(); - Assert.Equal(TiffBitsPerPixel.Pixel4, tiffMeta.BitsPerPixel); - Assert.Equal(TiffBitsPerPixel.Pixel24, tiffMetaOut.BitsPerPixel); + Assert.Equal(TiffBitsPerPixel.Bit4, tiffMeta.BitsPerPixel); + Assert.Equal(TiffBitsPerPixel.Bit24, tiffMetaOut.BitsPerPixel); Assert.Equal(TiffCompression.Lzw, tiffMeta.Compression); Assert.Equal(TiffCompression.None, tiffMetaOut.Compression); @@ -377,7 +377,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal(54, coreMetaOut.VerticalResolution, 8); //// Assert.Equal(tiffEncoder.Compression, tiffMetaOut.Compression); - Assert.Equal(TiffBitsPerPixel.Pixel24, tiffMetaOut.BitsPerPixel); + Assert.Equal(TiffBitsPerPixel.Bit24, tiffMetaOut.BitsPerPixel); Assert.Equal((uint)w, frameMetaOut.Width); Assert.Equal((uint)h, frameMetaOut.Height); From 8f3f35c51e9aa44d24c32a1603deacd164f4e443 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 1 Mar 2021 18:45:54 +0100 Subject: [PATCH 213/275] Re-add tests for color palette and deflate/lzw compression --- .../Formats/Tiff/TiffEncoderTests.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index c40ffa84c..ceb40d745 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -39,6 +39,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [InlineData(TiffEncodingMode.Rgb, TiffEncoderCompression.Deflate, TiffBitsPerPixel.Bit24, TiffCompression.Deflate)] [InlineData(TiffEncodingMode.Gray, TiffEncoderCompression.Deflate, TiffBitsPerPixel.Bit8, TiffCompression.Deflate)] [InlineData(TiffEncodingMode.BiColor, TiffEncoderCompression.Deflate, TiffBitsPerPixel.Bit1, TiffCompression.Deflate)] + [InlineData(TiffEncodingMode.ColorPalette, TiffEncoderCompression.Deflate, TiffBitsPerPixel.Bit8, TiffCompression.Deflate)] [InlineData(TiffEncodingMode.Default, TiffEncoderCompression.PackBits, TiffBitsPerPixel.Bit24, TiffCompression.PackBits)] [InlineData(TiffEncodingMode.Rgb, TiffEncoderCompression.PackBits, TiffBitsPerPixel.Bit24, TiffCompression.PackBits)] [InlineData(TiffEncodingMode.ColorPalette, TiffEncoderCompression.PackBits, TiffBitsPerPixel.Bit8, TiffCompression.PackBits)] @@ -46,6 +47,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [InlineData(TiffEncodingMode.BiColor, TiffEncoderCompression.PackBits, TiffBitsPerPixel.Bit1, TiffCompression.PackBits)] [InlineData(TiffEncodingMode.Rgb, TiffEncoderCompression.Lzw, TiffBitsPerPixel.Bit24, TiffCompression.Lzw)] [InlineData(TiffEncodingMode.Gray, TiffEncoderCompression.Lzw, TiffBitsPerPixel.Bit8, TiffCompression.Lzw)] + [InlineData(TiffEncodingMode.ColorPalette, TiffEncoderCompression.Lzw, TiffBitsPerPixel.Bit8, TiffCompression.Lzw)] [InlineData(TiffEncodingMode.BiColor, TiffEncoderCompression.CcittGroup3Fax, TiffBitsPerPixel.Bit1, TiffCompression.CcittGroup3Fax)] [InlineData(TiffEncodingMode.BiColor, TiffEncoderCompression.ModifiedHuffman, TiffBitsPerPixel.Bit1, TiffCompression.Ccitt1D)] public void EncoderOptions_Work(TiffEncodingMode mode, TiffEncoderCompression compression, TiffBitsPerPixel expectedBitsPerPixel, TiffCompression expectedCompression) @@ -91,6 +93,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal(expectedBitsPerPixel, meta.BitsPerPixel); } + [Theory] [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffEncoderCompression.CcittGroup3Fax, TiffCompression.CcittGroup3Fax)] [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffEncoderCompression.ModifiedHuffman, TiffCompression.Ccitt1D)] @@ -210,6 +213,30 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.ColorPalette, TiffEncoderCompression.PackBits, useExactComparer: false, compareTolerance: 0.001f); + [Theory] + [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeColorPalette_WithDeflateCompression_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => + TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.ColorPalette, TiffEncoderCompression.Deflate, useExactComparer: false, compareTolerance: 0.001f); + + [Theory] + [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeColorPalette_WithDeflateCompressionAndPredictor_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => + TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.ColorPalette, TiffEncoderCompression.Deflate, usePredictor: true, useExactComparer: false, compareTolerance: 0.001f); + + [Theory] + [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeColorPalette_WithLzwCompression_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => + TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.ColorPalette, TiffEncoderCompression.Lzw, useExactComparer: false, compareTolerance: 0.001f); + + [Theory] + [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeColorPalette_WithLzwCompressionAndPredictor_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => + TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.ColorPalette, TiffEncoderCompression.Lzw, usePredictor: true, useExactComparer: false, compareTolerance: 0.001f); + [Theory] [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeBiColor_Works(TestImageProvider provider) From 22e92ade6a66b564c0197f74d2b78f6aec8d9827 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 1 Mar 2021 18:54:06 +0100 Subject: [PATCH 214/275] Rename TiffBitsPerSample enum values --- src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs | 10 +++++----- .../Formats/Tiff/TiffBitsPerSampleExtensions.cs | 16 ++++++++-------- .../Formats/Tiff/TiffDecoderOptionsParser.cs | 14 +++++++------- src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs | 2 +- .../Formats/Tiff/TiffMetadataTests.cs | 2 +- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs b/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs index b556e5b95..c93e9b91b 100644 --- a/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs +++ b/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs @@ -16,21 +16,21 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// One bit per sample for bicolor images. /// - One, + Bit1, /// /// Four bits per sample for grayscale images with 16 different levels of gray or paletted images with a palette of 16 colors. /// - Four, + Bit4, /// /// Eight bits per sample for grayscale images with 256 different levels of gray or paletted images with a palette of 256 colors. /// - Eight, + Bit8, /// - /// Each channel has 8 Bits. + /// 24 bits per sample, each color channel has 8 Bits. /// - Rgb888, + Bit24, } } diff --git a/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs b/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs index 307ae4ee9..1b0778eb5 100644 --- a/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs +++ b/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs @@ -18,13 +18,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff { switch (tiffBitsPerSample) { - case TiffBitsPerSample.One: + case TiffBitsPerSample.Bit1: return TiffConstants.BitsPerSample1Bit; - case TiffBitsPerSample.Four: + case TiffBitsPerSample.Bit4: return TiffConstants.BitsPerSample4Bit; - case TiffBitsPerSample.Eight: + case TiffBitsPerSample.Bit8: return TiffConstants.BitsPerSample8Bit; - case TiffBitsPerSample.Rgb888: + case TiffBitsPerSample.Bit24: return TiffConstants.BitsPerSampleRgb8Bit; default: @@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff bitsPerSample[1] == TiffConstants.BitsPerSampleRgb8Bit[1] && bitsPerSample[2] == TiffConstants.BitsPerSampleRgb8Bit[2]) { - return TiffBitsPerSample.Rgb888; + return TiffBitsPerSample.Bit24; } break; @@ -55,17 +55,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff case 1: if (bitsPerSample[0] == TiffConstants.BitsPerSample1Bit[0]) { - return TiffBitsPerSample.One; + return TiffBitsPerSample.Bit1; } if (bitsPerSample[0] == TiffConstants.BitsPerSample4Bit[0]) { - return TiffBitsPerSample.Four; + return TiffBitsPerSample.Bit4; } if (bitsPerSample[0] == TiffConstants.BitsPerSample8Bit[0]) { - return TiffBitsPerSample.Eight; + return TiffBitsPerSample.Bit8; } break; diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs index fa90c4522..d11af7085 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs @@ -79,19 +79,19 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff switch (options.BitsPerSample) { - case TiffBitsPerSample.Eight: + case TiffBitsPerSample.Bit8: { options.ColorType = TiffColorType.WhiteIsZero8; break; } - case TiffBitsPerSample.Four: + case TiffBitsPerSample.Bit4: { options.ColorType = TiffColorType.WhiteIsZero4; break; } - case TiffBitsPerSample.One: + case TiffBitsPerSample.Bit1: { options.ColorType = TiffColorType.WhiteIsZero1; break; @@ -116,19 +116,19 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff switch (options.BitsPerSample) { - case TiffBitsPerSample.Eight: + case TiffBitsPerSample.Bit8: { options.ColorType = TiffColorType.BlackIsZero8; break; } - case TiffBitsPerSample.Four: + case TiffBitsPerSample.Bit4: { options.ColorType = TiffColorType.BlackIsZero4; break; } - case TiffBitsPerSample.One: + case TiffBitsPerSample.Bit1: { options.ColorType = TiffColorType.BlackIsZero1; break; @@ -153,7 +153,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff if (options.PlanarConfiguration == TiffPlanarConfiguration.Chunky) { - options.ColorType = options.BitsPerSample == TiffBitsPerSample.Rgb888 ? TiffColorType.Rgb888 : TiffColorType.Rgb; + options.ColorType = options.BitsPerSample == TiffBitsPerSample.Bit24 ? TiffColorType.Rgb888 : TiffColorType.Rgb; } else { diff --git a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs index 5d3674422..5217650da 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs @@ -97,7 +97,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff if (this.PhotometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero || this.PhotometricInterpretation == TiffPhotometricInterpretation.BlackIsZero) { - return TiffBitsPerSample.One; + return TiffBitsPerSample.Bit1; } TiffThrowHelper.ThrowNotSupported("The TIFF BitsPerSample entry is missing which is required to decode the image."); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index a7279c942..dd5fc1a28 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -188,7 +188,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal(32u, frame.Width); Assert.Equal(32u, frame.Height); - Assert.Equal(TiffBitsPerSample.Four, frame.BitsPerSample); + Assert.Equal(TiffBitsPerSample.Bit4, frame.BitsPerSample); Assert.Equal(TiffCompression.Lzw, frame.Compression); Assert.Equal(TiffPhotometricInterpretation.PaletteColor, frame.PhotometricInterpretation); Assert.Equal("This is Название", frame.ExifProfile.GetValue(ExifTag.ImageDescription).Value); From 6bb1c8095aa9d8d75256b10dc6e87857a27f234b Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 3 Mar 2021 11:23:39 +0100 Subject: [PATCH 215/275] Add explicit bit values to BitsPerSample --- src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs b/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs index c93e9b91b..bc74cbc5f 100644 --- a/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs +++ b/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs @@ -11,26 +11,26 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// The Bits per samples is not known. /// - Unknown, + Unknown = 0, /// /// One bit per sample for bicolor images. /// - Bit1, + Bit1 = 1, /// /// Four bits per sample for grayscale images with 16 different levels of gray or paletted images with a palette of 16 colors. /// - Bit4, + Bit4 = 4, /// /// Eight bits per sample for grayscale images with 256 different levels of gray or paletted images with a palette of 256 colors. /// - Bit8, + Bit8 = 8, /// /// 24 bits per sample, each color channel has 8 Bits. /// - Bit24, + Bit24 = 24, } } From b58825345b901588fdf4ce48ff2acb69d1fb72b7 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 5 Mar 2021 14:00:09 +0100 Subject: [PATCH 216/275] Use BinaryPrimitives instead of BitConverter and scratch buffer to avoid allocations --- .../Formats/Tiff/TiffEncoderCore.cs | 17 +++++--- .../Formats/Tiff/Writers/TiffStreamWriter.cs | 40 ++++++++++++++----- .../Formats/Tiff/Utils/TiffWriterTests.cs | 5 ++- 3 files changed, 46 insertions(+), 16 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 300814421..52a367645 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -36,6 +36,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// private readonly MemoryAllocator memoryAllocator; + /// + /// A scratch buffer to reduce allocations. + /// + private readonly byte[] buffer = new byte[4]; + /// /// The global configuration. /// @@ -238,15 +243,17 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff writer.Write(ExifWriter.GetNumberOfComponents(entry)); uint length = ExifWriter.GetLength(entry); - var raw = new byte[length]; - int sz = ExifWriter.WriteValue(entry, raw, 0); - DebugGuard.IsTrue(sz == raw.Length, "Incorrect number of bytes written"); - if (raw.Length <= 4) + if (length <= 4) { - writer.WritePadded(raw); + int sz = ExifWriter.WriteValue(entry, this.buffer, 0); + DebugGuard.IsTrue(sz == length, "Incorrect number of bytes written"); + writer.WritePadded(this.buffer.AsSpan(0, sz)); } else { + var raw = new byte[length]; + int sz = ExifWriter.WriteValue(entry, raw, 0); + DebugGuard.IsTrue(sz == raw.Length, "Incorrect number of bytes written"); largeDataBlocks.Add(raw); writer.Write(dataOffset); dataOffset += (uint)(raw.Length + (raw.Length % 2)); diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs index 39d46c878..7a49d4b82 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers.Binary; using System.IO; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers @@ -13,6 +14,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers { private static readonly byte[] PaddingBytes = new byte[4]; + /// + /// A scratch buffer to reduce allocations. + /// + private readonly byte[] buffer = new byte[4]; + /// /// Initializes a new instance of the class. /// @@ -37,7 +43,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers /// /// Writes an empty four bytes to the stream, returning the offset to be written later. /// - /// The offset to be written later + /// The offset to be written later. public long PlaceMarker() { long offset = this.BaseStream.Position; @@ -69,8 +75,16 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers /// The two-byte unsigned integer to write. public void Write(ushort value) { - byte[] bytes = BitConverter.GetBytes(value); - this.BaseStream.Write(bytes, 0, 2); + if (this.IsLittleEndian) + { + BinaryPrimitives.WriteUInt16LittleEndian(this.buffer, value); + } + else + { + BinaryPrimitives.WriteUInt16BigEndian(this.buffer, value); + } + + this.BaseStream.Write(this.buffer.AsSpan(0, 2)); } /// @@ -79,21 +93,29 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers /// The four-byte unsigned integer to write. public void Write(uint value) { - byte[] bytes = BitConverter.GetBytes(value); - this.BaseStream.Write(bytes, 0, 4); + if (this.IsLittleEndian) + { + BinaryPrimitives.WriteUInt32LittleEndian(this.buffer, value); + } + else + { + BinaryPrimitives.WriteUInt32BigEndian(this.buffer, value); + } + + this.BaseStream.Write(this.buffer.AsSpan(0, 4)); } /// /// Writes an array of bytes to the current stream, padded to four-bytes. /// /// The bytes to write. - public void WritePadded(byte[] value) + public void WritePadded(Span value) { - this.BaseStream.Write(value, 0, value.Length); + this.BaseStream.Write(value); - if (value.Length < 4) + if (value.Length % 4 != 0) { - this.BaseStream.Write(PaddingBytes, 0, 4 - value.Length); + this.BaseStream.Write(PaddingBytes, 0, 4 - (value.Length % 4)); } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs index 7ea2e4cc4..b1389cec5 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs @@ -76,12 +76,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Utils } [Theory] - [InlineData(new byte[] { }, new byte[] { 0, 0, 0, 0 })] + [InlineData(new byte[] { }, new byte[] { })] [InlineData(new byte[] { 2 }, new byte[] { 2, 0, 0, 0 })] [InlineData(new byte[] { 2, 4 }, new byte[] { 2, 4, 0, 0 })] [InlineData(new byte[] { 2, 4, 6 }, new byte[] { 2, 4, 6, 0 })] [InlineData(new byte[] { 2, 4, 6, 8 }, new byte[] { 2, 4, 6, 8 })] - [InlineData(new byte[] { 2, 4, 6, 8, 10, 12 }, new byte[] { 2, 4, 6, 8, 10, 12 })] + [InlineData(new byte[] { 2, 4, 6, 8, 10, 12 }, new byte[] { 2, 4, 6, 8, 10, 12, 0, 0 })] + public void WritePadded_WritesByteArray(byte[] bytes, byte[] expectedResult) { using var stream = new MemoryStream(); From f9570d37f7aab536e35d5444cd4df872d702721c Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Mon, 8 Mar 2021 17:04:31 +0300 Subject: [PATCH 217/275] Remove TiffFrameMetadataResolutionExtensions class --- .../Common/Helpers/UnitConverter.cs | 50 +++++++++- .../Tiff/TiffEncoderEntriesCollector.cs | 78 +++++++-------- .../Formats/Tiff/TiffFrameMetadata.cs | 48 +++++---- .../TiffFrameMetadataResolutionExtensions.cs | 97 ------------------- .../Tiff/Writers/TiffBaseColorWriter.cs | 6 +- .../Formats/Tiff/Writers/TiffPaletteWriter.cs | 2 +- .../Formats/Tiff/TiffEncoderTests.cs | 1 - .../Formats/Tiff/TiffMetadataTests.cs | 2 +- 8 files changed, 118 insertions(+), 166 deletions(-) delete mode 100644 src/ImageSharp/Formats/Tiff/TiffFrameMetadataResolutionExtensions.cs diff --git a/src/ImageSharp/Common/Helpers/UnitConverter.cs b/src/ImageSharp/Common/Helpers/UnitConverter.cs index 4a6e6abcb..efc0e0e15 100644 --- a/src/ImageSharp/Common/Helpers/UnitConverter.cs +++ b/src/ImageSharp/Common/Helpers/UnitConverter.cs @@ -30,6 +30,11 @@ namespace SixLabors.ImageSharp.Common.Helpers /// private const double InchesInMeter = 1 / 0.0254D; + /// + /// The default resolution unit value. + /// + private const PixelResolutionUnit DefaultResolutionUnit = PixelResolutionUnit.PixelsPerInch; + /// /// Scales the value from centimeters to meters. /// @@ -89,7 +94,50 @@ namespace SixLabors.ImageSharp.Common.Helpers IExifValue resolution = profile.GetValue(ExifTag.ResolutionUnit); // EXIF is 1, 2, 3 so we minus "1" off the result. - return resolution is null ? default : (PixelResolutionUnit)(byte)(resolution.Value - 1); + return resolution is null ? DefaultResolutionUnit : (PixelResolutionUnit)(byte)(resolution.Value - 1); + } + + /// + /// Sets the exif profile resolution values. + /// + /// The exif profile. + /// The resolution unit. + /// The horizontal resolution value. + /// The vertical resolution value. + [MethodImpl(InliningOptions.ShortMethod)] + public static void SetResolutionValues(ExifProfile exifProfile, PixelResolutionUnit unit, double horizontal, double vertical) + { + switch (unit) + { + case PixelResolutionUnit.AspectRatio: + case PixelResolutionUnit.PixelsPerInch: + case PixelResolutionUnit.PixelsPerCentimeter: + break; + case PixelResolutionUnit.PixelsPerMeter: + { + unit = PixelResolutionUnit.PixelsPerCentimeter; + horizontal = UnitConverter.MeterToCm(horizontal); + vertical = UnitConverter.MeterToCm(vertical); + } + + break; + default: + unit = PixelResolutionUnit.PixelsPerInch; + break; + } + + exifProfile.SetValue(ExifTag.ResolutionUnit, (ushort)(unit + 1)); + + if (unit == PixelResolutionUnit.AspectRatio) + { + exifProfile.RemoveValue(ExifTag.XResolution); + exifProfile.RemoveValue(ExifTag.YResolution); + } + else + { + exifProfile.SetValue(ExifTag.XResolution, new Rational(horizontal)); + exifProfile.SetValue(ExifTag.YResolution, new Rational(vertical)); + } } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs index 0707ee321..f1d6114f8 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System.Collections.Generic; - +using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; @@ -12,6 +12,8 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { internal class TiffEncoderEntriesCollector { + private const string SoftwareValue = "ImageSharp"; + public List Entries { get; } = new List(); public void ProcessGeneral(Image image) @@ -21,18 +23,20 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff public void ProcessImageFormat(TiffEncoderCore encoder) => new ImageFormatProcessor(this).Process(encoder); - public void Add(IExifValue entry) + public void AddOrReplace(IExifValue entry) { - IExifValue exist = this.Entries.Find(t => t.Tag == entry.Tag); - if (exist != null) + int index = this.Entries.FindIndex(t => t.Tag == entry.Tag); + if (index >= 0) { - this.Entries.Remove(exist); + this.Entries[index] = entry; + } + else + { + this.Entries.Add(entry); } - - this.Entries.Add(entry); } - private void AddInternal(IExifValue entry) => this.Entries.Add(entry); + private void Add(IExifValue entry) => this.Entries.Add(entry); private class GeneralProcessor { @@ -57,12 +61,12 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff var software = new ExifString(ExifTagValue.Software) { - Value = "ImageSharp" + Value = SoftwareValue }; - this.collector.AddInternal(width); - this.collector.AddInternal(height); - this.collector.AddInternal(software); + this.collector.Add(width); + this.collector.Add(height); + this.collector.Add(software); this.ProcessResolution(image.Metadata, frameMetadata); @@ -70,7 +74,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff this.ProcessMetadata(frameMetadata); } - private static bool IsMetadata(ExifTag tag) + private static bool IsPureMetadata(ExifTag tag) { switch ((ExifTagValue)(ushort)tag) { @@ -107,29 +111,22 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff private void ProcessResolution(ImageMetadata imageMetadata, TiffFrameMetadata frameMetadata) { - frameMetadata.SetResolutions( + UnitConverter.SetResolutionValues( + frameMetadata.ExifProfile, imageMetadata.ResolutionUnits, imageMetadata.HorizontalResolution, imageMetadata.VerticalResolution); - var xResolution = new ExifRational(ExifTagValue.XResolution) - { - Value = frameMetadata.ExifProfile.GetValue(ExifTag.XResolution).Value - }; + this.collector.Add(frameMetadata.ExifProfile.GetValue(ExifTag.ResolutionUnit).DeepClone()); - var yResolution = new ExifRational(ExifTagValue.YResolution) - { - Value = frameMetadata.ExifProfile.GetValue(ExifTag.YResolution).Value - }; + IExifValue xResolution = frameMetadata.ExifProfile.GetValue(ExifTag.XResolution)?.DeepClone(); + IExifValue yResolution = frameMetadata.ExifProfile.GetValue(ExifTag.YResolution)?.DeepClone(); - var resolutionUnit = new ExifShort(ExifTagValue.ResolutionUnit) + if (xResolution != null && yResolution != null) { - Value = frameMetadata.ExifProfile.GetValue(ExifTag.ResolutionUnit).Value - }; - - this.collector.AddInternal(xResolution); - this.collector.AddInternal(yResolution); - this.collector.AddInternal(resolutionUnit); + this.collector.Add(xResolution); + this.collector.Add(yResolution); + } } private void ProcessMetadata(TiffFrameMetadata frameMetadata) @@ -160,7 +157,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff break; case ExifParts.IfdTags: - if (!IsMetadata(entry.Tag)) + if (!IsPureMetadata(entry.Tag)) { continue; } @@ -170,7 +167,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff if (!this.collector.Entries.Exists(t => t.Tag == entry.Tag)) { - this.collector.AddInternal(entry.DeepClone()); + this.collector.AddOrReplace(entry.DeepClone()); } } } @@ -194,7 +191,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff Value = imageMetadata.IptcProfile.Data }; - this.collector.AddInternal(iptc); + this.collector.Add(iptc); } else { @@ -208,7 +205,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff Value = imageMetadata.IccProfile.ToByteArray() }; - this.collector.AddInternal(icc); + this.collector.Add(icc); } else { @@ -223,7 +220,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff Value = tiffMetadata.XmpProfile }; - this.collector.AddInternal(xmp); + this.collector.Add(xmp); } else { @@ -262,10 +259,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff Value = (ushort)encoder.PhotometricInterpretation }; - this.collector.Add(samplesPerPixel); - this.collector.Add(bitPerSample); - this.collector.Add(compression); - this.collector.Add(photometricInterpretation); + this.collector.AddOrReplace(samplesPerPixel); + this.collector.AddOrReplace(bitPerSample); + this.collector.AddOrReplace(compression); + this.collector.AddOrReplace(photometricInterpretation); if (encoder.UseHorizontalPredictor) { @@ -273,7 +270,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { var predictor = new ExifShort(ExifTagValue.Predictor) { Value = (ushort)TiffPredictor.Horizontal }; - this.collector.Add(predictor); + this.collector.AddOrReplace(predictor); } } } @@ -282,12 +279,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { switch (encoder.PhotometricInterpretation) { - case TiffPhotometricInterpretation.Rgb: - return 3; case TiffPhotometricInterpretation.PaletteColor: case TiffPhotometricInterpretation.BlackIsZero: case TiffPhotometricInterpretation.WhiteIsZero: return 1; + case TiffPhotometricInterpretation.Rgb: default: return 3; } diff --git a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs index 5217650da..242c60974 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; +using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Metadata; @@ -15,9 +16,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// public class TiffFrameMetadata : IDeepCloneable { - // 2 (Inch) - internal const ushort DefaultResolutionUnit = 2; - private const TiffPlanarConfiguration DefaultPlanarConfiguration = TiffPlanarConfiguration.Chunky; private const TiffPredictor DefaultPredictor = TiffPredictor.None; @@ -212,13 +210,13 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// Gets the resolution of the image in x- direction. ///
/// The density of the image in x- direction. - public double? HorizontalResolution => this.GetResolution(ExifTag.XResolution); + public double? HorizontalResolution => this.ExifProfile.GetValue(ExifTag.XResolution)?.Value.ToDouble(); /// /// Gets the resolution of the image in y- direction. /// /// The density of the image in y- direction. - public double? VerticalResolution => this.GetResolution(ExifTag.YResolution); + public double? VerticalResolution => this.ExifProfile.GetValue(ExifTag.YResolution)?.Value.ToDouble(); /// /// Gets how the components of each pixel are stored. @@ -228,7 +226,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// /// Gets the unit of measurement for XResolution and YResolution. /// - public PixelResolutionUnit ResolutionUnit => this.GetResolutionUnit(); + public PixelResolutionUnit ResolutionUnit => UnitConverter.ExifProfileToResolutionUnit(this.ExifProfile); /// /// Gets a color map for palette color images. @@ -252,28 +250,16 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff public TiffSampleFormat[] SampleFormat => this.ExifProfile.GetValue(ExifTag.SampleFormat)?.Value?.Select(a => (TiffSampleFormat)a).ToArray(); /// - /// Clears the metadata. + /// Clears the pure metadata. /// public void ClearMetadata() { var tags = new List(); foreach (IExifValue entry in this.ExifProfile.Values) { - switch ((ExifTagValue)(ushort)entry.Tag) + if (IsFormatTag((ExifTagValue)(ushort)entry.Tag)) { - case ExifTagValue.ImageWidth: - case ExifTagValue.ImageLength: - case ExifTagValue.ResolutionUnit: - case ExifTagValue.XResolution: - case ExifTagValue.YResolution: - //// image format tags - case ExifTagValue.Predictor: - case ExifTagValue.PlanarConfiguration: - case ExifTagValue.PhotometricInterpretation: - case ExifTagValue.BitsPerSample: - case ExifTagValue.ColorMap: - tags.Add(entry); - break; + tags.Add(entry); } } @@ -282,5 +268,25 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// public IDeepCloneable DeepClone() => new TiffFrameMetadata() { ExifProfile = this.ExifProfile.DeepClone() }; + + private static bool IsFormatTag(ExifTagValue tag) + { + switch (tag) + { + case ExifTagValue.ImageWidth: + case ExifTagValue.ImageLength: + case ExifTagValue.ResolutionUnit: + case ExifTagValue.XResolution: + case ExifTagValue.YResolution: + case ExifTagValue.Predictor: + case ExifTagValue.PlanarConfiguration: + case ExifTagValue.PhotometricInterpretation: + case ExifTagValue.BitsPerSample: + case ExifTagValue.ColorMap: + return true; + } + + return false; + } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffFrameMetadataResolutionExtensions.cs b/src/ImageSharp/Formats/Tiff/TiffFrameMetadataResolutionExtensions.cs deleted file mode 100644 index 86a128ca3..000000000 --- a/src/ImageSharp/Formats/Tiff/TiffFrameMetadataResolutionExtensions.cs +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.Common.Helpers; -using SixLabors.ImageSharp.Metadata; -using SixLabors.ImageSharp.Metadata.Profiles.Exif; - -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff -{ - internal static class TiffFrameMetadataResolutionExtensions - { - public static void SetResolutions(this TiffFrameMetadata meta, PixelResolutionUnit unit, double horizontal, double vertical) - { - switch (unit) - { - case PixelResolutionUnit.AspectRatio: - case PixelResolutionUnit.PixelsPerInch: - case PixelResolutionUnit.PixelsPerCentimeter: - break; - case PixelResolutionUnit.PixelsPerMeter: - { - unit = PixelResolutionUnit.PixelsPerCentimeter; - horizontal = UnitConverter.MeterToCm(horizontal); - vertical = UnitConverter.MeterToCm(vertical); - } - - break; - default: - unit = PixelResolutionUnit.PixelsPerInch; - break; - } - - meta.ExifProfile.SetValue(ExifTag.ResolutionUnit, (ushort)(unit + 1)); - meta.SetResolution(ExifTag.XResolution, horizontal); - meta.SetResolution(ExifTag.YResolution, vertical); - } - - public static PixelResolutionUnit GetResolutionUnit(this TiffFrameMetadata meta) - { - ushort res = meta.ExifProfile.GetValue(ExifTag.ResolutionUnit)?.Value ?? TiffFrameMetadata.DefaultResolutionUnit; - - return (PixelResolutionUnit)(res - 1); - } - - public static double? GetResolution(this TiffFrameMetadata meta, ExifTag tag) - { - IExifValue resolution = meta.ExifProfile.GetValue(tag); - if (resolution == null) - { - return null; - } - - double res = resolution.Value.ToDouble(); - switch (meta.ResolutionUnit) - { - case PixelResolutionUnit.AspectRatio: - return 0; - case PixelResolutionUnit.PixelsPerCentimeter: - return UnitConverter.CmToInch(res); - case PixelResolutionUnit.PixelsPerMeter: - return UnitConverter.MeterToInch(res); - case PixelResolutionUnit.PixelsPerInch: - default: - // DefaultResolutionUnit is Inch - return res; - } - } - - private static void SetResolution(this TiffFrameMetadata meta, ExifTag tag, double? value) - { - if (value == null) - { - meta.ExifProfile.RemoveValue(tag); - return; - } - - double res = value.Value; - switch (meta.ResolutionUnit) - { - case PixelResolutionUnit.AspectRatio: - res = 0; - break; - case PixelResolutionUnit.PixelsPerCentimeter: - res = UnitConverter.InchToCm(res); - break; - case PixelResolutionUnit.PixelsPerMeter: - res = UnitConverter.InchToMeter(res); - break; - case PixelResolutionUnit.PixelsPerInch: - default: - break; - } - - meta.ExifProfile.SetValue(tag, new Rational(res)); - } - } -} diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs index 70c91fa89..32adf95c0 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs @@ -87,17 +87,17 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers /// The strip byte counts. private void AddStripTags(int rowsPerStrip, uint[] stripOffsets, uint[] stripByteCounts) { - this.EntriesCollector.Add(new ExifLong(ExifTagValue.RowsPerStrip) + this.EntriesCollector.AddOrReplace(new ExifLong(ExifTagValue.RowsPerStrip) { Value = (uint)rowsPerStrip }); - this.EntriesCollector.Add(new ExifLongArray(ExifTagValue.StripOffsets) + this.EntriesCollector.AddOrReplace(new ExifLongArray(ExifTagValue.StripOffsets) { Value = stripOffsets }); - this.EntriesCollector.Add(new ExifLongArray(ExifTagValue.StripByteCounts) + this.EntriesCollector.AddOrReplace(new ExifLongArray(ExifTagValue.StripByteCounts) { Value = stripByteCounts }); diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs index b094c22fc..00f468720 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs @@ -121,7 +121,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers Value = palette }; - this.EntriesCollector.Add(colorMap); + this.EntriesCollector.AddOrReplace(colorMap); } } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index ceb40d745..ae26bf626 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -93,7 +93,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal(expectedBitsPerPixel, meta.BitsPerPixel); } - [Theory] [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffEncoderCompression.CcittGroup3Fax, TiffCompression.CcittGroup3Fax)] [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffEncoderCompression.ModifiedHuffman, TiffCompression.Ccitt1D)] diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index dd5fc1a28..54b46cd3e 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -198,10 +198,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal(1, frame.SamplesPerPixel); Assert.Equal(32u, frame.RowsPerStrip); Assert.Equal(new Number[] { 297u }, frame.StripByteCounts, new NumberComparer()); + Assert.Equal(PixelResolutionUnit.PixelsPerInch, frame.ResolutionUnit); Assert.Equal(10, frame.HorizontalResolution); Assert.Equal(10, frame.VerticalResolution); Assert.Equal(TiffPlanarConfiguration.Chunky, frame.PlanarConfiguration); - Assert.Equal(PixelResolutionUnit.PixelsPerInch, frame.ResolutionUnit); Assert.Equal("IrfanView", frame.ExifProfile.GetValue(ExifTag.Software).Value); Assert.Null(frame.ExifProfile.GetValue(ExifTag.DateTime)?.Value); Assert.Equal("This is author1;Author2", frame.ExifProfile.GetValue(ExifTag.Artist).Value); From 4b28acff6d5a5f795106dd866b7dbb5173d06be2 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 11 Mar 2021 16:06:58 +0100 Subject: [PATCH 218/275] Remove TiffEncoderCompression, use TiffCompression enum instead --- .../Compressors/DeflateCompressor.cs | 2 +- .../Compression/Compressors/LzwCompressor.cs | 2 +- .../Compression/Compressors/NoCompressor.cs | 3 +- .../Compressors/PackBitsCompressor.cs | 3 +- .../Compressors/T4BitCompressor.cs | 3 +- .../Tiff/Compression/TiffBaseCompressor.cs | 2 +- .../Tiff/Compression/TiffCompressorFactory.cs | 22 ++- .../Formats/Tiff/Constants/TiffCompression.cs | 18 ++ .../Formats/Tiff/ITiffEncoderOptions.cs | 3 +- src/ImageSharp/Formats/Tiff/TiffEncoder.cs | 3 +- .../Formats/Tiff/TiffEncoderCompression.cs | 41 ----- .../Formats/Tiff/TiffEncoderCore.cs | 6 +- .../Tiff/TiffEncoderEntriesCollector.cs | 10 +- .../Formats/Tiff/Writers/TiffBiColorWriter.cs | 3 +- .../Formats/Tiff/TiffEncoderTests.cs | 158 ++++++++---------- .../Formats/Tiff/TiffMetadataTests.cs | 2 +- 16 files changed, 127 insertions(+), 154 deletions(-) delete mode 100644 src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/DeflateCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/DeflateCompressor.cs index f8fa2b89c..153b1fc88 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/DeflateCompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/DeflateCompressor.cs @@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors => this.compressionLevel = compressionLevel; /// - public override TiffEncoderCompression Method => TiffEncoderCompression.Deflate; + public override TiffCompression Method => TiffCompression.Deflate; /// public override void Initialize(int rowsPerStrip) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/LzwCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/LzwCompressor.cs index d29cf6157..2341f23b1 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/LzwCompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/LzwCompressor.cs @@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors } /// - public override TiffEncoderCompression Method => TiffEncoderCompression.Lzw; + public override TiffCompression Method => TiffCompression.Lzw; /// public override void Initialize(int rowsPerStrip) => this.lzwEncoder = new TiffLzwEncoder(this.Allocator); diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/NoCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/NoCompressor.cs index 79fbd29b4..b63186eb8 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/NoCompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/NoCompressor.cs @@ -3,6 +3,7 @@ using System; using System.IO; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors @@ -15,7 +16,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors } /// - public override TiffEncoderCompression Method => TiffEncoderCompression.None; + public override TiffCompression Method => TiffCompression.None; /// public override void Initialize(int rowsPerStrip) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsCompressor.cs index 5353b17c3..5d2a31d2d 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsCompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsCompressor.cs @@ -3,6 +3,7 @@ using System; using System.IO; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors @@ -17,7 +18,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors } /// - public override TiffEncoderCompression Method => TiffEncoderCompression.PackBits; + public override TiffCompression Method => TiffCompression.PackBits; /// public override void Initialize(int rowsPerStrip) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitCompressor.cs index bb631c5a9..9224b27a4 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitCompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitCompressor.cs @@ -5,6 +5,7 @@ using System; using System.Buffers; using System.Collections.Generic; using System.IO; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors @@ -202,7 +203,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors } /// - public override TiffEncoderCompression Method => this.useModifiedHuffman ? TiffEncoderCompression.ModifiedHuffman : TiffEncoderCompression.CcittGroup3Fax; + public override TiffCompression Method => this.useModifiedHuffman ? TiffCompression.Ccitt1D : TiffCompression.CcittGroup3Fax; /// public override void Initialize(int rowsPerStrip) diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompressor.cs index 6514fbe83..f7412e240 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompressor.cs @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression /// /// Gets the compression method to use. /// - public abstract TiffEncoderCompression Method { get; } + public abstract TiffCompression Method { get; } /// /// Gets the output stream to write the compressed image to. diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressorFactory.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressorFactory.cs index 7cd39d8cb..cce7567a2 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressorFactory.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressorFactory.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression internal static class TiffCompressorFactory { public static TiffBaseCompressor Create( - TiffEncoderCompression method, + TiffCompression method, Stream output, MemoryAllocator allocator, int width, @@ -22,36 +22,42 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression { switch (method) { - case TiffEncoderCompression.None: + // The following compression types are not implemented in the encoder and will default to no compression instead. + case TiffCompression.ItuTRecT43: + case TiffCompression.ItuTRecT82: + case TiffCompression.Jpeg: + case TiffCompression.OldJpeg: + case TiffCompression.OldDeflate: + case TiffCompression.None: DebugGuard.IsTrue(compressionLevel == DeflateCompressionLevel.DefaultCompression, "No deflate compression level is expected to be set"); DebugGuard.IsTrue(predictor == TiffPredictor.None, "Predictor should only be used with lzw or deflate compression"); return new NoCompressor(output, allocator, width, bitsPerPixel); - case TiffEncoderCompression.PackBits: + case TiffCompression.PackBits: DebugGuard.IsTrue(compressionLevel == DeflateCompressionLevel.DefaultCompression, "No deflate compression level is expected to be set"); DebugGuard.IsTrue(predictor == TiffPredictor.None, "Predictor should only be used with lzw or deflate compression"); return new PackBitsCompressor(output, allocator, width, bitsPerPixel); - case TiffEncoderCompression.Deflate: + case TiffCompression.Deflate: return new DeflateCompressor(output, allocator, width, bitsPerPixel, predictor, compressionLevel); - case TiffEncoderCompression.Lzw: + case TiffCompression.Lzw: DebugGuard.IsTrue(compressionLevel == DeflateCompressionLevel.DefaultCompression, "No deflate compression level is expected to be set"); return new LzwCompressor(output, allocator, width, bitsPerPixel, predictor); - case TiffEncoderCompression.CcittGroup3Fax: + case TiffCompression.CcittGroup3Fax: DebugGuard.IsTrue(compressionLevel == DeflateCompressionLevel.DefaultCompression, "No deflate compression level is expected to be set"); DebugGuard.IsTrue(predictor == TiffPredictor.None, "Predictor should only be used with lzw or deflate compression"); return new T4BitCompressor(output, allocator, width, bitsPerPixel, false); - case TiffEncoderCompression.ModifiedHuffman: + case TiffCompression.Ccitt1D: DebugGuard.IsTrue(compressionLevel == DeflateCompressionLevel.DefaultCompression, "No deflate compression level is expected to be set"); DebugGuard.IsTrue(predictor == TiffPredictor.None, "Predictor should only be used with lzw or deflate compression"); return new T4BitCompressor(output, allocator, width, bitsPerPixel, true); default: - throw TiffThrowHelper.NotSupportedCompressor(nameof(method)); + throw TiffThrowHelper.NotSupportedCompressor(method.ToString()); } } } diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs index d5717dbfb..6a6bd7911 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs @@ -30,6 +30,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants /// /// T6-encoding: CCITT T.6 bi-level encoding (see Section 11 of the TIFF 6.0 specification). + /// + /// Note: The TIFF encoder does not yet support this compression and will default to use no compression instead, + /// if this is choosen. /// CcittGroup4Fax = 4, @@ -40,11 +43,17 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants /// /// JPEG compression - obsolete (see Section 22 of the TIFF 6.0 specification). + /// + /// Note: The TIFF encoder does not support this compression and will default to use no compression instead, + /// if this is choosen. /// OldJpeg = 6, /// /// JPEG compression (see TIFF Specification, supplement 2). + /// + /// Note: The TIFF encoder does not yet support this compression and will default to use no compression instead, + /// if this is choosen. /// Jpeg = 7, @@ -55,16 +64,25 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants /// /// Deflate compression - old. + /// + /// Note: The TIFF encoder does not support this compression and will default to use no compression instead, + /// if this is choosen. /// OldDeflate = 32946, /// /// ITU-T Rec. T.82 coding, applying ITU-T Rec. T.85 (JBIG) (see RFC2301). + /// + /// Note: The TIFF encoder does not yet support this compression and will default to use no compression instead, + /// if this is choosen. /// ItuTRecT82 = 9, /// /// ITU-T Rec. T.43 representation, using ITU-T Rec. T.82 (JBIG) (see RFC2301). + /// + /// Note: The TIFF encoder does not yet support this compression and will default to use no compression instead, + /// if this is choosen. /// ItuTRecT43 = 10 } diff --git a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs index efa5dcf85..c823b50c2 100644 --- a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs +++ b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Compression.Zlib; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Processing.Processors.Quantization; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff @@ -19,7 +20,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// /// Gets the compression type to use. /// - TiffEncoderCompression Compression { get; } + TiffCompression Compression { get; } /// /// Gets the compression level 1-9 for the deflate compression mode. diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs index 7e075c2e6..b273b82e7 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Compression.Zlib; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Quantization; @@ -21,7 +22,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff public TiffBitsPerPixel? BitsPerPixel { get; set; } /// - public TiffEncoderCompression Compression { get; set; } = TiffEncoderCompression.None; + public TiffCompression Compression { get; set; } = TiffCompression.None; /// public DeflateCompressionLevel CompressionLevel { get; set; } = DeflateCompressionLevel.DefaultCompression; diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs deleted file mode 100644 index f2e94d316..000000000 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff -{ - /// - /// Indicates which tiff compression is used. - /// - public enum TiffEncoderCompression - { - /// - /// No compression is used. - /// - None, - - /// - /// Use zlib compression. - /// - Deflate, - - /// - /// Use lzw compression. - /// - Lzw, - - /// - /// Use PackBits to compression the image data. - /// - PackBits, - - /// - /// Use CCITT T4 1D compression. Note: This is only valid for bi-level images. - /// - CcittGroup3Fax, - - /// - /// Use the modified Huffman RLE. Note: This is only valid for bi-level images. - /// - ModifiedHuffman, - } -} diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 52a367645..ce55ecd1f 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -86,7 +86,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// /// Gets or sets the compression implementation to use when encoding the image. /// - internal TiffEncoderCompression CompressionType { get; set; } + internal TiffCompression CompressionType { get; set; } /// /// Gets the encoding mode to use. RGB, RGB with color palette or gray. @@ -278,7 +278,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff private void SetMode(TiffMetadata tiffMetadata) { // Make sure, that the fax compressions are only used together with the BiColor mode. - if (this.CompressionType == TiffEncoderCompression.CcittGroup3Fax || this.CompressionType == TiffEncoderCompression.ModifiedHuffman) + if (this.CompressionType == TiffCompression.CcittGroup3Fax || this.CompressionType == TiffCompression.Ccitt1D) { // Default means the user has not specified a preferred encoding mode. if (this.Mode == TiffEncodingMode.Default) @@ -350,7 +350,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff this.PhotometricInterpretation = TiffPhotometricInterpretation.PaletteColor; break; case TiffEncodingMode.BiColor: - if (this.CompressionType == TiffEncoderCompression.CcittGroup3Fax || this.CompressionType == TiffEncoderCompression.ModifiedHuffman) + if (this.CompressionType == TiffCompression.CcittGroup3Fax || this.CompressionType == TiffCompression.Ccitt1D) { // The “normal” PhotometricInterpretation for bilevel CCITT compressed data is WhiteIsZero. this.PhotometricInterpretation = TiffPhotometricInterpretation.WhiteIsZero; diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs index 0707ee321..7cc47e51b 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs @@ -332,13 +332,13 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff { switch (encoder.CompressionType) { - case TiffEncoderCompression.Deflate: + case TiffCompression.Deflate: // Deflate is allowed for all modes. return (ushort)TiffCompression.Deflate; - case TiffEncoderCompression.PackBits: + case TiffCompression.PackBits: // PackBits is allowed for all modes. return (ushort)TiffCompression.PackBits; - case TiffEncoderCompression.Lzw: + case TiffCompression.Lzw: if (encoder.Mode == TiffEncodingMode.Rgb || encoder.Mode == TiffEncodingMode.Gray || encoder.Mode == TiffEncodingMode.ColorPalette) { return (ushort)TiffCompression.Lzw; @@ -346,7 +346,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff break; - case TiffEncoderCompression.CcittGroup3Fax: + case TiffCompression.CcittGroup3Fax: if (encoder.Mode == TiffEncodingMode.BiColor) { return (ushort)TiffCompression.CcittGroup3Fax; @@ -354,7 +354,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff break; - case TiffEncoderCompression.ModifiedHuffman: + case TiffCompression.Ccitt1D: if (encoder.Mode == TiffEncodingMode.BiColor) { return (ushort)TiffCompression.Ccitt1D; diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs index edeee81a1..7ab081499 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs @@ -5,6 +5,7 @@ using System; using System.Buffers; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; @@ -46,7 +47,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers PixelOperations.Instance.ToL8Bytes(this.Configuration, pixels, pixelAsGraySpan, pixels.Length); - if (compressor.Method == TiffEncoderCompression.CcittGroup3Fax || compressor.Method == TiffEncoderCompression.ModifiedHuffman) + if (compressor.Method == TiffCompression.CcittGroup3Fax || compressor.Method == TiffCompression.Ccitt1D) { // Special case for T4BitCompressor. compressor.CompressStrip(pixelAsGraySpan, height); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index ceb40d745..2a1f800a2 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -30,27 +30,32 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff } [Theory] - [InlineData(TiffEncodingMode.Default, TiffEncoderCompression.None, TiffBitsPerPixel.Bit24, TiffCompression.None)] - [InlineData(TiffEncodingMode.Rgb, TiffEncoderCompression.None, TiffBitsPerPixel.Bit24, TiffCompression.None)] - [InlineData(TiffEncodingMode.ColorPalette, TiffEncoderCompression.None, TiffBitsPerPixel.Bit8, TiffCompression.None)] - [InlineData(TiffEncodingMode.Gray, TiffEncoderCompression.None, TiffBitsPerPixel.Bit8, TiffCompression.None)] - [InlineData(TiffEncodingMode.BiColor, TiffEncoderCompression.None, TiffBitsPerPixel.Bit1, TiffCompression.None)] - [InlineData(TiffEncodingMode.Default, TiffEncoderCompression.Deflate, TiffBitsPerPixel.Bit24, TiffCompression.Deflate)] - [InlineData(TiffEncodingMode.Rgb, TiffEncoderCompression.Deflate, TiffBitsPerPixel.Bit24, TiffCompression.Deflate)] - [InlineData(TiffEncodingMode.Gray, TiffEncoderCompression.Deflate, TiffBitsPerPixel.Bit8, TiffCompression.Deflate)] - [InlineData(TiffEncodingMode.BiColor, TiffEncoderCompression.Deflate, TiffBitsPerPixel.Bit1, TiffCompression.Deflate)] - [InlineData(TiffEncodingMode.ColorPalette, TiffEncoderCompression.Deflate, TiffBitsPerPixel.Bit8, TiffCompression.Deflate)] - [InlineData(TiffEncodingMode.Default, TiffEncoderCompression.PackBits, TiffBitsPerPixel.Bit24, TiffCompression.PackBits)] - [InlineData(TiffEncodingMode.Rgb, TiffEncoderCompression.PackBits, TiffBitsPerPixel.Bit24, TiffCompression.PackBits)] - [InlineData(TiffEncodingMode.ColorPalette, TiffEncoderCompression.PackBits, TiffBitsPerPixel.Bit8, TiffCompression.PackBits)] - [InlineData(TiffEncodingMode.Gray, TiffEncoderCompression.PackBits, TiffBitsPerPixel.Bit8, TiffCompression.PackBits)] - [InlineData(TiffEncodingMode.BiColor, TiffEncoderCompression.PackBits, TiffBitsPerPixel.Bit1, TiffCompression.PackBits)] - [InlineData(TiffEncodingMode.Rgb, TiffEncoderCompression.Lzw, TiffBitsPerPixel.Bit24, TiffCompression.Lzw)] - [InlineData(TiffEncodingMode.Gray, TiffEncoderCompression.Lzw, TiffBitsPerPixel.Bit8, TiffCompression.Lzw)] - [InlineData(TiffEncodingMode.ColorPalette, TiffEncoderCompression.Lzw, TiffBitsPerPixel.Bit8, TiffCompression.Lzw)] - [InlineData(TiffEncodingMode.BiColor, TiffEncoderCompression.CcittGroup3Fax, TiffBitsPerPixel.Bit1, TiffCompression.CcittGroup3Fax)] - [InlineData(TiffEncodingMode.BiColor, TiffEncoderCompression.ModifiedHuffman, TiffBitsPerPixel.Bit1, TiffCompression.Ccitt1D)] - public void EncoderOptions_Work(TiffEncodingMode mode, TiffEncoderCompression compression, TiffBitsPerPixel expectedBitsPerPixel, TiffCompression expectedCompression) + [InlineData(TiffEncodingMode.Default, TiffCompression.None, TiffBitsPerPixel.Bit24, TiffCompression.None)] + [InlineData(TiffEncodingMode.Rgb, TiffCompression.None, TiffBitsPerPixel.Bit24, TiffCompression.None)] + [InlineData(TiffEncodingMode.ColorPalette, TiffCompression.None, TiffBitsPerPixel.Bit8, TiffCompression.None)] + [InlineData(TiffEncodingMode.Gray, TiffCompression.None, TiffBitsPerPixel.Bit8, TiffCompression.None)] + [InlineData(TiffEncodingMode.BiColor, TiffCompression.None, TiffBitsPerPixel.Bit1, TiffCompression.None)] + [InlineData(TiffEncodingMode.Default, TiffCompression.Deflate, TiffBitsPerPixel.Bit24, TiffCompression.Deflate)] + [InlineData(TiffEncodingMode.Rgb, TiffCompression.Deflate, TiffBitsPerPixel.Bit24, TiffCompression.Deflate)] + [InlineData(TiffEncodingMode.Gray, TiffCompression.Deflate, TiffBitsPerPixel.Bit8, TiffCompression.Deflate)] + [InlineData(TiffEncodingMode.BiColor, TiffCompression.Deflate, TiffBitsPerPixel.Bit1, TiffCompression.Deflate)] + [InlineData(TiffEncodingMode.ColorPalette, TiffCompression.Deflate, TiffBitsPerPixel.Bit8, TiffCompression.Deflate)] + [InlineData(TiffEncodingMode.Default, TiffCompression.PackBits, TiffBitsPerPixel.Bit24, TiffCompression.PackBits)] + [InlineData(TiffEncodingMode.Rgb, TiffCompression.PackBits, TiffBitsPerPixel.Bit24, TiffCompression.PackBits)] + [InlineData(TiffEncodingMode.ColorPalette, TiffCompression.PackBits, TiffBitsPerPixel.Bit8, TiffCompression.PackBits)] + [InlineData(TiffEncodingMode.Gray, TiffCompression.PackBits, TiffBitsPerPixel.Bit8, TiffCompression.PackBits)] + [InlineData(TiffEncodingMode.BiColor, TiffCompression.PackBits, TiffBitsPerPixel.Bit1, TiffCompression.PackBits)] + [InlineData(TiffEncodingMode.Rgb, TiffCompression.Lzw, TiffBitsPerPixel.Bit24, TiffCompression.Lzw)] + [InlineData(TiffEncodingMode.Gray, TiffCompression.Lzw, TiffBitsPerPixel.Bit8, TiffCompression.Lzw)] + [InlineData(TiffEncodingMode.ColorPalette, TiffCompression.Lzw, TiffBitsPerPixel.Bit8, TiffCompression.Lzw)] + [InlineData(TiffEncodingMode.BiColor, TiffCompression.CcittGroup3Fax, TiffBitsPerPixel.Bit1, TiffCompression.CcittGroup3Fax)] + [InlineData(TiffEncodingMode.BiColor, TiffCompression.Ccitt1D, TiffBitsPerPixel.Bit1, TiffCompression.Ccitt1D)] + [InlineData(TiffEncodingMode.Rgb, TiffCompression.ItuTRecT43, TiffBitsPerPixel.Bit24, TiffCompression.None)] + [InlineData(TiffEncodingMode.Rgb, TiffCompression.ItuTRecT82, TiffBitsPerPixel.Bit24, TiffCompression.None)] + [InlineData(TiffEncodingMode.Rgb, TiffCompression.Jpeg, TiffBitsPerPixel.Bit24, TiffCompression.None)] + [InlineData(TiffEncodingMode.Rgb, TiffCompression.OldDeflate, TiffBitsPerPixel.Bit24, TiffCompression.None)] + [InlineData(TiffEncodingMode.Rgb, TiffCompression.OldJpeg, TiffBitsPerPixel.Bit24, TiffCompression.None)] + public void EncoderOptions_Work(TiffEncodingMode mode, TiffCompression compression, TiffBitsPerPixel expectedBitsPerPixel, TiffCompression expectedCompression) { // arrange var tiffEncoder = new TiffEncoder { Mode = mode, Compression = compression }; @@ -93,13 +98,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal(expectedBitsPerPixel, meta.BitsPerPixel); } - [Theory] - [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffEncoderCompression.CcittGroup3Fax, TiffCompression.CcittGroup3Fax)] - [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffEncoderCompression.ModifiedHuffman, TiffCompression.Ccitt1D)] - [WithFile(GrayscaleUncompressed, PixelTypes.L8, TiffEncoderCompression.CcittGroup3Fax, TiffCompression.CcittGroup3Fax)] - [WithFile(PaletteDeflateMultistrip, PixelTypes.L8, TiffEncoderCompression.ModifiedHuffman, TiffCompression.Ccitt1D)] - public void TiffEncoder_EncodesWithCorrectBiColorModeCompression(TestImageProvider provider, TiffEncoderCompression compression, TiffCompression expectedCompression) + [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffCompression.CcittGroup3Fax, TiffCompression.CcittGroup3Fax)] + [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffCompression.Ccitt1D, TiffCompression.Ccitt1D)] + [WithFile(GrayscaleUncompressed, PixelTypes.L8, TiffCompression.CcittGroup3Fax, TiffCompression.CcittGroup3Fax)] + [WithFile(PaletteDeflateMultistrip, PixelTypes.L8, TiffCompression.Ccitt1D, TiffCompression.Ccitt1D)] + public void TiffEncoder_EncodesWithCorrectBiColorModeCompression(TestImageProvider provider, TiffCompression compression, TiffCompression expectedCompression) where TPixel : unmanaged, IPixel { // arrange @@ -120,11 +124,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff } [Theory] - [InlineData(TiffEncodingMode.ColorPalette, TiffEncoderCompression.CcittGroup3Fax)] - [InlineData(TiffEncodingMode.ColorPalette, TiffEncoderCompression.ModifiedHuffman)] - [InlineData(TiffEncodingMode.Gray, TiffEncoderCompression.ModifiedHuffman)] - [InlineData(TiffEncodingMode.Rgb, TiffEncoderCompression.ModifiedHuffman)] - public void TiffEncoder_IncompatibilityOptions(TiffEncodingMode mode, TiffEncoderCompression compression) + [InlineData(TiffEncodingMode.ColorPalette, TiffCompression.CcittGroup3Fax)] + [InlineData(TiffEncodingMode.ColorPalette, TiffCompression.Ccitt1D)] + [InlineData(TiffEncodingMode.Gray, TiffCompression.Ccitt1D)] + [InlineData(TiffEncodingMode.Rgb, TiffCompression.Ccitt1D)] + public void TiffEncoder_IncompatibilityOptions(TiffEncodingMode mode, TiffCompression compression) { // arrange using var input = new Image(10, 10); @@ -143,27 +147,27 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Theory] [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeRgb_WithDeflateCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffEncodingMode.Rgb, TiffEncoderCompression.Deflate); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffEncodingMode.Rgb, TiffCompression.Deflate); [Theory] [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeRgb_WithDeflateCompressionAndPredictor_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffEncodingMode.Rgb, TiffEncoderCompression.Deflate, usePredictor: true); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffEncodingMode.Rgb, TiffCompression.Deflate, usePredictor: true); [Theory] [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeRgb_WithLzwCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffEncodingMode.Rgb, TiffEncoderCompression.Lzw); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffEncodingMode.Rgb, TiffCompression.Lzw); [Theory] [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeRgb_WithLzwCompressionAndPredictor_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffEncodingMode.Rgb, TiffEncoderCompression.Lzw, usePredictor: true); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffEncodingMode.Rgb, TiffCompression.Lzw, usePredictor: true); [Theory] [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeRgb_WithPackBitsCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffEncodingMode.Rgb, TiffEncoderCompression.PackBits); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffEncodingMode.Rgb, TiffCompression.PackBits); [Theory] [WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] @@ -173,27 +177,27 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Theory] [WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeGray_WithDeflateCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.Gray, TiffEncoderCompression.Deflate); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.Gray, TiffCompression.Deflate); [Theory] [WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeGray_WithDeflateCompressionAndPredictor_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.Gray, TiffEncoderCompression.Deflate, usePredictor: true); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.Gray, TiffCompression.Deflate, usePredictor: true); [Theory] [WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeGray_WithLzwCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.Gray, TiffEncoderCompression.Lzw); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.Gray, TiffCompression.Lzw); [Theory] [WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeGray_WithLzwCompressionAndPredictor_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.Gray, TiffEncoderCompression.Lzw, usePredictor: true); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.Gray, TiffCompression.Lzw, usePredictor: true); [Theory] [WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeGray_WithPackBitsCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.Gray, TiffEncoderCompression.PackBits); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.Gray, TiffCompression.PackBits); [Theory] [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] @@ -211,31 +215,31 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_WithPackBitsCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => - TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.ColorPalette, TiffEncoderCompression.PackBits, useExactComparer: false, compareTolerance: 0.001f); + TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.ColorPalette, TiffCompression.PackBits, useExactComparer: false, compareTolerance: 0.001f); [Theory] [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_WithDeflateCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => - TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.ColorPalette, TiffEncoderCompression.Deflate, useExactComparer: false, compareTolerance: 0.001f); + TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.ColorPalette, TiffCompression.Deflate, useExactComparer: false, compareTolerance: 0.001f); [Theory] [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_WithDeflateCompressionAndPredictor_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => - TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.ColorPalette, TiffEncoderCompression.Deflate, usePredictor: true, useExactComparer: false, compareTolerance: 0.001f); + TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.ColorPalette, TiffCompression.Deflate, usePredictor: true, useExactComparer: false, compareTolerance: 0.001f); [Theory] [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_WithLzwCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => - TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.ColorPalette, TiffEncoderCompression.Lzw, useExactComparer: false, compareTolerance: 0.001f); + TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.ColorPalette, TiffCompression.Lzw, useExactComparer: false, compareTolerance: 0.001f); [Theory] [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_WithLzwCompressionAndPredictor_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => - TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.ColorPalette, TiffEncoderCompression.Lzw, usePredictor: true, useExactComparer: false, compareTolerance: 0.001f); + TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.ColorPalette, TiffCompression.Lzw, usePredictor: true, useExactComparer: false, compareTolerance: 0.001f); [Theory] [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] @@ -245,42 +249,42 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Theory] [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeBiColor_WithDeflateCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffEncodingMode.BiColor, TiffEncoderCompression.Deflate); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffEncodingMode.BiColor, TiffCompression.Deflate); [Theory] [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeBiColor_WithPackBitsCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffEncodingMode.BiColor, TiffEncoderCompression.PackBits); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffEncodingMode.BiColor, TiffCompression.PackBits); [Theory] [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeBiColor_WithCcittGroup3FaxCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffEncodingMode.BiColor, TiffEncoderCompression.CcittGroup3Fax); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffEncodingMode.BiColor, TiffCompression.CcittGroup3Fax); [Theory] [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeBiColor_WithModifiedHuffmanCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffEncodingMode.BiColor, TiffEncoderCompression.ModifiedHuffman); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffEncodingMode.BiColor, TiffCompression.Ccitt1D); [Theory] - [WithFile(GrayscaleUncompressed, PixelTypes.L8, TiffEncodingMode.Gray, TiffEncoderCompression.PackBits, 16 * 1024)] - [WithFile(PaletteDeflateMultistrip, PixelTypes.L8, TiffEncodingMode.ColorPalette, TiffEncoderCompression.Lzw, 32 * 1024)] - [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffEncodingMode.Rgb, TiffEncoderCompression.Deflate, 64 * 1024)] - [WithFile(RgbUncompressed, PixelTypes.Rgb24, TiffEncodingMode.Rgb, TiffEncoderCompression.None, 10 * 1024)] - [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffEncodingMode.Rgb, TiffEncoderCompression.None, 30 * 1024)] - [WithFile(RgbUncompressed, PixelTypes.Rgb48, TiffEncodingMode.Rgb, TiffEncoderCompression.None, 70 * 1024)] - public void TiffEncoder_StripLength(TestImageProvider provider, TiffEncodingMode mode, TiffEncoderCompression compression, int maxSize) + [WithFile(GrayscaleUncompressed, PixelTypes.L8, TiffEncodingMode.Gray, TiffCompression.PackBits, 16 * 1024)] + [WithFile(PaletteDeflateMultistrip, PixelTypes.L8, TiffEncodingMode.ColorPalette, TiffCompression.Lzw, 32 * 1024)] + [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffEncodingMode.Rgb, TiffCompression.Deflate, 64 * 1024)] + [WithFile(RgbUncompressed, PixelTypes.Rgb24, TiffEncodingMode.Rgb, TiffCompression.None, 10 * 1024)] + [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffEncodingMode.Rgb, TiffCompression.None, 30 * 1024)] + [WithFile(RgbUncompressed, PixelTypes.Rgb48, TiffEncodingMode.Rgb, TiffCompression.None, 70 * 1024)] + public void TiffEncoder_StripLength(TestImageProvider provider, TiffEncodingMode mode, TiffCompression compression, int maxSize) where TPixel : unmanaged, IPixel => TestStripLength(provider, mode, compression, maxSize); [Theory] - [WithFile(Calliphora_BiColorUncompressed, PixelTypes.L8, TiffEncodingMode.BiColor, TiffEncoderCompression.CcittGroup3Fax, 9 * 1024)] - public void TiffEncoder_StripLength_OutOfBounds(TestImageProvider provider, TiffEncodingMode mode, TiffEncoderCompression compression, int maxSize) + [WithFile(Calliphora_BiColorUncompressed, PixelTypes.L8, TiffEncodingMode.BiColor, TiffCompression.CcittGroup3Fax, 9 * 1024)] + public void TiffEncoder_StripLength_OutOfBounds(TestImageProvider provider, TiffEncodingMode mode, TiffCompression compression, int maxSize) where TPixel : unmanaged, IPixel => //// CcittGroup3Fax compressed data length can be larger than the original length Assert.Throws(() => TestStripLength(provider, mode, compression, maxSize)); - private static void TestStripLength(TestImageProvider provider, TiffEncodingMode mode, TiffEncoderCompression compression, int maxSize) + private static void TestStripLength(TestImageProvider provider, TiffEncodingMode mode, TiffCompression compression, int maxSize) where TPixel : unmanaged, IPixel { // arrange @@ -306,12 +310,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.True((uint)sz <= maxSize); } - // for uncompressed more accurate test - if (compression == TiffEncoderCompression.None) + // For uncompressed more accurate test. + if (compression == TiffCompression.None) { for (int i = 0; i < meta.StripByteCounts.Length - 1; i++) { - // the difference must be less than one row + // The difference must be less than one row. int stripBytes = (int)meta.StripByteCounts[i]; var widthBytes = (meta.BitsPerPixel + 7) / 8 * (int)meta.Width; @@ -319,12 +323,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff } } - // compare with reference + // Compare with reference. TestTiffEncoderCore( provider, (TiffBitsPerPixel)inputMeta.BitsPerPixel, mode, - Convert(inputMeta.Compression), + inputMeta.Compression, maxStripSize: maxSize); } @@ -332,7 +336,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff TestImageProvider provider, TiffBitsPerPixel bitsPerPixel, TiffEncodingMode mode, - TiffEncoderCompression compression = TiffEncoderCompression.None, + TiffCompression compression = TiffCompression.None, bool usePredictor = false, bool useExactComparer = true, int maxStripSize = 0, @@ -352,25 +356,5 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // Does DebugSave & load reference CompareToReferenceInput(): image.VerifyEncoder(provider, "tiff", bitsPerPixel, encoder, useExactComparer ? ImageComparer.Exact : ImageComparer.Tolerant(compareTolerance), referenceDecoder: ReferenceDecoder); } - - private static TiffEncoderCompression Convert(TiffCompression compression) - { - switch (compression) - { - default: - case TiffCompression.None: - return TiffEncoderCompression.None; - case TiffCompression.Deflate: - return TiffEncoderCompression.Deflate; - case TiffCompression.Lzw: - return TiffEncoderCompression.Lzw; - case TiffCompression.PackBits: - return TiffEncoderCompression.PackBits; - case TiffCompression.Ccitt1D: - return TiffEncoderCompression.ModifiedHuffman; - case TiffCompression.CcittGroup3Fax: - return TiffEncoderCompression.CcittGroup3Fax; - } - } } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index dd5fc1a28..6b17c1907 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -354,7 +354,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff frameMeta.ExifProfile.SetValue(ExifTag.DateTime, datetime); // Save to Tiff - var tiffEncoder = new TiffEncoder { Mode = TiffEncodingMode.Default, Compression = TiffEncoderCompression.Deflate }; + var tiffEncoder = new TiffEncoder { Mode = TiffEncodingMode.Default, Compression = TiffCompression.Deflate }; if (!preserveMetadata) { ClearMeta(image); From 3c1dc94664ac411423557b19511e7fdf96823a47 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 11 Mar 2021 16:15:06 +0100 Subject: [PATCH 219/275] Fix build errors in benchmark project --- .../Codecs/EncodeTiff.cs | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/tests/ImageSharp.Benchmarks/Codecs/EncodeTiff.cs b/tests/ImageSharp.Benchmarks/Codecs/EncodeTiff.cs index e40907c8e..99b6f437e 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/EncodeTiff.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/EncodeTiff.cs @@ -7,6 +7,7 @@ using System.IO; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats.Experimental.Tiff; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; @@ -28,13 +29,13 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs public string TestImage { get; set; } [Params( - TiffEncoderCompression.None, - TiffEncoderCompression.Deflate, - TiffEncoderCompression.Lzw, - TiffEncoderCompression.PackBits, - TiffEncoderCompression.CcittGroup3Fax, - TiffEncoderCompression.ModifiedHuffman)] - public TiffEncoderCompression Compression { get; set; } + TiffCompression.None, + TiffCompression.Deflate, + TiffCompression.Lzw, + TiffCompression.PackBits, + TiffCompression.CcittGroup3Fax, + TiffCompression.Ccitt1D)] + public TiffCompression Compression { get; set; } [GlobalSetup] public void ReadImages() @@ -73,7 +74,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs TiffEncodingMode mode = TiffEncodingMode.Default; // workaround for 1-bit bug - if (this.Compression == TiffEncoderCompression.CcittGroup3Fax || this.Compression == TiffEncoderCompression.ModifiedHuffman) + if (this.Compression == TiffCompression.CcittGroup3Fax || this.Compression == TiffCompression.Ccitt1D) { mode = TiffEncodingMode.BiColor; } @@ -98,20 +99,20 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs return null; } - private static EncoderValue Cast(TiffEncoderCompression compression) + private static EncoderValue Cast(TiffCompression compression) { switch (compression) { - case TiffEncoderCompression.None: + case TiffCompression.None: return EncoderValue.CompressionNone; - case TiffEncoderCompression.CcittGroup3Fax: + case TiffCompression.CcittGroup3Fax: return EncoderValue.CompressionCCITT3; - case TiffEncoderCompression.ModifiedHuffman: + case TiffCompression.Ccitt1D: return EncoderValue.CompressionRle; - case TiffEncoderCompression.Lzw: + case TiffCompression.Lzw: return EncoderValue.CompressionLZW; default: From 8bba0132e83031a445173c9d5a6eeb6b92f427c4 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 15 Mar 2021 13:29:41 +0100 Subject: [PATCH 220/275] Add Tiff Encoder/Decoder to AoT seeds --- src/ImageSharp/Advanced/AotCompilerTools.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/ImageSharp/Advanced/AotCompilerTools.cs b/src/ImageSharp/Advanced/AotCompilerTools.cs index ea4cd1c8c..2221a5372 100644 --- a/src/ImageSharp/Advanced/AotCompilerTools.cs +++ b/src/ImageSharp/Advanced/AotCompilerTools.cs @@ -7,6 +7,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Experimental.Tiff; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg.Components; @@ -56,7 +57,7 @@ namespace SixLabors.ImageSharp.Advanced /// necessary methods to complete the SaveAsGif call. That's it, otherwise you should NEVER need this method!!! /// [Preserve] - private static void SeedEverything() + private static void SeedPixelFormats() { try { @@ -199,6 +200,7 @@ namespace SixLabors.ImageSharp.Advanced default(JpegEncoderCore).Encode(default, default, default); default(PngEncoderCore).Encode(default, default, default); default(TgaEncoderCore).Encode(default, default, default); + default(TiffEncoderCore).Encode(default, default, default); } /// @@ -214,6 +216,7 @@ namespace SixLabors.ImageSharp.Advanced default(JpegDecoderCore).Decode(default, default, default); default(PngDecoderCore).Decode(default, default, default); default(TgaDecoderCore).Decode(default, default, default); + default(TiffDecoderCore).Decode(default, default, default); } /// @@ -229,6 +232,7 @@ namespace SixLabors.ImageSharp.Advanced AotCompileImageEncoder(); AotCompileImageEncoder(); AotCompileImageEncoder(); + AotCompileImageEncoder(); } /// @@ -244,6 +248,7 @@ namespace SixLabors.ImageSharp.Advanced AotCompileImageDecoder(); AotCompileImageDecoder(); AotCompileImageDecoder(); + AotCompileImageDecoder(); } /// From 13fbde9213db25652d23ca9e612fd22e59da516b Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 27 Apr 2021 20:46:29 +0200 Subject: [PATCH 221/275] Add setters for tiff metadata properties --- .../Formats/Tiff/Constants/TiffCompression.cs | 10 ++-- .../TiffPhotometricInterpretation.cs | 4 +- .../Formats/Tiff/ITiffEncoderOptions.cs | 4 +- .../Formats/Tiff/TiffEncoderCore.cs | 6 +- src/ImageSharp/Formats/Tiff/TiffMetadata.cs | 17 +++--- .../Formats/Tiff/TiffEncoderTests.cs | 56 ++++++++++++++++--- .../Formats/Tiff/Utils/TiffWriterTests.cs | 8 +-- 7 files changed, 72 insertions(+), 33 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs index 6a6bd7911..40cc76845 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs @@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants /// JPEG compression - obsolete (see Section 22 of the TIFF 6.0 specification). /// /// Note: The TIFF encoder does not support this compression and will default to use no compression instead, - /// if this is choosen. + /// if this is chosen. /// OldJpeg = 6, @@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants /// JPEG compression (see TIFF Specification, supplement 2). /// /// Note: The TIFF encoder does not yet support this compression and will default to use no compression instead, - /// if this is choosen. + /// if this is chosen. /// Jpeg = 7, @@ -66,7 +66,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants /// Deflate compression - old. /// /// Note: The TIFF encoder does not support this compression and will default to use no compression instead, - /// if this is choosen. + /// if this is chosen. /// OldDeflate = 32946, @@ -74,7 +74,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants /// ITU-T Rec. T.82 coding, applying ITU-T Rec. T.85 (JBIG) (see RFC2301). /// /// Note: The TIFF encoder does not yet support this compression and will default to use no compression instead, - /// if this is choosen. + /// if this is chosen. /// ItuTRecT82 = 9, @@ -82,7 +82,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants /// ITU-T Rec. T.43 representation, using ITU-T Rec. T.82 (JBIG) (see RFC2301). /// /// Note: The TIFF encoder does not yet support this compression and will default to use no compression instead, - /// if this is choosen. + /// if this is chosen. /// ItuTRecT43 = 10 } diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs index d5f234c3f..42d5c4140 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs @@ -19,12 +19,12 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants BlackIsZero = 1, /// - /// RGB + /// RGB image. /// Rgb = 2, /// - /// Palette Color + /// Palette Color. /// PaletteColor = 3, diff --git a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs index c823b50c2..75bea696f 100644 --- a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs +++ b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs @@ -13,9 +13,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff internal interface ITiffEncoderOptions { /// - /// Gets or sets the number of bits per pixel. + /// Gets the number of bits per pixel. /// - TiffBitsPerPixel? BitsPerPixel { get; set; } + TiffBitsPerPixel? BitsPerPixel { get; } /// /// Gets the compression type to use. diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index ce55ecd1f..1bff4aecd 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -293,10 +293,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff } } - if (this.Mode == TiffEncodingMode.Default) + if (this.Mode == TiffEncodingMode.Default && this.BitsPerPixel != null) { - // Preserve input bits per pixel, if no mode was specified. - switch (tiffMetadata.BitsPerPixel) + // Preserve input bits per pixel, if no encoding mode was specified. + switch (this.BitsPerPixel) { case TiffBitsPerPixel.Bit1: this.Mode = TiffEncodingMode.BiColor; diff --git a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs index e6b0bf868..6b5d4e1ca 100644 --- a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs @@ -33,24 +33,25 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff } /// - /// Gets the byte order. + /// Gets or sets the byte order. /// - public ByteOrder ByteOrder { get; internal set; } + public ByteOrder ByteOrder { get; set; } /// - /// Gets the number of bits per pixel. + /// Gets or sets the number of bits per pixel. /// - public TiffBitsPerPixel BitsPerPixel { get; internal set; } = TiffBitsPerPixel.Bit24; + public TiffBitsPerPixel? BitsPerPixel { get; set; } /// - /// Gets the compression used to create the TIFF file. + /// Gets or sets the compression used to create the TIFF file. + /// Defaults to None. /// - public TiffCompression Compression { get; internal set; } = TiffCompression.None; + public TiffCompression Compression { get; set; } = TiffCompression.None; /// - /// Gets the photometric interpretation which indicates how the pixels are to be interpreted, e.g. if the image is bicolor, RGB, color paletted etc. + /// Gets or sets the photometric interpretation which indicates how the pixels are to be interpreted, e.g. if the image is bicolor, RGB, color paletted etc. /// - public TiffPhotometricInterpretation PhotometricInterpretation { get; internal set; } + public TiffPhotometricInterpretation PhotometricInterpretation { get; set; } /// /// Gets or sets the XMP profile. diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 2a1f800a2..b611241f2 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -30,11 +30,53 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff } [Theory] - [InlineData(TiffEncodingMode.Default, TiffCompression.None, TiffBitsPerPixel.Bit24, TiffCompression.None)] - [InlineData(TiffEncodingMode.Rgb, TiffCompression.None, TiffBitsPerPixel.Bit24, TiffCompression.None)] - [InlineData(TiffEncodingMode.ColorPalette, TiffCompression.None, TiffBitsPerPixel.Bit8, TiffCompression.None)] - [InlineData(TiffEncodingMode.Gray, TiffCompression.None, TiffBitsPerPixel.Bit8, TiffCompression.None)] - [InlineData(TiffEncodingMode.BiColor, TiffCompression.None, TiffBitsPerPixel.Bit1, TiffCompression.None)] + [InlineData(TiffEncodingMode.Default, TiffBitsPerPixel.Bit24)] + [InlineData(TiffEncodingMode.Rgb, TiffBitsPerPixel.Bit24)] + [InlineData(TiffEncodingMode.ColorPalette, TiffBitsPerPixel.Bit8)] + [InlineData(TiffEncodingMode.Gray, TiffBitsPerPixel.Bit8)] + [InlineData(TiffEncodingMode.BiColor, TiffBitsPerPixel.Bit1)] + public void EncoderOptions_SetEncodingMode_Works(TiffEncodingMode mode, TiffBitsPerPixel expectedBitsPerPixel) + { + // arrange + var tiffEncoder = new TiffEncoder { Mode = mode }; + Image input = new Image(10, 10); + using var memStream = new MemoryStream(); + + // act + input.Save(memStream, tiffEncoder); + + // assert + memStream.Position = 0; + using var output = Image.Load(Configuration, memStream); + TiffMetadata meta = output.Metadata.GetTiffMetadata(); + Assert.Equal(expectedBitsPerPixel, meta.BitsPerPixel); + Assert.Equal(TiffCompression.None, meta.Compression); + } + + [Theory] + [InlineData(TiffBitsPerPixel.Bit24)] + [InlineData(TiffBitsPerPixel.Bit8)] + [InlineData(TiffBitsPerPixel.Bit4)] + [InlineData(TiffBitsPerPixel.Bit1)] + public void EncoderOptions_SetBitPerPixel_Works(TiffBitsPerPixel bitsPerPixel) + { + // arrange + var tiffEncoder = new TiffEncoder { BitsPerPixel = bitsPerPixel }; + Image input = new Image(10, 10); + using var memStream = new MemoryStream(); + + // act + input.Save(memStream, tiffEncoder); + + // assert + memStream.Position = 0; + using var output = Image.Load(Configuration, memStream); + TiffMetadata meta = output.Metadata.GetTiffMetadata(); + Assert.Equal(bitsPerPixel, meta.BitsPerPixel); + Assert.Equal(TiffCompression.None, meta.Compression); + } + + [Theory] [InlineData(TiffEncodingMode.Default, TiffCompression.Deflate, TiffBitsPerPixel.Bit24, TiffCompression.Deflate)] [InlineData(TiffEncodingMode.Rgb, TiffCompression.Deflate, TiffBitsPerPixel.Bit24, TiffCompression.Deflate)] [InlineData(TiffEncodingMode.Gray, TiffCompression.Deflate, TiffBitsPerPixel.Bit8, TiffCompression.Deflate)] @@ -55,7 +97,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [InlineData(TiffEncodingMode.Rgb, TiffCompression.Jpeg, TiffBitsPerPixel.Bit24, TiffCompression.None)] [InlineData(TiffEncodingMode.Rgb, TiffCompression.OldDeflate, TiffBitsPerPixel.Bit24, TiffCompression.None)] [InlineData(TiffEncodingMode.Rgb, TiffCompression.OldJpeg, TiffBitsPerPixel.Bit24, TiffCompression.None)] - public void EncoderOptions_Work(TiffEncodingMode mode, TiffCompression compression, TiffBitsPerPixel expectedBitsPerPixel, TiffCompression expectedCompression) + public void EncoderOptions_SetEncodingModeAndCompression_Works(TiffEncodingMode mode, TiffCompression compression, TiffBitsPerPixel expectedBitsPerPixel, TiffCompression expectedCompression) { // arrange var tiffEncoder = new TiffEncoder { Mode = mode, Compression = compression }; @@ -80,7 +122,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [WithFile(Rgb4BitPalette, PixelTypes.Rgba32, TiffBitsPerPixel.Bit4)] [WithFile(RgbPalette, PixelTypes.Rgba32, TiffBitsPerPixel.Bit8)] [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32, TiffBitsPerPixel.Bit8)] - public void TiffEncoder_PreserveBitsPerPixel(TestImageProvider provider, TiffBitsPerPixel expectedBitsPerPixel) + public void TiffEncoder_PreservesBitsPerPixel(TestImageProvider provider, TiffBitsPerPixel expectedBitsPerPixel) where TPixel : unmanaged, IPixel { // arrange diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs index b1389cec5..df84c51c8 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs @@ -3,7 +3,6 @@ using System.IO; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers; -using SixLabors.ImageSharp.Memory; using Xunit; @@ -12,9 +11,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Utils [Trait("Format", "Tiff")] public class TiffWriterTests { - private static readonly MemoryAllocator MemoryAllocator = new ArrayPoolMemoryAllocator(); - private static readonly Configuration Configuration = Configuration.Default; - [Fact] public void IsLittleEndian_IsTrueOnWindows() { @@ -40,7 +36,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Utils { using var stream = new MemoryStream(); using var writer = new TiffStreamWriter(stream); - writer.Write((byte)42); + writer.Write(42); Assert.Equal(new byte[] { 42 }, stream.ToArray()); } @@ -60,7 +56,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Utils { using var stream = new MemoryStream(); using var writer = new TiffStreamWriter(stream); - writer.Write((ushort)1234); + writer.Write(1234); Assert.Equal(new byte[] { 0xD2, 0x04 }, stream.ToArray()); } From fa6401c0117001170dde2172f116145e718dd178 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 7 May 2021 14:51:46 +0200 Subject: [PATCH 222/275] Change tiff namespace to SixLabors.ImageSharp.Formats.Tiff; --- src/ImageSharp/Advanced/AotCompilerTools.cs | 2 +- .../Formats/ImageExtensions.Save.cs | 20 +++++++++---------- .../Formats/ImageExtensions.Save.tt | 20 +++++++++---------- .../Tiff/Compression/BitWriterUtils.cs | 2 +- .../Compressors/DeflateCompressor.cs | 4 ++-- .../Compression/Compressors/LzwCompressor.cs | 4 ++-- .../Compression/Compressors/NoCompressor.cs | 4 ++-- .../Compressors/PackBitsCompressor.cs | 4 ++-- .../Compression/Compressors/PackBitsWriter.cs | 2 +- .../Compressors/T4BitCompressor.cs | 4 ++-- .../Compression/Compressors/TiffLzwEncoder.cs | 2 +- .../Decompressors/DeflateTiffCompression.cs | 5 +++-- .../Compression/Decompressors/LzwString.cs | 4 ++-- .../Decompressors/LzwTiffCompression.cs | 5 ++--- .../ModifiedHuffmanTiffCompression.cs | 4 ++-- .../Decompressors/NoneTiffCompression.cs | 2 +- .../Decompressors/PackBitsTiffCompression.cs | 2 +- .../Compression/Decompressors/T4BitReader.cs | 2 +- .../Decompressors/T4TiffCompression.cs | 4 ++-- .../Decompressors/TiffLzwDecoder.cs | 6 +++--- .../Tiff/Compression/FaxCompressionOptions.cs | 2 +- .../Tiff/Compression/HorizontalPredictor.cs | 2 +- .../Tiff/Compression/TiffBaseCompression.cs | 4 ++-- .../Tiff/Compression/TiffBaseCompressor.cs | 4 ++-- .../Tiff/Compression/TiffBaseDecompressor.cs | 4 ++-- .../Tiff/Compression/TiffCompressorFactory.cs | 6 +++--- .../Compression/TiffDecoderCompressionType.cs | 2 +- .../Compression/TiffDecompressorsFactory.cs | 6 +++--- .../Formats/Tiff/ConfigurationExtensions.cs | 2 +- .../Formats/Tiff/Constants/TiffCompression.cs | 2 +- .../Formats/Tiff/Constants/TiffConstants.cs | 2 +- .../Tiff/Constants/TiffExtraSamples.cs | 2 +- .../Formats/Tiff/Constants/TiffFillOrder.cs | 2 +- .../Tiff/Constants/TiffNewSubfileType.cs | 2 +- .../Formats/Tiff/Constants/TiffOrientation.cs | 2 +- .../TiffPhotometricInterpretation.cs | 2 +- .../Tiff/Constants/TiffPlanarConfiguration.cs | 2 +- .../Formats/Tiff/Constants/TiffPredictor.cs | 2 +- .../Tiff/Constants/TiffSampleFormat.cs | 2 +- .../Formats/Tiff/Constants/TiffSubfileType.cs | 2 +- .../Tiff/Constants/TiffThresholding.cs | 2 +- .../Formats/Tiff/ITiffDecoderOptions.cs | 2 +- .../Formats/Tiff/ITiffEncoderOptions.cs | 4 ++-- .../Formats/Tiff/Ifd/DirectoryReader.cs | 4 ++-- .../Formats/Tiff/Ifd/EntryReader.cs | 4 ++-- .../Formats/Tiff/MetadataExtensions.cs | 2 +- .../BlackIsZero1TiffColor.cs | 2 +- .../BlackIsZero4TiffColor.cs | 2 +- .../BlackIsZero8TiffColor.cs | 2 +- .../BlackIsZeroTiffColor.cs | 4 ++-- .../PaletteTiffColor.cs | 4 ++-- .../Rgb888TiffColor.cs | 2 +- .../RgbPlanarTiffColor.cs | 4 ++-- .../PhotometricInterpretation/RgbTiffColor.cs | 4 ++-- .../TiffBaseColorDecoder.cs | 2 +- .../TiffColorDecoderFactory.cs | 2 +- .../TiffColorType.cs | 2 +- .../WhiteIsZero1TiffColor.cs | 2 +- .../WhiteIsZero4TiffColor.cs | 2 +- .../WhiteIsZero8TiffColor.cs | 2 +- .../WhiteIsZeroTiffColor.cs | 4 ++-- .../Formats/Tiff/TiffBitsPerPixel.cs | 2 +- .../Tiff/TiffBitsPerSampleExtensions.cs | 4 ++-- .../Formats/Tiff/TiffConfigurationModule.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffDecoder.cs | 4 ++-- .../Formats/Tiff/TiffDecoderCore.cs | 9 ++++----- .../Tiff/TiffDecoderMetadataCreator.cs | 2 +- .../Formats/Tiff/TiffDecoderOptionsParser.cs | 7 +++---- src/ImageSharp/Formats/Tiff/TiffEncoder.cs | 6 +++--- .../Formats/Tiff/TiffEncoderCore.cs | 8 ++++---- .../Tiff/TiffEncoderEntriesCollector.cs | 4 ++-- .../Formats/Tiff/TiffEncodingMode.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffFormat.cs | 4 ++-- .../Formats/Tiff/TiffFrameMetadata.cs | 5 ++--- .../Formats/Tiff/TiffImageFormatDetector.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffMetadata.cs | 4 ++-- .../Formats/Tiff/TiffThrowHelper.cs | 2 +- .../Formats/Tiff/Utils/BitReader.cs | 2 +- .../Tiff/Writers/TiffBaseColorWriter.cs | 4 ++-- .../Formats/Tiff/Writers/TiffBiColorWriter.cs | 6 +++--- .../Tiff/Writers/TiffColorWriterFactory.cs | 2 +- .../Tiff/Writers/TiffCompositeColorWriter.cs | 4 ++-- .../Formats/Tiff/Writers/TiffGrayWriter.cs | 2 +- .../Formats/Tiff/Writers/TiffPaletteWriter.cs | 4 ++-- .../Formats/Tiff/Writers/TiffRgbWriter.cs | 2 +- .../Formats/Tiff/Writers/TiffStreamWriter.cs | 2 +- .../Codecs/DecodeTiff.cs | 2 +- .../Codecs/EncodeTiff.cs | 4 ++-- .../DeflateTiffCompressionTests.cs | 4 ++-- .../Compression/LzwTiffCompressionTests.cs | 6 +++--- .../Compression/NoneTiffCompressionTests.cs | 2 +- .../PackBitsTiffCompressionTests.cs | 4 ++-- .../Formats/Tiff/ImageExtensionsTest.cs | 2 +- .../BlackIsZeroTiffColorTests.cs | 2 +- .../PaletteTiffColorTests.cs | 2 +- .../RgbPlanarTiffColorTests.cs | 2 +- .../RgbTiffColorTests.cs | 2 +- .../WhiteIsZeroTiffColorTests.cs | 2 +- .../Formats/Tiff/TiffDecoderTests.cs | 2 +- .../Formats/Tiff/TiffEncoderHeaderTests.cs | 4 ++-- .../Formats/Tiff/TiffEncoderTests.cs | 4 ++-- .../Formats/Tiff/TiffFormatTests.cs | 2 +- .../Formats/Tiff/TiffMetadataTests.cs | 3 +-- .../Formats/Tiff/Utils/TiffWriterTests.cs | 2 +- .../Profiles/IPTC/IptcProfileTests.cs | 2 +- .../TestUtilities/TestEnvironment.Formats.cs | 2 +- 106 files changed, 179 insertions(+), 185 deletions(-) diff --git a/src/ImageSharp/Advanced/AotCompilerTools.cs b/src/ImageSharp/Advanced/AotCompilerTools.cs index 2221a5372..be2e964fc 100644 --- a/src/ImageSharp/Advanced/AotCompilerTools.cs +++ b/src/ImageSharp/Advanced/AotCompilerTools.cs @@ -7,12 +7,12 @@ using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Bmp; -using SixLabors.ImageSharp.Formats.Experimental.Tiff; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Tga; +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; diff --git a/src/ImageSharp/Formats/ImageExtensions.Save.cs b/src/ImageSharp/Formats/ImageExtensions.Save.cs index 6673803a4..0f8b1e16d 100644 --- a/src/ImageSharp/Formats/ImageExtensions.Save.cs +++ b/src/ImageSharp/Formats/ImageExtensions.Save.cs @@ -6,13 +6,13 @@ using System.IO; using System.Threading; using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Formats.Experimental.Tiff; using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Tga; +using SixLabors.ImageSharp.Formats.Tiff; namespace SixLabors.ImageSharp { @@ -537,7 +537,7 @@ namespace SixLabors.ImageSharp cancellationToken); /// - /// EXPERIMENTAL! Saves the image to the given stream with the Tiff format. + /// Saves the image to the given stream with the Tiff format. /// /// The image this method extends. /// The file path to save the image to. @@ -545,7 +545,7 @@ namespace SixLabors.ImageSharp public static void SaveAsTiff(this Image source, string path) => SaveAsTiff(source, path, null); /// - /// EXPERIMENTAL! Saves the image to the given stream with the Tiff format. + /// Saves the image to the given stream with the Tiff format. /// /// The image this method extends. /// The file path to save the image to. @@ -554,7 +554,7 @@ namespace SixLabors.ImageSharp public static Task SaveAsTiffAsync(this Image source, string path) => SaveAsTiffAsync(source, path, null); /// - /// EXPERIMENTAL! Saves the image to the given stream with the Tiff format. + /// Saves the image to the given stream with the Tiff format. /// /// The image this method extends. /// The file path to save the image to. @@ -565,7 +565,7 @@ namespace SixLabors.ImageSharp => SaveAsTiffAsync(source, path, null, cancellationToken); /// - /// EXPERIMENTAL! Saves the image to the given stream with the Tiff format. + /// Saves the image to the given stream with the Tiff format. /// /// The image this method extends. /// The file path to save the image to. @@ -577,7 +577,7 @@ namespace SixLabors.ImageSharp encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TiffFormat.Instance)); /// - /// EXPERIMENTAL! Saves the image to the given stream with the Tiff format. + /// Saves the image to the given stream with the Tiff format. /// /// The image this method extends. /// The file path to save the image to. @@ -592,7 +592,7 @@ namespace SixLabors.ImageSharp cancellationToken); /// - /// EXPERIMENTAL! Saves the image to the given stream with the Tiff format. + /// Saves the image to the given stream with the Tiff format. /// /// The image this method extends. /// The stream to save the image to. @@ -601,7 +601,7 @@ namespace SixLabors.ImageSharp => SaveAsTiff(source, stream, null); /// - /// EXPERIMENTAL! Saves the image to the given stream with the Tiff format. + /// Saves the image to the given stream with the Tiff format. /// /// The image this method extends. /// The stream to save the image to. @@ -612,7 +612,7 @@ namespace SixLabors.ImageSharp => SaveAsTiffAsync(source, stream, null, cancellationToken); /// - /// EXPERIMENTAL! Saves the image to the given stream with the Tiff format. + /// Saves the image to the given stream with the Tiff format. /// /// The image this method extends. /// The stream to save the image to. @@ -625,7 +625,7 @@ namespace SixLabors.ImageSharp encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TiffFormat.Instance)); /// - /// EXPERIMENTAL! Saves the image to the given stream with the Tiff format. + /// Saves the image to the given stream with the Tiff format. /// /// The image this method extends. /// The stream to save the image to. diff --git a/src/ImageSharp/Formats/ImageExtensions.Save.tt b/src/ImageSharp/Formats/ImageExtensions.Save.tt index 8954c19e5..af9531225 100644 --- a/src/ImageSharp/Formats/ImageExtensions.Save.tt +++ b/src/ImageSharp/Formats/ImageExtensions.Save.tt @@ -9,7 +9,6 @@ using System.IO; using System.Threading; using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Formats.Experimental.Tiff; <# var formats = new []{ @@ -40,10 +39,9 @@ namespace SixLabors.ImageSharp <# foreach (string fmt in formats) { - string experimentalString = fmt == "Tiff" || fmt == "Webp" ? @"EXPERIMENTAL! " : ""; #> /// - /// <#= experimentalString #>Saves the image to the given stream with the <#= fmt #> format. + /// Saves the image to the given stream with the <#= fmt #> format. /// /// The image this method extends. /// The file path to save the image to. @@ -51,7 +49,7 @@ namespace SixLabors.ImageSharp public static void SaveAs<#= fmt #>(this Image source, string path) => SaveAs<#= fmt #>(source, path, null); /// - /// <#= experimentalString #>Saves the image to the given stream with the <#= fmt #> format. + /// Saves the image to the given stream with the <#= fmt #> format. /// /// The image this method extends. /// The file path to save the image to. @@ -60,7 +58,7 @@ namespace SixLabors.ImageSharp public static Task SaveAs<#= fmt #>Async(this Image source, string path) => SaveAs<#= fmt #>Async(source, path, null); /// - /// <#= experimentalString #>Saves the image to the given stream with the <#= fmt #> format. + /// Saves the image to the given stream with the <#= fmt #> format. /// /// The image this method extends. /// The file path to save the image to. @@ -71,7 +69,7 @@ namespace SixLabors.ImageSharp => SaveAs<#= fmt #>Async(source, path, null, cancellationToken); /// - /// <#= experimentalString #>Saves the image to the given stream with the <#= fmt #> format. + /// Saves the image to the given stream with the <#= fmt #> format. /// /// The image this method extends. /// The file path to save the image to. @@ -83,7 +81,7 @@ namespace SixLabors.ImageSharp encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(<#= fmt #>Format.Instance)); /// - /// <#= experimentalString #>Saves the image to the given stream with the <#= fmt #> format. + /// Saves the image to the given stream with the <#= fmt #> format. /// /// The image this method extends. /// The file path to save the image to. @@ -98,7 +96,7 @@ namespace SixLabors.ImageSharp cancellationToken); /// - /// <#= experimentalString #>Saves the image to the given stream with the <#= fmt #> format. + /// Saves the image to the given stream with the <#= fmt #> format. /// /// The image this method extends. /// The stream to save the image to. @@ -107,7 +105,7 @@ namespace SixLabors.ImageSharp => SaveAs<#= fmt #>(source, stream, null); /// - /// <#= experimentalString #>Saves the image to the given stream with the <#= fmt #> format. + /// Saves the image to the given stream with the <#= fmt #> format. /// /// The image this method extends. /// The stream to save the image to. @@ -118,7 +116,7 @@ namespace SixLabors.ImageSharp => SaveAs<#= fmt #>Async(source, stream, null, cancellationToken); /// - /// <#= experimentalString #>Saves the image to the given stream with the <#= fmt #> format. + /// Saves the image to the given stream with the <#= fmt #> format. /// /// The image this method extends. /// The stream to save the image to. @@ -131,7 +129,7 @@ namespace SixLabors.ImageSharp encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(<#= fmt #>Format.Instance)); /// - /// <#= experimentalString #>Saves the image to the given stream with the <#= fmt #> format. + /// Saves the image to the given stream with the <#= fmt #> format. /// /// The image this method extends. /// The stream to save the image to. diff --git a/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs b/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs index 597a91b68..08d147526 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression +namespace SixLabors.ImageSharp.Formats.Tiff.Compression { internal static class BitWriterUtils { diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/DeflateCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/DeflateCompressor.cs index 153b1fc88..ad72b5e73 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/DeflateCompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/DeflateCompressor.cs @@ -4,10 +4,10 @@ using System; using System.IO; using SixLabors.ImageSharp.Compression.Zlib; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors +namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors { internal class DeflateCompressor : TiffBaseCompressor { diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/LzwCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/LzwCompressor.cs index 2341f23b1..d8a20513d 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/LzwCompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/LzwCompressor.cs @@ -3,10 +3,10 @@ using System; using System.IO; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors +namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors { internal class LzwCompressor : TiffBaseCompressor { diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/NoCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/NoCompressor.cs index b63186eb8..319ca97d9 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/NoCompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/NoCompressor.cs @@ -3,10 +3,10 @@ using System; using System.IO; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors +namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors { internal class NoCompressor : TiffBaseCompressor { diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsCompressor.cs index 5d2a31d2d..61db21fa8 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsCompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsCompressor.cs @@ -3,10 +3,10 @@ using System; using System.IO; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors +namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors { internal class PackBitsCompressor : TiffBaseCompressor { diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsWriter.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsWriter.cs index 67ff3eba8..73c3f36f8 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsWriter.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors +namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors { /// /// Pack Bits compression for tiff images. See Tiff Spec v6, section 9. diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitCompressor.cs index 9224b27a4..11149007f 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitCompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitCompressor.cs @@ -5,10 +5,10 @@ using System; using System.Buffers; using System.Collections.Generic; using System.IO; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors +namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors { /// /// Bitwriter for writing compressed CCITT T4 1D data. diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffLzwEncoder.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffLzwEncoder.cs index 0e73b9fb4..baeabdbb2 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffLzwEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffLzwEncoder.cs @@ -7,7 +7,7 @@ using System.IO; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors +namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors { /* This implementation is a port of a java tiff encoder by Harald Kuhr: https://github.com/haraldk/TwelveMonkeys diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs index a6ca8c78d..67af4ff6c 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs @@ -5,11 +5,12 @@ using System; using System.IO.Compression; using SixLabors.ImageSharp.Compression.Zlib; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors +namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { /// /// Class to handle cases where TIFF image data is compressed using Deflate compression. diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwString.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwString.cs index ebe731941..0f4fb9c9e 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwString.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwString.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors +namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { /// /// Represents a lzw string with a code word and a code length. @@ -77,7 +77,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso } LzwString e = this; - var endIdx = this.Length - 1; + int endIdx = this.Length - 1; if (endIdx >= buffer.Length) { TiffThrowHelper.ThrowImageFormatException("Error reading lzw compressed stream. Either pixel buffer to write to is to small or code length is invalid!"); diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs index f3b37b09c..7e75dd4f0 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs @@ -2,12 +2,11 @@ // Licensed under the Apache License, Version 2.0. using System; - -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors +namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { /// /// Class to handle cases where TIFF image data is compressed using LZW compression. diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs index 9c2495efc..017591e53 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs @@ -3,11 +3,11 @@ using System; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors +namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { /// /// Class to handle cases where TIFF image data is compressed using Modified Huffman Compression. diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/NoneTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/NoneTiffCompression.cs index 3475b74ce..58a1c9878 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/NoneTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/NoneTiffCompression.cs @@ -6,7 +6,7 @@ using System; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors +namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { /// /// Class to handle cases where TIFF image data is not compressed. diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/PackBitsTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/PackBitsTiffCompression.cs index 727e0eeb7..e14736b73 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/PackBitsTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/PackBitsTiffCompression.cs @@ -7,7 +7,7 @@ using System.Buffers; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors +namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { /// /// Class to handle cases where TIFF image data is compressed using PackBits compression. diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs index 8aac1d91b..09f8c71f7 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs @@ -8,7 +8,7 @@ using System.IO; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors +namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { /// /// Bitreader for reading compressed CCITT T4 1D data. diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs index 549f75846..76f088364 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs @@ -3,11 +3,11 @@ using System; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors +namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { /// /// Class to handle cases where TIFF image data is compressed using CCITT T4 compression. diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffLzwDecoder.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffLzwDecoder.cs index 2f7ff0ee3..5f482bd0b 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffLzwDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffLzwDecoder.cs @@ -4,7 +4,7 @@ using System; using System.IO; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors +namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { /* This implementation is based on a port of a java tiff decoder by Harald Kuhr: https://github.com/haraldk/TwelveMonkeys @@ -95,7 +95,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso /// and sets the stream, where the compressed data should be read from. /// /// The stream to read from. - /// is null. + /// is null. public TiffLzwDecoder(Stream stream) { Guard.NotNull(stream, nameof(stream)); @@ -240,7 +240,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso this.nextBits += 8; } - var code = (this.nextData >> (this.nextBits - this.bitsPerCode)) & this.bitMask; + int code = (this.nextData >> (this.nextBits - this.bitsPerCode)) & this.bitMask; this.nextBits -= this.bitsPerCode; return code; diff --git a/src/ImageSharp/Formats/Tiff/Compression/FaxCompressionOptions.cs b/src/ImageSharp/Formats/Tiff/Compression/FaxCompressionOptions.cs index d5171db65..19103de92 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/FaxCompressionOptions.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/FaxCompressionOptions.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression +namespace SixLabors.ImageSharp.Formats.Tiff.Compression { /// /// Fax compression options, see TIFF spec page 51f (T4Options). diff --git a/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs b/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs index f3062c2a0..34741cd93 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs @@ -7,7 +7,7 @@ using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression +namespace SixLabors.ImageSharp.Formats.Tiff.Compression { /// /// Methods for undoing the horizontal prediction used in combination with deflate and LZW compressed TIFF images. diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs index a403ca064..5bd4cd1f1 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs @@ -2,10 +2,10 @@ // Licensed under the Apache License, Version 2.0. using System; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression +namespace SixLabors.ImageSharp.Formats.Tiff.Compression { internal abstract class TiffBaseCompression : IDisposable { diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompressor.cs index f7412e240..c5c5c466d 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompressor.cs @@ -3,10 +3,10 @@ using System; using System.IO; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression +namespace SixLabors.ImageSharp.Formats.Tiff.Compression { internal abstract class TiffBaseCompressor : TiffBaseCompression { diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseDecompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseDecompressor.cs index 2f40214eb..a289e306a 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseDecompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseDecompressor.cs @@ -4,11 +4,11 @@ using System; using System.IO; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression +namespace SixLabors.ImageSharp.Formats.Tiff.Compression { /// /// The base tiff decompressor class. diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressorFactory.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressorFactory.cs index cce7567a2..14a0c6e9d 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressorFactory.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressorFactory.cs @@ -3,11 +3,11 @@ using System.IO; using SixLabors.ImageSharp.Compression.Zlib; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors; +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression +namespace SixLabors.ImageSharp.Formats.Tiff.Compression { internal static class TiffCompressorFactory { diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffDecoderCompressionType.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffDecoderCompressionType.cs index 247d91e63..80bc0af5a 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffDecoderCompressionType.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffDecoderCompressionType.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression +namespace SixLabors.ImageSharp.Formats.Tiff.Compression { /// /// Provides enumeration of the various TIFF compression types the decoder can handle. diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs index 641142d4c..a6d44f4d3 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs @@ -1,11 +1,11 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors; +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression +namespace SixLabors.ImageSharp.Formats.Tiff.Compression { internal static class TiffDecompressorsFactory { diff --git a/src/ImageSharp/Formats/Tiff/ConfigurationExtensions.cs b/src/ImageSharp/Formats/Tiff/ConfigurationExtensions.cs index 6496206d0..49f87e090 100644 --- a/src/ImageSharp/Formats/Tiff/ConfigurationExtensions.cs +++ b/src/ImageSharp/Formats/Tiff/ConfigurationExtensions.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Helper methods for the Configuration. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs index 40cc76845..b40647a93 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants +namespace SixLabors.ImageSharp.Formats.Tiff.Constants { /// /// Enumeration representing the compression formats defined by the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs index 894a6d348..3553c61c5 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants +namespace SixLabors.ImageSharp.Formats.Tiff.Constants { /// /// Defines constants defined in the TIFF specification. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffExtraSamples.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffExtraSamples.cs index d5b69bdab..c10167d25 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffExtraSamples.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffExtraSamples.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants +namespace SixLabors.ImageSharp.Formats.Tiff.Constants { /// /// Enumeration representing the possible uses of extra components in TIFF format files. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffFillOrder.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffFillOrder.cs index 3d6c1cea5..1bb75f836 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffFillOrder.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffFillOrder.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants +namespace SixLabors.ImageSharp.Formats.Tiff.Constants { /// /// Enumeration representing the fill orders defined by the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs index fea32e412..3b84120a5 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants +namespace SixLabors.ImageSharp.Formats.Tiff.Constants { /// /// Enumeration representing the sub-file types defined by the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffOrientation.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffOrientation.cs index d022a7e77..a5305d482 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffOrientation.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffOrientation.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants +namespace SixLabors.ImageSharp.Formats.Tiff.Constants { /// /// Enumeration representing the image orientations defined by the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs index 42d5c4140..d43ccb408 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants +namespace SixLabors.ImageSharp.Formats.Tiff.Constants { /// /// Enumeration representing the photometric interpretation formats defined by the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffPlanarConfiguration.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffPlanarConfiguration.cs index 835df35e3..ea526ede5 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffPlanarConfiguration.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffPlanarConfiguration.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants +namespace SixLabors.ImageSharp.Formats.Tiff.Constants { /// /// Enumeration representing how the components of each pixel are stored the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffPredictor.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffPredictor.cs index 67e456517..092bb7aa5 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffPredictor.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffPredictor.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants +namespace SixLabors.ImageSharp.Formats.Tiff.Constants { /// /// A mathematical operator that is applied to the image data before an encoding scheme is applied. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffSampleFormat.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffSampleFormat.cs index 072172ba7..81899c5fd 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffSampleFormat.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffSampleFormat.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants +namespace SixLabors.ImageSharp.Formats.Tiff.Constants { /// /// Specifies how to interpret each data sample in a pixel. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffSubfileType.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffSubfileType.cs index f0fb03fd7..ff735de86 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffSubfileType.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffSubfileType.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants +namespace SixLabors.ImageSharp.Formats.Tiff.Constants { /// /// Enumeration representing the sub-file types defined by the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffThresholding.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffThresholding.cs index 05391b233..fce0b175c 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffThresholding.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffThresholding.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants +namespace SixLabors.ImageSharp.Formats.Tiff.Constants { /// /// Enumeration representing the thresholding applied to image data defined by the Tiff file-format. diff --git a/src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs b/src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs index 7a3ab6cd8..cee66694b 100644 --- a/src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs +++ b/src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Encapsulates the options for the . diff --git a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs index 75bea696f..44c53c857 100644 --- a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs +++ b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs @@ -2,10 +2,10 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Compression.Zlib; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Processing.Processors.Quantization; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Encapsulates the options for the . diff --git a/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs index 2343eca5e..ea090c92b 100644 --- a/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs +++ b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs @@ -4,10 +4,10 @@ using System; using System.Collections.Generic; using System.IO; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Metadata.Profiles.Exif; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// The TIFF IFD reader class. diff --git a/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs b/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs index df45e5434..123a64cc1 100644 --- a/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs +++ b/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs @@ -5,10 +5,10 @@ using System; using System.Collections.Generic; using System.IO; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Metadata.Profiles.Exif; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { internal class EntryReader : BaseExifReader { diff --git a/src/ImageSharp/Formats/Tiff/MetadataExtensions.cs b/src/ImageSharp/Formats/Tiff/MetadataExtensions.cs index 1946221cb..b9da86fc4 100644 --- a/src/ImageSharp/Formats/Tiff/MetadataExtensions.cs +++ b/src/ImageSharp/Formats/Tiff/MetadataExtensions.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Formats.Experimental.Tiff; +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Metadata; namespace SixLabors.ImageSharp diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs index 542d675d7..b4d609a6f 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs @@ -6,7 +6,7 @@ using System; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Implements the 'BlackIsZero' photometric interpretation (optimized for bilevel images). diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs index 4d6ffa6a9..d39e28038 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs @@ -6,7 +6,7 @@ using System; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Implements the 'BlackIsZero' photometric interpretation (optimized for 4-bit grayscale images). diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs index 7bffd4a92..d62898a08 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs @@ -6,7 +6,7 @@ using System; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Implements the 'BlackIsZero' photometric interpretation (optimized for 8-bit grayscale images). diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs index ddbae3242..271672e25 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs @@ -3,11 +3,11 @@ using System; using System.Numerics; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; +using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Implements the 'BlackIsZero' photometric interpretation (for all bit depths). diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs index b54fb90bf..2aefa2ee7 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs @@ -3,11 +3,11 @@ using System; using System.Numerics; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; +using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Implements the 'PaletteTiffColor' photometric interpretation (for all bit depths). diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs index 8fd82d98e..ad3e0909b 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs @@ -6,7 +6,7 @@ using System; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Implements the 'RGB' photometric interpretation (optimized for 8-bit full color images). diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs index 3ff8e34a0..531fd6509 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs @@ -2,11 +2,11 @@ // Licensed under the Apache License, Version 2.0. using System.Numerics; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; +using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Implements the 'RGB' photometric interpretation with 'Planar' layout (for all bit depths). diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs index 712bf6f2a..ef1bc0a23 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs @@ -3,11 +3,11 @@ using System; using System.Numerics; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; +using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Implements the 'RGB' photometric interpretation (for all bit depths). diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffBaseColorDecoder.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffBaseColorDecoder.cs index 7c0e831b5..ad67c463f 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffBaseColorDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffBaseColorDecoder.cs @@ -5,7 +5,7 @@ using System; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// The base class for photometric interpretation decoders. diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory.cs index 633edd2cb..91ae9c2ab 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory.cs @@ -3,7 +3,7 @@ using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { internal static class TiffColorDecoderFactory where TPixel : unmanaged, IPixel diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs index 24c8f6da5..8eb0fb4de 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Provides enumeration of the various TIFF photometric interpretation implementation types. diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs index 4bef2dba8..f7be45cdd 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs @@ -6,7 +6,7 @@ using System; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Implements the 'WhiteIsZero' photometric interpretation (optimized for bilevel images). diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs index 7eca9b966..4360b2aa2 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs @@ -6,7 +6,7 @@ using System; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Implements the 'WhiteIsZero' photometric interpretation (optimized for 4-bit grayscale images). diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs index 68b74f60a..e03758e65 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs @@ -6,7 +6,7 @@ using System; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Implements the 'WhiteIsZero' photometric interpretation (optimized for 8-bit grayscale images). diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs index 0ce1df8d8..e4e9179f0 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs @@ -3,11 +3,11 @@ using System; using System.Numerics; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; +using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Implements the 'WhiteIsZero' photometric interpretation (for all bit depths). diff --git a/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs b/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs index 77fd4f6f8..35a9a590b 100644 --- a/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs +++ b/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Enumerates the available bits per pixel for the tiff format. diff --git a/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs b/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs index 1b0778eb5..a0c7eb021 100644 --- a/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs +++ b/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs @@ -2,8 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System; -using SixLabors.ImageSharp.Formats.Experimental.Tiff; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Tiff.Constants; namespace SixLabors.ImageSharp.Formats.Tiff { diff --git a/src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs b/src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs index 07345608e..e96dba207 100644 --- a/src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs +++ b/src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Registers the image encoders, decoders and mime type detectors for the TIFF format. diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs index b7bce0a84..9d52e34df 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs @@ -6,10 +6,10 @@ using System.Threading; using System.Threading.Tasks; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// - /// EXPERIMENTAL! Image decoder for generating an image out of a TIFF stream. + /// Image decoder for generating an image out of a TIFF stream. /// public class TiffDecoder : IImageDecoder, ITiffDecoderOptions, IImageInfoDetector { diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index bedd132fa..f3e82f86d 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -3,16 +3,15 @@ using System.Collections.Generic; using System.Threading; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; -using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Performs the tiff decoding operation. @@ -300,7 +299,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff for (int stripIndex = 0; stripIndex < stripOffsets.Length; stripIndex++) { int stripHeight = stripIndex < stripOffsets.Length - 1 || frame.Height % rowsPerStrip == 0 ? rowsPerStrip : frame.Height % rowsPerStrip; - var top = rowsPerStrip * stripIndex; + int top = rowsPerStrip * stripIndex; if (top + stripHeight > frame.Height) { // Make sure we ignore any strips that are not needed for the image (if too many are present) diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs index b1696dc86..7f97303ed 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs @@ -10,7 +10,7 @@ using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Icc; using SixLabors.ImageSharp.Metadata.Profiles.Iptc; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// The decoder metadata creator. diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs index d11af7085..6b8e6b84e 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs @@ -1,12 +1,11 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; -using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Metadata.Profiles.Exif; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// The decoder options parser. diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs index b273b82e7..5bca172b5 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs @@ -7,14 +7,14 @@ using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Compression.Zlib; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Quantization; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// - /// EXPERIMENTAL! Encoder for writing the data image to a stream in TIFF format. + /// Encoder for writing the data image to a stream in TIFF format. /// public class TiffEncoder : IImageEncoder, ITiffEncoderOptions { diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 1bff4aecd..8f68e192f 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -8,9 +8,9 @@ using System.Threading; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Compression.Zlib; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers; +using SixLabors.ImageSharp.Formats.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Tiff.Writers; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; @@ -18,7 +18,7 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Quantization; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Performs the TIFF encoding operation. diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs index f623ebed5..4d059aa67 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs @@ -3,12 +3,12 @@ using System.Collections.Generic; using SixLabors.ImageSharp.Common.Helpers; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { internal class TiffEncoderEntriesCollector { diff --git a/src/ImageSharp/Formats/Tiff/TiffEncodingMode.cs b/src/ImageSharp/Formats/Tiff/TiffEncodingMode.cs index 934cd6826..374505195 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncodingMode.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncodingMode.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Enum for the different tiff encoding options. diff --git a/src/ImageSharp/Formats/Tiff/TiffFormat.cs b/src/ImageSharp/Formats/Tiff/TiffFormat.cs index ea57f67cc..ffae32093 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFormat.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFormat.cs @@ -3,9 +3,9 @@ using System.Collections.Generic; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Tiff.Constants; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Encapsulates the means to encode and decode Tiff images. diff --git a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs index 242c60974..4386f4932 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs @@ -4,12 +4,11 @@ using System.Collections.Generic; using System.Linq; using SixLabors.ImageSharp.Common.Helpers; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; -using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Provides Tiff specific metadata information for the frame. diff --git a/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs b/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs index 624e0858c..f7e6f7a99 100644 --- a/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs +++ b/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Detects tiff file headers diff --git a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs index 6b5d4e1ca..99777a0f3 100644 --- a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs @@ -2,9 +2,9 @@ // Licensed under the Apache License, Version 2.0. using System; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Tiff.Constants; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Provides Tiff specific metadata information for the image. diff --git a/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs b/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs index db91fcbcb..c5ebf481a 100644 --- a/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs +++ b/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs @@ -4,7 +4,7 @@ using System; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff { /// /// Cold path optimizations for throwing tiff format based exceptions. diff --git a/src/ImageSharp/Formats/Tiff/Utils/BitReader.cs b/src/ImageSharp/Formats/Tiff/Utils/BitReader.cs index 067d119dd..40e67c1b0 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/BitReader.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils +namespace SixLabors.ImageSharp.Formats.Tiff.Utils { /// /// Utility class to read a sequence of bits from an array diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs index 32adf95c0..0a04a5089 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs @@ -2,12 +2,12 @@ // Licensed under the Apache License, Version 2.0. using System; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers +namespace SixLabors.ImageSharp.Formats.Tiff.Writers { internal abstract class TiffBaseColorWriter : IDisposable where TPixel : unmanaged, IPixel diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs index 7ab081499..e37cba7cf 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs @@ -4,14 +4,14 @@ using System; using System.Buffers; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers +namespace SixLabors.ImageSharp.Formats.Tiff.Writers { internal sealed class TiffBiColorWriter : TiffBaseColorWriter where TPixel : unmanaged, IPixel diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffColorWriterFactory.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffColorWriterFactory.cs index 4dcb47b47..01c1833f1 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffColorWriterFactory.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffColorWriterFactory.cs @@ -5,7 +5,7 @@ using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Quantization; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers +namespace SixLabors.ImageSharp.Formats.Tiff.Writers { internal static class TiffColorWriterFactory { diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffCompositeColorWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffCompositeColorWriter.cs index 018ac6fa0..4df57f7e8 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffCompositeColorWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffCompositeColorWriter.cs @@ -2,11 +2,11 @@ // Licensed under the Apache License, Version 2.0. using System; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers +namespace SixLabors.ImageSharp.Formats.Tiff.Writers { /// /// The base class for composite color types: 8-bit gray, 24-bit RGB (4-bit gray, 16-bit (565/555) RGB, 32-bit RGB, CMYK, YCbCr). diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffGrayWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffGrayWriter.cs index 4da9a9b97..117960ba7 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffGrayWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffGrayWriter.cs @@ -5,7 +5,7 @@ using System; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers +namespace SixLabors.ImageSharp.Formats.Tiff.Writers { internal sealed class TiffGrayWriter : TiffCompositeColorWriter where TPixel : unmanaged, IPixel diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs index 00f468720..712578f81 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs @@ -6,13 +6,13 @@ using System.Buffers; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Quantization; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers +namespace SixLabors.ImageSharp.Formats.Tiff.Writers { internal sealed class TiffPaletteWriter : TiffBaseColorWriter where TPixel : unmanaged, IPixel diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffRgbWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffRgbWriter.cs index acb0030bb..a3050f8a2 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffRgbWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffRgbWriter.cs @@ -5,7 +5,7 @@ using System; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers +namespace SixLabors.ImageSharp.Formats.Tiff.Writers { internal sealed class TiffRgbWriter : TiffCompositeColorWriter where TPixel : unmanaged, IPixel diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs index 7a49d4b82..8c1d7b759 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs @@ -5,7 +5,7 @@ using System; using System.Buffers.Binary; using System.IO; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers +namespace SixLabors.ImageSharp.Formats.Tiff.Writers { /// /// Utility class for writing TIFF data to a . diff --git a/tests/ImageSharp.Benchmarks/Codecs/DecodeTiff.cs b/tests/ImageSharp.Benchmarks/Codecs/DecodeTiff.cs index 44ffae1d9..b388b5d70 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/DecodeTiff.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/DecodeTiff.cs @@ -9,7 +9,7 @@ using System.IO; using BenchmarkDotNet.Attributes; -using SixLabors.ImageSharp.Formats.Experimental.Tiff; +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; diff --git a/tests/ImageSharp.Benchmarks/Codecs/EncodeTiff.cs b/tests/ImageSharp.Benchmarks/Codecs/EncodeTiff.cs index 99b6f437e..3c318e229 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/EncodeTiff.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/EncodeTiff.cs @@ -6,8 +6,8 @@ using System.IO; using BenchmarkDotNet.Attributes; -using SixLabors.ImageSharp.Formats.Experimental.Tiff; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs index cdf0f68f6..782a504d5 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs @@ -3,8 +3,8 @@ using System.IO; using SixLabors.ImageSharp.Compression.Zlib; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors; +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.IO; using Xunit; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs index 9108cdaf8..bf585e9c8 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs @@ -2,9 +2,9 @@ // Licensed under the Apache License, Version 2.0. using System.IO; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors; +using SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors; +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.IO; using Xunit; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs index d5435a8a4..82ecb315b 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System.IO; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors; +using SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors; using SixLabors.ImageSharp.IO; using Xunit; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs index d0649934d..b67cb8325 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs @@ -3,8 +3,8 @@ using System; using System.IO; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors; +using SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors; +using SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/ImageExtensionsTest.cs b/tests/ImageSharp.Tests/Formats/Tiff/ImageExtensionsTest.cs index eb06a082b..e2aeeb9e8 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/ImageExtensionsTest.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/ImageExtensionsTest.cs @@ -4,7 +4,7 @@ using System.IO; using System.Threading.Tasks; using SixLabors.ImageSharp.Formats; -using SixLabors.ImageSharp.Formats.Experimental.Tiff; +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.PixelFormats; using Xunit; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs index d7a4bc7ed..780c390f7 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; -using SixLabors.ImageSharp.Formats.Experimental.Tiff; +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.PixelFormats; using Xunit; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs index 8425e97a6..823d5f5a5 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; -using SixLabors.ImageSharp.Formats.Experimental.Tiff; +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.PixelFormats; using Xunit; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs index ca4b19f83..78105e420 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; -using SixLabors.ImageSharp.Formats.Experimental.Tiff; +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs index d79068b22..dd232ccc3 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; -using SixLabors.ImageSharp.Formats.Experimental.Tiff; +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.PixelFormats; using Xunit; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs index d29b668a8..716ec8e21 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; -using SixLabors.ImageSharp.Formats.Experimental.Tiff; +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.PixelFormats; using Xunit; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index 0fba4525b..ae1de1734 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -5,7 +5,7 @@ using System; using System.IO; -using SixLabors.ImageSharp.Formats.Experimental.Tiff; +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs index 107fd6079..acbe2b489 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs @@ -3,8 +3,8 @@ using System.IO; -using SixLabors.ImageSharp.Formats.Experimental.Tiff; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers; +using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Tiff.Writers; using SixLabors.ImageSharp.Memory; using Xunit; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index b611241f2..21a976620 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -4,8 +4,8 @@ using System.IO; using SixLabors.ImageSharp.Formats; -using SixLabors.ImageSharp.Formats.Experimental.Tiff; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffFormatTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffFormatTests.cs index 1bf891a36..4510bf912 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffFormatTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffFormatTests.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Formats.Experimental.Tiff; +using SixLabors.ImageSharp.Formats.Tiff; using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tiff diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index 237e952d1..5763b0e8a 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -6,9 +6,8 @@ using System.Globalization; using System.IO; using System.Linq; -using SixLabors.ImageSharp.Formats.Experimental.Tiff; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Icc; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs index df84c51c8..6eea0a1a9 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System.IO; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers; +using SixLabors.ImageSharp.Formats.Tiff.Writers; using Xunit; diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs index 7ec1e90ac..e62a5cbab 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs @@ -5,8 +5,8 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using SixLabors.ImageSharp.Formats.Experimental.Tiff; using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Metadata.Profiles.Iptc; using SixLabors.ImageSharp.PixelFormats; using Xunit; diff --git a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs index dba954056..f68fb4d95 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs @@ -5,11 +5,11 @@ using System; using System.IO; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Bmp; -using SixLabors.ImageSharp.Formats.Experimental.Tiff; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Tga; +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; namespace SixLabors.ImageSharp.Tests From de6e9ddb44d20339e7ec26feaa73f579a8f3e1fc Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 7 May 2021 18:53:45 +0200 Subject: [PATCH 223/275] Use enum for the horizontal predictor method --- .../Formats/Tiff/Constants/TiffPredictor.cs | 2 ++ .../Formats/Tiff/ITiffEncoderOptions.cs | 4 ++-- src/ImageSharp/Formats/Tiff/TiffEncoder.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs | 10 +++++----- .../Formats/Tiff/TiffEncoderEntriesCollector.cs | 2 +- .../Formats/Tiff/TiffEncoderTests.cs | 16 ++++++++-------- 6 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffPredictor.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffPredictor.cs index 092bb7aa5..5eaa9f1d1 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffPredictor.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffPredictor.cs @@ -20,6 +20,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Constants /// /// Floating point horizontal differencing. + /// + /// Note: The Tiff Encoder does not yet support this. If this is chosen, the encoder will fallback to none. /// FloatingPoint = 3 } diff --git a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs index 44c53c857..9ebf2f025 100644 --- a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs +++ b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs @@ -35,9 +35,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff TiffEncodingMode Mode { get; } /// - /// Gets a value indicating whether to use horizontal prediction. This can improve the compression ratio with deflate or lzw compression. + /// Gets a value indicating which horizontal prediction to use. This can improve the compression ratio with deflate or lzw compression. /// - bool UseHorizontalPredictor { get; } + TiffPredictor HorizontalPredictor { get; } /// /// Gets the quantizer for creating a color palette image. diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs index 5bca172b5..bf943a995 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs @@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff public TiffEncodingMode Mode { get; set; } /// - public bool UseHorizontalPredictor { get; set; } + public TiffPredictor HorizontalPredictor { get; set; } /// public IQuantizer Quantizer { get; set; } diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 8f68e192f..42300969b 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -72,7 +72,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff this.Mode = options.Mode; this.quantizer = options.Quantizer ?? KnownQuantizers.Octree; this.BitsPerPixel = options.BitsPerPixel; - this.UseHorizontalPredictor = options.UseHorizontalPredictor; + this.HorizontalPredictor = options.HorizontalPredictor; this.CompressionType = options.Compression; this.compressionLevel = options.CompressionLevel; this.maxStripBytes = options.MaxStripBytes; @@ -95,9 +95,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff internal TiffEncodingMode Mode { get; private set; } /// - /// Gets a value indicating whether to use horizontal prediction. This can improve the compression ratio with deflate compression. + /// Gets a value indicating which horizontal predictor to use. This can improve the compression ratio with deflate compression. /// - internal bool UseHorizontalPredictor { get; } + internal TiffPredictor HorizontalPredictor { get; } /// /// Gets the bits per pixel. @@ -164,7 +164,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff var entriesCollector = new TiffEncoderEntriesCollector(); // Write the image bytes to the steam. - var imageDataStart = (uint)writer.Position; + uint imageDataStart = (uint)writer.Position; TiffBitsPerPixel? tiffBitsPerPixel = this.BitsPerPixel; if (tiffBitsPerPixel != null) @@ -176,7 +176,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff image.Width, (int)tiffBitsPerPixel, this.compressionLevel, - this.UseHorizontalPredictor ? TiffPredictor.Horizontal : TiffPredictor.None); + this.HorizontalPredictor != TiffPredictor.FloatingPoint ? this.HorizontalPredictor : TiffPredictor.None); using TiffBaseColorWriter colorWriter = TiffColorWriterFactory.Create( this.Mode, diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs index 4d059aa67..c0ad474b2 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs @@ -264,7 +264,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff this.collector.AddOrReplace(compression); this.collector.AddOrReplace(photometricInterpretation); - if (encoder.UseHorizontalPredictor) + if (encoder.HorizontalPredictor == TiffPredictor.Horizontal) { if (encoder.Mode == TiffEncodingMode.Rgb || encoder.Mode == TiffEncodingMode.Gray || encoder.Mode == TiffEncodingMode.ColorPalette) { diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 21a976620..20eb49376 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -194,7 +194,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Theory] [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeRgb_WithDeflateCompressionAndPredictor_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffEncodingMode.Rgb, TiffCompression.Deflate, usePredictor: true); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffEncodingMode.Rgb, TiffCompression.Deflate, TiffPredictor.Horizontal); [Theory] [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] @@ -204,7 +204,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Theory] [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeRgb_WithLzwCompressionAndPredictor_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffEncodingMode.Rgb, TiffCompression.Lzw, usePredictor: true); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffEncodingMode.Rgb, TiffCompression.Lzw, TiffPredictor.Horizontal); [Theory] [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] @@ -224,7 +224,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Theory] [WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeGray_WithDeflateCompressionAndPredictor_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.Gray, TiffCompression.Deflate, usePredictor: true); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.Gray, TiffCompression.Deflate, TiffPredictor.Horizontal); [Theory] [WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] @@ -234,7 +234,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Theory] [WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeGray_WithLzwCompressionAndPredictor_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.Gray, TiffCompression.Lzw, usePredictor: true); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.Gray, TiffCompression.Lzw, TiffPredictor.Horizontal); [Theory] [WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] @@ -269,7 +269,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_WithDeflateCompressionAndPredictor_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => - TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.ColorPalette, TiffCompression.Deflate, usePredictor: true, useExactComparer: false, compareTolerance: 0.001f); + TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.ColorPalette, TiffCompression.Deflate, TiffPredictor.Horizontal, useExactComparer: false, compareTolerance: 0.001f); [Theory] [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] @@ -281,7 +281,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_WithLzwCompressionAndPredictor_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => - TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.ColorPalette, TiffCompression.Lzw, usePredictor: true, useExactComparer: false, compareTolerance: 0.001f); + TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.ColorPalette, TiffCompression.Lzw, TiffPredictor.Horizontal, useExactComparer: false, compareTolerance: 0.001f); [Theory] [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] @@ -379,7 +379,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff TiffBitsPerPixel bitsPerPixel, TiffEncodingMode mode, TiffCompression compression = TiffCompression.None, - bool usePredictor = false, + TiffPredictor predictor = TiffPredictor.None, bool useExactComparer = true, int maxStripSize = 0, float compareTolerance = 0.01f) @@ -391,7 +391,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Mode = mode, BitsPerPixel = bitsPerPixel, Compression = compression, - UseHorizontalPredictor = usePredictor, + HorizontalPredictor = predictor, MaxStripBytes = maxStripSize }; From 5fcb5fcf0f60a5240ed78bfbc4478a4c52ba8dc5 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 7 May 2021 20:10:54 +0200 Subject: [PATCH 224/275] Review changes --- .../Compressors/DeflateCompressor.cs | 2 +- .../Compression/Compressors/LzwCompressor.cs | 2 +- .../Compression/Compressors/NoCompressor.cs | 2 +- .../Compressors/PackBitsCompressor.cs | 2 +- .../Compression/Compressors/PackBitsWriter.cs | 4 +- .../Compressors/T4BitCompressor.cs | 8 +-- .../Tiff/Compression/HorizontalPredictor.cs | 4 +- .../Formats/Tiff/Constants/TiffConstants.cs | 5 ++ .../Tiff/Constants/TiffNewSubfileType.cs | 12 ++-- .../Formats/Tiff/Constants/TiffPredictor.cs | 2 +- .../Formats/Tiff/ITiffDecoderOptions.cs | 4 +- .../Formats/Tiff/ITiffEncoderOptions.cs | 5 -- .../BlackIsZero1TiffColor.cs | 6 +- .../BlackIsZero4TiffColor.cs | 6 +- .../BlackIsZero8TiffColor.cs | 6 +- .../BlackIsZeroTiffColor.cs | 2 +- .../PaletteTiffColor.cs | 2 +- .../Rgb888TiffColor.cs | 6 +- .../RgbPlanarTiffColor.cs | 2 +- .../PhotometricInterpretation/RgbTiffColor.cs | 2 +- .../TiffBaseColorDecoder.cs | 16 +---- .../TiffColorDecoderFactory.cs | 2 +- .../TiffColorType.cs | 2 +- .../WhiteIsZero1TiffColor.cs | 6 +- .../WhiteIsZero4TiffColor.cs | 6 +- .../WhiteIsZero8TiffColor.cs | 6 +- .../WhiteIsZeroTiffColor.cs | 2 +- src/ImageSharp/Formats/Tiff/README.md | 5 +- .../Formats/Tiff/TiffDecoderCore.cs | 1 + .../Formats/Tiff/TiffDecoderOptionsParser.cs | 1 + src/ImageSharp/Formats/Tiff/TiffEncoder.cs | 3 - .../Formats/Tiff/TiffEncoderCore.cs | 68 ++++++++++--------- .../Tiff/Writers/TiffBaseColorWriter.cs | 6 ++ .../Formats/Tiff/Writers/TiffStreamWriter.cs | 2 +- .../Metadata/Profiles/Exif/ExifReader.cs | 36 ++++------ .../BlackIsZeroTiffColorTests.cs | 2 +- .../PaletteTiffColorTests.cs | 21 +++--- .../RgbPlanarTiffColorTests.cs | 2 +- .../RgbTiffColorTests.cs | 2 +- .../WhiteIsZeroTiffColorTests.cs | 2 +- .../Formats/Tiff/TiffEncoderTests.cs | 38 +++++------ .../ImageSharp.Tests/ImageSharp.Tests.csproj | 1 - 42 files changed, 136 insertions(+), 178 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/DeflateCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/DeflateCompressor.cs index ad72b5e73..225036f90 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/DeflateCompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/DeflateCompressor.cs @@ -9,7 +9,7 @@ using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors { - internal class DeflateCompressor : TiffBaseCompressor + internal sealed class DeflateCompressor : TiffBaseCompressor { private readonly DeflateCompressionLevel compressionLevel; diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/LzwCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/LzwCompressor.cs index d8a20513d..d2ae9096e 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/LzwCompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/LzwCompressor.cs @@ -8,7 +8,7 @@ using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors { - internal class LzwCompressor : TiffBaseCompressor + internal sealed class LzwCompressor : TiffBaseCompressor { private TiffLzwEncoder lzwEncoder; diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/NoCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/NoCompressor.cs index 319ca97d9..79bb2e98f 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/NoCompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/NoCompressor.cs @@ -8,7 +8,7 @@ using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors { - internal class NoCompressor : TiffBaseCompressor + internal sealed class NoCompressor : TiffBaseCompressor { public NoCompressor(Stream output, MemoryAllocator memoryAllocator, int width, int bitsPerPixel) : base(output, memoryAllocator, width, bitsPerPixel) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsCompressor.cs index 61db21fa8..5a2383187 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsCompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsCompressor.cs @@ -8,7 +8,7 @@ using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors { - internal class PackBitsCompressor : TiffBaseCompressor + internal sealed class PackBitsCompressor : TiffBaseCompressor { private IManagedByteBuffer pixelData; diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsWriter.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsWriter.cs index 73c3f36f8..30d21e54c 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsWriter.cs @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors while (posInRowSpan < rowSpan.Length) { - var useReplicateRun = IsReplicateRun(rowSpan, posInRowSpan); + bool useReplicateRun = IsReplicateRun(rowSpan, posInRowSpan); if (useReplicateRun) { if (literalRunLength > 0) @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors } // Write a run with the same bytes. - var runLength = FindRunLength(rowSpan, posInRowSpan, maxRunLength); + int runLength = FindRunLength(rowSpan, posInRowSpan, maxRunLength); WriteRun(rowSpan, posInRowSpan, runLength, compressedRowSpan, bytesWritten); bytesWritten += 2; diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitCompressor.cs index 11149007f..a016fb712 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitCompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitCompressor.cs @@ -13,13 +13,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors /// /// Bitwriter for writing compressed CCITT T4 1D data. /// - internal class T4BitCompressor : TiffBaseCompressor + internal sealed class T4BitCompressor : TiffBaseCompressor { private const uint WhiteZeroRunTermCode = 0x35; private const uint BlackZeroRunTermCode = 0x37; - private static readonly List MakeupRunLength = new List() + private static readonly uint[] MakeupRunLength = { 64, 128, 192, 256, 320, 384, 448, 512, 576, 640, 704, 768, 832, 896, 960, 1024, 1088, 1152, 1216, 1280, 1344, 1408, 1472, 1536, 1600, 1664, 1728, 1792, 1856, 1920, 1984, 2048, 2112, 2176, 2240, 2304, 2368, 2432, 2496, 2560 }; @@ -368,7 +368,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors private uint GetBestFittingMakeupRunLength(uint runLength) { - for (int i = 0; i < MakeupRunLength.Count - 1; i++) + for (int i = 0; i < MakeupRunLength.Length - 1; i++) { if (MakeupRunLength[i] <= runLength && MakeupRunLength[i + 1] > runLength) { @@ -376,7 +376,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors } } - return MakeupRunLength[MakeupRunLength.Count - 1]; + return MakeupRunLength[MakeupRunLength.Length - 1]; } private uint GetTermCode(uint runLength, out uint codeLength, bool isWhiteRun) diff --git a/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs b/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs index 34741cd93..1b1254e3f 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs @@ -95,7 +95,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression private static void Undo8Bit(Span pixelBytes, int width) { - var rowBytesCount = width; + int rowBytesCount = width; int height = pixelBytes.Length / rowBytesCount; for (int y = 0; y < height; y++) { @@ -112,7 +112,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression private static void Undo24Bit(Span pixelBytes, int width) { - var rowBytesCount = width * 3; + int rowBytesCount = width * 3; int height = pixelBytes.Length / rowBytesCount; for (int y = 0; y < height; y++) { diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs index 3553c61c5..a30890a69 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs @@ -75,6 +75,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Constants /// public const int SizeOfDouble = 8; + /// + /// The default strip size is 8k. + /// + public const int DefaultStripSize = 8 * 1024; + /// /// The bits per sample for 1 bit bicolor images. /// diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs index 3b84120a5..4ed6aafbb 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffNewSubfileType.cs @@ -14,31 +14,31 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Constants /// /// A full-resolution image. /// - FullImage = 0x0000, + FullImage = 0, /// /// Reduced-resolution version of another image in this TIFF file. /// - Preview = 0x0001, + Preview = 1, /// /// A single page of a multi-page image. /// - SinglePage = 0x0002, + SinglePage = 2, /// /// A transparency mask for another image in this TIFF file. /// - TransparencyMask = 0x0004, + TransparencyMask = 4, /// /// Alternative reduced-resolution version of another image in this TIFF file (see DNG specification). /// - AlternativePreview = 0x10000, + AlternativePreview = 65536, /// /// Mixed raster content (see RFC2301). /// - MixedRasterContent = 0x0008 + MixedRasterContent = 8 } } diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffPredictor.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffPredictor.cs index 5eaa9f1d1..6bde23cac 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffPredictor.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffPredictor.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Constants public enum TiffPredictor : ushort { /// - /// No prediction scheme used before coding + /// No prediction. /// None = 1, diff --git a/src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs b/src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs index cee66694b..537238439 100644 --- a/src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs +++ b/src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Formats.Tiff @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Encapsulates the options for the . /// - public interface ITiffDecoderOptions + internal interface ITiffDecoderOptions { /// /// Gets a value indicating whether the metadata should be ignored when the image is being decoded. diff --git a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs index 9ebf2f025..03c25ea13 100644 --- a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs +++ b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs @@ -43,10 +43,5 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// Gets the quantizer for creating a color palette image. /// IQuantizer Quantizer { get; } - - /// - /// Gets the maximum size of strip (bytes). - /// - int MaxStripBytes { get; } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs index b4d609a6f..6724adec0 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs @@ -6,7 +6,7 @@ using System; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation { /// /// Implements the 'BlackIsZero' photometric interpretation (optimized for bilevel images). @@ -15,10 +15,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff internal class BlackIsZero1TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { - public BlackIsZero1TiffColor() - { - } - /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs index d39e28038..cf59c1222 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs @@ -6,7 +6,7 @@ using System; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation { /// /// Implements the 'BlackIsZero' photometric interpretation (optimized for 4-bit grayscale images). @@ -14,10 +14,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff internal class BlackIsZero4TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { - public BlackIsZero4TiffColor() - { - } - /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs index d62898a08..096f0449b 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs @@ -6,7 +6,7 @@ using System; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation { /// /// Implements the 'BlackIsZero' photometric interpretation (optimized for 8-bit grayscale images). @@ -14,10 +14,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff internal class BlackIsZero8TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { - public BlackIsZero8TiffColor() - { - } - /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs index 271672e25..83cef8e75 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs @@ -7,7 +7,7 @@ using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation { /// /// Implements the 'BlackIsZero' photometric interpretation (for all bit depths). diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs index 2aefa2ee7..7ed25f822 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs @@ -7,7 +7,7 @@ using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation { /// /// Implements the 'PaletteTiffColor' photometric interpretation (for all bit depths). diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs index ad3e0909b..e45863a57 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs @@ -6,7 +6,7 @@ using System; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation { /// /// Implements the 'RGB' photometric interpretation (optimized for 8-bit full color images). @@ -14,10 +14,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff internal class Rgb888TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { - public Rgb888TiffColor() - { - } - /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs index 531fd6509..ba98f829c 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs @@ -6,7 +6,7 @@ using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation { /// /// Implements the 'RGB' photometric interpretation with 'Planar' layout (for all bit depths). diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs index ef1bc0a23..816ba67b7 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs @@ -7,7 +7,7 @@ using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation { /// /// Implements the 'RGB' photometric interpretation (for all bit depths). diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffBaseColorDecoder.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffBaseColorDecoder.cs index ad67c463f..c08b26ef1 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffBaseColorDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffBaseColorDecoder.cs @@ -5,7 +5,7 @@ using System; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation { /// /// The base class for photometric interpretation decoders. @@ -14,20 +14,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff internal abstract class TiffBaseColorDecoder where TPixel : unmanaged, IPixel { - protected TiffBaseColorDecoder() - { - } - - /* - /// - /// Gets the photometric interpretation value. - /// - /// - /// The photometric interpretation value. - /// - public TiffColorType ColorType { get; } - */ - /// /// Decodes source raw pixel data using the current photometric interpretation. /// diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory.cs index 91ae9c2ab..0a7941dfb 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory.cs @@ -3,7 +3,7 @@ using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation { internal static class TiffColorDecoderFactory where TPixel : unmanaged, IPixel diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs index 8eb0fb4de..a9007b640 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation { /// /// Provides enumeration of the various TIFF photometric interpretation implementation types. diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs index f7be45cdd..465414257 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs @@ -6,7 +6,7 @@ using System; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation { /// /// Implements the 'WhiteIsZero' photometric interpretation (optimized for bilevel images). @@ -14,10 +14,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff internal class WhiteIsZero1TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { - public WhiteIsZero1TiffColor() - { - } - /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs index 4360b2aa2..dae89db28 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs @@ -6,7 +6,7 @@ using System; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation { /// /// Implements the 'WhiteIsZero' photometric interpretation (optimized for 4-bit grayscale images). @@ -14,10 +14,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff internal class WhiteIsZero4TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { - public WhiteIsZero4TiffColor() - { - } - /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs index e03758e65..1b141f9f6 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs @@ -6,7 +6,7 @@ using System; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation { /// /// Implements the 'WhiteIsZero' photometric interpretation (optimized for 8-bit grayscale images). @@ -14,10 +14,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff internal class WhiteIsZero8TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { - public WhiteIsZero8TiffColor() - { - } - /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs index e4e9179f0..697fe2f07 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs @@ -7,7 +7,7 @@ using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Tiff +namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation { /// /// Implements the 'WhiteIsZero' photometric interpretation (for all bit depths). diff --git a/src/ImageSharp/Formats/Tiff/README.md b/src/ImageSharp/Formats/Tiff/README.md index 565ecb6cd..5b116b819 100644 --- a/src/ImageSharp/Formats/Tiff/README.md +++ b/src/ImageSharp/Formats/Tiff/README.md @@ -25,6 +25,9 @@ ## Implementation Status +- The Decoder and Encoder currently only supports a single frame per image. +- Some compression formats are not yet supported. See the list below. + ### Deviations from the TIFF spec (to be fixed) - Decoder @@ -46,7 +49,7 @@ |Lzw | Y | Y | Based on ImageSharp GIF LZW implementation - this code could be modified to be (i) shared, or (ii) optimised for each case | |Old Jpeg | | | We should not even try to support this | |Jpeg (Technote 2) | | | | -|Deflate (Technote 2) | Y | Y | Based on PNG Deflate. | +|Deflate (Technote 2) | Y | Y | Based on PNG Deflate. | |Old Deflate (Technote 2) | | Y | | ### Photometric Interpretation Formats diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index f3e82f86d..fe6778fc2 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Threading; using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Formats.Tiff.Constants; +using SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs index 6b8e6b84e..aaf4502cd 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs @@ -3,6 +3,7 @@ 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 diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs index bf943a995..b66ba339c 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs @@ -36,9 +36,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// public IQuantizer Quantizer { get; set; } - /// - public int MaxStripBytes { get; set; } = TiffEncoderCore.DefaultStripSize; - /// public void Encode(Image image, Stream stream) where TPixel : unmanaged, IPixel diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 42300969b..6654a6e4b 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -25,8 +25,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// internal sealed class TiffEncoderCore : IImageEncoderInternals { - public const int DefaultStripSize = 8 * 1024; - private static readonly ushort ByteOrderMarker = BitConverter.IsLittleEndian ? TiffConstants.ByteOrderLittleEndianShort : TiffConstants.ByteOrderBigEndianShort; @@ -56,11 +54,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// private readonly DeflateCompressionLevel compressionLevel; - /// - /// The maximum number of bytes for a strip. - /// - private readonly int maxStripBytes; - /// /// Initializes a new instance of the class. /// @@ -75,7 +68,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff this.HorizontalPredictor = options.HorizontalPredictor; this.CompressionType = options.Compression; this.compressionLevel = options.CompressionLevel; - this.maxStripBytes = options.MaxStripBytes; } /// @@ -120,10 +112,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff this.configuration = image.GetConfiguration(); ImageMetadata metadata = image.Metadata; TiffMetadata tiffMetadata = metadata.GetTiffMetadata(); - this.BitsPerPixel ??= tiffMetadata.BitsPerPixel; this.SetMode(tiffMetadata); - this.SetBitsPerPixel(); + this.SetBitsPerPixel(tiffMetadata); this.SetPhotometricInterpretation(); using (var writer = new TiffStreamWriter(stream)) @@ -176,7 +167,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff image.Width, (int)tiffBitsPerPixel, this.compressionLevel, - this.HorizontalPredictor != TiffPredictor.FloatingPoint ? this.HorizontalPredictor : TiffPredictor.None); + this.HorizontalPredictor == TiffPredictor.Horizontal ? this.HorizontalPredictor : TiffPredictor.None); using TiffBaseColorWriter colorWriter = TiffColorWriterFactory.Create( this.Mode, @@ -210,8 +201,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff DebugGuard.MustBeGreaterThan(height, 0, nameof(height)); DebugGuard.MustBeGreaterThan(bytesPerRow, 0, nameof(bytesPerRow)); - int stripBytes = this.maxStripBytes > 0 ? this.maxStripBytes : DefaultStripSize; - int rowsPerStrip = stripBytes / bytesPerRow; + int rowsPerStrip = TiffConstants.DefaultStripSize / bytesPerRow; return rowsPerStrip > 0 ? (rowsPerStrip < height ? rowsPerStrip : height) : 1; } @@ -293,30 +283,46 @@ namespace SixLabors.ImageSharp.Formats.Tiff } } - if (this.Mode == TiffEncodingMode.Default && this.BitsPerPixel != null) + if (this.Mode == TiffEncodingMode.Default && tiffMetadata.BitsPerPixel != null) { // Preserve input bits per pixel, if no encoding mode was specified. - switch (this.BitsPerPixel) - { - case TiffBitsPerPixel.Bit1: - this.Mode = TiffEncodingMode.BiColor; - break; - case TiffBitsPerPixel.Bit4: - this.Mode = TiffEncodingMode.ColorPalette; - break; - case TiffBitsPerPixel.Bit8: - this.Mode = tiffMetadata.PhotometricInterpretation == TiffPhotometricInterpretation.PaletteColor ? TiffEncodingMode.ColorPalette : TiffEncodingMode.Gray; - - break; - default: - this.Mode = TiffEncodingMode.Rgb; - break; - } + this.SetModeWithBitsPerPixel(tiffMetadata.BitsPerPixel, tiffMetadata.PhotometricInterpretation); + + return; + } + + if (this.BitsPerPixel != null) + { + // The user has specified a bits per pixel, so use that to determine the encoding mode. + this.SetModeWithBitsPerPixel(this.BitsPerPixel, tiffMetadata.PhotometricInterpretation); } } - private void SetBitsPerPixel() + private void SetModeWithBitsPerPixel(TiffBitsPerPixel? bitsPerPixel, TiffPhotometricInterpretation photometricInterpretation) { + switch (bitsPerPixel) + { + case TiffBitsPerPixel.Bit1: + this.Mode = TiffEncodingMode.BiColor; + break; + case TiffBitsPerPixel.Bit4: + this.Mode = TiffEncodingMode.ColorPalette; + break; + case TiffBitsPerPixel.Bit8: + this.Mode = photometricInterpretation == TiffPhotometricInterpretation.PaletteColor + ? TiffEncodingMode.ColorPalette + : TiffEncodingMode.Gray; + + break; + default: + this.Mode = TiffEncodingMode.Rgb; + break; + } + } + + private void SetBitsPerPixel(TiffMetadata tiffMetadata) + { + this.BitsPerPixel ??= tiffMetadata.BitsPerPixel; switch (this.Mode) { case TiffEncodingMode.BiColor: diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs index 0a04a5089..232daa18d 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs @@ -22,8 +22,14 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers this.EntriesCollector = entriesCollector; } + /// + /// Gets the bits per pixel. + /// public abstract int BitsPerPixel { get; } + /// + /// Gets the bytes per row. + /// public int BytesPerRow => ((this.Image.Width * this.BitsPerPixel) + 7) / 8; protected ImageFrame Image { get; } diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs index 8c1d7b759..05a1ca7a2 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs @@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers /// /// Utility class for writing TIFF data to a . /// - internal class TiffStreamWriter : IDisposable + internal sealed class TiffStreamWriter : IDisposable { private static readonly byte[] PaddingBytes = new byte[4]; diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs index 57a9a8cc6..a867b984e 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs @@ -91,9 +91,6 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif private readonly byte[] buf2 = new byte[2]; private readonly Stream data; - - private bool isBigEndian; - private List invalidTags; private uint exifOffset; @@ -120,11 +117,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// public uint ThumbnailOffset { get; protected set; } - public bool IsBigEndian - { - get => this.isBigEndian; - protected set => this.isBigEndian = value; - } + public bool IsBigEndian { get; protected set; } protected abstract void RegisterExtLoader(uint offset, Action loader); @@ -330,7 +323,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif { uint newOffset = this.ConvertToUInt32(this.offsetBuffer); - // Ensure that the new index does not overrun the data + // Ensure that the new index does not overrun the data. if (newOffset > int.MaxValue || (newOffset + size) > this.data.Length) { this.AddInvalidTag(new UnkownExifTag(tag)); @@ -339,7 +332,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif this.RegisterExtLoader(newOffset, () => { - var dataBuffer = new byte[size]; + byte[] dataBuffer = new byte[size]; this.Seek(newOffset); if (this.TryReadSpan(dataBuffer)) { @@ -365,8 +358,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif foreach (IExifValue val in values) { - // sometimes duplicates appear, - // can compare val.Tag == exif.Tag + // Sometimes duplicates appear, can compare val.Tag == exif.Tag if (val == exif) { Debug.WriteLine($"Duplicate Exif tag: tag={exif.Tag}, dataType={exif.DataType}"); @@ -403,11 +395,11 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return false; } - int readed = this.data.Read(span); - return readed == length; + int read = this.data.Read(span); + return read == length; } - // Known as Long in Exif Specification + // Known as Long in Exif Specification. protected uint ReadUInt32() => this.TryReadSpan(this.buf4) ? this.ConvertToUInt32(this.buf4) @@ -424,7 +416,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return default; } - long intValue = this.isBigEndian + long intValue = this.IsBigEndian ? BinaryPrimitives.ReadInt64BigEndian(buffer) : BinaryPrimitives.ReadInt64LittleEndian(buffer); @@ -433,13 +425,13 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif private uint ConvertToUInt32(ReadOnlySpan buffer) { - // Known as Long in Exif Specification + // Known as Long in Exif Specification. if (buffer.Length < 4) { return default; } - return this.isBigEndian + return this.IsBigEndian ? BinaryPrimitives.ReadUInt32BigEndian(buffer) : BinaryPrimitives.ReadUInt32LittleEndian(buffer); } @@ -451,7 +443,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return default; } - return this.isBigEndian + return this.IsBigEndian ? BinaryPrimitives.ReadUInt16BigEndian(buffer) : BinaryPrimitives.ReadUInt16LittleEndian(buffer); } @@ -463,7 +455,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return default; } - int intValue = this.isBigEndian + int intValue = this.IsBigEndian ? BinaryPrimitives.ReadInt32BigEndian(buffer) : BinaryPrimitives.ReadInt32LittleEndian(buffer); @@ -492,7 +484,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return default; } - return this.isBigEndian + return this.IsBigEndian ? BinaryPrimitives.ReadInt32BigEndian(buffer) : BinaryPrimitives.ReadInt32LittleEndian(buffer); } @@ -517,7 +509,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return default; } - return this.isBigEndian + return this.IsBigEndian ? BinaryPrimitives.ReadInt16BigEndian(buffer) : BinaryPrimitives.ReadInt16LittleEndian(buffer); } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs index 780c390f7..579ee0290 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; -using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; using SixLabors.ImageSharp.PixelFormats; using Xunit; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs index 823d5f5a5..0da1d8bbd 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; -using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; using SixLabors.ImageSharp.PixelFormats; using Xunit; @@ -13,9 +13,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.PhotometricInterpretation [Trait("Format", "Tiff")] public class PaletteTiffColorTests : PhotometricInterpretationTestBase { - public static uint[][] Palette4ColorPalette { get => GeneratePalette(16); } + public static uint[][] Palette4ColorPalette => GeneratePalette(16); - public static ushort[] Palette4ColorMap { get => GenerateColorMap(Palette4ColorPalette); } + public static ushort[] Palette4ColorMap => GenerateColorMap(Palette4ColorPalette); private static readonly byte[] Palette4Bytes4X4 = { @@ -54,9 +54,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.PhotometricInterpretation } } - public static uint[][] Palette8ColorPalette { get => GeneratePalette(256); } + public static uint[][] Palette8ColorPalette => GeneratePalette(256); - public static ushort[] Palette8ColorMap { get => GenerateColorMap(Palette8ColorPalette); } + public static ushort[] Palette8ColorMap => GenerateColorMap(Palette8ColorPalette); private static readonly byte[] Palette8Bytes4X4 = { @@ -83,13 +83,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.PhotometricInterpretation [Theory] [MemberData(nameof(Palette4Data))] [MemberData(nameof(Palette8Data))] - public void Decode_WritesPixelData(byte[] inputData, ushort bitsPerSample, ushort[] colorMap, int left, int top, int width, int height, Rgba32[][] expectedResult) - { - AssertDecode(expectedResult, pixels => - { - new PaletteTiffColor(new[] { bitsPerSample }, colorMap).Decode(inputData, pixels, left, top, width, height); - }); - } + public void Decode_WritesPixelData(byte[] inputData, ushort bitsPerSample, ushort[] colorMap, int left, int top, int width, int height, Rgba32[][] expectedResult) => AssertDecode(expectedResult, pixels => + { + new PaletteTiffColor(new[] { bitsPerSample }, colorMap).Decode(inputData, pixels, left, top, width, height); + }); private static uint[][] GeneratePalette(int count) { diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs index 78105e420..abfae6ab4 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; -using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs index dd232ccc3..4abde8f17 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; -using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; using SixLabors.ImageSharp.PixelFormats; using Xunit; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs index 716ec8e21..620fddd7d 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; -using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; using SixLabors.ImageSharp.PixelFormats; using Xunit; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 20eb49376..c2edb9e1c 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -309,28 +309,28 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffEncodingMode.BiColor, TiffCompression.Ccitt1D); [Theory] - [WithFile(GrayscaleUncompressed, PixelTypes.L8, TiffEncodingMode.Gray, TiffCompression.PackBits, 16 * 1024)] - [WithFile(PaletteDeflateMultistrip, PixelTypes.L8, TiffEncodingMode.ColorPalette, TiffCompression.Lzw, 32 * 1024)] - [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffEncodingMode.Rgb, TiffCompression.Deflate, 64 * 1024)] - [WithFile(RgbUncompressed, PixelTypes.Rgb24, TiffEncodingMode.Rgb, TiffCompression.None, 10 * 1024)] - [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffEncodingMode.Rgb, TiffCompression.None, 30 * 1024)] - [WithFile(RgbUncompressed, PixelTypes.Rgb48, TiffEncodingMode.Rgb, TiffCompression.None, 70 * 1024)] - public void TiffEncoder_StripLength(TestImageProvider provider, TiffEncodingMode mode, TiffCompression compression, int maxSize) + [WithFile(GrayscaleUncompressed, PixelTypes.L8, TiffEncodingMode.Gray, TiffCompression.PackBits)] + [WithFile(PaletteDeflateMultistrip, PixelTypes.L8, TiffEncodingMode.ColorPalette, TiffCompression.Lzw)] + [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffEncodingMode.Rgb, TiffCompression.Deflate)] + [WithFile(RgbUncompressed, PixelTypes.Rgb24, TiffEncodingMode.Rgb, TiffCompression.None)] + [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffEncodingMode.Rgb, TiffCompression.None)] + [WithFile(RgbUncompressed, PixelTypes.Rgb48, TiffEncodingMode.Rgb, TiffCompression.None)] + public void TiffEncoder_StripLength(TestImageProvider provider, TiffEncodingMode mode, TiffCompression compression) where TPixel : unmanaged, IPixel => - TestStripLength(provider, mode, compression, maxSize); + TestStripLength(provider, mode, compression); [Theory] - [WithFile(Calliphora_BiColorUncompressed, PixelTypes.L8, TiffEncodingMode.BiColor, TiffCompression.CcittGroup3Fax, 9 * 1024)] - public void TiffEncoder_StripLength_OutOfBounds(TestImageProvider provider, TiffEncodingMode mode, TiffCompression compression, int maxSize) + [WithFile(Calliphora_BiColorUncompressed, PixelTypes.L8, TiffEncodingMode.BiColor, TiffCompression.CcittGroup3Fax)] + public void TiffEncoder_StripLength_OutOfBounds(TestImageProvider provider, TiffEncodingMode mode, TiffCompression compression) where TPixel : unmanaged, IPixel => //// CcittGroup3Fax compressed data length can be larger than the original length - Assert.Throws(() => TestStripLength(provider, mode, compression, maxSize)); + Assert.Throws(() => TestStripLength(provider, mode, compression)); - private static void TestStripLength(TestImageProvider provider, TiffEncodingMode mode, TiffCompression compression, int maxSize) + private static void TestStripLength(TestImageProvider provider, TiffEncodingMode mode, TiffCompression compression) where TPixel : unmanaged, IPixel { // arrange - var tiffEncoder = new TiffEncoder() { Mode = mode, Compression = compression, MaxStripBytes = maxSize }; + var tiffEncoder = new TiffEncoder() { Mode = mode, Compression = compression }; Image input = provider.GetImage(); using var memStream = new MemoryStream(); TiffFrameMetadata inputMeta = input.Frames.RootFrame.Metadata.GetTiffMetadata(); @@ -349,7 +349,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff foreach (Number sz in meta.StripByteCounts) { - Assert.True((uint)sz <= maxSize); + Assert.True((uint)sz <= TiffConstants.DefaultStripSize); } // For uncompressed more accurate test. @@ -359,9 +359,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { // The difference must be less than one row. int stripBytes = (int)meta.StripByteCounts[i]; - var widthBytes = (meta.BitsPerPixel + 7) / 8 * (int)meta.Width; + int widthBytes = (meta.BitsPerPixel + 7) / 8 * (int)meta.Width; - Assert.True((maxSize - stripBytes) < widthBytes); + Assert.True((TiffConstants.DefaultStripSize - stripBytes) < widthBytes); } } @@ -370,8 +370,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff provider, (TiffBitsPerPixel)inputMeta.BitsPerPixel, mode, - inputMeta.Compression, - maxStripSize: maxSize); + inputMeta.Compression); } private static void TestTiffEncoderCore( @@ -391,8 +390,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Mode = mode, BitsPerPixel = bitsPerPixel, Compression = compression, - HorizontalPredictor = predictor, - MaxStripBytes = maxStripSize + HorizontalPredictor = predictor }; // Does DebugSave & load reference CompareToReferenceInput(): diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index 0a25ae0f9..b6482455e 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -23,7 +23,6 @@ - From 10a3e5eba0e4846657cd107f2f40d5f8457b82f0 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 8 May 2021 21:36:10 +0100 Subject: [PATCH 225/275] Update HorizontalPredictor.cs --- .../Tiff/Compression/HorizontalPredictor.cs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs b/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs index 1b1254e3f..ae2f17dbb 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs @@ -117,17 +117,18 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression for (int y = 0; y < height; y++) { Span rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount); - Span rowRgb = MemoryMarshal.Cast(rowBytes); + Span rowRgb = MemoryMarshal.Cast(rowBytes).Slice(0, width); + ref Rgb24 rowRgbBase = ref MemoryMarshal.GetReference(rowRgb); + byte r = rowRgbBase.R; + byte g = rowRgbBase.G; + byte b = rowRgbBase.B; - byte r = rowRgb[0].R; - byte g = rowRgb[0].G; - byte b = rowRgb[0].B; - for (int x = 1; x < width; x++) + for (int x = 1; x < rowRgb.Length; x++) { ref Rgb24 pixel = ref rowRgb[x]; - r += rowRgb[x].R; - g += rowRgb[x].G; - b += rowRgb[x].B; + r += pixel.R; + g += pixel.G; + b += pixel.B; var rgb = new Rgb24(r, g, b); pixel.FromRgb24(rgb); } From 10a12f752ad07959e3f2e68b34b0afbb00ae825a Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 8 May 2021 21:37:36 +0100 Subject: [PATCH 226/275] Add TODO to TiffLZWDecoder --- .../Formats/Tiff/Compression/Decompressors/TiffLzwDecoder.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffLzwDecoder.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffLzwDecoder.cs index 5f482bd0b..68d3a7f2a 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffLzwDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffLzwDecoder.cs @@ -101,6 +101,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors Guard.NotNull(stream, nameof(stream)); this.stream = stream; + + // TODO: Investigate a manner by which we can avoid this allocation. this.table = new LzwString[TableSize]; for (int i = 0; i < 256; i++) { From 4516ec445aa6ebc28c45b2c77e34f754b5186cca Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 9 May 2021 10:37:06 +0200 Subject: [PATCH 227/275] Rename generic type classes to include the type in the filename --- ...lackIsZero1TiffColor.cs => BlackIsZero1TiffColor{TPixel}.cs} | 0 ...lackIsZero4TiffColor.cs => BlackIsZero4TiffColor{TPixel}.cs} | 0 ...lackIsZero8TiffColor.cs => BlackIsZero8TiffColor{TPixel}.cs} | 0 ...{BlackIsZeroTiffColor.cs => BlackIsZeroTiffColor{TPixel}.cs} | 0 .../{PaletteTiffColor.cs => PaletteTiffColor{TPixel}.cs} | 0 .../{Rgb888TiffColor.cs => Rgb888TiffColor{TPixel}.cs} | 0 .../{RgbPlanarTiffColor.cs => RgbPlanarTiffColor{TPixel}.cs} | 2 +- .../{RgbTiffColor.cs => RgbTiffColor{TPixel}.cs} | 0 ...{TiffBaseColorDecoder.cs => TiffBaseColorDecoder{TPixel}.cs} | 0 ...olorDecoderFactory.cs => TiffColorDecoderFactory{TPixel}.cs} | 0 ...hiteIsZero1TiffColor.cs => WhiteIsZero1TiffColor{TPixel}.cs} | 0 ...hiteIsZero4TiffColor.cs => WhiteIsZero4TiffColor{TPixel}.cs} | 0 ...hiteIsZero8TiffColor.cs => WhiteIsZero8TiffColor{TPixel}.cs} | 0 ...{WhiteIsZeroTiffColor.cs => WhiteIsZeroTiffColor{TPixel}.cs} | 0 .../{TiffBaseColorWriter.cs => TiffBaseColorWriter{TPixel}.cs} | 0 .../{TiffBiColorWriter.cs => TiffBiColorWriter{TPixel}.cs} | 0 ...positeColorWriter.cs => TiffCompositeColorWriter{TPixel}.cs} | 0 .../Writers/{TiffGrayWriter.cs => TiffGrayWriter{TPixel}.cs} | 0 .../{TiffPaletteWriter.cs => TiffPaletteWriter{TPixel}.cs} | 0 .../Tiff/Writers/{TiffRgbWriter.cs => TiffRgbWriter{TPixel}.cs} | 0 20 files changed, 1 insertion(+), 1 deletion(-) rename src/ImageSharp/Formats/Tiff/PhotometricInterpretation/{BlackIsZero1TiffColor.cs => BlackIsZero1TiffColor{TPixel}.cs} (100%) rename src/ImageSharp/Formats/Tiff/PhotometricInterpretation/{BlackIsZero4TiffColor.cs => BlackIsZero4TiffColor{TPixel}.cs} (100%) rename src/ImageSharp/Formats/Tiff/PhotometricInterpretation/{BlackIsZero8TiffColor.cs => BlackIsZero8TiffColor{TPixel}.cs} (100%) rename src/ImageSharp/Formats/Tiff/PhotometricInterpretation/{BlackIsZeroTiffColor.cs => BlackIsZeroTiffColor{TPixel}.cs} (100%) rename src/ImageSharp/Formats/Tiff/PhotometricInterpretation/{PaletteTiffColor.cs => PaletteTiffColor{TPixel}.cs} (100%) rename src/ImageSharp/Formats/Tiff/PhotometricInterpretation/{Rgb888TiffColor.cs => Rgb888TiffColor{TPixel}.cs} (100%) rename src/ImageSharp/Formats/Tiff/PhotometricInterpretation/{RgbPlanarTiffColor.cs => RgbPlanarTiffColor{TPixel}.cs} (97%) rename src/ImageSharp/Formats/Tiff/PhotometricInterpretation/{RgbTiffColor.cs => RgbTiffColor{TPixel}.cs} (100%) rename src/ImageSharp/Formats/Tiff/PhotometricInterpretation/{TiffBaseColorDecoder.cs => TiffBaseColorDecoder{TPixel}.cs} (100%) rename src/ImageSharp/Formats/Tiff/PhotometricInterpretation/{TiffColorDecoderFactory.cs => TiffColorDecoderFactory{TPixel}.cs} (100%) rename src/ImageSharp/Formats/Tiff/PhotometricInterpretation/{WhiteIsZero1TiffColor.cs => WhiteIsZero1TiffColor{TPixel}.cs} (100%) rename src/ImageSharp/Formats/Tiff/PhotometricInterpretation/{WhiteIsZero4TiffColor.cs => WhiteIsZero4TiffColor{TPixel}.cs} (100%) rename src/ImageSharp/Formats/Tiff/PhotometricInterpretation/{WhiteIsZero8TiffColor.cs => WhiteIsZero8TiffColor{TPixel}.cs} (100%) rename src/ImageSharp/Formats/Tiff/PhotometricInterpretation/{WhiteIsZeroTiffColor.cs => WhiteIsZeroTiffColor{TPixel}.cs} (100%) rename src/ImageSharp/Formats/Tiff/Writers/{TiffBaseColorWriter.cs => TiffBaseColorWriter{TPixel}.cs} (100%) rename src/ImageSharp/Formats/Tiff/Writers/{TiffBiColorWriter.cs => TiffBiColorWriter{TPixel}.cs} (100%) rename src/ImageSharp/Formats/Tiff/Writers/{TiffCompositeColorWriter.cs => TiffCompositeColorWriter{TPixel}.cs} (100%) rename src/ImageSharp/Formats/Tiff/Writers/{TiffGrayWriter.cs => TiffGrayWriter{TPixel}.cs} (100%) rename src/ImageSharp/Formats/Tiff/Writers/{TiffPaletteWriter.cs => TiffPaletteWriter{TPixel}.cs} (100%) rename src/ImageSharp/Formats/Tiff/Writers/{TiffRgbWriter.cs => TiffRgbWriter{TPixel}.cs} (100%) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs similarity index 100% rename from src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor.cs rename to src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor{TPixel}.cs similarity index 100% rename from src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor.cs rename to src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor{TPixel}.cs diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor{TPixel}.cs similarity index 100% rename from src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor.cs rename to src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero8TiffColor{TPixel}.cs diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor{TPixel}.cs similarity index 100% rename from src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs rename to src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor{TPixel}.cs diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor{TPixel}.cs similarity index 100% rename from src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs rename to src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor{TPixel}.cs diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor{TPixel}.cs similarity index 100% rename from src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs rename to src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor{TPixel}.cs diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor{TPixel}.cs similarity index 97% rename from src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs rename to src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor{TPixel}.cs index ba98f829c..e45dd44bd 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor{TPixel}.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// /// Implements the 'RGB' photometric interpretation with 'Planar' layout (for all bit depths). /// - internal class RgbPlanarTiffColor /* : TiffColorDecoder */ + internal class RgbPlanarTiffColor where TPixel : unmanaged, IPixel { private readonly float rFactor; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor{TPixel}.cs similarity index 100% rename from src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs rename to src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor{TPixel}.cs diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffBaseColorDecoder.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffBaseColorDecoder{TPixel}.cs similarity index 100% rename from src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffBaseColorDecoder.cs rename to src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffBaseColorDecoder{TPixel}.cs diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs similarity index 100% rename from src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory.cs rename to src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs similarity index 100% rename from src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor.cs rename to src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor{TPixel}.cs similarity index 100% rename from src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor.cs rename to src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor{TPixel}.cs diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor{TPixel}.cs similarity index 100% rename from src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor.cs rename to src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor{TPixel}.cs diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor{TPixel}.cs similarity index 100% rename from src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs rename to src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor{TPixel}.cs diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter{TPixel}.cs similarity index 100% rename from src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs rename to src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter{TPixel}.cs diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter{TPixel}.cs similarity index 100% rename from src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs rename to src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter{TPixel}.cs diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffCompositeColorWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffCompositeColorWriter{TPixel}.cs similarity index 100% rename from src/ImageSharp/Formats/Tiff/Writers/TiffCompositeColorWriter.cs rename to src/ImageSharp/Formats/Tiff/Writers/TiffCompositeColorWriter{TPixel}.cs diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffGrayWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffGrayWriter{TPixel}.cs similarity index 100% rename from src/ImageSharp/Formats/Tiff/Writers/TiffGrayWriter.cs rename to src/ImageSharp/Formats/Tiff/Writers/TiffGrayWriter{TPixel}.cs diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter{TPixel}.cs similarity index 100% rename from src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs rename to src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter{TPixel}.cs diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffRgbWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffRgbWriter{TPixel}.cs similarity index 100% rename from src/ImageSharp/Formats/Tiff/Writers/TiffRgbWriter.cs rename to src/ImageSharp/Formats/Tiff/Writers/TiffRgbWriter{TPixel}.cs From a67bdc222de688ea3fad870dcfcdb75b833925e6 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 9 May 2021 11:50:20 +0200 Subject: [PATCH 228/275] Remove the Func from SetSingle and SetArray --- .../Metadata/Profiles/Exif/ExifReader.cs | 1 - .../Profiles/Exif/Values/ExifNumberArray.cs | 62 ++++++++++++++----- 2 files changed, 48 insertions(+), 15 deletions(-) diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs index a867b984e..6e671b3ec 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs @@ -391,7 +391,6 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif int length = span.Length; if ((this.data.Length - this.data.Position) < length) { - span = default; return false; } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs index f447173c7..2d3a93aed 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using System; - namespace SixLabors.ImageSharp.Metadata.Profiles.Exif { internal sealed class ExifNumberArray : ExifArrayValue @@ -48,21 +46,21 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif switch (value) { case int val: - return this.SetSingle(val, v => (Number)v); + return this.SetSingle(val); case uint val: - return this.SetSingle(val, v => (Number)v); + return this.SetSingle(val); case short val: - return this.SetSingle(val, v => (Number)v); + return this.SetSingle(val); case ushort val: - return this.SetSingle(val, v => (Number)v); + return this.SetSingle(val); case int[] array: - return this.SetArray(array, v => (Number)v); + return this.SetArray(array); case uint[] array: - return this.SetArray(array, v => (Number)v); + return this.SetArray(array); case short[] array: - return this.SetArray(array, v => (Number)v); + return this.SetArray(array); case ushort[] array: - return this.SetArray(array, v => (Number)v); + return this.SetArray(array); } return false; @@ -70,18 +68,54 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif public override IExifValue DeepClone() => new ExifNumberArray(this); - private bool SetSingle(T value, Func converter) + private bool SetSingle(Number value) + { + this.Value = new[] { value }; + return true; + } + + private bool SetArray(int[] values) + { + var numbers = new Number[values.Length]; + for (int i = 0; i < values.Length; i++) + { + numbers[i] = values[i]; + } + + this.Value = numbers; + return true; + } + + private bool SetArray(uint[] values) { - this.Value = new Number[] { converter(value) }; + var numbers = new Number[values.Length]; + for (int i = 0; i < values.Length; i++) + { + numbers[i] = values[i]; + } + + this.Value = numbers; + return true; + } + + private bool SetArray(short[] values) + { + var numbers = new Number[values.Length]; + for (int i = 0; i < values.Length; i++) + { + numbers[i] = values[i]; + } + + this.Value = numbers; return true; } - private bool SetArray(T[] values, Func converter) + private bool SetArray(ushort[] values) { var numbers = new Number[values.Length]; for (int i = 0; i < values.Length; i++) { - numbers[i] = converter(values[i]); + numbers[i] = values[i]; } this.Value = numbers; From fa68e1b8439b204f989ca27749ae0caee1edecf8 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 9 May 2021 19:10:56 +0200 Subject: [PATCH 229/275] Read and write Exif Profile --- .../Tiff/TiffDecoderMetadataCreator.cs | 10 ++- .../Tiff/TiffEncoderEntriesCollector.cs | 22 ++++- .../Metadata/Profiles/Exif/ExifParts.cs | 4 +- .../Formats/Tiff/TiffDecoderTests.cs | 6 +- .../Formats/Tiff/TiffMetadataTests.cs | 3 +- .../Profiles/Exif/ExifProfileTests.cs | 80 +++++++++++++++---- 6 files changed, 94 insertions(+), 31 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs index 7f97303ed..9a495ad31 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Icc; @@ -57,9 +56,14 @@ namespace SixLabors.ImageSharp.Formats.Tiff } } + if (coreMetadata.ExifProfile == null) + { + coreMetadata.ExifProfile = frame?.ExifProfile.DeepClone(); + } + if (coreMetadata.IptcProfile == null) { - if (TryGetIptc(frame.ExifProfile.Values, out var iptcBytes)) + if (TryGetIptc(frame.ExifProfile.Values, out byte[] iptcBytes)) { coreMetadata.IptcProfile = new IptcProfile(iptcBytes); } @@ -95,7 +99,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff // Some Encoders write the data type of IPTC as long. if (iptc.DataType == ExifDataType.Long) { - var iptcValues = (uint[])iptc.GetValue(); + uint[] iptcValues = (uint[])iptc.GetValue(); iptcBytes = new byte[iptcValues.Length * 4]; Buffer.BlockCopy(iptcValues, 0, iptcBytes, 0, iptcValues.Length * 4); if (iptcBytes[0] == 0x1c) diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs index c0ad474b2..4916a9804 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs @@ -66,12 +66,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff this.collector.Add(width); this.collector.Add(height); - this.collector.Add(software); this.ProcessResolution(image.Metadata, frameMetadata); - this.ProcessProfiles(image.Metadata, frameMetadata); this.ProcessMetadata(frameMetadata); + + if (!this.collector.Entries.Exists(t => t.Tag == ExifTag.Software)) + { + this.collector.Add(software); + } } private static bool IsPureMetadata(ExifTag tag) @@ -174,9 +177,20 @@ namespace SixLabors.ImageSharp.Formats.Tiff private void ProcessProfiles(ImageMetadata imageMetadata, TiffFrameMetadata tiffFrameMetadata) { - if (imageMetadata.ExifProfile != null) + if (imageMetadata.ExifProfile != null && imageMetadata.ExifProfile.Parts != ExifParts.None) { - // todo: implement processing exif profile + imageMetadata.SyncProfiles(); + foreach (IExifValue entry in imageMetadata.ExifProfile.Values) + { + if (!this.collector.Entries.Exists(t => t.Tag == entry.Tag) && entry.GetValue() != null) + { + ExifParts entryPart = ExifTags.GetPart(entry.Tag); + if (entryPart != ExifParts.None && imageMetadata.ExifProfile.Parts.HasFlag(entryPart)) + { + this.collector.AddOrReplace(entry.DeepClone()); + } + } + } } else { diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifParts.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifParts.cs index dc12f3819..0a9c879ce 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifParts.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifParts.cs @@ -24,12 +24,12 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// /// ExifTags /// - ExifTags = 4, + ExifTags = 2, /// /// GPSTags /// - GpsTags = 8, + GpsTags = 4, /// /// All diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index ae1de1734..bffb60302 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -42,9 +42,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff where TPixel : unmanaged, IPixel => Assert.Throws(() => provider.GetImage(TiffDecoder)); [Theory] - [InlineData(TestImages.Tiff.RgbUncompressed, 24, 256, 256, 300, 300, PixelResolutionUnit.PixelsPerInch)] - [InlineData(TestImages.Tiff.SmallRgbDeflate, 24, 32, 32, 96, 96, PixelResolutionUnit.PixelsPerInch)] - [InlineData(TestImages.Tiff.Calliphora_GrayscaleUncompressed, 8, 804, 1198, 96, 96, PixelResolutionUnit.PixelsPerInch)] + [InlineData(RgbUncompressed, 24, 256, 256, 300, 300, PixelResolutionUnit.PixelsPerInch)] + [InlineData(SmallRgbDeflate, 24, 32, 32, 96, 96, PixelResolutionUnit.PixelsPerInch)] + [InlineData(Calliphora_GrayscaleUncompressed, 8, 804, 1198, 96, 96, PixelResolutionUnit.PixelsPerInch)] public void Identify(string imagePath, int expectedPixelSize, int expectedWidth, int expectedHeight, double expectedHResolution, double expectedVResolution, PixelResolutionUnit expectedResolutionUnit) { var testFile = TestFile.Create(imagePath); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index 5763b0e8a..f501299fd 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -292,8 +292,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal(frameMeta.HorizontalResolution, frameMetaOut.HorizontalResolution); Assert.Equal(frameMeta.VerticalResolution, frameMetaOut.VerticalResolution); - Assert.Equal("ImageSharp", frameMetaOut.ExifProfile.GetValue(ExifTag.Software).Value); - if (preserveMetadata) { Assert.Equal(tiffMeta.XmpProfile, tiffMetaOut.XmpProfile); @@ -311,6 +309,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { Assert.Null(tiffMetaOut.XmpProfile); + Assert.Equal("ImageSharp", frameMetaOut.ExifProfile.GetValue(ExifTag.Software).Value); Assert.Null(frameMeta.ExifProfile.GetValue(ExifTag.Software)?.Value); Assert.Null(frameMeta.ExifProfile.GetValue(ExifTag.ImageDescription)?.Value); Assert.Null(frameMeta.ExifProfile.GetValue(ExifTag.Make)?.Value); diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs index 10f6ff9bf..1f4bbaea9 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; - +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; @@ -28,7 +28,12 @@ namespace SixLabors.ImageSharp.Tests /// /// Writes a png file. /// - Png + Png, + + /// + /// Writes a tiff file. + /// + Tiff, } private static readonly Dictionary TestProfileValues = new Dictionary @@ -69,7 +74,7 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void ConstructorEmpty() { - new ExifProfile((byte[])null); + new ExifProfile(null); new ExifProfile(new byte[] { }); } @@ -92,6 +97,7 @@ namespace SixLabors.ImageSharp.Tests [Theory] [InlineData(TestImageWriteFormat.Jpeg)] [InlineData(TestImageWriteFormat.Png)] + [InlineData(TestImageWriteFormat.Tiff)] public void WriteFraction(TestImageWriteFormat imageFormat) { using (var memStream = new MemoryStream()) @@ -135,6 +141,7 @@ namespace SixLabors.ImageSharp.Tests [Theory] [InlineData(TestImageWriteFormat.Jpeg)] [InlineData(TestImageWriteFormat.Png)] + [InlineData(TestImageWriteFormat.Tiff)] public void ReadWriteInfinity(TestImageWriteFormat imageFormat) { Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateRgba32Image(); @@ -161,9 +168,17 @@ namespace SixLabors.ImageSharp.Tests } [Theory] - [InlineData(TestImageWriteFormat.Jpeg)] - [InlineData(TestImageWriteFormat.Png)] - public void SetValue(TestImageWriteFormat imageFormat) + /* The original exif profile has 19 values, the written profile should be 3 less. + 1 x due to setting of null "ReferenceBlackWhite" value. + 2 x due to use of non-standard padding tag 0xEA1C listed in EXIF Tool. We can read those values but adhere + strictly to the 2.3.1 specification when writing. (TODO: Support 2.3.2) + https://exiftool.org/TagNames/EXIF.html */ + [InlineData(TestImageWriteFormat.Jpeg, 16)] + [InlineData(TestImageWriteFormat.Png, 16)] + /* Note: The tiff format has 24 expected profile values, because some tiff specific exif + values will be written in addition to the original profile. */ + [InlineData(TestImageWriteFormat.Tiff, 24)] + public void SetValue(TestImageWriteFormat imageFormat, int expectedProfileValueCount) { Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateRgba32Image(); image.Metadata.ExifProfile.SetValue(ExifTag.Software, "ImageSharp"); @@ -206,18 +221,12 @@ namespace SixLabors.ImageSharp.Tests // todo: duplicate tags Assert.Equal(2, image.Metadata.ExifProfile.Values.Count(v => (ushort)v.Tag == 59932)); - int profileCount = image.Metadata.ExifProfile.Values.Count; image = WriteAndRead(image, imageFormat); Assert.NotNull(image.Metadata.ExifProfile); Assert.Equal(0, image.Metadata.ExifProfile.Values.Count(v => (ushort)v.Tag == 59932)); - // Should be 3 less. - // 1 x due to setting of null "ReferenceBlackWhite" value. - // 2 x due to use of non-standard padding tag 0xEA1C listed in EXIF Tool. We can read those values but adhere - // strictly to the 2.3.1 specification when writing. (TODO: Support 2.3.2) - // https://exiftool.org/TagNames/EXIF.html - Assert.Equal(profileCount - 3, image.Metadata.ExifProfile.Values.Count); + Assert.Equal(expectedProfileValueCount, image.Metadata.ExifProfile.Values.Count); software = image.Metadata.ExifProfile.GetValue(ExifTag.Software); Assert.Equal("15", software.Value); @@ -233,20 +242,42 @@ namespace SixLabors.ImageSharp.Tests latitude = image.Metadata.ExifProfile.GetValue(ExifTag.GPSLatitude); Assert.Equal(expectedLatitude, latitude.Value); + } + [Theory] + [InlineData(TestImageWriteFormat.Jpeg)] + [InlineData(TestImageWriteFormat.Png)] + public void WriteOnlyExifTags_Works(TestImageWriteFormat imageFormat) + { + // Arrange + Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateRgba32Image(); image.Metadata.ExifProfile.Parts = ExifParts.ExifTags; + // Act image = WriteAndRead(image, imageFormat); + // Assert Assert.NotNull(image.Metadata.ExifProfile); - Assert.Equal(8, image.Metadata.ExifProfile.Values.Count); + Assert.Equal(7, image.Metadata.ExifProfile.Values.Count); + foreach (IExifValue exifProfileValue in image.Metadata.ExifProfile.Values) + { + Assert.True(ExifTags.GetPart(exifProfileValue.Tag) == ExifParts.ExifTags); + } + } + + [Fact] + public void RemoveEntry_Works() + { + // Arrange + Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateRgba32Image(); + int profileCount = image.Metadata.ExifProfile.Values.Count; + // Assert Assert.NotNull(image.Metadata.ExifProfile.GetValue(ExifTag.ColorSpace)); Assert.True(image.Metadata.ExifProfile.RemoveValue(ExifTag.ColorSpace)); Assert.False(image.Metadata.ExifProfile.RemoveValue(ExifTag.ColorSpace)); Assert.Null(image.Metadata.ExifProfile.GetValue(ExifTag.ColorSpace)); - - Assert.Equal(7, image.Metadata.ExifProfile.Values.Count); + Assert.Equal(profileCount - 1, image.Metadata.ExifProfile.Values.Count); } [Fact] @@ -382,6 +413,7 @@ namespace SixLabors.ImageSharp.Tests [Theory] [InlineData(TestImageWriteFormat.Jpeg)] [InlineData(TestImageWriteFormat.Png)] + [InlineData(TestImageWriteFormat.Tiff)] public void WritingImagePreservesExifProfile(TestImageWriteFormat imageFormat) { // Arrange @@ -456,8 +488,10 @@ namespace SixLabors.ImageSharp.Tests return WriteAndReadJpeg(image); case TestImageWriteFormat.Png: return WriteAndReadPng(image); + case TestImageWriteFormat.Tiff: + return WriteAndReadTiff(image); default: - throw new ArgumentException("Unexpected test image format, only Jpeg and Png are allowed"); + throw new ArgumentException("Unexpected test image format, only Jpeg, Png and Tiff are allowed"); } } @@ -485,6 +519,18 @@ namespace SixLabors.ImageSharp.Tests } } + private static Image WriteAndReadTiff(Image image) + { + using (var memStream = new MemoryStream()) + { + image.SaveAsTiff(memStream, new TiffEncoder()); + image.Dispose(); + + memStream.Position = 0; + return Image.Load(memStream, new TiffDecoder()); + } + } + private static void TestProfile(ExifProfile profile) { Assert.NotNull(profile); From 85a65ae6f92fd70fd66e92d1f1b6f35b6357af91 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 10 May 2021 12:19:49 +0200 Subject: [PATCH 230/275] Remove width and height from tiff frame metadata --- .../Formats/Tiff/TiffDecoderCore.cs | 41 ++++++- .../Formats/Tiff/TiffFrameMetadata.cs | 61 +--------- .../Formats/Tiff/TiffEncoderTests.cs | 13 ++- .../Formats/Tiff/TiffMetadataTests.cs | 107 +++++++++--------- 4 files changed, 101 insertions(+), 121 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index fe6778fc2..75cd06361 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -155,7 +155,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff ImageMetadata metadata = TiffDecoderMetadataCreator.Create(framesMetadata, this.ignoreMetadata, reader.ByteOrder); TiffFrameMetadata root = framesMetadata[0]; - return new ImageInfo(new PixelTypeInfo(root.BitsPerSample.BitsPerPixel()), (int)root.Width, (int)root.Height, metadata); + int width = GetImageWidth(root); + int height = GetImageHeight(root); + + return new ImageInfo(new PixelTypeInfo(root.BitsPerSample.BitsPerPixel()), width, height, metadata); } /// @@ -176,8 +179,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff this.VerifyAndParse(frameMetaData); - int width = (int)frameMetaData.Width; - int height = (int)frameMetaData.Height; + int width = GetImageWidth(frameMetaData); + int height = GetImageHeight(frameMetaData); var frame = new ImageFrame(this.Configuration, width, height, coreMetadata); int rowsPerStrip = (int)frameMetaData.RowsPerStrip; @@ -312,5 +315,37 @@ namespace SixLabors.ImageSharp.Formats.Tiff colorDecoder.Decode(stripBuffer.GetSpan(), pixels, 0, top, frame.Width, stripHeight); } } + + /// + /// Gets the width of the image frame. + /// + /// The image frame. + /// The image width. + private static int GetImageWidth(TiffFrameMetadata frame) + { + IExifValue width = frame.ExifProfile.GetValue(ExifTag.ImageWidth); + if (width == null) + { + TiffThrowHelper.ThrowImageFormatException("The TIFF image frame is missing the ImageWidth"); + } + + return (int)width.Value; + } + + /// + /// Gets the height of the image frame. + /// + /// The image frame. + /// The image height. + private static int GetImageHeight(TiffFrameMetadata frame) + { + IExifValue height = frame.ExifProfile.GetValue(ExifTag.ImageLength); + if (height == null) + { + TiffThrowHelper.ThrowImageFormatException("The TIFF image frame is missing the ImageLength"); + } + + return (int)height.Value; + } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs index 4386f4932..119a60d92 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using System.Collections.Generic; using System.Linq; using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Formats.Tiff.Constants; @@ -21,13 +20,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff private ExifProfile frameTags; - /// - /// Initializes a new instance of the class. - /// - public TiffFrameMetadata() - { - } - /// /// Gets the Tiff directory tags. /// @@ -47,40 +39,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// public TiffSubfileType? OldSubfileType => (TiffSubfileType?)this.ExifProfile.GetValue(ExifTag.OldSubfileType)?.Value; - /// - /// Gets the number of columns in the image, i.e., the number of pixels per row. - /// - public Number Width - { - get - { - IExifValue width = this.ExifProfile.GetValue(ExifTag.ImageWidth); - if (width == null) - { - TiffThrowHelper.ThrowImageFormatException("The TIFF image is missing the ImageWidth"); - } - - return width.Value; - } - } - - /// - /// Gets the number of rows of pixels in the image. - /// - public Number Height - { - get - { - IExifValue height = this.ExifProfile.GetValue(ExifTag.ImageLength); - if (height == null) - { - TiffThrowHelper.ThrowImageFormatException("The TIFF image is missing the ImageLength"); - } - - return height.Value; - } - } - /// /// Gets the number of bits per component. /// @@ -88,7 +46,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff { get { - var bits = this.ExifProfile.GetValue(ExifTag.BitsPerSample)?.Value; + ushort[] bits = this.ExifProfile.GetValue(ExifTag.BitsPerSample)?.Value; if (bits == null) { if (this.PhotometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero @@ -248,23 +206,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// public TiffSampleFormat[] SampleFormat => this.ExifProfile.GetValue(ExifTag.SampleFormat)?.Value?.Select(a => (TiffSampleFormat)a).ToArray(); - /// - /// Clears the pure metadata. - /// - public void ClearMetadata() - { - var tags = new List(); - foreach (IExifValue entry in this.ExifProfile.Values) - { - if (IsFormatTag((ExifTagValue)(ushort)entry.Tag)) - { - tags.Add(entry); - } - } - - this.ExifProfile = new ExifProfile(tags, this.ExifProfile.InvalidTags); - } - /// public IDeepCloneable DeepClone() => new TiffFrameMetadata() { ExifProfile = this.ExifProfile.DeepClone() }; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index c2edb9e1c..4242a7341 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -39,7 +39,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { // arrange var tiffEncoder = new TiffEncoder { Mode = mode }; - Image input = new Image(10, 10); + using Image input = new Image(10, 10); using var memStream = new MemoryStream(); // act @@ -62,7 +62,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { // arrange var tiffEncoder = new TiffEncoder { BitsPerPixel = bitsPerPixel }; - Image input = new Image(10, 10); + using Image input = new Image(10, 10); using var memStream = new MemoryStream(); // act @@ -101,7 +101,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { // arrange var tiffEncoder = new TiffEncoder { Mode = mode, Compression = compression }; - Image input = new Image(10, 10); + using Image input = new Image(10, 10); using var memStream = new MemoryStream(); // act @@ -331,7 +331,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { // arrange var tiffEncoder = new TiffEncoder() { Mode = mode, Compression = compression }; - Image input = provider.GetImage(); + using Image input = provider.GetImage(); using var memStream = new MemoryStream(); TiffFrameMetadata inputMeta = input.Frames.RootFrame.Metadata.GetTiffMetadata(); @@ -340,8 +340,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // assert memStream.Position = 0; - var output = Image.Load(Configuration, memStream); + using var output = Image.Load(Configuration, memStream); TiffFrameMetadata meta = output.Frames.RootFrame.Metadata.GetTiffMetadata(); + ImageFrame rootFrame = output.Frames.RootFrame; Assert.True(output.Height > (int)meta.RowsPerStrip); Assert.True(meta.StripOffsets.Length > 1); @@ -359,7 +360,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { // The difference must be less than one row. int stripBytes = (int)meta.StripByteCounts[i]; - int widthBytes = (meta.BitsPerPixel + 7) / 8 * (int)meta.Width; + int widthBytes = (meta.BitsPerPixel + 7) / 8 * rootFrame.Width; Assert.True((TiffConstants.DefaultStripSize - stripBytes) < widthBytes); } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index f501299fd..5de1ce6e8 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -182,41 +182,42 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal(10, image.Metadata.HorizontalResolution); Assert.Equal(10, image.Metadata.VerticalResolution); - TiffFrameMetadata frame = image.Frames.RootFrame.Metadata.GetTiffMetadata(); - Assert.Equal(30, frame.ExifProfile.Values.Count); - - Assert.Equal(32u, frame.Width); - Assert.Equal(32u, frame.Height); - Assert.Equal(TiffBitsPerSample.Bit4, frame.BitsPerSample); - Assert.Equal(TiffCompression.Lzw, frame.Compression); - Assert.Equal(TiffPhotometricInterpretation.PaletteColor, frame.PhotometricInterpretation); - Assert.Equal("This is Название", frame.ExifProfile.GetValue(ExifTag.ImageDescription).Value); - Assert.Equal("This is Изготовитель камеры", frame.ExifProfile.GetValue(ExifTag.Make).Value); - Assert.Equal("This is Модель камеры", frame.ExifProfile.GetValue(ExifTag.Model).Value); - Assert.Equal(new Number[] { 8u }, frame.StripOffsets, new NumberComparer()); - Assert.Equal(1, frame.SamplesPerPixel); - Assert.Equal(32u, frame.RowsPerStrip); - Assert.Equal(new Number[] { 297u }, frame.StripByteCounts, new NumberComparer()); - Assert.Equal(PixelResolutionUnit.PixelsPerInch, frame.ResolutionUnit); - Assert.Equal(10, frame.HorizontalResolution); - Assert.Equal(10, frame.VerticalResolution); - Assert.Equal(TiffPlanarConfiguration.Chunky, frame.PlanarConfiguration); - Assert.Equal("IrfanView", frame.ExifProfile.GetValue(ExifTag.Software).Value); - Assert.Null(frame.ExifProfile.GetValue(ExifTag.DateTime)?.Value); - Assert.Equal("This is author1;Author2", frame.ExifProfile.GetValue(ExifTag.Artist).Value); - Assert.Null(frame.ExifProfile.GetValue(ExifTag.HostComputer)?.Value); - Assert.Equal(48, frame.ColorMap.Length); - Assert.Equal(10537, frame.ColorMap[0]); - Assert.Equal(14392, frame.ColorMap[1]); - Assert.Equal(58596, frame.ColorMap[46]); - Assert.Equal(3855, frame.ColorMap[47]); - - Assert.Null(frame.ExtraSamples); - Assert.Equal(TiffPredictor.None, frame.Predictor); - Assert.Null(frame.SampleFormat); - Assert.Equal("This is Авторские права", frame.ExifProfile.GetValue(ExifTag.Copyright).Value); - Assert.Equal(4, frame.ExifProfile.GetValue(ExifTag.Rating).Value); - Assert.Equal(75, frame.ExifProfile.GetValue(ExifTag.RatingPercent).Value); + TiffFrameMetadata frameMetadata = image.Frames.RootFrame.Metadata.GetTiffMetadata(); + ImageFrame rootFrame = image.Frames.RootFrame; + Assert.Equal(30, frameMetadata.ExifProfile.Values.Count); + + Assert.Equal(32, rootFrame.Width); + Assert.Equal(32, rootFrame.Height); + Assert.Equal(TiffBitsPerSample.Bit4, frameMetadata.BitsPerSample); + Assert.Equal(TiffCompression.Lzw, frameMetadata.Compression); + Assert.Equal(TiffPhotometricInterpretation.PaletteColor, frameMetadata.PhotometricInterpretation); + Assert.Equal("This is Название", frameMetadata.ExifProfile.GetValue(ExifTag.ImageDescription).Value); + Assert.Equal("This is Изготовитель камеры", frameMetadata.ExifProfile.GetValue(ExifTag.Make).Value); + Assert.Equal("This is Модель камеры", frameMetadata.ExifProfile.GetValue(ExifTag.Model).Value); + Assert.Equal(new Number[] { 8u }, frameMetadata.StripOffsets, new NumberComparer()); + Assert.Equal(1, frameMetadata.SamplesPerPixel); + 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("IrfanView", frameMetadata.ExifProfile.GetValue(ExifTag.Software).Value); + Assert.Null(frameMetadata.ExifProfile.GetValue(ExifTag.DateTime)?.Value); + Assert.Equal("This is author1;Author2", frameMetadata.ExifProfile.GetValue(ExifTag.Artist).Value); + Assert.Null(frameMetadata.ExifProfile.GetValue(ExifTag.HostComputer)?.Value); + 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); + Assert.Equal("This is Авторские права", frameMetadata.ExifProfile.GetValue(ExifTag.Copyright).Value); + Assert.Equal(4, frameMetadata.ExifProfile.GetValue(ExifTag.Rating).Value); + Assert.Equal(75, frameMetadata.ExifProfile.GetValue(ExifTag.RatingPercent).Value); } } @@ -232,17 +233,17 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal(2, image.Frames.Count); - TiffFrameMetadata frame0 = image.Frames[0].Metadata.GetTiffMetadata(); - Assert.Equal(TiffNewSubfileType.FullImage, frame0.SubfileType); - Assert.Null(frame0.OldSubfileType); - Assert.Equal(255u, frame0.Width); - Assert.Equal(255u, frame0.Height); - - TiffFrameMetadata frame1 = image.Frames[1].Metadata.GetTiffMetadata(); - Assert.Equal(TiffNewSubfileType.Preview, frame1.SubfileType); - Assert.Null(frame1.OldSubfileType); - Assert.Equal(255u, frame1.Width); - Assert.Equal(255u, frame1.Height); + TiffFrameMetadata frame0MetaData = image.Frames[0].Metadata.GetTiffMetadata(); + Assert.Equal(TiffNewSubfileType.FullImage, frame0MetaData.SubfileType); + Assert.Null(frame0MetaData.OldSubfileType); + Assert.Equal(255, image.Frames[0].Width); + Assert.Equal(255, image.Frames[0].Height); + + TiffFrameMetadata frame1MetaData = image.Frames[1].Metadata.GetTiffMetadata(); + Assert.Equal(TiffNewSubfileType.Preview, frame1MetaData.SubfileType); + Assert.Null(frame1MetaData.OldSubfileType); + Assert.Equal(255, image.Frames[1].Width); + Assert.Equal(255, image.Frames[1].Height); } } @@ -258,6 +259,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff ImageMetadata coreMeta = image.Metadata; TiffMetadata tiffMeta = image.Metadata.GetTiffMetadata(); TiffFrameMetadata frameMeta = image.Frames.RootFrame.Metadata.GetTiffMetadata(); + ImageFrame frameRoot = image.Frames.RootFrame; // Save to Tiff var tiffEncoder = new TiffEncoder() { Mode = TiffEncodingMode.Rgb }; @@ -276,6 +278,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff ImageMetadata coreMetaOut = output.Metadata; TiffMetadata tiffMetaOut = output.Metadata.GetTiffMetadata(); TiffFrameMetadata frameMetaOut = output.Frames.RootFrame.Metadata.GetTiffMetadata(); + ImageFrame rootFrameOut = output.Frames.RootFrame; Assert.Equal(TiffBitsPerPixel.Bit4, tiffMeta.BitsPerPixel); Assert.Equal(TiffBitsPerPixel.Bit24, tiffMetaOut.BitsPerPixel); @@ -286,8 +289,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal(coreMeta.VerticalResolution, coreMetaOut.VerticalResolution); Assert.Equal(coreMeta.ResolutionUnits, coreMetaOut.ResolutionUnits); - Assert.Equal(frameMeta.Width, frameMetaOut.Width); - Assert.Equal(frameMeta.Height, frameMetaOut.Height); + Assert.Equal(frameRoot.Width, rootFrameOut.Width); + Assert.Equal(frameRoot.Height, rootFrameOut.Height); Assert.Equal(frameMeta.ResolutionUnit, frameMetaOut.ResolutionUnit); Assert.Equal(frameMeta.HorizontalResolution, frameMetaOut.HorizontalResolution); Assert.Equal(frameMeta.VerticalResolution, frameMetaOut.VerticalResolution); @@ -364,7 +367,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // Assert ms.Position = 0; using var output = Image.Load(this.configuration, ms); - TiffMetadata meta = output.Metadata.GetTiffMetadata(); + ImageFrame rootFrameOut = output.Frames.RootFrame; ImageMetadata coreMetaOut = output.Metadata; TiffMetadata tiffMetaOut = output.Metadata.GetTiffMetadata(); @@ -377,8 +380,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff //// Assert.Equal(tiffEncoder.Compression, tiffMetaOut.Compression); Assert.Equal(TiffBitsPerPixel.Bit24, tiffMetaOut.BitsPerPixel); - Assert.Equal((uint)w, frameMetaOut.Width); - Assert.Equal((uint)h, frameMetaOut.Height); + Assert.Equal(w, rootFrameOut.Width); + Assert.Equal(h, rootFrameOut.Height); Assert.Equal(frameMeta.ResolutionUnit, frameMetaOut.ResolutionUnit); Assert.Equal(frameMeta.HorizontalResolution, frameMetaOut.HorizontalResolution); Assert.Equal(frameMeta.VerticalResolution, frameMetaOut.VerticalResolution); @@ -427,7 +430,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff tiffMeta.XmpProfile = null; - frameMeta.ClearMetadata(); + frameMeta.ExifProfile = null; } } } From 875db673866d44f7ff791cabeeccec5001d92235 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 10 May 2021 13:12:45 +0200 Subject: [PATCH 231/275] Remove Tiff specific values from the EXIF profile --- .../Tiff/TiffDecoderMetadataCreator.cs | 17 ++++++++++++++-- .../Formats/Tiff/TiffFrameMetadata.cs | 20 ------------------- .../Profiles/Exif/ExifProfileTests.cs | 4 +--- 3 files changed, 16 insertions(+), 25 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs index 9a495ad31..300108341 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs @@ -56,9 +56,22 @@ namespace SixLabors.ImageSharp.Formats.Tiff } } - if (coreMetadata.ExifProfile == null) + if (coreMetadata.ExifProfile == null && frame.ExifProfile != null) { - coreMetadata.ExifProfile = frame?.ExifProfile.DeepClone(); + coreMetadata.ExifProfile = frame.ExifProfile.DeepClone(); + + // Remove Tiff specific tags from the profile. + coreMetadata.ExifProfile.RemoveValue(ExifTag.ImageWidth); + coreMetadata.ExifProfile.RemoveValue(ExifTag.ImageLength); + coreMetadata.ExifProfile.RemoveValue(ExifTag.ResolutionUnit); + coreMetadata.ExifProfile.RemoveValue(ExifTag.Predictor); + coreMetadata.ExifProfile.RemoveValue(ExifTag.PlanarConfiguration); + coreMetadata.ExifProfile.RemoveValue(ExifTag.PhotometricInterpretation); + coreMetadata.ExifProfile.RemoveValue(ExifTag.BitsPerSample); + coreMetadata.ExifProfile.RemoveValue(ExifTag.ColorMap); + coreMetadata.ExifProfile.RemoveValue(ExifTag.Compression); + coreMetadata.ExifProfile.RemoveValue(ExifTag.StripOffsets); + coreMetadata.ExifProfile.RemoveValue(ExifTag.StripByteCounts); } if (coreMetadata.IptcProfile == null) diff --git a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs index 119a60d92..e00fac151 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs @@ -208,25 +208,5 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// public IDeepCloneable DeepClone() => new TiffFrameMetadata() { ExifProfile = this.ExifProfile.DeepClone() }; - - private static bool IsFormatTag(ExifTagValue tag) - { - switch (tag) - { - case ExifTagValue.ImageWidth: - case ExifTagValue.ImageLength: - case ExifTagValue.ResolutionUnit: - case ExifTagValue.XResolution: - case ExifTagValue.YResolution: - case ExifTagValue.Predictor: - case ExifTagValue.PlanarConfiguration: - case ExifTagValue.PhotometricInterpretation: - case ExifTagValue.BitsPerSample: - case ExifTagValue.ColorMap: - return true; - } - - return false; - } } } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs index 1f4bbaea9..23d29b4eb 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs @@ -175,9 +175,7 @@ namespace SixLabors.ImageSharp.Tests https://exiftool.org/TagNames/EXIF.html */ [InlineData(TestImageWriteFormat.Jpeg, 16)] [InlineData(TestImageWriteFormat.Png, 16)] - /* Note: The tiff format has 24 expected profile values, because some tiff specific exif - values will be written in addition to the original profile. */ - [InlineData(TestImageWriteFormat.Tiff, 24)] + [InlineData(TestImageWriteFormat.Tiff, 16)] public void SetValue(TestImageWriteFormat imageFormat, int expectedProfileValueCount) { Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateRgba32Image(); From 587910f3e3724f9a604a562d8bb5d14c47f7af8d Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 10 May 2021 15:42:07 +0200 Subject: [PATCH 232/275] Remove PhotometricInterpretation and Compression from tiff metadata, because those are already present in the frame metadata --- .../Tiff/TiffDecoderMetadataCreator.cs | 2 - .../Formats/Tiff/TiffEncoderCore.cs | 10 +- src/ImageSharp/Formats/Tiff/TiffMetadata.cs | 16 +-- .../Formats/Tiff/TiffEncoderTests.cs | 13 +- .../Formats/Tiff/TiffMetadataTests.cs | 123 ++++++------------ 5 files changed, 55 insertions(+), 109 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs index 300108341..327e36633 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs @@ -40,8 +40,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff TiffMetadata tiffMetadata = coreMetadata.GetTiffMetadata(); tiffMetadata.ByteOrder = byteOrder; tiffMetadata.BitsPerPixel = GetBitsPerPixel(rootFrameMetadata); - tiffMetadata.Compression = rootFrameMetadata.Compression; - tiffMetadata.PhotometricInterpretation = rootFrameMetadata.PhotometricInterpretation; if (!ignoreMetadata) { diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 6654a6e4b..09fdffa24 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -112,8 +112,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff this.configuration = image.GetConfiguration(); ImageMetadata metadata = image.Metadata; TiffMetadata tiffMetadata = metadata.GetTiffMetadata(); + TiffFrameMetadata rootFrameMetaData = image.Frames.RootFrame.Metadata.GetTiffMetadata(); + TiffPhotometricInterpretation photometricInterpretation = rootFrameMetaData.PhotometricInterpretation; - this.SetMode(tiffMetadata); + this.SetMode(tiffMetadata, photometricInterpretation); this.SetBitsPerPixel(tiffMetadata); this.SetPhotometricInterpretation(); @@ -265,7 +267,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff return nextIfdMarker; } - private void SetMode(TiffMetadata tiffMetadata) + private void SetMode(TiffMetadata tiffMetadata, TiffPhotometricInterpretation photometricInterpretation) { // Make sure, that the fax compressions are only used together with the BiColor mode. if (this.CompressionType == TiffCompression.CcittGroup3Fax || this.CompressionType == TiffCompression.Ccitt1D) @@ -286,7 +288,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff if (this.Mode == TiffEncodingMode.Default && tiffMetadata.BitsPerPixel != null) { // Preserve input bits per pixel, if no encoding mode was specified. - this.SetModeWithBitsPerPixel(tiffMetadata.BitsPerPixel, tiffMetadata.PhotometricInterpretation); + this.SetModeWithBitsPerPixel(tiffMetadata.BitsPerPixel, photometricInterpretation); return; } @@ -294,7 +296,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff if (this.BitsPerPixel != null) { // The user has specified a bits per pixel, so use that to determine the encoding mode. - this.SetModeWithBitsPerPixel(this.BitsPerPixel, tiffMetadata.PhotometricInterpretation); + this.SetModeWithBitsPerPixel(this.BitsPerPixel, photometricInterpretation); } } diff --git a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs index 99777a0f3..5923e831a 100644 --- a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using SixLabors.ImageSharp.Formats.Tiff.Constants; namespace SixLabors.ImageSharp.Formats.Tiff { @@ -26,8 +25,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff { this.ByteOrder = other.ByteOrder; this.BitsPerPixel = other.BitsPerPixel; - this.Compression = other.Compression; - this.PhotometricInterpretation = other.PhotometricInterpretation; this.XmpProfile = other.XmpProfile != null ? new byte[other.XmpProfile.Length] : null; other.XmpProfile?.AsSpan().CopyTo(this.XmpProfile.AsSpan()); } @@ -38,21 +35,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff public ByteOrder ByteOrder { get; set; } /// - /// Gets or sets the number of bits per pixel. + /// Gets or sets the number of bits per pixel for the root frame. /// public TiffBitsPerPixel? BitsPerPixel { get; set; } - /// - /// Gets or sets the compression used to create the TIFF file. - /// Defaults to None. - /// - public TiffCompression Compression { get; set; } = TiffCompression.None; - - /// - /// Gets or sets the photometric interpretation which indicates how the pixels are to be interpreted, e.g. if the image is bicolor, RGB, color paletted etc. - /// - public TiffPhotometricInterpretation PhotometricInterpretation { get; set; } - /// /// Gets or sets the XMP profile. /// For internal use only. ImageSharp not support XMP profile. diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 4242a7341..dda695568 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -49,8 +49,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff memStream.Position = 0; using var output = Image.Load(Configuration, memStream); TiffMetadata meta = output.Metadata.GetTiffMetadata(); + TiffFrameMetadata frameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata(); Assert.Equal(expectedBitsPerPixel, meta.BitsPerPixel); - Assert.Equal(TiffCompression.None, meta.Compression); + Assert.Equal(TiffCompression.None, frameMetaData.Compression); } [Theory] @@ -72,8 +73,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff memStream.Position = 0; using var output = Image.Load(Configuration, memStream); TiffMetadata meta = output.Metadata.GetTiffMetadata(); + TiffFrameMetadata frameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata(); Assert.Equal(bitsPerPixel, meta.BitsPerPixel); - Assert.Equal(TiffCompression.None, meta.Compression); + Assert.Equal(TiffCompression.None, frameMetaData.Compression); } [Theory] @@ -111,8 +113,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff memStream.Position = 0; using var output = Image.Load(Configuration, memStream); TiffMetadata meta = output.Metadata.GetTiffMetadata(); + TiffFrameMetadata frameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata(); Assert.Equal(expectedBitsPerPixel, meta.BitsPerPixel); - Assert.Equal(expectedCompression, meta.Compression); + Assert.Equal(expectedCompression, frameMetaData.Compression); } [Theory] @@ -160,9 +163,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff memStream.Position = 0; using var output = Image.Load(Configuration, memStream); TiffMetadata meta = output.Metadata.GetTiffMetadata(); - + TiffFrameMetadata frameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata(); Assert.Equal(TiffBitsPerPixel.Bit1, meta.BitsPerPixel); - Assert.Equal(expectedCompression, meta.Compression); + Assert.Equal(expectedCompression, frameMetaData.Compression); } [Theory] diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index 5de1ce6e8..92412234b 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -39,24 +39,18 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff byte[] xmpData = { 1, 1, 1 }; var meta = new TiffMetadata { - Compression = TiffCompression.Deflate, BitsPerPixel = TiffBitsPerPixel.Bit8, ByteOrder = ByteOrder.BigEndian, - XmpProfile = xmpData, - PhotometricInterpretation = TiffPhotometricInterpretation.Rgb + XmpProfile = xmpData }; var clone = (TiffMetadata)meta.DeepClone(); - clone.Compression = TiffCompression.None; clone.BitsPerPixel = TiffBitsPerPixel.Bit24; clone.ByteOrder = ByteOrder.LittleEndian; - clone.PhotometricInterpretation = TiffPhotometricInterpretation.YCbCr; - Assert.False(meta.Compression == clone.Compression); Assert.False(meta.BitsPerPixel == clone.BitsPerPixel); Assert.False(meta.ByteOrder == clone.ByteOrder); - Assert.False(meta.PhotometricInterpretation == clone.PhotometricInterpretation); Assert.False(meta.XmpProfile.Equals(clone.XmpProfile)); Assert.True(meta.XmpProfile.SequenceEqual(clone.XmpProfile)); } @@ -78,44 +72,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal(expectedBitsPerPixel, tiffMetadata.BitsPerPixel); } - [Theory] - [InlineData(GrayscaleUncompressed, TiffCompression.None)] - [InlineData(RgbDeflate, TiffCompression.Deflate)] - [InlineData(SmallRgbLzw, TiffCompression.Lzw)] - [InlineData(Calliphora_Fax3Compressed, TiffCompression.CcittGroup3Fax)] - [InlineData(Calliphora_Fax4Compressed, TiffCompression.CcittGroup4Fax)] - [InlineData(Calliphora_HuffmanCompressed, TiffCompression.Ccitt1D)] - [InlineData(Calliphora_RgbPackbits, TiffCompression.PackBits)] - public void Identify_DetectsCorrectCompression(string imagePath, TiffCompression expectedCompression) - { - var testFile = TestFile.Create(imagePath); - using var stream = new MemoryStream(testFile.Bytes, false); - - IImageInfo imageInfo = Image.Identify(this.configuration, stream); - - Assert.NotNull(imageInfo); - TiffMetadata tiffMetadata = imageInfo.Metadata.GetTiffMetadata(); - Assert.NotNull(tiffMetadata); - Assert.Equal(expectedCompression, tiffMetadata.Compression); - } - - [Theory] - [InlineData(Calliphora_RgbUncompressed, TiffPhotometricInterpretation.Rgb)] - [InlineData(Calliphora_BiColorUncompressed, TiffPhotometricInterpretation.BlackIsZero)] - [InlineData(Calliphora_PaletteUncompressed, TiffPhotometricInterpretation.PaletteColor)] - public void Identify_DetectsCorrectPhotometricInterpretation(string imagePath, TiffPhotometricInterpretation expectedPhotometricInterpretation) - { - var testFile = TestFile.Create(imagePath); - using var stream = new MemoryStream(testFile.Bytes, false); - - IImageInfo imageInfo = Image.Identify(this.configuration, stream); - - Assert.NotNull(imageInfo); - TiffMetadata tiffMetadata = imageInfo.Metadata.GetTiffMetadata(); - Assert.NotNull(tiffMetadata); - Assert.Equal(expectedPhotometricInterpretation, tiffMetadata.PhotometricInterpretation); - } - [Theory] [InlineData(GrayscaleUncompressed, ByteOrder.BigEndian)] [InlineData(LittleEndianByteOrder, ByteOrder.LittleEndian)] @@ -256,10 +212,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // Load Tiff image using Image image = provider.GetImage(new TiffDecoder() { IgnoreMetadata = false }); - ImageMetadata coreMeta = image.Metadata; - TiffMetadata tiffMeta = image.Metadata.GetTiffMetadata(); - TiffFrameMetadata frameMeta = image.Frames.RootFrame.Metadata.GetTiffMetadata(); - ImageFrame frameRoot = image.Frames.RootFrame; + ImageMetadata inputMetaData = image.Metadata; + TiffMetadata tiffMetaInput = image.Metadata.GetTiffMetadata(); + TiffFrameMetadata frameMetaInput = image.Frames.RootFrame.Metadata.GetTiffMetadata(); + ImageFrame frameRootInput = image.Frames.RootFrame; + + Assert.Equal(TiffCompression.Lzw, frameMetaInput.Compression); + Assert.Equal(TiffBitsPerPixel.Bit4, tiffMetaInput.BitsPerPixel); // Save to Tiff var tiffEncoder = new TiffEncoder() { Mode = TiffEncodingMode.Rgb }; @@ -273,54 +232,52 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // Assert ms.Position = 0; - using var output = Image.Load(this.configuration, ms); + using var encodedImage = Image.Load(this.configuration, ms); - ImageMetadata coreMetaOut = output.Metadata; - TiffMetadata tiffMetaOut = output.Metadata.GetTiffMetadata(); - TiffFrameMetadata frameMetaOut = output.Frames.RootFrame.Metadata.GetTiffMetadata(); - ImageFrame rootFrameOut = output.Frames.RootFrame; + ImageMetadata encodedImageMetaData = encodedImage.Metadata; + TiffMetadata tiffMetaDataEncodedImage = encodedImage.Metadata.GetTiffMetadata(); + TiffFrameMetadata tiffMetaDataEncodedRootFrame = encodedImage.Frames.RootFrame.Metadata.GetTiffMetadata(); + ImageFrame rootFrameEncodedImage = encodedImage.Frames.RootFrame; - Assert.Equal(TiffBitsPerPixel.Bit4, tiffMeta.BitsPerPixel); - Assert.Equal(TiffBitsPerPixel.Bit24, tiffMetaOut.BitsPerPixel); - Assert.Equal(TiffCompression.Lzw, tiffMeta.Compression); - Assert.Equal(TiffCompression.None, tiffMetaOut.Compression); + Assert.Equal(TiffBitsPerPixel.Bit24, tiffMetaDataEncodedImage.BitsPerPixel); + Assert.Equal(TiffCompression.None, tiffMetaDataEncodedRootFrame.Compression); - Assert.Equal(coreMeta.HorizontalResolution, coreMetaOut.HorizontalResolution); - Assert.Equal(coreMeta.VerticalResolution, coreMetaOut.VerticalResolution); - Assert.Equal(coreMeta.ResolutionUnits, coreMetaOut.ResolutionUnits); + Assert.Equal(inputMetaData.HorizontalResolution, encodedImageMetaData.HorizontalResolution); + Assert.Equal(inputMetaData.VerticalResolution, encodedImageMetaData.VerticalResolution); + Assert.Equal(inputMetaData.ResolutionUnits, encodedImageMetaData.ResolutionUnits); - Assert.Equal(frameRoot.Width, rootFrameOut.Width); - Assert.Equal(frameRoot.Height, rootFrameOut.Height); - Assert.Equal(frameMeta.ResolutionUnit, frameMetaOut.ResolutionUnit); - Assert.Equal(frameMeta.HorizontalResolution, frameMetaOut.HorizontalResolution); - Assert.Equal(frameMeta.VerticalResolution, frameMetaOut.VerticalResolution); + Assert.Equal(frameRootInput.Width, rootFrameEncodedImage.Width); + Assert.Equal(frameRootInput.Height, rootFrameEncodedImage.Height); + Assert.Equal(frameMetaInput.ResolutionUnit, tiffMetaDataEncodedRootFrame.ResolutionUnit); + Assert.Equal(frameMetaInput.HorizontalResolution, tiffMetaDataEncodedRootFrame.HorizontalResolution); + Assert.Equal(frameMetaInput.VerticalResolution, tiffMetaDataEncodedRootFrame.VerticalResolution); if (preserveMetadata) { - Assert.Equal(tiffMeta.XmpProfile, tiffMetaOut.XmpProfile); + Assert.Equal(tiffMetaInput.XmpProfile, tiffMetaDataEncodedImage.XmpProfile); - Assert.Equal("IrfanView", frameMeta.ExifProfile.GetValue(ExifTag.Software).Value); - Assert.Equal("This is Название", frameMeta.ExifProfile.GetValue(ExifTag.ImageDescription).Value); - Assert.Equal("This is Изготовитель камеры", frameMeta.ExifProfile.GetValue(ExifTag.Make).Value); - Assert.Equal("This is Авторские права", frameMeta.ExifProfile.GetValue(ExifTag.Copyright).Value); + Assert.Equal("IrfanView", frameMetaInput.ExifProfile.GetValue(ExifTag.Software).Value); + Assert.Equal("This is Название", frameMetaInput.ExifProfile.GetValue(ExifTag.ImageDescription).Value); + Assert.Equal("This is Изготовитель камеры", frameMetaInput.ExifProfile.GetValue(ExifTag.Make).Value); + Assert.Equal("This is Авторские права", frameMetaInput.ExifProfile.GetValue(ExifTag.Copyright).Value); - Assert.Equal(frameMeta.ExifProfile.GetValue(ExifTag.ImageDescription).Value, frameMetaOut.ExifProfile.GetValue(ExifTag.ImageDescription).Value); - Assert.Equal(frameMeta.ExifProfile.GetValue(ExifTag.Make).Value, frameMetaOut.ExifProfile.GetValue(ExifTag.Make).Value); - Assert.Equal(frameMeta.ExifProfile.GetValue(ExifTag.Copyright).Value, frameMetaOut.ExifProfile.GetValue(ExifTag.Copyright).Value); + Assert.Equal(frameMetaInput.ExifProfile.GetValue(ExifTag.ImageDescription).Value, tiffMetaDataEncodedRootFrame.ExifProfile.GetValue(ExifTag.ImageDescription).Value); + Assert.Equal(frameMetaInput.ExifProfile.GetValue(ExifTag.Make).Value, tiffMetaDataEncodedRootFrame.ExifProfile.GetValue(ExifTag.Make).Value); + Assert.Equal(frameMetaInput.ExifProfile.GetValue(ExifTag.Copyright).Value, tiffMetaDataEncodedRootFrame.ExifProfile.GetValue(ExifTag.Copyright).Value); } else { - Assert.Null(tiffMetaOut.XmpProfile); + Assert.Null(tiffMetaDataEncodedImage.XmpProfile); - Assert.Equal("ImageSharp", frameMetaOut.ExifProfile.GetValue(ExifTag.Software).Value); - Assert.Null(frameMeta.ExifProfile.GetValue(ExifTag.Software)?.Value); - Assert.Null(frameMeta.ExifProfile.GetValue(ExifTag.ImageDescription)?.Value); - Assert.Null(frameMeta.ExifProfile.GetValue(ExifTag.Make)?.Value); - Assert.Null(frameMeta.ExifProfile.GetValue(ExifTag.Copyright)?.Value); + Assert.Equal("ImageSharp", tiffMetaDataEncodedRootFrame.ExifProfile.GetValue(ExifTag.Software).Value); + Assert.Null(frameMetaInput.ExifProfile.GetValue(ExifTag.Software)?.Value); + Assert.Null(frameMetaInput.ExifProfile.GetValue(ExifTag.ImageDescription)?.Value); + Assert.Null(frameMetaInput.ExifProfile.GetValue(ExifTag.Make)?.Value); + Assert.Null(frameMetaInput.ExifProfile.GetValue(ExifTag.Copyright)?.Value); - Assert.Null(frameMetaOut.ExifProfile.GetValue(ExifTag.ImageDescription)?.Value); - Assert.Null(frameMetaOut.ExifProfile.GetValue(ExifTag.Make)?.Value); - Assert.Null(frameMetaOut.ExifProfile.GetValue(ExifTag.Copyright)?.Value); + Assert.Null(tiffMetaDataEncodedRootFrame.ExifProfile.GetValue(ExifTag.ImageDescription)?.Value); + Assert.Null(tiffMetaDataEncodedRootFrame.ExifProfile.GetValue(ExifTag.Make)?.Value); + Assert.Null(tiffMetaDataEncodedRootFrame.ExifProfile.GetValue(ExifTag.Copyright)?.Value); } } From 275a6cc27d30be50e576e540f0720eb38df10094 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 10 May 2021 19:26:56 +0200 Subject: [PATCH 233/275] Remove Exif profile from the image metadata: each frame will have its own ExifProfile --- .../Formats/Tiff/TiffDecoderMetadataCreator.cs | 18 ------------------ .../Tiff/TiffEncoderEntriesCollector.cs | 8 ++++---- .../Metadata/Profiles/Exif/ExifProfileTests.cs | 13 +------------ 3 files changed, 5 insertions(+), 34 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs index 327e36633..aa44b0b03 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs @@ -54,24 +54,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff } } - if (coreMetadata.ExifProfile == null && frame.ExifProfile != null) - { - coreMetadata.ExifProfile = frame.ExifProfile.DeepClone(); - - // Remove Tiff specific tags from the profile. - coreMetadata.ExifProfile.RemoveValue(ExifTag.ImageWidth); - coreMetadata.ExifProfile.RemoveValue(ExifTag.ImageLength); - coreMetadata.ExifProfile.RemoveValue(ExifTag.ResolutionUnit); - coreMetadata.ExifProfile.RemoveValue(ExifTag.Predictor); - coreMetadata.ExifProfile.RemoveValue(ExifTag.PlanarConfiguration); - coreMetadata.ExifProfile.RemoveValue(ExifTag.PhotometricInterpretation); - coreMetadata.ExifProfile.RemoveValue(ExifTag.BitsPerSample); - coreMetadata.ExifProfile.RemoveValue(ExifTag.ColorMap); - coreMetadata.ExifProfile.RemoveValue(ExifTag.Compression); - coreMetadata.ExifProfile.RemoveValue(ExifTag.StripOffsets); - coreMetadata.ExifProfile.RemoveValue(ExifTag.StripByteCounts); - } - if (coreMetadata.IptcProfile == null) { if (TryGetIptc(frame.ExifProfile.Values, out byte[] iptcBytes)) diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs index 4916a9804..f20395221 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs @@ -177,15 +177,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff private void ProcessProfiles(ImageMetadata imageMetadata, TiffFrameMetadata tiffFrameMetadata) { - if (imageMetadata.ExifProfile != null && imageMetadata.ExifProfile.Parts != ExifParts.None) + ExifProfile exifProfile = tiffFrameMetadata.ExifProfile; + if (exifProfile != null && exifProfile.Parts != ExifParts.None) { - imageMetadata.SyncProfiles(); - foreach (IExifValue entry in imageMetadata.ExifProfile.Values) + foreach (IExifValue entry in exifProfile.Values) { if (!this.collector.Entries.Exists(t => t.Tag == entry.Tag) && entry.GetValue() != null) { ExifParts entryPart = ExifTags.GetPart(entry.Tag); - if (entryPart != ExifParts.None && imageMetadata.ExifProfile.Parts.HasFlag(entryPart)) + if (entryPart != ExifParts.None && exifProfile.Parts.HasFlag(entryPart)) { this.collector.AddOrReplace(entry.DeepClone()); } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs index 23d29b4eb..208222a85 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs @@ -28,12 +28,7 @@ namespace SixLabors.ImageSharp.Tests /// /// Writes a png file. /// - Png, - - /// - /// Writes a tiff file. - /// - Tiff, + Png } private static readonly Dictionary TestProfileValues = new Dictionary @@ -97,7 +92,6 @@ namespace SixLabors.ImageSharp.Tests [Theory] [InlineData(TestImageWriteFormat.Jpeg)] [InlineData(TestImageWriteFormat.Png)] - [InlineData(TestImageWriteFormat.Tiff)] public void WriteFraction(TestImageWriteFormat imageFormat) { using (var memStream = new MemoryStream()) @@ -141,7 +135,6 @@ namespace SixLabors.ImageSharp.Tests [Theory] [InlineData(TestImageWriteFormat.Jpeg)] [InlineData(TestImageWriteFormat.Png)] - [InlineData(TestImageWriteFormat.Tiff)] public void ReadWriteInfinity(TestImageWriteFormat imageFormat) { Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateRgba32Image(); @@ -175,7 +168,6 @@ namespace SixLabors.ImageSharp.Tests https://exiftool.org/TagNames/EXIF.html */ [InlineData(TestImageWriteFormat.Jpeg, 16)] [InlineData(TestImageWriteFormat.Png, 16)] - [InlineData(TestImageWriteFormat.Tiff, 16)] public void SetValue(TestImageWriteFormat imageFormat, int expectedProfileValueCount) { Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateRgba32Image(); @@ -411,7 +403,6 @@ namespace SixLabors.ImageSharp.Tests [Theory] [InlineData(TestImageWriteFormat.Jpeg)] [InlineData(TestImageWriteFormat.Png)] - [InlineData(TestImageWriteFormat.Tiff)] public void WritingImagePreservesExifProfile(TestImageWriteFormat imageFormat) { // Arrange @@ -486,8 +477,6 @@ namespace SixLabors.ImageSharp.Tests return WriteAndReadJpeg(image); case TestImageWriteFormat.Png: return WriteAndReadPng(image); - case TestImageWriteFormat.Tiff: - return WriteAndReadTiff(image); default: throw new ArgumentException("Unexpected test image format, only Jpeg, Png and Tiff are allowed"); } From 411c7d6520d8ee01e4a7fe4420a0159c96656a4a Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 12 May 2021 20:27:45 +0200 Subject: [PATCH 234/275] Add setters for TiffFrameMetaData properties, initialize properties from frame ExifProfile --- .../Formats/Tiff/Constants/TiffCompression.cs | 5 + .../Tiff/TiffBitsPerSampleExtensions.cs | 2 - .../Formats/Tiff/TiffDecoderCore.cs | 7 +- .../Tiff/TiffDecoderMetadataCreator.cs | 2 +- .../Formats/Tiff/TiffDecoderOptionsParser.cs | 18 +- .../Formats/Tiff/TiffEncoderCore.cs | 51 ++-- .../Formats/Tiff/TiffFrameMetadata.cs | 242 ++++++++-------- .../Formats/Tiff/TiffEncoderTests.cs | 15 +- .../Formats/Tiff/TiffMetadataTests.cs | 268 ++++++------------ .../Profiles/Exif/ExifProfileTests.cs | 14 +- 10 files changed, 264 insertions(+), 360 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs index b40647a93..031494fc5 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs @@ -8,6 +8,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Constants /// public enum TiffCompression : ushort { + /// + /// A invalid compression value. + /// + Invalid = 0, + /// /// No compression. /// diff --git a/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs b/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs index a0c7eb021..33e13d08e 100644 --- a/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs +++ b/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Formats.Tiff.Constants; namespace SixLabors.ImageSharp.Formats.Tiff @@ -28,7 +27,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff return TiffConstants.BitsPerSampleRgb8Bit; default: - TiffThrowHelper.ThrowNotSupported("The bits per pixels are not supported"); return Array.Empty(); } } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 75cd06361..d67ffc069 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -148,7 +148,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff var framesMetadata = new List(); foreach (ExifProfile ifd in directories) { - var meta = new TiffFrameMetadata() { ExifProfile = ifd }; + var meta = new TiffFrameMetadata(ifd); + meta.Initialize(ifd); framesMetadata.Add(meta); } @@ -158,7 +159,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff int width = GetImageWidth(root); int height = GetImageHeight(root); - return new ImageInfo(new PixelTypeInfo(root.BitsPerSample.BitsPerPixel()), width, height, metadata); + return new ImageInfo(new PixelTypeInfo(root.BitsPerPixel), width, height, metadata); } /// @@ -175,7 +176,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff { var coreMetadata = new ImageFrameMetadata(); frameMetaData = coreMetadata.GetTiffMetadata(); - frameMetaData.ExifProfile = tags; + frameMetaData.Initialize(tags); this.VerifyAndParse(frameMetaData); diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs index aa44b0b03..5b67c363a 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs @@ -126,6 +126,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff } private static TiffBitsPerPixel GetBitsPerPixel(TiffFrameMetadata firstFrameMetaData) - => (TiffBitsPerPixel)firstFrameMetaData.BitsPerSample.BitsPerPixel(); + => (TiffBitsPerPixel)firstFrameMetaData.BitsPerPixel; } } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs index aaf4502cd..b9f30a3ef 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs @@ -20,6 +20,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The IFD entries container to read the image format information for. public static void VerifyAndParse(this TiffDecoderCore options, TiffFrameMetadata entries) { + if (entries.TileOffsets != null) + { + TiffThrowHelper.ThrowNotSupported("Tiled images are not supported."); + } + if (entries.ExtraSamples != null) { TiffThrowHelper.ThrowNotSupported("ExtraSamples is not supported."); @@ -30,11 +35,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff TiffThrowHelper.ThrowNotSupported("The lower-order bits of the byte FillOrder is not supported."); } - if (entries.ExifProfile.GetValue(ExifTag.TileOffsets) != null) - { - TiffThrowHelper.ThrowNotSupported("Tiled images are not supported."); - } - if (entries.Predictor == TiffPredictor.FloatingPoint) { TiffThrowHelper.ThrowNotSupported("TIFF images with FloatingPoint horizontal predictor are not supported."); @@ -51,16 +51,18 @@ namespace SixLabors.ImageSharp.Formats.Tiff } } - if (entries.ExifProfile.GetValue(ExifTag.StripRowCounts) != null) + if (entries.StripRowCounts != null) { TiffThrowHelper.ThrowNotSupported("Variable-sized strips are not supported."); } + entries.VerifyRequiredFieldsArePresent(); + options.PlanarConfiguration = entries.PlanarConfiguration; options.Predictor = entries.Predictor; options.PhotometricInterpretation = entries.PhotometricInterpretation; - options.BitsPerSample = entries.BitsPerSample; - options.BitsPerPixel = entries.BitsPerSample.BitsPerPixel(); + options.BitsPerSample = entries.BitsPerSample.GetValueOrDefault(); + options.BitsPerPixel = entries.BitsPerSample.GetValueOrDefault().BitsPerPixel(); ParseColorType(options, entries); ParseCompression(options, entries); diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 09fdffa24..24fd46526 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -66,7 +66,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff this.quantizer = options.Quantizer ?? KnownQuantizers.Octree; this.BitsPerPixel = options.BitsPerPixel; this.HorizontalPredictor = options.HorizontalPredictor; - this.CompressionType = options.Compression; + this.CompressionType = options.Compression != TiffCompression.Invalid ? options.Compression : TiffCompression.None; this.compressionLevel = options.CompressionLevel; } @@ -113,7 +113,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff ImageMetadata metadata = image.Metadata; TiffMetadata tiffMetadata = metadata.GetTiffMetadata(); TiffFrameMetadata rootFrameMetaData = image.Frames.RootFrame.Metadata.GetTiffMetadata(); - TiffPhotometricInterpretation photometricInterpretation = rootFrameMetaData.PhotometricInterpretation; + TiffPhotometricInterpretation photometricInterpretation = this.Mode == TiffEncodingMode.ColorPalette + ? TiffPhotometricInterpretation.PaletteColor : rootFrameMetaData.PhotometricInterpretation; this.SetMode(tiffMetadata, photometricInterpretation); this.SetBitsPerPixel(tiffMetadata); @@ -159,31 +160,27 @@ namespace SixLabors.ImageSharp.Formats.Tiff // Write the image bytes to the steam. uint imageDataStart = (uint)writer.Position; - TiffBitsPerPixel? tiffBitsPerPixel = this.BitsPerPixel; - if (tiffBitsPerPixel != null) - { - using TiffBaseCompressor compressor = TiffCompressorFactory.Create( - this.CompressionType, - writer.BaseStream, - this.memoryAllocator, - image.Width, - (int)tiffBitsPerPixel, - this.compressionLevel, - this.HorizontalPredictor == TiffPredictor.Horizontal ? this.HorizontalPredictor : TiffPredictor.None); - - using TiffBaseColorWriter colorWriter = TiffColorWriterFactory.Create( - this.Mode, - image.Frames.RootFrame, - this.quantizer, - this.memoryAllocator, - this.configuration, - entriesCollector, - (int)tiffBitsPerPixel); - - int rowsPerStrip = this.CalcRowsPerStrip(image.Frames.RootFrame.Height, colorWriter.BytesPerRow); - - colorWriter.Write(compressor, rowsPerStrip); - } + using TiffBaseCompressor compressor = TiffCompressorFactory.Create( + this.CompressionType, + writer.BaseStream, + this.memoryAllocator, + image.Width, + (int)this.BitsPerPixel, + this.compressionLevel, + this.HorizontalPredictor == TiffPredictor.Horizontal ? this.HorizontalPredictor : TiffPredictor.None); + + using TiffBaseColorWriter colorWriter = TiffColorWriterFactory.Create( + this.Mode, + image.Frames.RootFrame, + this.quantizer, + this.memoryAllocator, + this.configuration, + entriesCollector, + (int)this.BitsPerPixel); + + int rowsPerStrip = this.CalcRowsPerStrip(image.Frames.RootFrame.Height, colorWriter.BytesPerRow); + + colorWriter.Write(compressor, rowsPerStrip); entriesCollector.ProcessImageFormat(this); entriesCollector.ProcessGeneral(image); diff --git a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs index e00fac151..d98b3ac94 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs @@ -18,195 +18,201 @@ namespace SixLabors.ImageSharp.Formats.Tiff private const TiffPredictor DefaultPredictor = TiffPredictor.None; - private ExifProfile frameTags; - /// - /// Gets the Tiff directory tags. + /// Initializes a new instance of the class. /// - public ExifProfile ExifProfile - { - get => this.frameTags ??= new ExifProfile(); - internal set => this.frameTags = value; - } + public TiffFrameMetadata() => this.Initialize(new ExifProfile()); /// - /// Gets a general indication of the kind of data contained in this subfile. + /// Initializes a new instance of the class. /// - public TiffNewSubfileType SubfileType => (TiffNewSubfileType?)this.ExifProfile.GetValue(ExifTag.SubfileType)?.Value ?? TiffNewSubfileType.FullImage; + /// The Tiff frame directory tags. + public TiffFrameMetadata(ExifProfile frameTags) => this.Initialize(frameTags); /// - /// Gets a general indication of the kind of data contained in this subfile. + /// Initializes a new instance of the class with a given ExifProfile. /// - public TiffSubfileType? OldSubfileType => (TiffSubfileType?)this.ExifProfile.GetValue(ExifTag.OldSubfileType)?.Value; + /// The Tiff frame directory tags. + public void Initialize(ExifProfile frameTags) + { + this.ExifProfile = frameTags; + + this.FillOrder = (TiffFillOrder?)this.ExifProfile.GetValue(ExifTag.FillOrder)?.Value ?? TiffFillOrder.MostSignificantBitFirst; + this.Compression = this.ExifProfile.GetValue(ExifTag.Compression) != null ? (TiffCompression)this.ExifProfile.GetValue(ExifTag.Compression).Value : TiffCompression.None; + this.SubfileType = (TiffNewSubfileType?)this.ExifProfile.GetValue(ExifTag.SubfileType)?.Value ?? TiffNewSubfileType.FullImage; + this.OldSubfileType = (TiffSubfileType?)this.ExifProfile.GetValue(ExifTag.OldSubfileType)?.Value; + this.HorizontalResolution = this.ExifProfile.GetValue(ExifTag.XResolution)?.Value.ToDouble(); + this.VerticalResolution = this.ExifProfile.GetValue(ExifTag.YResolution)?.Value.ToDouble(); + this.PlanarConfiguration = (TiffPlanarConfiguration?)this.ExifProfile.GetValue(ExifTag.PlanarConfiguration)?.Value ?? DefaultPlanarConfiguration; + this.ResolutionUnit = UnitConverter.ExifProfileToResolutionUnit(this.ExifProfile); + this.ColorMap = this.ExifProfile.GetValue(ExifTag.ColorMap)?.Value; + this.ExtraSamples = this.ExifProfile.GetValue(ExifTag.ExtraSamples)?.Value; + this.Predictor = (TiffPredictor?)this.ExifProfile.GetValue(ExifTag.Predictor)?.Value ?? DefaultPredictor; + this.SampleFormat = this.ExifProfile.GetValue(ExifTag.SampleFormat)?.Value?.Select(a => (TiffSampleFormat)a).ToArray(); + this.SamplesPerPixel = this.ExifProfile.GetValue(ExifTag.SamplesPerPixel)?.Value; + this.StripRowCounts = this.ExifProfile.GetValue(ExifTag.StripRowCounts)?.Value; + this.RowsPerStrip = this.ExifProfile.GetValue(ExifTag.RowsPerStrip) != null ? this.ExifProfile.GetValue(ExifTag.RowsPerStrip).Value : TiffConstants.RowsPerStripInfinity; + this.TileOffsets = this.ExifProfile.GetValue(ExifTag.TileOffsets)?.Value; + + this.PhotometricInterpretation = this.ExifProfile.GetValue(ExifTag.PhotometricInterpretation) != null ? + (TiffPhotometricInterpretation)this.ExifProfile.GetValue(ExifTag.PhotometricInterpretation).Value : TiffPhotometricInterpretation.WhiteIsZero; + + // Required Fields for decoding the image. + this.StripOffsets = this.ExifProfile.GetValue(ExifTag.StripOffsets)?.Value; + this.StripByteCounts = this.ExifProfile.GetValue(ExifTag.StripByteCounts)?.Value; + + ushort[] bits = this.ExifProfile.GetValue(ExifTag.BitsPerSample)?.Value; + if (bits == null) + { + if (this.PhotometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero || this.PhotometricInterpretation == TiffPhotometricInterpretation.BlackIsZero) + { + this.BitsPerSample = TiffBitsPerSample.Bit1; + } + + this.BitsPerSample = null; + } + else + { + this.BitsPerSample = bits.GetBitsPerSample(); + } + + this.BitsPerPixel = this.BitsPerSample.GetValueOrDefault().BitsPerPixel(); + } /// - /// Gets the number of bits per component. + /// Verifies that the required fields for decoding an image are present. + /// If not, a ImageFormatException will be thrown. /// - public TiffBitsPerSample BitsPerSample + public void VerifyRequiredFieldsArePresent() { - get + if (this.StripOffsets == null) { - ushort[] bits = this.ExifProfile.GetValue(ExifTag.BitsPerSample)?.Value; - if (bits == null) - { - if (this.PhotometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero - || this.PhotometricInterpretation == TiffPhotometricInterpretation.BlackIsZero) - { - return TiffBitsPerSample.Bit1; - } + TiffThrowHelper.ThrowImageFormatException("StripOffsets are missing and are required for decoding the TIFF image!"); + } - TiffThrowHelper.ThrowNotSupported("The TIFF BitsPerSample entry is missing which is required to decode the image."); - } + if (this.StripByteCounts == null) + { + TiffThrowHelper.ThrowImageFormatException("StripByteCounts are missing and are required for decoding the TIFF image!"); + } - return bits.GetBitsPerSample(); + if (this.BitsPerSample == null) + { + TiffThrowHelper.ThrowNotSupported("The TIFF BitsPerSample entry is missing which is required to decode the image!"); } } /// - /// Gets the bits per pixel. + /// Gets the Tiff directory tags. /// - public int BitsPerPixel => this.BitsPerSample.BitsPerPixel(); + public ExifProfile ExifProfile { get; internal set; } /// - /// Gets the compression scheme used on the image data. + /// Gets or sets a general indication of the kind of data contained in this subfile. /// - /// The compression scheme used on the image data. - public TiffCompression Compression - { - get - { - IExifValue compression = this.ExifProfile.GetValue(ExifTag.Compression); - if (compression == null) - { - return TiffCompression.None; - } + public TiffNewSubfileType? SubfileType { get; set; } - return (TiffCompression)compression.Value; - } - } + /// + /// Gets or sets a general indication of the kind of data contained in this subfile. + /// + public TiffSubfileType? OldSubfileType { get; set; } /// - /// Gets the color space of the image data. + /// Gets or sets the number of bits per component. /// - public TiffPhotometricInterpretation PhotometricInterpretation - { - get - { - IExifValue photometricInterpretation = this.ExifProfile.GetValue(ExifTag.PhotometricInterpretation); - if (photometricInterpretation == null) - { - return TiffPhotometricInterpretation.WhiteIsZero; - } + public TiffBitsPerSample? BitsPerSample { get; set; } - return (TiffPhotometricInterpretation)photometricInterpretation.Value; - } - } + /// + /// Gets or sets the bits per pixel. + /// + public int BitsPerPixel { get; set; } /// - /// Gets the logical order of bits within a byte. + /// Gets or sets the compression scheme used on the image data. /// - internal TiffFillOrder FillOrder => (TiffFillOrder?)this.ExifProfile.GetValue(ExifTag.FillOrder)?.Value ?? TiffFillOrder.MostSignificantBitFirst; + /// The compression scheme used on the image data. + public TiffCompression Compression { get; set; } /// - /// Gets for each strip, the byte offset of that strip. + /// Gets or sets the color space of the image data. /// - public Number[] StripOffsets - { - get - { - IExifValue stripOffsets = this.ExifProfile.GetValue(ExifTag.StripOffsets); - if (stripOffsets == null) - { - TiffThrowHelper.ThrowImageFormatException("StripOffsets are missing"); - } + public TiffPhotometricInterpretation PhotometricInterpretation { get; set; } - return stripOffsets.Value; - } - } + /// + /// Gets or sets the logical order of bits within a byte. + /// + internal TiffFillOrder FillOrder { get; set; } /// - /// Gets the number of components per pixel. + /// Gets or sets for each strip, the byte offset of that strip. /// - public ushort SamplesPerPixel => this.ExifProfile.GetValue(ExifTag.SamplesPerPixel).Value; + public Number[] StripOffsets { get; set; } /// - /// Gets the number of rows per strip. + /// Gets or sets the strip row counts. /// - public Number RowsPerStrip - { - get - { - IExifValue rowsPerStrip = this.ExifProfile.GetValue(ExifTag.RowsPerStrip); - if (rowsPerStrip == null) - { - return TiffConstants.RowsPerStripInfinity; - } + public uint[] StripRowCounts { get; set; } - return rowsPerStrip.Value; - } - } + /// + /// Gets or sets the number of components per pixel. + /// + public ushort? SamplesPerPixel { get; set; } /// - /// Gets for each strip, the number of bytes in the strip after compression. + /// Gets or sets the number of rows per strip. /// - public Number[] StripByteCounts - { - get - { - IExifValue stripByteCounts = this.ExifProfile.GetValue(ExifTag.StripByteCounts); - if (stripByteCounts == null) - { - TiffThrowHelper.ThrowImageFormatException("StripByteCounts are missing"); - } + public Number RowsPerStrip { get; set; } - return stripByteCounts.Value; - } - } + /// + /// 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 the resolution of the image in x- direction. + /// Gets or sets the resolution of the image in y-direction. /// - /// The density of the image in x- direction. - public double? HorizontalResolution => this.ExifProfile.GetValue(ExifTag.XResolution)?.Value.ToDouble(); + public double? VerticalResolution { get; set; } /// - /// Gets the resolution of the image in y- direction. + /// Gets or sets how the components of each pixel are stored. /// - /// The density of the image in y- direction. - public double? VerticalResolution => this.ExifProfile.GetValue(ExifTag.YResolution)?.Value.ToDouble(); + public TiffPlanarConfiguration PlanarConfiguration { get; set; } /// - /// Gets how the components of each pixel are stored. + /// Gets or sets the unit of measurement for XResolution and YResolution. /// - public TiffPlanarConfiguration PlanarConfiguration => (TiffPlanarConfiguration?)this.ExifProfile.GetValue(ExifTag.PlanarConfiguration)?.Value ?? DefaultPlanarConfiguration; + public PixelResolutionUnit ResolutionUnit { get; set; } /// - /// Gets the unit of measurement for XResolution and YResolution. + /// Gets or sets a color map for palette color images. /// - public PixelResolutionUnit ResolutionUnit => UnitConverter.ExifProfileToResolutionUnit(this.ExifProfile); + public ushort[] ColorMap { get; set; } /// - /// Gets a color map for palette color images. + /// Gets or sets the description of extra components. /// - public ushort[] ColorMap => this.ExifProfile.GetValue(ExifTag.ColorMap)?.Value; + public ushort[] ExtraSamples { get; set; } /// - /// Gets the description of extra components. + /// Gets or sets the tile offsets. /// - public ushort[] ExtraSamples => this.ExifProfile.GetValue(ExifTag.ExtraSamples)?.Value; + public uint[] TileOffsets { get; set; } /// - /// Gets a mathematical operator that is applied to the image data before an encoding scheme is applied. + /// Gets or sets a mathematical operator that is applied to the image data before an encoding scheme is applied. /// - public TiffPredictor Predictor => (TiffPredictor?)this.ExifProfile.GetValue(ExifTag.Predictor)?.Value ?? DefaultPredictor; + public TiffPredictor Predictor { get; set; } /// - /// Gets the specifies how to interpret each data sample in a pixel. - /// + /// Gets or sets the specifies how to interpret each data sample in a pixel. /// - public TiffSampleFormat[] SampleFormat => this.ExifProfile.GetValue(ExifTag.SampleFormat)?.Value?.Select(a => (TiffSampleFormat)a).ToArray(); + public TiffSampleFormat[] SampleFormat { get; set; } /// - public IDeepCloneable DeepClone() => new TiffFrameMetadata() { ExifProfile = this.ExifProfile.DeepClone() }; + public IDeepCloneable DeepClone() => new TiffFrameMetadata(this.ExifProfile.DeepClone()); } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index dda695568..77098c42c 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -254,7 +254,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [WithFile(Rgb4BitPalette, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_With4Bit_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => - TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.ColorPalette, useExactComparer: false, compareTolerance: 0.001f); + // Note: The magick reference decoder does not support 4 bit tiff's, so we use our TIFF decoder instead. + TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit4, TiffEncodingMode.ColorPalette, useExactComparer: false, compareTolerance: 0.001f, imageDecoder: new TiffDecoder()); [Theory] [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] @@ -384,8 +385,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff TiffCompression compression = TiffCompression.None, TiffPredictor predictor = TiffPredictor.None, bool useExactComparer = true, - int maxStripSize = 0, - float compareTolerance = 0.01f) + float compareTolerance = 0.01f, + IImageDecoder imageDecoder = null) where TPixel : unmanaged, IPixel { using Image image = provider.GetImage(); @@ -398,7 +399,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff }; // Does DebugSave & load reference CompareToReferenceInput(): - image.VerifyEncoder(provider, "tiff", bitsPerPixel, encoder, useExactComparer ? ImageComparer.Exact : ImageComparer.Tolerant(compareTolerance), referenceDecoder: ReferenceDecoder); + image.VerifyEncoder( + provider, + "tiff", + bitsPerPixel, + encoder, + useExactComparer ? ImageComparer.Exact : ImageComparer.Tolerant(compareTolerance), + referenceDecoder: imageDecoder ?? ReferenceDecoder); } } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index 92412234b..45b53eae8 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; @@ -34,7 +35,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff } [Fact] - public void CloneIsDeep() + public void TiffMetadata_CloneIsDeep() { byte[] xmpData = { 1, 1, 1 }; var meta = new TiffMetadata @@ -55,6 +56,27 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.True(meta.XmpProfile.SequenceEqual(clone.XmpProfile)); } + [Theory] + [WithFile(SampleMetadata, PixelTypes.Rgba32)] + public void TiffFrameMetadata_CloneIsDeep(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TiffDecoder)) + { + TiffFrameMetadata meta = image.Frames.RootFrame.Metadata.GetTiffMetadata(); + var cloneSameAsSampleMetaData = (TiffFrameMetadata)meta.DeepClone(); + VerifyExpectedFrameMetaDataIsPresent(cloneSameAsSampleMetaData); + + 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)); + } + } + [Theory] [InlineData(Calliphora_BiColorUncompressed, TiffBitsPerPixel.Bit1)] [InlineData(GrayscaleUncompressed, TiffBitsPerPixel.Bit8)] @@ -130,53 +152,63 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { using (Image image = provider.GetImage(TiffDecoder)) { - TiffMetadata meta = image.Metadata.GetTiffMetadata(); - - Assert.NotNull(meta); - Assert.Equal(ByteOrder.LittleEndian, meta.ByteOrder); - Assert.Equal(PixelResolutionUnit.PixelsPerInch, image.Metadata.ResolutionUnits); - Assert.Equal(10, image.Metadata.HorizontalResolution); - Assert.Equal(10, image.Metadata.VerticalResolution); - - TiffFrameMetadata frameMetadata = image.Frames.RootFrame.Metadata.GetTiffMetadata(); ImageFrame rootFrame = image.Frames.RootFrame; - Assert.Equal(30, frameMetadata.ExifProfile.Values.Count); - Assert.Equal(32, rootFrame.Width); Assert.Equal(32, rootFrame.Height); - Assert.Equal(TiffBitsPerSample.Bit4, frameMetadata.BitsPerSample); - Assert.Equal(TiffCompression.Lzw, frameMetadata.Compression); - Assert.Equal(TiffPhotometricInterpretation.PaletteColor, frameMetadata.PhotometricInterpretation); - Assert.Equal("This is Название", frameMetadata.ExifProfile.GetValue(ExifTag.ImageDescription).Value); - Assert.Equal("This is Изготовитель камеры", frameMetadata.ExifProfile.GetValue(ExifTag.Make).Value); - Assert.Equal("This is Модель камеры", frameMetadata.ExifProfile.GetValue(ExifTag.Model).Value); - Assert.Equal(new Number[] { 8u }, frameMetadata.StripOffsets, new NumberComparer()); - Assert.Equal(1, frameMetadata.SamplesPerPixel); - 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("IrfanView", frameMetadata.ExifProfile.GetValue(ExifTag.Software).Value); - Assert.Null(frameMetadata.ExifProfile.GetValue(ExifTag.DateTime)?.Value); - Assert.Equal("This is author1;Author2", frameMetadata.ExifProfile.GetValue(ExifTag.Artist).Value); - Assert.Null(frameMetadata.ExifProfile.GetValue(ExifTag.HostComputer)?.Value); - 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); - Assert.Equal("This is Авторские права", frameMetadata.ExifProfile.GetValue(ExifTag.Copyright).Value); - Assert.Equal(4, frameMetadata.ExifProfile.GetValue(ExifTag.Rating).Value); - Assert.Equal(75, frameMetadata.ExifProfile.GetValue(ExifTag.RatingPercent).Value); + + TiffFrameMetadata frameMetaData = rootFrame.Metadata.GetTiffMetadata(); + Assert.NotNull(frameMetaData); + + ImageMetadata imageMetaData = image.Metadata; + Assert.NotNull(imageMetaData); + Assert.Equal(PixelResolutionUnit.PixelsPerInch, imageMetaData.ResolutionUnits); + Assert.Equal(10, imageMetaData.HorizontalResolution); + Assert.Equal(10, imageMetaData.VerticalResolution); + + TiffMetadata tiffMetaData = image.Metadata.GetTiffMetadata(); + Assert.NotNull(tiffMetaData); + Assert.Equal(ByteOrder.LittleEndian, tiffMetaData.ByteOrder); + Assert.Equal(TiffBitsPerPixel.Bit4, tiffMetaData.BitsPerPixel); + + VerifyExpectedFrameMetaDataIsPresent(frameMetaData); } } + private static void VerifyExpectedFrameMetaDataIsPresent(TiffFrameMetadata frameMetaData) + { + Assert.Equal(30, frameMetaData.ExifProfile.Values.Count); + Assert.Equal(TiffBitsPerSample.Bit4, frameMetaData.BitsPerSample); + Assert.Equal(TiffCompression.Lzw, frameMetaData.Compression); + Assert.Equal(TiffPhotometricInterpretation.PaletteColor, frameMetaData.PhotometricInterpretation); + Assert.Equal("This is Название", frameMetaData.ExifProfile.GetValue(ExifTag.ImageDescription).Value); + Assert.Equal("This is Изготовитель камеры", frameMetaData.ExifProfile.GetValue(ExifTag.Make).Value); + Assert.Equal("This is Модель камеры", frameMetaData.ExifProfile.GetValue(ExifTag.Model).Value); + 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("IrfanView", frameMetaData.ExifProfile.GetValue(ExifTag.Software).Value); + Assert.Null(frameMetaData.ExifProfile.GetValue(ExifTag.DateTime)?.Value); + Assert.Equal("This is author1;Author2", frameMetaData.ExifProfile.GetValue(ExifTag.Artist).Value); + Assert.Null(frameMetaData.ExifProfile.GetValue(ExifTag.HostComputer)?.Value); + 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); + Assert.Equal("This is Авторские права", frameMetaData.ExifProfile.GetValue(ExifTag.Copyright).Value); + Assert.Equal(4, frameMetaData.ExifProfile.GetValue(ExifTag.Rating).Value); + Assert.Equal(75, frameMetaData.ExifProfile.GetValue(ExifTag.RatingPercent).Value); + } + [Theory] [WithFile(MultiframeDeflateWithPreview, PixelTypes.Rgba32)] public void SubfileType(TestImageProvider provider) @@ -204,9 +236,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff } [Theory] - [WithFile(SampleMetadata, PixelTypes.Rgba32, true)] - [WithFile(SampleMetadata, PixelTypes.Rgba32, false)] - public void PreserveMetadata(TestImageProvider provider, bool preserveMetadata) + [WithFile(SampleMetadata, PixelTypes.Rgba32)] + public void Encode_PreservesMetadata(TestImageProvider provider) where TPixel : unmanaged, IPixel { // Load Tiff image @@ -222,11 +253,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // Save to Tiff var tiffEncoder = new TiffEncoder() { Mode = TiffEncodingMode.Rgb }; - if (!preserveMetadata) - { - ClearMeta(image); - } - using var ms = new MemoryStream(); image.Save(ms, tiffEncoder); @@ -252,142 +278,16 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal(frameMetaInput.HorizontalResolution, tiffMetaDataEncodedRootFrame.HorizontalResolution); Assert.Equal(frameMetaInput.VerticalResolution, tiffMetaDataEncodedRootFrame.VerticalResolution); - if (preserveMetadata) - { - Assert.Equal(tiffMetaInput.XmpProfile, tiffMetaDataEncodedImage.XmpProfile); - - Assert.Equal("IrfanView", frameMetaInput.ExifProfile.GetValue(ExifTag.Software).Value); - Assert.Equal("This is Название", frameMetaInput.ExifProfile.GetValue(ExifTag.ImageDescription).Value); - Assert.Equal("This is Изготовитель камеры", frameMetaInput.ExifProfile.GetValue(ExifTag.Make).Value); - Assert.Equal("This is Авторские права", frameMetaInput.ExifProfile.GetValue(ExifTag.Copyright).Value); - - Assert.Equal(frameMetaInput.ExifProfile.GetValue(ExifTag.ImageDescription).Value, tiffMetaDataEncodedRootFrame.ExifProfile.GetValue(ExifTag.ImageDescription).Value); - Assert.Equal(frameMetaInput.ExifProfile.GetValue(ExifTag.Make).Value, tiffMetaDataEncodedRootFrame.ExifProfile.GetValue(ExifTag.Make).Value); - Assert.Equal(frameMetaInput.ExifProfile.GetValue(ExifTag.Copyright).Value, tiffMetaDataEncodedRootFrame.ExifProfile.GetValue(ExifTag.Copyright).Value); - } - else - { - Assert.Null(tiffMetaDataEncodedImage.XmpProfile); - - Assert.Equal("ImageSharp", tiffMetaDataEncodedRootFrame.ExifProfile.GetValue(ExifTag.Software).Value); - Assert.Null(frameMetaInput.ExifProfile.GetValue(ExifTag.Software)?.Value); - Assert.Null(frameMetaInput.ExifProfile.GetValue(ExifTag.ImageDescription)?.Value); - Assert.Null(frameMetaInput.ExifProfile.GetValue(ExifTag.Make)?.Value); - Assert.Null(frameMetaInput.ExifProfile.GetValue(ExifTag.Copyright)?.Value); - - Assert.Null(tiffMetaDataEncodedRootFrame.ExifProfile.GetValue(ExifTag.ImageDescription)?.Value); - Assert.Null(tiffMetaDataEncodedRootFrame.ExifProfile.GetValue(ExifTag.Make)?.Value); - Assert.Null(tiffMetaDataEncodedRootFrame.ExifProfile.GetValue(ExifTag.Copyright)?.Value); - } - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void CreateMetadata(bool preserveMetadata) - { - // Create image - int w = 10; - int h = 20; - using Image image = new Image(w, h); - - // set metadata - ImageMetadata coreMeta = image.Metadata; - TiffMetadata tiffMeta = image.Metadata.GetTiffMetadata(); - TiffFrameMetadata frameMeta = image.Frames.RootFrame.Metadata.GetTiffMetadata(); - - tiffMeta.XmpProfile = new byte[] { 1, 2, 3, 4, 5 }; - - coreMeta.IptcProfile = new IptcProfile(); - coreMeta.IptcProfile.SetValue(IptcTag.Caption, "iptc caption"); - - coreMeta.IccProfile = new IccProfile(new IccProfileHeader() { CreationDate = DateTime.Now }, new IccTagDataEntry[] { new IccTextTagDataEntry("test string"), new IccDataTagDataEntry(new byte[] { 11, 22, 33, 44 }) }); - - coreMeta.ResolutionUnits = PixelResolutionUnit.PixelsPerMeter; - coreMeta.HorizontalResolution = 4500; - coreMeta.VerticalResolution = 5400; - - var datetime = DateTime.Now.ToString(CultureInfo.InvariantCulture); - frameMeta.ExifProfile.SetValue(ExifTag.ImageDescription, "test ImageDescription"); - frameMeta.ExifProfile.SetValue(ExifTag.DateTime, datetime); - - // Save to Tiff - var tiffEncoder = new TiffEncoder { Mode = TiffEncodingMode.Default, Compression = TiffCompression.Deflate }; - if (!preserveMetadata) - { - ClearMeta(image); - } - - using var ms = new MemoryStream(); - image.Save(ms, tiffEncoder); - - // Assert - ms.Position = 0; - using var output = Image.Load(this.configuration, ms); - ImageFrame rootFrameOut = output.Frames.RootFrame; - - ImageMetadata coreMetaOut = output.Metadata; - TiffMetadata tiffMetaOut = output.Metadata.GetTiffMetadata(); - TiffFrameMetadata frameMetaOut = output.Frames.RootFrame.Metadata.GetTiffMetadata(); - - Assert.Equal(PixelResolutionUnit.PixelsPerCentimeter, coreMetaOut.ResolutionUnits); - Assert.Equal(45, coreMetaOut.HorizontalResolution); - Assert.Equal(54, coreMetaOut.VerticalResolution, 8); - - //// Assert.Equal(tiffEncoder.Compression, tiffMetaOut.Compression); - Assert.Equal(TiffBitsPerPixel.Bit24, tiffMetaOut.BitsPerPixel); - - Assert.Equal(w, rootFrameOut.Width); - Assert.Equal(h, rootFrameOut.Height); - Assert.Equal(frameMeta.ResolutionUnit, frameMetaOut.ResolutionUnit); - Assert.Equal(frameMeta.HorizontalResolution, frameMetaOut.HorizontalResolution); - Assert.Equal(frameMeta.VerticalResolution, frameMetaOut.VerticalResolution); - - Assert.Equal("ImageSharp", frameMetaOut.ExifProfile.GetValue(ExifTag.Software)?.Value); - - if (preserveMetadata) - { - Assert.NotNull(tiffMeta.XmpProfile); - Assert.NotNull(coreMeta.IptcProfile); - Assert.NotNull(coreMeta.IccProfile); - - Assert.Equal(tiffMeta.XmpProfile, tiffMetaOut.XmpProfile); - Assert.Equal(coreMeta.IptcProfile.Data, coreMetaOut.IptcProfile.Data); - Assert.Equal(coreMeta.IccProfile.ToByteArray(), coreMetaOut.IccProfile.ToByteArray()); - - Assert.Equal("test ImageDescription", frameMeta.ExifProfile.GetValue(ExifTag.ImageDescription).Value); - Assert.Equal(datetime, frameMeta.ExifProfile.GetValue(ExifTag.DateTime)?.Value); - - Assert.Equal(frameMeta.ExifProfile.GetValue(ExifTag.ImageDescription).Value, frameMetaOut.ExifProfile.GetValue(ExifTag.ImageDescription).Value); - Assert.Equal(frameMeta.ExifProfile.GetValue(ExifTag.DateTime).Value, frameMetaOut.ExifProfile.GetValue(ExifTag.DateTime).Value); - } - else - { - Assert.Null(tiffMetaOut.XmpProfile); - Assert.Null(coreMetaOut.IptcProfile); - Assert.Null(coreMetaOut.IccProfile); - - Assert.Null(frameMeta.ExifProfile.GetValue(ExifTag.ImageDescription)?.Value); - Assert.Null(frameMeta.ExifProfile.GetValue(ExifTag.DateTime)?.Value); - - Assert.Null(frameMetaOut.ExifProfile.GetValue(ExifTag.ImageDescription)?.Value); - Assert.Null(frameMetaOut.ExifProfile.GetValue(ExifTag.DateTime)?.Value); - } - } - - private static void ClearMeta(Image image) - { - ImageMetadata coreMeta = image.Metadata; - TiffMetadata tiffMeta = image.Metadata.GetTiffMetadata(); - TiffFrameMetadata frameMeta = image.Frames.RootFrame.Metadata.GetTiffMetadata(); - - coreMeta.ExifProfile = null; - coreMeta.IccProfile = null; - coreMeta.IptcProfile = null; + Assert.Equal(tiffMetaInput.XmpProfile, tiffMetaDataEncodedImage.XmpProfile); - tiffMeta.XmpProfile = null; + Assert.Equal("IrfanView", frameMetaInput.ExifProfile.GetValue(ExifTag.Software).Value); + Assert.Equal("This is Название", frameMetaInput.ExifProfile.GetValue(ExifTag.ImageDescription).Value); + Assert.Equal("This is Изготовитель камеры", frameMetaInput.ExifProfile.GetValue(ExifTag.Make).Value); + Assert.Equal("This is Авторские права", frameMetaInput.ExifProfile.GetValue(ExifTag.Copyright).Value); - frameMeta.ExifProfile = null; + Assert.Equal(frameMetaInput.ExifProfile.GetValue(ExifTag.ImageDescription).Value, tiffMetaDataEncodedRootFrame.ExifProfile.GetValue(ExifTag.ImageDescription).Value); + Assert.Equal(frameMetaInput.ExifProfile.GetValue(ExifTag.Make).Value, tiffMetaDataEncodedRootFrame.ExifProfile.GetValue(ExifTag.Make).Value); + Assert.Equal(frameMetaInput.ExifProfile.GetValue(ExifTag.Copyright).Value, tiffMetaDataEncodedRootFrame.ExifProfile.GetValue(ExifTag.Copyright).Value); } } } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs index 208222a85..fef890a65 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs @@ -478,7 +478,7 @@ namespace SixLabors.ImageSharp.Tests case TestImageWriteFormat.Png: return WriteAndReadPng(image); default: - throw new ArgumentException("Unexpected test image format, only Jpeg, Png and Tiff are allowed"); + throw new ArgumentException("Unexpected test image format, only Jpeg and Png are allowed"); } } @@ -506,18 +506,6 @@ namespace SixLabors.ImageSharp.Tests } } - private static Image WriteAndReadTiff(Image image) - { - using (var memStream = new MemoryStream()) - { - image.SaveAsTiff(memStream, new TiffEncoder()); - image.Dispose(); - - memStream.Position = 0; - return Image.Load(memStream, new TiffDecoder()); - } - } - private static void TestProfile(ExifProfile profile) { Assert.NotNull(profile); From 04b6f3ffcfe4a714b7491fcfa8827ae8764f5ebe Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 14 May 2021 11:43:41 +0200 Subject: [PATCH 235/275] Remove ExifProfile from TiffFrameMetadata, Add ExifProfile and XmpProfile to ImageFrameMetaData --- .../Formats/Tiff/Ifd/DirectoryReader.cs | 7 + .../Formats/Tiff/TiffDecoderCore.cs | 52 ++-- .../Tiff/TiffDecoderMetadataCreator.cs | 82 +++++-- .../Formats/Tiff/TiffDecoderOptionsParser.cs | 14 +- .../Tiff/TiffEncoderEntriesCollector.cs | 39 +-- .../Formats/Tiff/TiffFrameMetadata.cs | 230 ++++++++++++------ src/ImageSharp/Formats/Tiff/TiffMetadata.cs | 10 - src/ImageSharp/Metadata/ImageFrameMetadata.cs | 20 +- .../Formats/Tiff/TiffEncoderTests.cs | 2 +- .../Formats/Tiff/TiffMetadataTests.cs | 94 +++---- .../Metadata/ImageFrameMetadataTests.cs | 28 ++- .../Profiles/Exif/ExifProfileTests.cs | 5 +- 12 files changed, 359 insertions(+), 224 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs index ea090c92b..8b2c6bd3a 100644 --- a/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs +++ b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs @@ -24,8 +24,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff public DirectoryReader(Stream stream) => this.stream = stream; + /// + /// Gets the byte order. + /// public ByteOrder ByteOrder { get; private set; } + /// + /// Reads image file directories. + /// + /// Image file directories. public IEnumerable Read() { this.ByteOrder = ReadByteOrder(this.stream); diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index d67ffc069..da0d73832 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System.Collections.Generic; +using System.Linq; using System.Threading; using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Formats.Tiff.Constants; @@ -109,15 +110,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff IEnumerable directories = reader.Read(); var frames = new List>(); - var framesMetadata = new List(); + var tiffFramesMataData = new List(); foreach (ExifProfile ifd in directories) { ImageFrame frame = this.DecodeFrame(ifd, out TiffFrameMetadata frameMetadata); frames.Add(frame); - framesMetadata.Add(frameMetadata); + tiffFramesMataData.Add(frameMetadata); } - ImageMetadata metadata = TiffDecoderMetadataCreator.Create(framesMetadata, this.ignoreMetadata, reader.ByteOrder); + ImageMetadata metadata = TiffDecoderMetadataCreator.Create(frames, tiffFramesMataData, this.ignoreMetadata, reader.ByteOrder); // todo: tiff frames can have different sizes { @@ -153,11 +154,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff framesMetadata.Add(meta); } - ImageMetadata metadata = TiffDecoderMetadataCreator.Create(framesMetadata, this.ignoreMetadata, reader.ByteOrder); + ImageMetadata metadata = TiffDecoderMetadataCreator.Create(framesMetadata, reader.ByteOrder); TiffFrameMetadata root = framesMetadata[0]; - int width = GetImageWidth(root); - int height = GetImageHeight(root); + ExifProfile rootFrameExifProfile = directories.First(); + int width = GetImageWidth(rootFrameExifProfile); + int height = GetImageHeight(rootFrameExifProfile); return new ImageInfo(new PixelTypeInfo(root.BitsPerPixel), width, height, metadata); } @@ -167,26 +169,28 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// The pixel format. /// The IFD tags. - /// The frame metadata. + /// The tiff frame metadata. /// /// The tiff frame. /// - private ImageFrame DecodeFrame(ExifProfile tags, out TiffFrameMetadata frameMetaData) + private ImageFrame DecodeFrame(ExifProfile tags, out TiffFrameMetadata tiffFrameMetaData) where TPixel : unmanaged, IPixel { - var coreMetadata = new ImageFrameMetadata(); - frameMetaData = coreMetadata.GetTiffMetadata(); - frameMetaData.Initialize(tags); + ImageFrameMetadata imageFrameMetaData = this.ignoreMetadata ? + new ImageFrameMetadata() : + new ImageFrameMetadata { ExifProfile = tags, XmpProfile = tags.GetValue(ExifTag.XMP)?.Value }; + tiffFrameMetaData = imageFrameMetaData.GetTiffMetadata(); + tiffFrameMetaData.Initialize(tags); - this.VerifyAndParse(frameMetaData); + this.VerifyAndParse(tiffFrameMetaData); - int width = GetImageWidth(frameMetaData); - int height = GetImageHeight(frameMetaData); - var frame = new ImageFrame(this.Configuration, width, height, coreMetadata); + int width = GetImageWidth(tags); + int height = GetImageHeight(tags); + var frame = new ImageFrame(this.Configuration, width, height, imageFrameMetaData); - int rowsPerStrip = (int)frameMetaData.RowsPerStrip; - Number[] stripOffsets = frameMetaData.StripOffsets; - Number[] stripByteCounts = frameMetaData.StripByteCounts; + int rowsPerStrip = (int)tiffFrameMetaData.RowsPerStrip; + Number[] stripOffsets = tiffFrameMetaData.StripOffsets; + Number[] stripByteCounts = tiffFrameMetaData.StripByteCounts; if (this.PlanarConfiguration == TiffPlanarConfiguration.Planar) { @@ -320,11 +324,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Gets the width of the image frame. /// - /// The image frame. + /// The image frame exif profile. /// The image width. - private static int GetImageWidth(TiffFrameMetadata frame) + private static int GetImageWidth(ExifProfile exifProfile) { - IExifValue width = frame.ExifProfile.GetValue(ExifTag.ImageWidth); + IExifValue width = exifProfile.GetValue(ExifTag.ImageWidth); if (width == null) { TiffThrowHelper.ThrowImageFormatException("The TIFF image frame is missing the ImageWidth"); @@ -336,11 +340,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Gets the height of the image frame. /// - /// The image frame. + /// The image frame exif profile. /// The image height. - private static int GetImageHeight(TiffFrameMetadata frame) + private static int GetImageHeight(ExifProfile exifProfile) { - IExifValue height = frame.ExifProfile.GetValue(ExifTag.ImageLength); + IExifValue height = exifProfile.GetValue(ExifTag.ImageLength); if (height == null) { TiffThrowHelper.ThrowImageFormatException("The TIFF image frame is missing the ImageLength"); diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs index 5b67c363a..77d3d041c 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs @@ -8,6 +8,7 @@ using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Icc; using SixLabors.ImageSharp.Metadata.Profiles.Iptc; +using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Tiff { @@ -16,64 +17,91 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// internal static class TiffDecoderMetadataCreator { - public static ImageMetadata Create(List frames, bool ignoreMetadata, ByteOrder byteOrder) + public static ImageMetadata Create(List> frames, List framesMetaData, bool ignoreMetadata, ByteOrder byteOrder) + where TPixel : unmanaged, IPixel { - if (frames.Count < 1) + DebugGuard.IsTrue(frames.Count() == framesMetaData.Count, nameof(frames), "Image frames and frames metdadata should be the same size."); + + if (framesMetaData.Count < 1) { TiffThrowHelper.ThrowImageFormatException("Expected at least one frame."); } - var coreMetadata = new ImageMetadata(); - TiffFrameMetadata rootFrameMetadata = frames[0]; - - coreMetadata.ResolutionUnits = rootFrameMetadata.ResolutionUnit; - if (rootFrameMetadata.HorizontalResolution != null) - { - coreMetadata.HorizontalResolution = rootFrameMetadata.HorizontalResolution.Value; - } + var imageMetaData = new ImageMetadata(); + TiffFrameMetadata rootFrameMetadata = framesMetaData[0]; + SetResolution(imageMetaData, rootFrameMetadata); - if (rootFrameMetadata.VerticalResolution != null) - { - coreMetadata.VerticalResolution = rootFrameMetadata.VerticalResolution.Value; - } - - TiffMetadata tiffMetadata = coreMetadata.GetTiffMetadata(); + TiffMetadata tiffMetadata = imageMetaData.GetTiffMetadata(); tiffMetadata.ByteOrder = byteOrder; tiffMetadata.BitsPerPixel = GetBitsPerPixel(rootFrameMetadata); if (!ignoreMetadata) { - foreach (TiffFrameMetadata frame in frames) + for (int i = 0; i < frames.Count; i++) { - if (tiffMetadata.XmpProfile == null) + ImageFrame frame = frames[i]; + ImageFrameMetadata frameMetaData = frame.Metadata; + if (frameMetaData.XmpProfile == null) { - IExifValue val = frame.ExifProfile.GetValue(ExifTag.XMP); + IExifValue val = frameMetaData.ExifProfile.GetValue(ExifTag.XMP); if (val != null) { - tiffMetadata.XmpProfile = val.Value; + frameMetaData.XmpProfile = val.Value; } } - if (coreMetadata.IptcProfile == null) + if (imageMetaData.IptcProfile == null) { - if (TryGetIptc(frame.ExifProfile.Values, out byte[] iptcBytes)) + if (TryGetIptc(frameMetaData.ExifProfile.Values, out byte[] iptcBytes)) { - coreMetadata.IptcProfile = new IptcProfile(iptcBytes); + imageMetaData.IptcProfile = new IptcProfile(iptcBytes); } } - if (coreMetadata.IccProfile == null) + if (imageMetaData.IccProfile == null) { - IExifValue val = frame.ExifProfile.GetValue(ExifTag.IccProfile); + IExifValue val = frameMetaData.ExifProfile.GetValue(ExifTag.IccProfile); if (val != null) { - coreMetadata.IccProfile = new IccProfile(val.Value); + imageMetaData.IccProfile = new IccProfile(val.Value); } } } } - return coreMetadata; + return imageMetaData; + } + + public static ImageMetadata Create(List framesMetaData, ByteOrder byteOrder) + { + if (framesMetaData.Count < 1) + { + TiffThrowHelper.ThrowImageFormatException("Expected at least one frame."); + } + + var imageMetaData = new ImageMetadata(); + TiffFrameMetadata rootFrameMetadata = framesMetaData[0]; + SetResolution(imageMetaData, rootFrameMetadata); + + TiffMetadata tiffMetadata = imageMetaData.GetTiffMetadata(); + tiffMetadata.ByteOrder = byteOrder; + tiffMetadata.BitsPerPixel = GetBitsPerPixel(rootFrameMetadata); + + return imageMetaData; + } + + private static void SetResolution(ImageMetadata imageMetaData, TiffFrameMetadata rootFrameMetadata) + { + imageMetaData.ResolutionUnits = rootFrameMetadata.ResolutionUnit; + if (rootFrameMetadata.HorizontalResolution != null) + { + imageMetaData.HorizontalResolution = rootFrameMetadata.HorizontalResolution.Value; + } + + if (rootFrameMetadata.VerticalResolution != null) + { + imageMetaData.VerticalResolution = rootFrameMetadata.VerticalResolution.Value; + } } private static bool TryGetIptc(IReadOnlyList exifValues, out byte[] iptcBytes) diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs index b9f30a3ef..45a55b274 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs @@ -4,7 +4,6 @@ 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 { @@ -194,9 +193,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff } } - private static void ParseCompression(this TiffDecoderCore options, TiffFrameMetadata entries) + private static void ParseCompression(this TiffDecoderCore options, TiffFrameMetadata tiffFrameMetaData) { - TiffCompression compression = entries.Compression; + TiffCompression compression = tiffFrameMetaData.Compression; switch (compression) { case TiffCompression.None: @@ -227,12 +226,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff case TiffCompression.CcittGroup3Fax: { options.CompressionType = TiffDecoderCompressionType.T4; - IExifValue t4options = entries.ExifProfile.GetValue(ExifTag.T4Options); - if (t4options != null) - { - var t4OptionValue = (FaxCompressionOptions)t4options.GetValue(); - options.FaxCompressionOptions = t4OptionValue; - } + options.FaxCompressionOptions = tiffFrameMetaData.FaxCompressionOptions; break; } @@ -245,7 +239,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff default: { - TiffThrowHelper.ThrowNotSupported("The specified TIFF compression format is not supported: " + compression); + TiffThrowHelper.ThrowNotSupported($"The specified TIFF compression format {compression} is not supported"); break; } } diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs index f20395221..391f7d541 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs @@ -47,7 +47,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff public void Process(Image image) where TPixel : unmanaged, IPixel { - TiffFrameMetadata frameMetadata = image.Frames.RootFrame.Metadata.GetTiffMetadata(); + ImageFrame rootFrame = image.Frames.RootFrame; + ExifProfile rootFrameExifProfile = rootFrame.Metadata.ExifProfile ?? new ExifProfile(); + byte[] rootFrameXmpBytes = rootFrame.Metadata.XmpProfile; var width = new ExifLong(ExifTagValue.ImageWidth) { @@ -67,9 +69,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff this.collector.Add(width); this.collector.Add(height); - this.ProcessResolution(image.Metadata, frameMetadata); - this.ProcessProfiles(image.Metadata, frameMetadata); - this.ProcessMetadata(frameMetadata); + this.ProcessResolution(image.Metadata, rootFrameExifProfile); + this.ProcessProfiles(image.Metadata, rootFrameExifProfile, rootFrameXmpBytes); + this.ProcessMetadata(rootFrameExifProfile); if (!this.collector.Entries.Exists(t => t.Tag == ExifTag.Software)) { @@ -112,18 +114,18 @@ namespace SixLabors.ImageSharp.Formats.Tiff } } - private void ProcessResolution(ImageMetadata imageMetadata, TiffFrameMetadata frameMetadata) + private void ProcessResolution(ImageMetadata imageMetadata, ExifProfile exifProfile) { UnitConverter.SetResolutionValues( - frameMetadata.ExifProfile, + exifProfile, imageMetadata.ResolutionUnits, imageMetadata.HorizontalResolution, imageMetadata.VerticalResolution); - this.collector.Add(frameMetadata.ExifProfile.GetValue(ExifTag.ResolutionUnit).DeepClone()); + this.collector.Add(exifProfile.GetValue(ExifTag.ResolutionUnit).DeepClone()); - IExifValue xResolution = frameMetadata.ExifProfile.GetValue(ExifTag.XResolution)?.DeepClone(); - IExifValue yResolution = frameMetadata.ExifProfile.GetValue(ExifTag.YResolution)?.DeepClone(); + IExifValue xResolution = exifProfile.GetValue(ExifTag.XResolution)?.DeepClone(); + IExifValue yResolution = exifProfile.GetValue(ExifTag.YResolution)?.DeepClone(); if (xResolution != null && yResolution != null) { @@ -132,9 +134,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff } } - private void ProcessMetadata(TiffFrameMetadata frameMetadata) + private void ProcessMetadata(ExifProfile exifProfile) { - foreach (IExifValue entry in frameMetadata.ExifProfile.Values) + foreach (IExifValue entry in exifProfile.Values) { // todo: skip subIfd if (entry.DataType == ExifDataType.Ifd) @@ -175,9 +177,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff } } - private void ProcessProfiles(ImageMetadata imageMetadata, TiffFrameMetadata tiffFrameMetadata) + private void ProcessProfiles(ImageMetadata imageMetadata, ExifProfile exifProfile, byte[] xmpProfile) { - ExifProfile exifProfile = tiffFrameMetadata.ExifProfile; if (exifProfile != null && exifProfile.Parts != ExifParts.None) { foreach (IExifValue entry in exifProfile.Values) @@ -194,7 +195,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff } else { - tiffFrameMetadata.ExifProfile.RemoveValue(ExifTag.SubIFDOffset); + exifProfile.RemoveValue(ExifTag.SubIFDOffset); } if (imageMetadata.IptcProfile != null) @@ -209,7 +210,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff } else { - tiffFrameMetadata.ExifProfile.RemoveValue(ExifTag.IPTC); + exifProfile.RemoveValue(ExifTag.IPTC); } if (imageMetadata.IccProfile != null) @@ -223,22 +224,22 @@ namespace SixLabors.ImageSharp.Formats.Tiff } else { - tiffFrameMetadata.ExifProfile.RemoveValue(ExifTag.IccProfile); + exifProfile.RemoveValue(ExifTag.IccProfile); } TiffMetadata tiffMetadata = imageMetadata.GetTiffMetadata(); - if (tiffMetadata.XmpProfile != null) + if (xmpProfile != null) { var xmp = new ExifByteArray(ExifTagValue.XMP, ExifDataType.Byte) { - Value = tiffMetadata.XmpProfile + Value = xmpProfile }; this.collector.Add(xmp); } else { - tiffFrameMetadata.ExifProfile.RemoveValue(ExifTag.XMP); + exifProfile.RemoveValue(ExifTag.XMP); } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs index d98b3ac94..f237ed329 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs @@ -1,8 +1,10 @@ // 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; @@ -21,7 +23,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Initializes a new instance of the class. /// - public TiffFrameMetadata() => this.Initialize(new ExifProfile()); + public TiffFrameMetadata() + { + } /// /// Initializes a new instance of the class. @@ -29,83 +33,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The Tiff frame directory tags. public TiffFrameMetadata(ExifProfile frameTags) => this.Initialize(frameTags); - /// - /// Initializes a new instance of the class with a given ExifProfile. - /// - /// The Tiff frame directory tags. - public void Initialize(ExifProfile frameTags) - { - this.ExifProfile = frameTags; - - this.FillOrder = (TiffFillOrder?)this.ExifProfile.GetValue(ExifTag.FillOrder)?.Value ?? TiffFillOrder.MostSignificantBitFirst; - this.Compression = this.ExifProfile.GetValue(ExifTag.Compression) != null ? (TiffCompression)this.ExifProfile.GetValue(ExifTag.Compression).Value : TiffCompression.None; - this.SubfileType = (TiffNewSubfileType?)this.ExifProfile.GetValue(ExifTag.SubfileType)?.Value ?? TiffNewSubfileType.FullImage; - this.OldSubfileType = (TiffSubfileType?)this.ExifProfile.GetValue(ExifTag.OldSubfileType)?.Value; - this.HorizontalResolution = this.ExifProfile.GetValue(ExifTag.XResolution)?.Value.ToDouble(); - this.VerticalResolution = this.ExifProfile.GetValue(ExifTag.YResolution)?.Value.ToDouble(); - this.PlanarConfiguration = (TiffPlanarConfiguration?)this.ExifProfile.GetValue(ExifTag.PlanarConfiguration)?.Value ?? DefaultPlanarConfiguration; - this.ResolutionUnit = UnitConverter.ExifProfileToResolutionUnit(this.ExifProfile); - this.ColorMap = this.ExifProfile.GetValue(ExifTag.ColorMap)?.Value; - this.ExtraSamples = this.ExifProfile.GetValue(ExifTag.ExtraSamples)?.Value; - this.Predictor = (TiffPredictor?)this.ExifProfile.GetValue(ExifTag.Predictor)?.Value ?? DefaultPredictor; - this.SampleFormat = this.ExifProfile.GetValue(ExifTag.SampleFormat)?.Value?.Select(a => (TiffSampleFormat)a).ToArray(); - this.SamplesPerPixel = this.ExifProfile.GetValue(ExifTag.SamplesPerPixel)?.Value; - this.StripRowCounts = this.ExifProfile.GetValue(ExifTag.StripRowCounts)?.Value; - this.RowsPerStrip = this.ExifProfile.GetValue(ExifTag.RowsPerStrip) != null ? this.ExifProfile.GetValue(ExifTag.RowsPerStrip).Value : TiffConstants.RowsPerStripInfinity; - this.TileOffsets = this.ExifProfile.GetValue(ExifTag.TileOffsets)?.Value; - - this.PhotometricInterpretation = this.ExifProfile.GetValue(ExifTag.PhotometricInterpretation) != null ? - (TiffPhotometricInterpretation)this.ExifProfile.GetValue(ExifTag.PhotometricInterpretation).Value : TiffPhotometricInterpretation.WhiteIsZero; - - // Required Fields for decoding the image. - this.StripOffsets = this.ExifProfile.GetValue(ExifTag.StripOffsets)?.Value; - this.StripByteCounts = this.ExifProfile.GetValue(ExifTag.StripByteCounts)?.Value; - - ushort[] bits = this.ExifProfile.GetValue(ExifTag.BitsPerSample)?.Value; - if (bits == null) - { - if (this.PhotometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero || this.PhotometricInterpretation == TiffPhotometricInterpretation.BlackIsZero) - { - this.BitsPerSample = TiffBitsPerSample.Bit1; - } - - this.BitsPerSample = null; - } - else - { - this.BitsPerSample = bits.GetBitsPerSample(); - } - - this.BitsPerPixel = 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!"); - } - } - - /// - /// Gets the Tiff directory tags. - /// - public ExifProfile ExifProfile { get; internal set; } - /// /// Gets or sets a general indication of the kind of data contained in this subfile. /// @@ -129,9 +56,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Gets or sets the compression scheme used on the image data. /// - /// 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. /// @@ -212,7 +143,146 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// 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)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) + { + this.BitsPerSample = TiffBitsPerSample.Bit1; + } + + this.BitsPerSample = null; + } + else + { + this.BitsPerSample = bits.GetBitsPerSample(); + } + + this.BitsPerPixel = 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() => new TiffFrameMetadata(this.ExifProfile.DeepClone()); + public IDeepCloneable DeepClone() + { + var clone = new TiffFrameMetadata(); + + clone.FillOrder = this.FillOrder; + clone.Compression = this.Compression; + clone.FaxCompressionOptions = this.FaxCompressionOptions; + clone.SubfileType = this.SubfileType ?? TiffNewSubfileType.FullImage; + clone.OldSubfileType = this.OldSubfileType ?? TiffSubfileType.FullImage; + clone.HorizontalResolution = this.HorizontalResolution ?? ImageMetadata.DefaultHorizontalResolution; + clone.VerticalResolution = this.VerticalResolution ?? ImageMetadata.DefaultVerticalResolution; + clone.ResolutionUnit = this.ResolutionUnit; + clone.PlanarConfiguration = this.PlanarConfiguration; + + 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/src/ImageSharp/Formats/Tiff/TiffMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs index 5923e831a..9d36d6613 100644 --- a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using System; - namespace SixLabors.ImageSharp.Formats.Tiff { /// @@ -25,8 +23,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff { this.ByteOrder = other.ByteOrder; this.BitsPerPixel = other.BitsPerPixel; - this.XmpProfile = other.XmpProfile != null ? new byte[other.XmpProfile.Length] : null; - other.XmpProfile?.AsSpan().CopyTo(this.XmpProfile.AsSpan()); } /// @@ -39,12 +35,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// public TiffBitsPerPixel? BitsPerPixel { get; set; } - /// - /// Gets or sets the XMP profile. - /// For internal use only. ImageSharp not support XMP profile. - /// - internal byte[] XmpProfile { get; set; } - /// public IDeepCloneable DeepClone() => new TiffMetadata(this); } diff --git a/src/ImageSharp/Metadata/ImageFrameMetadata.cs b/src/ImageSharp/Metadata/ImageFrameMetadata.cs index 2021f1249..761cce1c1 100644 --- a/src/ImageSharp/Metadata/ImageFrameMetadata.cs +++ b/src/ImageSharp/Metadata/ImageFrameMetadata.cs @@ -1,8 +1,10 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; using System.Collections.Generic; using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; namespace SixLabors.ImageSharp.Metadata { @@ -35,8 +37,22 @@ namespace SixLabors.ImageSharp.Metadata { this.formatMetadata.Add(meta.Key, meta.Value.DeepClone()); } + + this.ExifProfile = other.ExifProfile?.DeepClone(); + this.XmpProfile = other.XmpProfile != null ? new byte[other.XmpProfile.Length] : null; + other.XmpProfile?.AsSpan().CopyTo(this.XmpProfile.AsSpan()); } + /// + /// Gets or sets the Exif profile. + /// + public ExifProfile ExifProfile { get; set; } + + /// + /// Gets or sets the XMP profile. + /// + public byte[] XmpProfile { get; set; } + /// public ImageFrameMetadata DeepClone() => new ImageFrameMetadata(this); @@ -63,4 +79,4 @@ namespace SixLabors.ImageSharp.Metadata return newMeta; } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 77098c42c..f24ce0c64 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -254,7 +254,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [WithFile(Rgb4BitPalette, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_With4Bit_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => - // Note: The magick reference decoder does not support 4 bit tiff's, so we use our TIFF decoder instead. + //// Note: The magick reference decoder does not support 4 bit tiff's, so we use our TIFF decoder instead. TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit4, TiffEncodingMode.ColorPalette, useExactComparer: false, compareTolerance: 0.001f, imageDecoder: new TiffDecoder()); [Theory] diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index 45b53eae8..66a90418b 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -1,9 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Collections.Generic; -using System.Globalization; using System.IO; using System.Linq; @@ -11,7 +8,6 @@ using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; -using SixLabors.ImageSharp.Metadata.Profiles.Icc; using SixLabors.ImageSharp.Metadata.Profiles.Iptc; using SixLabors.ImageSharp.PixelFormats; @@ -37,12 +33,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Fact] public void TiffMetadata_CloneIsDeep() { - byte[] xmpData = { 1, 1, 1 }; var meta = new TiffMetadata { BitsPerPixel = TiffBitsPerPixel.Bit8, ByteOrder = ByteOrder.BigEndian, - XmpProfile = xmpData }; var clone = (TiffMetadata)meta.DeepClone(); @@ -52,8 +46,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.False(meta.BitsPerPixel == clone.BitsPerPixel); Assert.False(meta.ByteOrder == clone.ByteOrder); - Assert.False(meta.XmpProfile.Equals(clone.XmpProfile)); - Assert.True(meta.XmpProfile.SequenceEqual(clone.XmpProfile)); } [Theory] @@ -65,7 +57,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { TiffFrameMetadata meta = image.Frames.RootFrame.Metadata.GetTiffMetadata(); var cloneSameAsSampleMetaData = (TiffFrameMetadata)meta.DeepClone(); - VerifyExpectedFrameMetaDataIsPresent(cloneSameAsSampleMetaData); + VerifyExpectedTiffFrameMetaDataIsPresent(cloneSameAsSampleMetaData); var clone = (TiffFrameMetadata)meta.DeepClone(); @@ -119,15 +111,19 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff using (Image image = provider.GetImage(new TiffDecoder() { IgnoreMetadata = ignoreMetadata })) { TiffMetadata meta = image.Metadata.GetTiffMetadata(); + ImageFrameMetadata rootFrameMetaData = image.Frames.RootFrame.Metadata; Assert.NotNull(meta); if (ignoreMetadata) { - Assert.Null(meta.XmpProfile); + Assert.Null(rootFrameMetaData.XmpProfile); + Assert.Null(rootFrameMetaData.ExifProfile); } else { - Assert.NotNull(meta.XmpProfile); - Assert.Equal(2599, meta.XmpProfile.Length); + Assert.NotNull(rootFrameMetaData.XmpProfile); + Assert.NotNull(rootFrameMetaData.ExifProfile); + Assert.Equal(2599, rootFrameMetaData.XmpProfile.Length); + Assert.Equal(30, rootFrameMetaData.ExifProfile.Values.Count); } } } @@ -155,9 +151,25 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff ImageFrame rootFrame = image.Frames.RootFrame; Assert.Equal(32, rootFrame.Width); Assert.Equal(32, rootFrame.Height); - - TiffFrameMetadata frameMetaData = rootFrame.Metadata.GetTiffMetadata(); - Assert.NotNull(frameMetaData); + Assert.NotNull(rootFrame.Metadata.XmpProfile); + Assert.Equal(2599, rootFrame.Metadata.XmpProfile.Length); + + ExifProfile exifProfile = rootFrame.Metadata.ExifProfile; + Assert.NotNull(exifProfile); + Assert.Equal(30, exifProfile.Values.Count); + 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); + Assert.Equal("IrfanView", exifProfile.GetValue(ExifTag.Software).Value); + Assert.Null(exifProfile.GetValue(ExifTag.DateTime)?.Value); + Assert.Equal("This is author1;Author2", exifProfile.GetValue(ExifTag.Artist).Value); + Assert.Null(exifProfile.GetValue(ExifTag.HostComputer)?.Value); + 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); + + TiffFrameMetadata tiffFrameMetadata = rootFrame.Metadata.GetTiffMetadata(); + Assert.NotNull(tiffFrameMetadata); ImageMetadata imageMetaData = image.Metadata; Assert.NotNull(imageMetaData); @@ -170,31 +182,23 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal(ByteOrder.LittleEndian, tiffMetaData.ByteOrder); Assert.Equal(TiffBitsPerPixel.Bit4, tiffMetaData.BitsPerPixel); - VerifyExpectedFrameMetaDataIsPresent(frameMetaData); + VerifyExpectedTiffFrameMetaDataIsPresent(tiffFrameMetadata); } } - private static void VerifyExpectedFrameMetaDataIsPresent(TiffFrameMetadata frameMetaData) + private static void VerifyExpectedTiffFrameMetaDataIsPresent(TiffFrameMetadata frameMetaData) { - Assert.Equal(30, frameMetaData.ExifProfile.Values.Count); Assert.Equal(TiffBitsPerSample.Bit4, frameMetaData.BitsPerSample); Assert.Equal(TiffCompression.Lzw, frameMetaData.Compression); Assert.Equal(TiffPhotometricInterpretation.PaletteColor, frameMetaData.PhotometricInterpretation); - Assert.Equal("This is Название", frameMetaData.ExifProfile.GetValue(ExifTag.ImageDescription).Value); - Assert.Equal("This is Изготовитель камеры", frameMetaData.ExifProfile.GetValue(ExifTag.Make).Value); - Assert.Equal("This is Модель камеры", frameMetaData.ExifProfile.GetValue(ExifTag.Model).Value); - Assert.Equal(new Number[] {8u}, frameMetaData.StripOffsets, new NumberComparer()); + 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(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("IrfanView", frameMetaData.ExifProfile.GetValue(ExifTag.Software).Value); - Assert.Null(frameMetaData.ExifProfile.GetValue(ExifTag.DateTime)?.Value); - Assert.Equal("This is author1;Author2", frameMetaData.ExifProfile.GetValue(ExifTag.Artist).Value); - Assert.Null(frameMetaData.ExifProfile.GetValue(ExifTag.HostComputer)?.Value); Assert.Equal(48, frameMetaData.ColorMap.Length); Assert.Equal(10537, frameMetaData.ColorMap[0]); Assert.Equal(14392, frameMetaData.ColorMap[1]); @@ -204,9 +208,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Null(frameMetaData.ExtraSamples); Assert.Equal(TiffPredictor.None, frameMetaData.Predictor); Assert.Null(frameMetaData.SampleFormat); - Assert.Equal("This is Авторские права", frameMetaData.ExifProfile.GetValue(ExifTag.Copyright).Value); - Assert.Equal(4, frameMetaData.ExifProfile.GetValue(ExifTag.Rating).Value); - Assert.Equal(75, frameMetaData.ExifProfile.GetValue(ExifTag.RatingPercent).Value); } [Theory] @@ -223,13 +224,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff TiffFrameMetadata frame0MetaData = image.Frames[0].Metadata.GetTiffMetadata(); Assert.Equal(TiffNewSubfileType.FullImage, frame0MetaData.SubfileType); - Assert.Null(frame0MetaData.OldSubfileType); Assert.Equal(255, image.Frames[0].Width); Assert.Equal(255, image.Frames[0].Height); TiffFrameMetadata frame1MetaData = image.Frames[1].Metadata.GetTiffMetadata(); Assert.Equal(TiffNewSubfileType.Preview, frame1MetaData.SubfileType); - Assert.Null(frame1MetaData.OldSubfileType); Assert.Equal(255, image.Frames[1].Width); Assert.Equal(255, image.Frames[1].Height); } @@ -246,7 +245,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff ImageMetadata inputMetaData = image.Metadata; TiffMetadata tiffMetaInput = image.Metadata.GetTiffMetadata(); TiffFrameMetadata frameMetaInput = image.Frames.RootFrame.Metadata.GetTiffMetadata(); - ImageFrame frameRootInput = image.Frames.RootFrame; + ImageFrame rootFrameInput = image.Frames.RootFrame; + byte[] xmpProfileInput = rootFrameInput.Metadata.XmpProfile; + ExifProfile rootFrameExifProfileInput = rootFrameInput.Metadata.ExifProfile; Assert.Equal(TiffCompression.Lzw, frameMetaInput.Compression); Assert.Equal(TiffBitsPerPixel.Bit4, tiffMetaInput.BitsPerPixel); @@ -261,9 +262,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff using var encodedImage = Image.Load(this.configuration, ms); ImageMetadata encodedImageMetaData = encodedImage.Metadata; - TiffMetadata tiffMetaDataEncodedImage = encodedImage.Metadata.GetTiffMetadata(); - TiffFrameMetadata tiffMetaDataEncodedRootFrame = encodedImage.Frames.RootFrame.Metadata.GetTiffMetadata(); + TiffMetadata tiffMetaDataEncodedImage = encodedImageMetaData.GetTiffMetadata(); ImageFrame rootFrameEncodedImage = encodedImage.Frames.RootFrame; + TiffFrameMetadata tiffMetaDataEncodedRootFrame = rootFrameEncodedImage.Metadata.GetTiffMetadata(); + ExifProfile encodedImageExifProfile = rootFrameEncodedImage.Metadata.ExifProfile; + byte[] encodedImageXmpProfile = rootFrameEncodedImage.Metadata.XmpProfile; Assert.Equal(TiffBitsPerPixel.Bit24, tiffMetaDataEncodedImage.BitsPerPixel); Assert.Equal(TiffCompression.None, tiffMetaDataEncodedRootFrame.Compression); @@ -272,22 +275,23 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal(inputMetaData.VerticalResolution, encodedImageMetaData.VerticalResolution); Assert.Equal(inputMetaData.ResolutionUnits, encodedImageMetaData.ResolutionUnits); - Assert.Equal(frameRootInput.Width, rootFrameEncodedImage.Width); - Assert.Equal(frameRootInput.Height, rootFrameEncodedImage.Height); + 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); - Assert.Equal(tiffMetaInput.XmpProfile, tiffMetaDataEncodedImage.XmpProfile); + Assert.Equal(xmpProfileInput, encodedImageXmpProfile); - Assert.Equal("IrfanView", frameMetaInput.ExifProfile.GetValue(ExifTag.Software).Value); - Assert.Equal("This is Название", frameMetaInput.ExifProfile.GetValue(ExifTag.ImageDescription).Value); - Assert.Equal("This is Изготовитель камеры", frameMetaInput.ExifProfile.GetValue(ExifTag.Make).Value); - Assert.Equal("This is Авторские права", frameMetaInput.ExifProfile.GetValue(ExifTag.Copyright).Value); + 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(frameMetaInput.ExifProfile.GetValue(ExifTag.ImageDescription).Value, tiffMetaDataEncodedRootFrame.ExifProfile.GetValue(ExifTag.ImageDescription).Value); - Assert.Equal(frameMetaInput.ExifProfile.GetValue(ExifTag.Make).Value, tiffMetaDataEncodedRootFrame.ExifProfile.GetValue(ExifTag.Make).Value); - Assert.Equal(frameMetaInput.ExifProfile.GetValue(ExifTag.Copyright).Value, tiffMetaDataEncodedRootFrame.ExifProfile.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); } } } diff --git a/tests/ImageSharp.Tests/Metadata/ImageFrameMetadataTests.cs b/tests/ImageSharp.Tests/Metadata/ImageFrameMetadataTests.cs index 3f8904904..cf63db39b 100644 --- a/tests/ImageSharp.Tests/Metadata/ImageFrameMetadataTests.cs +++ b/tests/ImageSharp.Tests/Metadata/ImageFrameMetadataTests.cs @@ -1,11 +1,13 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System.Linq; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Metadata; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; using Xunit; -namespace SixLabors.ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.Metadata { /// /// Tests the class. @@ -36,8 +38,28 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void CloneIsDeep() { - var metaData = new ImageFrameMetadata(); + // arrange + byte[] xmpProfile = { 1, 2, 3 }; + var exifProfile = new ExifProfile(); + exifProfile.SetValue(ExifTag.Software, "UnitTest"); + exifProfile.SetValue(ExifTag.Artist, "UnitTest"); + var metaData = new ImageFrameMetadata() + { + XmpProfile = xmpProfile, + ExifProfile = exifProfile + }; + + // act ImageFrameMetadata clone = metaData.DeepClone(); + + // assert + Assert.NotNull(clone); + Assert.NotNull(clone.ExifProfile); + Assert.NotNull(clone.XmpProfile); + Assert.False(metaData.ExifProfile.Equals(clone.ExifProfile)); + Assert.True(metaData.ExifProfile.Values.Count == clone.ExifProfile.Values.Count); + Assert.False(metaData.XmpProfile.Equals(clone.XmpProfile)); + Assert.True(metaData.XmpProfile.SequenceEqual(clone.XmpProfile)); Assert.False(metaData.GetGifMetadata().Equals(clone.GetGifMetadata())); } } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs index fef890a65..cac46fd60 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; -using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; @@ -70,7 +69,7 @@ namespace SixLabors.ImageSharp.Tests public void ConstructorEmpty() { new ExifProfile(null); - new ExifProfile(new byte[] { }); + new ExifProfile(Array.Empty()); } [Fact] @@ -328,7 +327,7 @@ namespace SixLabors.ImageSharp.Tests var junk = new StringBuilder(); for (int i = 0; i < 65600; i++) { - junk.Append("a"); + junk.Append('a'); } var image = new Image(100, 100); From 09f4957ce5167ab877d29ecff6dff092bce263fb Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 14 May 2021 14:12:26 +0200 Subject: [PATCH 236/275] Make TiffFrameMetadata internal --- .../Formats/Tiff/MetadataExtensions.cs | 7 ---- .../Formats/Tiff/TiffDecoderCore.cs | 3 +- .../Formats/Tiff/TiffEncoderCore.cs | 14 ++++++- src/ImageSharp/Formats/Tiff/TiffFormat.cs | 5 +-- .../Formats/Tiff/TiffFrameMetadata.cs | 2 +- .../Formats/Tiff/TiffEncoderTests.cs | 37 ++++++++++--------- .../Formats/Tiff/TiffMetadataTests.cs | 16 ++++---- 7 files changed, 43 insertions(+), 41 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/MetadataExtensions.cs b/src/ImageSharp/Formats/Tiff/MetadataExtensions.cs index b9da86fc4..c1cd3b153 100644 --- a/src/ImageSharp/Formats/Tiff/MetadataExtensions.cs +++ b/src/ImageSharp/Formats/Tiff/MetadataExtensions.cs @@ -17,12 +17,5 @@ namespace SixLabors.ImageSharp /// The metadata this method extends. /// The . public static TiffMetadata GetTiffMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(TiffFormat.Instance); - - /// - /// Gets the tiff format specific metadata for the image frame. - /// - /// The metadata this method extends. - /// The . - public static TiffFrameMetadata GetTiffMetadata(this ImageFrameMetadata metadata) => metadata.GetFormatMetadata(TiffFormat.Instance); } } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index da0d73832..31c91c590 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -179,8 +179,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff ImageFrameMetadata imageFrameMetaData = this.ignoreMetadata ? new ImageFrameMetadata() : new ImageFrameMetadata { ExifProfile = tags, XmpProfile = tags.GetValue(ExifTag.XMP)?.Value }; - tiffFrameMetaData = imageFrameMetaData.GetTiffMetadata(); - tiffFrameMetaData.Initialize(tags); + tiffFrameMetaData = new TiffFrameMetadata(tags); this.VerifyAndParse(tiffFrameMetaData); diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 24fd46526..6faab8383 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -112,9 +112,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff this.configuration = image.GetConfiguration(); ImageMetadata metadata = image.Metadata; TiffMetadata tiffMetadata = metadata.GetTiffMetadata(); - TiffFrameMetadata rootFrameMetaData = image.Frames.RootFrame.Metadata.GetTiffMetadata(); + TiffPhotometricInterpretation rootFramePhotometricInterpretation = GetRootFramePhotometricInterpretation(image); TiffPhotometricInterpretation photometricInterpretation = this.Mode == TiffEncodingMode.ColorPalette - ? TiffPhotometricInterpretation.PaletteColor : rootFrameMetaData.PhotometricInterpretation; + ? TiffPhotometricInterpretation.PaletteColor : rootFramePhotometricInterpretation; this.SetMode(tiffMetadata, photometricInterpretation); this.SetBitsPerPixel(tiffMetadata); @@ -375,5 +375,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff break; } } + + private static TiffPhotometricInterpretation GetRootFramePhotometricInterpretation(Image image) + { + ExifProfile exifProfile = image.Frames.RootFrame.Metadata.ExifProfile; + TiffPhotometricInterpretation rootFramePhotometricInterpretation = + exifProfile?.GetValue(ExifTag.PhotometricInterpretation) != null + ? (TiffPhotometricInterpretation)exifProfile?.GetValue(ExifTag.PhotometricInterpretation).Value + : TiffPhotometricInterpretation.WhiteIsZero; + return rootFramePhotometricInterpretation; + } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffFormat.cs b/src/ImageSharp/Formats/Tiff/TiffFormat.cs index ffae32093..bf0c4ebb1 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFormat.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFormat.cs @@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Encapsulates the means to encode and decode Tiff images. /// - public class TiffFormat : IImageFormat + public class TiffFormat : IImageFormat { private TiffFormat() { @@ -35,8 +35,5 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// public TiffMetadata CreateDefaultFormatMetadata() => new TiffMetadata(); - - /// - public TiffFrameMetadata CreateDefaultFormatFrameMetadata() => new TiffFrameMetadata(); } } diff --git a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs index f237ed329..b5b874063 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Provides Tiff specific metadata information for the frame. /// - public class TiffFrameMetadata : IDeepCloneable + internal class TiffFrameMetadata : IDeepCloneable { private const TiffPlanarConfiguration DefaultPlanarConfiguration = TiffPlanarConfiguration.Chunky; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index f24ce0c64..d9ffc7897 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -6,6 +6,7 @@ using System.IO; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Formats.Tiff.Constants; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; @@ -49,9 +50,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff memStream.Position = 0; using var output = Image.Load(Configuration, memStream); TiffMetadata meta = output.Metadata.GetTiffMetadata(); - TiffFrameMetadata frameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata(); + ExifProfile exifProfile = output.Frames.RootFrame.Metadata.ExifProfile; Assert.Equal(expectedBitsPerPixel, meta.BitsPerPixel); - Assert.Equal(TiffCompression.None, frameMetaData.Compression); + Assert.Equal(TiffCompression.None, (TiffCompression)exifProfile.GetValue(ExifTag.Compression).Value); } [Theory] @@ -73,9 +74,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff memStream.Position = 0; using var output = Image.Load(Configuration, memStream); TiffMetadata meta = output.Metadata.GetTiffMetadata(); - TiffFrameMetadata frameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata(); + ExifProfile exifProfile = output.Frames.RootFrame.Metadata.ExifProfile; Assert.Equal(bitsPerPixel, meta.BitsPerPixel); - Assert.Equal(TiffCompression.None, frameMetaData.Compression); + Assert.Equal(TiffCompression.None, (TiffCompression)exifProfile.GetValue(ExifTag.Compression).Value); } [Theory] @@ -113,9 +114,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff memStream.Position = 0; using var output = Image.Load(Configuration, memStream); TiffMetadata meta = output.Metadata.GetTiffMetadata(); - TiffFrameMetadata frameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata(); + ExifProfile exifProfile = output.Frames.RootFrame.Metadata.ExifProfile; Assert.Equal(expectedBitsPerPixel, meta.BitsPerPixel); - Assert.Equal(expectedCompression, frameMetaData.Compression); + Assert.Equal(expectedCompression, (TiffCompression)exifProfile.GetValue(ExifTag.Compression).Value); } [Theory] @@ -163,9 +164,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff memStream.Position = 0; using var output = Image.Load(Configuration, memStream); TiffMetadata meta = output.Metadata.GetTiffMetadata(); - TiffFrameMetadata frameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata(); + ExifProfile exifProfile = output.Frames.RootFrame.Metadata.ExifProfile; Assert.Equal(TiffBitsPerPixel.Bit1, meta.BitsPerPixel); - Assert.Equal(expectedCompression, frameMetaData.Compression); + Assert.Equal(expectedCompression, (TiffCompression)exifProfile.GetValue(ExifTag.Compression).Value); } [Theory] @@ -337,7 +338,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff var tiffEncoder = new TiffEncoder() { Mode = mode, Compression = compression }; using Image input = provider.GetImage(); using var memStream = new MemoryStream(); - TiffFrameMetadata inputMeta = input.Frames.RootFrame.Metadata.GetTiffMetadata(); + ExifProfile exifProfileInput = input.Frames.RootFrame.Metadata.ExifProfile; + var inputMeta = new TiffFrameMetadata(exifProfileInput); // act input.Save(memStream, tiffEncoder); @@ -345,14 +347,15 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // assert memStream.Position = 0; using var output = Image.Load(Configuration, memStream); - TiffFrameMetadata meta = output.Frames.RootFrame.Metadata.GetTiffMetadata(); + ExifProfile exifProfileOutput = output.Frames.RootFrame.Metadata.ExifProfile; + var outputMeta = new TiffFrameMetadata(exifProfileOutput); ImageFrame rootFrame = output.Frames.RootFrame; - Assert.True(output.Height > (int)meta.RowsPerStrip); - Assert.True(meta.StripOffsets.Length > 1); - Assert.True(meta.StripByteCounts.Length > 1); + Assert.True(output.Height > (int)outputMeta.RowsPerStrip); + Assert.True(outputMeta.StripOffsets.Length > 1); + Assert.True(outputMeta.StripByteCounts.Length > 1); - foreach (Number sz in meta.StripByteCounts) + foreach (Number sz in outputMeta.StripByteCounts) { Assert.True((uint)sz <= TiffConstants.DefaultStripSize); } @@ -360,11 +363,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // For uncompressed more accurate test. if (compression == TiffCompression.None) { - for (int i = 0; i < meta.StripByteCounts.Length - 1; i++) + for (int i = 0; i < outputMeta.StripByteCounts.Length - 1; i++) { // The difference must be less than one row. - int stripBytes = (int)meta.StripByteCounts[i]; - int widthBytes = (meta.BitsPerPixel + 7) / 8 * rootFrame.Width; + int stripBytes = (int)outputMeta.StripByteCounts[i]; + int widthBytes = (outputMeta.BitsPerPixel + 7) / 8 * rootFrame.Width; Assert.True((TiffConstants.DefaultStripSize - stripBytes) < widthBytes); } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index 66a90418b..1c2793080 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -55,7 +55,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { using (Image image = provider.GetImage(TiffDecoder)) { - TiffFrameMetadata meta = image.Frames.RootFrame.Metadata.GetTiffMetadata(); + ExifProfile exifProfile = image.Frames.RootFrame.Metadata.ExifProfile; + var meta = new TiffFrameMetadata(exifProfile); var cloneSameAsSampleMetaData = (TiffFrameMetadata)meta.DeepClone(); VerifyExpectedTiffFrameMetaDataIsPresent(cloneSameAsSampleMetaData); @@ -168,9 +169,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal(4, exifProfile.GetValue(ExifTag.Rating).Value); Assert.Equal(75, exifProfile.GetValue(ExifTag.RatingPercent).Value); - TiffFrameMetadata tiffFrameMetadata = rootFrame.Metadata.GetTiffMetadata(); - Assert.NotNull(tiffFrameMetadata); - ImageMetadata imageMetaData = image.Metadata; Assert.NotNull(imageMetaData); Assert.Equal(PixelResolutionUnit.PixelsPerInch, imageMetaData.ResolutionUnits); @@ -182,12 +180,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal(ByteOrder.LittleEndian, tiffMetaData.ByteOrder); Assert.Equal(TiffBitsPerPixel.Bit4, tiffMetaData.BitsPerPixel); + var tiffFrameMetadata = new TiffFrameMetadata(exifProfile); VerifyExpectedTiffFrameMetaDataIsPresent(tiffFrameMetadata); } } 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); @@ -222,12 +222,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal(2, image.Frames.Count); - TiffFrameMetadata frame0MetaData = image.Frames[0].Metadata.GetTiffMetadata(); + var frame0MetaData = new TiffFrameMetadata(image.Frames[0].Metadata.ExifProfile); Assert.Equal(TiffNewSubfileType.FullImage, frame0MetaData.SubfileType); Assert.Equal(255, image.Frames[0].Width); Assert.Equal(255, image.Frames[0].Height); - TiffFrameMetadata frame1MetaData = image.Frames[1].Metadata.GetTiffMetadata(); + var frame1MetaData = new TiffFrameMetadata(image.Frames[1].Metadata.ExifProfile); Assert.Equal(TiffNewSubfileType.Preview, frame1MetaData.SubfileType); Assert.Equal(255, image.Frames[1].Width); Assert.Equal(255, image.Frames[1].Height); @@ -244,7 +244,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff ImageMetadata inputMetaData = image.Metadata; TiffMetadata tiffMetaInput = image.Metadata.GetTiffMetadata(); - TiffFrameMetadata frameMetaInput = image.Frames.RootFrame.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; @@ -264,7 +264,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff ImageMetadata encodedImageMetaData = encodedImage.Metadata; TiffMetadata tiffMetaDataEncodedImage = encodedImageMetaData.GetTiffMetadata(); ImageFrame rootFrameEncodedImage = encodedImage.Frames.RootFrame; - TiffFrameMetadata tiffMetaDataEncodedRootFrame = rootFrameEncodedImage.Metadata.GetTiffMetadata(); + var tiffMetaDataEncodedRootFrame = new TiffFrameMetadata(rootFrameEncodedImage.Metadata.ExifProfile); ExifProfile encodedImageExifProfile = rootFrameEncodedImage.Metadata.ExifProfile; byte[] encodedImageXmpProfile = rootFrameEncodedImage.Metadata.XmpProfile; From fab1f3a5a04bde7948779f7d6ff7ab193143b793 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 15 May 2021 15:49:25 +0100 Subject: [PATCH 237/275] Make XMP internal, minor cleanup --- .../Formats/Tiff/TiffDecoderCore.cs | 18 ++++++--------- .../Tiff/TiffDecoderMetadataCreator.cs | 11 +++------ .../Formats/Tiff/TiffFrameMetadata.cs | 23 ++++++++++--------- src/ImageSharp/Metadata/ImageFrameMetadata.cs | 2 +- 4 files changed, 23 insertions(+), 31 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 31c91c590..b281d2453 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -120,22 +120,18 @@ namespace SixLabors.ImageSharp.Formats.Tiff ImageMetadata metadata = TiffDecoderMetadataCreator.Create(frames, tiffFramesMataData, this.ignoreMetadata, reader.ByteOrder); - // todo: tiff frames can have different sizes + // TODO: Tiff frames can have different sizes + ImageFrame root = frames[0]; + this.Dimensions = root.Size(); + foreach (ImageFrame frame in frames) { - ImageFrame root = frames[0]; - this.Dimensions = root.Size(); - foreach (ImageFrame frame in frames) + if (frame.Size() != root.Size()) { - if (frame.Size() != root.Size()) - { - TiffThrowHelper.ThrowNotSupported("Images with different sizes are not supported"); - } + TiffThrowHelper.ThrowNotSupported("Images with different sizes are not supported"); } } - var image = new Image(this.Configuration, metadata, frames); - - return image; + return new Image(this.Configuration, metadata, frames); } /// diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs index 77d3d041c..337676297 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs @@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff public static ImageMetadata Create(List> frames, List framesMetaData, bool ignoreMetadata, ByteOrder byteOrder) where TPixel : unmanaged, IPixel { - DebugGuard.IsTrue(frames.Count() == framesMetaData.Count, nameof(frames), "Image frames and frames metdadata should be the same size."); + DebugGuard.IsTrue(frames.Count == framesMetaData.Count, nameof(frames), "Image frames and frames metadata should be the same size."); if (framesMetaData.Count < 1) { @@ -50,12 +50,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff } } - if (imageMetaData.IptcProfile == null) + if (imageMetaData.IptcProfile == null && TryGetIptc(frameMetaData.ExifProfile.Values, out byte[] iptcBytes)) { - if (TryGetIptc(frameMetaData.ExifProfile.Values, out byte[] iptcBytes)) - { - imageMetaData.IptcProfile = new IptcProfile(iptcBytes); - } + imageMetaData.IptcProfile = new IptcProfile(iptcBytes); } if (imageMetaData.IccProfile == null) @@ -146,8 +143,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff return true; } - - return false; } return false; diff --git a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs index b5b874063..e8ef6a2ff 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs @@ -217,17 +217,18 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// public IDeepCloneable DeepClone() { - var clone = new TiffFrameMetadata(); - - clone.FillOrder = this.FillOrder; - clone.Compression = this.Compression; - clone.FaxCompressionOptions = this.FaxCompressionOptions; - clone.SubfileType = this.SubfileType ?? TiffNewSubfileType.FullImage; - clone.OldSubfileType = this.OldSubfileType ?? TiffSubfileType.FullImage; - clone.HorizontalResolution = this.HorizontalResolution ?? ImageMetadata.DefaultHorizontalResolution; - clone.VerticalResolution = this.VerticalResolution ?? ImageMetadata.DefaultVerticalResolution; - clone.ResolutionUnit = this.ResolutionUnit; - clone.PlanarConfiguration = this.PlanarConfiguration; + 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 + }; if (this.ColorMap != null) { diff --git a/src/ImageSharp/Metadata/ImageFrameMetadata.cs b/src/ImageSharp/Metadata/ImageFrameMetadata.cs index 761cce1c1..ae298d518 100644 --- a/src/ImageSharp/Metadata/ImageFrameMetadata.cs +++ b/src/ImageSharp/Metadata/ImageFrameMetadata.cs @@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Metadata /// /// Gets or sets the XMP profile. /// - public byte[] XmpProfile { get; set; } + internal byte[] XmpProfile { get; set; } /// public ImageFrameMetadata DeepClone() => new ImageFrameMetadata(this); From b0ecabbbd743e7f21874964fb0e897e2dec4159d Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 18 May 2021 18:04:10 +0200 Subject: [PATCH 238/275] Remove BitsPerPixel from TiffMetaData, its already present in TiffFrameMetadata --- .../Formats/Tiff/TiffDecoderCore.cs | 2 +- .../Tiff/TiffDecoderMetadataCreator.cs | 2 -- .../Formats/Tiff/TiffEncoderCore.cs | 26 +++++++++++-------- .../Formats/Tiff/TiffFrameMetadata.cs | 6 ++--- src/ImageSharp/Formats/Tiff/TiffMetadata.cs | 11 +------- .../Formats/Tiff/TiffEncoderTests.cs | 25 +++++++++--------- .../Formats/Tiff/TiffMetadataTests.cs | 21 +++++++-------- 7 files changed, 43 insertions(+), 50 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index b281d2453..d476f9bb7 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -157,7 +157,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff int width = GetImageWidth(rootFrameExifProfile); int height = GetImageHeight(rootFrameExifProfile); - return new ImageInfo(new PixelTypeInfo(root.BitsPerPixel), width, height, metadata); + return new ImageInfo(new PixelTypeInfo((int)root.BitsPerPixel), width, height, metadata); } /// diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs index 337676297..d501392b0 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs @@ -33,7 +33,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff TiffMetadata tiffMetadata = imageMetaData.GetTiffMetadata(); tiffMetadata.ByteOrder = byteOrder; - tiffMetadata.BitsPerPixel = GetBitsPerPixel(rootFrameMetadata); if (!ignoreMetadata) { @@ -82,7 +81,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff TiffMetadata tiffMetadata = imageMetaData.GetTiffMetadata(); tiffMetadata.ByteOrder = byteOrder; - tiffMetadata.BitsPerPixel = GetBitsPerPixel(rootFrameMetadata); return imageMetaData; } diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 6faab8383..f930b3c9e 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -110,21 +110,25 @@ namespace SixLabors.ImageSharp.Formats.Tiff Guard.NotNull(stream, nameof(stream)); this.configuration = image.GetConfiguration(); - ImageMetadata metadata = image.Metadata; - TiffMetadata tiffMetadata = metadata.GetTiffMetadata(); + ExifProfile rootFrameExifProfile = image.Frames.RootFrame.Metadata.ExifProfile; TiffPhotometricInterpretation rootFramePhotometricInterpretation = GetRootFramePhotometricInterpretation(image); TiffPhotometricInterpretation photometricInterpretation = this.Mode == TiffEncodingMode.ColorPalette ? TiffPhotometricInterpretation.PaletteColor : rootFramePhotometricInterpretation; + TiffBitsPerPixel? rootFrameBitsPerPixel = null; + if (rootFrameExifProfile != null) + { + rootFrameBitsPerPixel = new TiffFrameMetadata(rootFrameExifProfile).BitsPerPixel; + } - this.SetMode(tiffMetadata, photometricInterpretation); - this.SetBitsPerPixel(tiffMetadata); + this.SetMode(rootFrameBitsPerPixel, photometricInterpretation); + this.SetBitsPerPixel(rootFrameBitsPerPixel); this.SetPhotometricInterpretation(); using (var writer = new TiffStreamWriter(stream)) { long firstIfdMarker = this.WriteHeader(writer); - // TODO: multiframing is not support + // TODO: multiframing is not supported this.WriteImage(writer, image, firstIfdMarker); } } @@ -264,7 +268,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff return nextIfdMarker; } - private void SetMode(TiffMetadata tiffMetadata, TiffPhotometricInterpretation photometricInterpretation) + private void SetMode(TiffBitsPerPixel? rootFrameBitsPerPixel, TiffPhotometricInterpretation photometricInterpretation) { // Make sure, that the fax compressions are only used together with the BiColor mode. if (this.CompressionType == TiffCompression.CcittGroup3Fax || this.CompressionType == TiffCompression.Ccitt1D) @@ -282,10 +286,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff } } - if (this.Mode == TiffEncodingMode.Default && tiffMetadata.BitsPerPixel != null) + if (this.Mode == TiffEncodingMode.Default && rootFrameBitsPerPixel.HasValue) { - // Preserve input bits per pixel, if no encoding mode was specified. - this.SetModeWithBitsPerPixel(tiffMetadata.BitsPerPixel, photometricInterpretation); + // Preserve input bits per pixel, if no encoding mode was specified and the input image has a bits per pixel set. + this.SetModeWithBitsPerPixel(rootFrameBitsPerPixel, photometricInterpretation); return; } @@ -319,9 +323,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff } } - private void SetBitsPerPixel(TiffMetadata tiffMetadata) + private void SetBitsPerPixel(TiffBitsPerPixel? rootFrameBitsPerPixel) { - this.BitsPerPixel ??= tiffMetadata.BitsPerPixel; + this.BitsPerPixel ??= rootFrameBitsPerPixel; switch (this.Mode) { case TiffEncodingMode.BiColor: diff --git a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs index e8ef6a2ff..61cce1573 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs @@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// Initializes a new instance of the class. /// /// The Tiff frame directory tags. - public TiffFrameMetadata(ExifProfile frameTags) => this.Initialize(frameTags); + public TiffFrameMetadata(ExifProfile frameTags) => this.Initialize(frameTags ?? new ExifProfile()); /// /// Gets or sets a general indication of the kind of data contained in this subfile. @@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Gets or sets the bits per pixel. /// - public int BitsPerPixel { get; set; } + public TiffBitsPerPixel BitsPerPixel { get; set; } /// /// Gets or sets the compression scheme used on the image data. @@ -189,7 +189,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff this.BitsPerSample = bits.GetBitsPerSample(); } - this.BitsPerPixel = this.BitsPerSample.GetValueOrDefault().BitsPerPixel(); + this.BitsPerPixel = (TiffBitsPerPixel)this.BitsPerSample.GetValueOrDefault().BitsPerPixel(); } /// diff --git a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs index 9d36d6613..cf1ab3754 100644 --- a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs @@ -19,22 +19,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// Initializes a new instance of the class. /// /// The metadata to create an instance from. - private TiffMetadata(TiffMetadata other) - { - this.ByteOrder = other.ByteOrder; - this.BitsPerPixel = other.BitsPerPixel; - } + private TiffMetadata(TiffMetadata other) => this.ByteOrder = other.ByteOrder; /// /// Gets or sets the byte order. /// public ByteOrder ByteOrder { get; set; } - /// - /// Gets or sets the number of bits per pixel for the root frame. - /// - public TiffBitsPerPixel? BitsPerPixel { get; set; } - /// public IDeepCloneable DeepClone() => new TiffMetadata(this); } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index d9ffc7897..85229a470 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -49,9 +49,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // assert memStream.Position = 0; using var output = Image.Load(Configuration, memStream); - TiffMetadata meta = output.Metadata.GetTiffMetadata(); ExifProfile exifProfile = output.Frames.RootFrame.Metadata.ExifProfile; - Assert.Equal(expectedBitsPerPixel, meta.BitsPerPixel); + var frameMetaData = new TiffFrameMetadata(exifProfile); + Assert.Equal(expectedBitsPerPixel, frameMetaData.BitsPerPixel); Assert.Equal(TiffCompression.None, (TiffCompression)exifProfile.GetValue(ExifTag.Compression).Value); } @@ -73,9 +73,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // assert memStream.Position = 0; using var output = Image.Load(Configuration, memStream); - TiffMetadata meta = output.Metadata.GetTiffMetadata(); ExifProfile exifProfile = output.Frames.RootFrame.Metadata.ExifProfile; - Assert.Equal(bitsPerPixel, meta.BitsPerPixel); + var frameMetaData = new TiffFrameMetadata(exifProfile); + Assert.Equal(bitsPerPixel, frameMetaData.BitsPerPixel); Assert.Equal(TiffCompression.None, (TiffCompression)exifProfile.GetValue(ExifTag.Compression).Value); } @@ -113,9 +113,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // assert memStream.Position = 0; using var output = Image.Load(Configuration, memStream); - TiffMetadata meta = output.Metadata.GetTiffMetadata(); ExifProfile exifProfile = output.Frames.RootFrame.Metadata.ExifProfile; - Assert.Equal(expectedBitsPerPixel, meta.BitsPerPixel); + var frameMetaData = new TiffFrameMetadata(exifProfile); + Assert.Equal(expectedBitsPerPixel, frameMetaData.BitsPerPixel); Assert.Equal(expectedCompression, (TiffCompression)exifProfile.GetValue(ExifTag.Compression).Value); } @@ -140,8 +140,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // assert memStream.Position = 0; using var output = Image.Load(Configuration, memStream); - TiffMetadata meta = output.Metadata.GetTiffMetadata(); - Assert.Equal(expectedBitsPerPixel, meta.BitsPerPixel); + ExifProfile exifProfile = output.Frames.RootFrame.Metadata.ExifProfile; + var frameMetaData = new TiffFrameMetadata(exifProfile); + Assert.Equal(expectedBitsPerPixel, frameMetaData.BitsPerPixel); } [Theory] @@ -163,9 +164,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // assert memStream.Position = 0; using var output = Image.Load(Configuration, memStream); - TiffMetadata meta = output.Metadata.GetTiffMetadata(); ExifProfile exifProfile = output.Frames.RootFrame.Metadata.ExifProfile; - Assert.Equal(TiffBitsPerPixel.Bit1, meta.BitsPerPixel); + var frameMetaData = new TiffFrameMetadata(exifProfile); + Assert.Equal(TiffBitsPerPixel.Bit1, frameMetaData.BitsPerPixel); Assert.Equal(expectedCompression, (TiffCompression)exifProfile.GetValue(ExifTag.Compression).Value); } @@ -367,7 +368,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { // The difference must be less than one row. int stripBytes = (int)outputMeta.StripByteCounts[i]; - int widthBytes = (outputMeta.BitsPerPixel + 7) / 8 * rootFrame.Width; + int widthBytes = ((int)outputMeta.BitsPerPixel + 7) / 8 * rootFrame.Width; Assert.True((TiffConstants.DefaultStripSize - stripBytes) < widthBytes); } @@ -376,7 +377,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // Compare with reference. TestTiffEncoderCore( provider, - (TiffBitsPerPixel)inputMeta.BitsPerPixel, + inputMeta.BitsPerPixel, mode, inputMeta.Compression); } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index 1c2793080..a302cc110 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -35,16 +35,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { var meta = new TiffMetadata { - BitsPerPixel = TiffBitsPerPixel.Bit8, ByteOrder = ByteOrder.BigEndian, }; var clone = (TiffMetadata)meta.DeepClone(); - clone.BitsPerPixel = TiffBitsPerPixel.Bit24; clone.ByteOrder = ByteOrder.LittleEndian; - Assert.False(meta.BitsPerPixel == clone.BitsPerPixel); Assert.False(meta.ByteOrder == clone.ByteOrder); } @@ -71,10 +68,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff } [Theory] - [InlineData(Calliphora_BiColorUncompressed, TiffBitsPerPixel.Bit1)] - [InlineData(GrayscaleUncompressed, TiffBitsPerPixel.Bit8)] - [InlineData(RgbUncompressed, TiffBitsPerPixel.Bit24)] - public void Identify_DetectsCorrectBitPerPixel(string imagePath, TiffBitsPerPixel expectedBitsPerPixel) + [InlineData(Calliphora_BiColorUncompressed, 1)] + [InlineData(GrayscaleUncompressed, 8)] + [InlineData(RgbUncompressed, 24)] + public void Identify_DetectsCorrectBitPerPixel(string imagePath, int expectedBitsPerPixel) { var testFile = TestFile.Create(imagePath); using var stream = new MemoryStream(testFile.Bytes, false); @@ -84,7 +81,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.NotNull(imageInfo); TiffMetadata tiffMetadata = imageInfo.Metadata.GetTiffMetadata(); Assert.NotNull(tiffMetadata); - Assert.Equal(expectedBitsPerPixel, tiffMetadata.BitsPerPixel); + Assert.Equal(expectedBitsPerPixel, imageInfo.PixelType.BitsPerPixel); } [Theory] @@ -178,7 +175,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff TiffMetadata tiffMetaData = image.Metadata.GetTiffMetadata(); Assert.NotNull(tiffMetaData); Assert.Equal(ByteOrder.LittleEndian, tiffMetaData.ByteOrder); - Assert.Equal(TiffBitsPerPixel.Bit4, tiffMetaData.BitsPerPixel); + + var frameMetaData = new TiffFrameMetadata(exifProfile); + Assert.Equal(TiffBitsPerPixel.Bit4, frameMetaData.BitsPerPixel); var tiffFrameMetadata = new TiffFrameMetadata(exifProfile); VerifyExpectedTiffFrameMetaDataIsPresent(tiffFrameMetadata); @@ -250,7 +249,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff ExifProfile rootFrameExifProfileInput = rootFrameInput.Metadata.ExifProfile; Assert.Equal(TiffCompression.Lzw, frameMetaInput.Compression); - Assert.Equal(TiffBitsPerPixel.Bit4, tiffMetaInput.BitsPerPixel); + Assert.Equal(TiffBitsPerPixel.Bit4, frameMetaInput.BitsPerPixel); // Save to Tiff var tiffEncoder = new TiffEncoder() { Mode = TiffEncodingMode.Rgb }; @@ -268,7 +267,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff ExifProfile encodedImageExifProfile = rootFrameEncodedImage.Metadata.ExifProfile; byte[] encodedImageXmpProfile = rootFrameEncodedImage.Metadata.XmpProfile; - Assert.Equal(TiffBitsPerPixel.Bit24, tiffMetaDataEncodedImage.BitsPerPixel); + Assert.Equal(TiffBitsPerPixel.Bit24, tiffMetaDataEncodedRootFrame.BitsPerPixel); Assert.Equal(TiffCompression.None, tiffMetaDataEncodedRootFrame.Compression); Assert.Equal(inputMetaData.HorizontalResolution, encodedImageMetaData.HorizontalResolution); From 6ba3f101faa421b670b41d9957e1e2aaf5a7b6ab Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 18 May 2021 18:06:19 +0200 Subject: [PATCH 239/275] Remove setting XMP profile twice --- .../Formats/Tiff/TiffDecoderMetadataCreator.cs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs index d501392b0..5bbcd6681 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs @@ -40,15 +40,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff { ImageFrame frame = frames[i]; ImageFrameMetadata frameMetaData = frame.Metadata; - if (frameMetaData.XmpProfile == null) - { - IExifValue val = frameMetaData.ExifProfile.GetValue(ExifTag.XMP); - if (val != null) - { - frameMetaData.XmpProfile = val.Value; - } - } - if (imageMetaData.IptcProfile == null && TryGetIptc(frameMetaData.ExifProfile.Values, out byte[] iptcBytes)) { imageMetaData.IptcProfile = new IptcProfile(iptcBytes); @@ -145,8 +136,5 @@ namespace SixLabors.ImageSharp.Formats.Tiff return false; } - - private static TiffBitsPerPixel GetBitsPerPixel(TiffFrameMetadata firstFrameMetaData) - => (TiffBitsPerPixel)firstFrameMetaData.BitsPerPixel; } } From e2bd192c7906d2a27e67214f7ca41782da7e85d5 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 18 May 2021 18:39:09 +0200 Subject: [PATCH 240/275] Move IPTC and ICC Profile to image frame metadata --- .../Tiff/TiffDecoderMetadataCreator.cs | 13 +++++------- src/ImageSharp/Metadata/ImageFrameMetadata.cs | 14 +++++++++++++ .../Formats/Tiff/TiffMetadataTests.cs | 5 +++-- .../Metadata/ImageFrameMetadataTests.cs | 20 +++++++++++++++++-- 4 files changed, 40 insertions(+), 12 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs index 5bbcd6681..3264d2cba 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs @@ -40,18 +40,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff { ImageFrame frame = frames[i]; ImageFrameMetadata frameMetaData = frame.Metadata; - if (imageMetaData.IptcProfile == null && TryGetIptc(frameMetaData.ExifProfile.Values, out byte[] iptcBytes)) + if (TryGetIptc(frameMetaData.ExifProfile.Values, out byte[] iptcBytes)) { - imageMetaData.IptcProfile = new IptcProfile(iptcBytes); + frameMetaData.IptcProfile = new IptcProfile(iptcBytes); } - if (imageMetaData.IccProfile == null) + IExifValue iccProfileBytes = frameMetaData.ExifProfile.GetValue(ExifTag.IccProfile); + if (iccProfileBytes != null) { - IExifValue val = frameMetaData.ExifProfile.GetValue(ExifTag.IccProfile); - if (val != null) - { - imageMetaData.IccProfile = new IccProfile(val.Value); - } + frameMetaData.IccProfile = new IccProfile(iccProfileBytes.Value); } } } diff --git a/src/ImageSharp/Metadata/ImageFrameMetadata.cs b/src/ImageSharp/Metadata/ImageFrameMetadata.cs index ae298d518..1819fd2bc 100644 --- a/src/ImageSharp/Metadata/ImageFrameMetadata.cs +++ b/src/ImageSharp/Metadata/ImageFrameMetadata.cs @@ -5,6 +5,8 @@ using System; using System.Collections.Generic; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Metadata.Profiles.Exif; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; +using SixLabors.ImageSharp.Metadata.Profiles.Iptc; namespace SixLabors.ImageSharp.Metadata { @@ -39,6 +41,8 @@ namespace SixLabors.ImageSharp.Metadata } this.ExifProfile = other.ExifProfile?.DeepClone(); + this.IccProfile = other.IccProfile?.DeepClone(); + this.IptcProfile = other.IptcProfile?.DeepClone(); this.XmpProfile = other.XmpProfile != null ? new byte[other.XmpProfile.Length] : null; other.XmpProfile?.AsSpan().CopyTo(this.XmpProfile.AsSpan()); } @@ -53,6 +57,16 @@ namespace SixLabors.ImageSharp.Metadata /// internal byte[] XmpProfile { get; set; } + /// + /// Gets or sets the list of ICC profiles. + /// + public IccProfile IccProfile { get; set; } + + /// + /// Gets or sets the iptc profile. + /// + public IptcProfile IptcProfile { get; set; } + /// public ImageFrameMetadata DeepClone() => new ImageFrameMetadata(this); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index a302cc110..5298c9998 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -133,8 +133,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { using Image image = provider.GetImage(TiffDecoder); - Assert.NotNull(image.Metadata.IptcProfile); - IptcValue byline = image.Metadata.IptcProfile.Values.FirstOrDefault(data => data.Tag == IptcTag.Byline); + IptcProfile iptcProfile = image.Frames.RootFrame.Metadata.IptcProfile; + Assert.NotNull(iptcProfile); + IptcValue byline = iptcProfile.Values.FirstOrDefault(data => data.Tag == IptcTag.Byline); Assert.NotNull(byline); Assert.Equal("Studio Mantyniemi", byline.Value); } diff --git a/tests/ImageSharp.Tests/Metadata/ImageFrameMetadataTests.cs b/tests/ImageSharp.Tests/Metadata/ImageFrameMetadataTests.cs index cf63db39b..f1a90d43e 100644 --- a/tests/ImageSharp.Tests/Metadata/ImageFrameMetadataTests.cs +++ b/tests/ImageSharp.Tests/Metadata/ImageFrameMetadataTests.cs @@ -4,8 +4,10 @@ using System.Linq; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Metadata; -using SixLabors.ImageSharp.Metadata.Profiles.Exif; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; +using ExifProfile = SixLabors.ImageSharp.Metadata.Profiles.Exif.ExifProfile; +using ExifTag = SixLabors.ImageSharp.Metadata.Profiles.Exif.ExifTag; namespace SixLabors.ImageSharp.Tests.Metadata { @@ -43,10 +45,20 @@ namespace SixLabors.ImageSharp.Tests.Metadata var exifProfile = new ExifProfile(); exifProfile.SetValue(ExifTag.Software, "UnitTest"); exifProfile.SetValue(ExifTag.Artist, "UnitTest"); + var iccProfile = new IccProfile() + { + Header = new IccProfileHeader() + { + CmmType = "Unittest" + } + }; + var iptcProfile = new ImageSharp.Metadata.Profiles.Iptc.IptcProfile(); var metaData = new ImageFrameMetadata() { XmpProfile = xmpProfile, - ExifProfile = exifProfile + ExifProfile = exifProfile, + IccProfile = iccProfile, + IptcProfile = iptcProfile }; // act @@ -56,11 +68,15 @@ namespace SixLabors.ImageSharp.Tests.Metadata Assert.NotNull(clone); Assert.NotNull(clone.ExifProfile); Assert.NotNull(clone.XmpProfile); + Assert.NotNull(clone.IccProfile); + Assert.NotNull(clone.IptcProfile); Assert.False(metaData.ExifProfile.Equals(clone.ExifProfile)); Assert.True(metaData.ExifProfile.Values.Count == clone.ExifProfile.Values.Count); Assert.False(metaData.XmpProfile.Equals(clone.XmpProfile)); Assert.True(metaData.XmpProfile.SequenceEqual(clone.XmpProfile)); Assert.False(metaData.GetGifMetadata().Equals(clone.GetGifMetadata())); + Assert.False(metaData.IccProfile.Equals(clone.IccProfile)); + Assert.False(metaData.IptcProfile.Equals(clone.IptcProfile)); } } } From a2424750c6d19745c9588b02901b689ea8ce91fd Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 18 May 2021 19:11:19 +0200 Subject: [PATCH 241/275] Fix failing IPTC test --- .../Metadata/Profiles/IPTC/IptcProfileTests.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs index e62a5cbab..b5bb53b5b 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs @@ -121,8 +121,9 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.IPTC { using (Image image = provider.GetImage(TiffDecoder)) { - Assert.NotNull(image.Metadata.IptcProfile); - var iptcValues = image.Metadata.IptcProfile.Values.ToList(); + IptcProfile iptc = image.Frames.RootFrame.Metadata.IptcProfile; + Assert.NotNull(iptc); + var iptcValues = iptc.Values.ToList(); IptcProfileContainsExpectedValues(iptcValues); } } From 8461c72ed9c9649c0249c271e3bedc6f239a0d9b Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 19 May 2021 10:25:17 +0200 Subject: [PATCH 242/275] Remove properties from TiffFrameMetadata, which can be accessed by the exifProfile directly --- .../Formats/Tiff/TiffDecoderCore.cs | 11 +- .../Tiff/TiffDecoderMetadataCreator.cs | 25 +- .../Formats/Tiff/TiffDecoderOptionsParser.cs | 67 ++++-- .../Formats/Tiff/TiffFrameMetadata.cs | 223 +----------------- .../Formats/Tiff/TiffEncoderTests.cs | 17 +- .../Formats/Tiff/TiffMetadataTests.cs | 96 ++++---- 6 files changed, 128 insertions(+), 311 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index d476f9bb7..b977c2536 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 3264d2cba..95d931b0e 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 45a55b274..9ce9ce8e6 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 61cce1573..7376b114b 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 85229a470..2fdc66347 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 5298c9998..3a6dea9aa 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); } } } From 4094be164725d5d3895fd5f221ed59d00e7ce423 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 19 May 2021 15:41:45 +0100 Subject: [PATCH 243/275] Attempt at making frame metadata public --- .../Formats/Tiff/MetadataExtensions.cs | 7 ++ .../Formats/Tiff/TiffDecoderCore.cs | 33 ++++------ .../Tiff/TiffDecoderMetadataCreator.cs | 13 +--- .../Formats/Tiff/TiffEncoderCore.cs | 60 ++++++++--------- src/ImageSharp/Formats/Tiff/TiffFormat.cs | 5 +- .../Formats/Tiff/TiffFrameMetadata.cs | 66 +++++++++++-------- .../Formats/Tiff/TiffEncoderTests.cs | 15 +++-- .../Formats/Tiff/TiffMetadataTests.cs | 10 +-- 8 files changed, 105 insertions(+), 104 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/MetadataExtensions.cs b/src/ImageSharp/Formats/Tiff/MetadataExtensions.cs index c1cd3b153..b9da86fc4 100644 --- a/src/ImageSharp/Formats/Tiff/MetadataExtensions.cs +++ b/src/ImageSharp/Formats/Tiff/MetadataExtensions.cs @@ -17,5 +17,12 @@ namespace SixLabors.ImageSharp /// The metadata this method extends. /// The . public static TiffMetadata GetTiffMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(TiffFormat.Instance); + + /// + /// Gets the tiff format specific metadata for the image frame. + /// + /// The metadata this method extends. + /// The . + public static TiffFrameMetadata GetTiffMetadata(this ImageFrameMetadata metadata) => metadata.GetFormatMetadata(TiffFormat.Instance); } } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index b977c2536..b7b764007 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -110,15 +110,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff IEnumerable directories = reader.Read(); var frames = new List>(); - var tiffFramesMataData = new List(); foreach (ExifProfile ifd in directories) { - ImageFrame frame = this.DecodeFrame(ifd, out TiffFrameMetadata frameMetadata); + ImageFrame frame = this.DecodeFrame(ifd); frames.Add(frame); - tiffFramesMataData.Add(frameMetadata); } - ImageMetadata metadata = TiffDecoderMetadataCreator.Create(frames, tiffFramesMataData, this.ignoreMetadata, reader.ByteOrder); + ImageMetadata metadata = TiffDecoderMetadataCreator.Create(frames, this.ignoreMetadata, reader.ByteOrder); // TODO: Tiff frames can have different sizes ImageFrame root = frames[0]; @@ -139,24 +137,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff { this.inputStream = stream; var reader = new DirectoryReader(stream); - IEnumerable directories = reader.Read(); - var framesMetadata = new List(); - foreach (ExifProfile ifd in directories) - { - var meta = new TiffFrameMetadata(ifd); - meta.Initialize(ifd); - framesMetadata.Add(meta); - } - - TiffFrameMetadata root = framesMetadata[0]; ExifProfile rootFrameExifProfile = directories.First(); - ImageMetadata metadata = TiffDecoderMetadataCreator.Create(framesMetadata, reader.ByteOrder, rootFrameExifProfile); + var rootMetadata = TiffFrameMetadata.Parse(rootFrameExifProfile); + + ImageMetadata metadata = TiffDecoderMetadataCreator.Create(reader.ByteOrder, rootFrameExifProfile); int width = GetImageWidth(rootFrameExifProfile); int height = GetImageHeight(rootFrameExifProfile); - return new ImageInfo(new PixelTypeInfo((int)root.BitsPerPixel), width, height, metadata); + return new ImageInfo(new PixelTypeInfo((int)rootMetadata.BitsPerPixel), width, height, metadata); } /// @@ -164,17 +154,18 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// The pixel format. /// The IFD tags. - /// The tiff frame metadata. /// /// The tiff frame. /// - private ImageFrame DecodeFrame(ExifProfile tags, out TiffFrameMetadata tiffFrameMetaData) + private ImageFrame DecodeFrame(ExifProfile tags) where TPixel : unmanaged, IPixel { ImageFrameMetadata imageFrameMetaData = this.ignoreMetadata ? new ImageFrameMetadata() : new ImageFrameMetadata { ExifProfile = tags, XmpProfile = tags.GetValue(ExifTag.XMP)?.Value }; - tiffFrameMetaData = new TiffFrameMetadata(tags); + + TiffFrameMetadata tiffFrameMetaData = imageFrameMetaData.GetTiffMetadata(); + TiffFrameMetadata.Parse(tiffFrameMetaData, tags); this.VerifyAndParse(tags, tiffFrameMetaData); @@ -220,9 +211,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff } int bytesPerRow = ((width * bitsPerPixel) + 7) / 8; - int stripBytes = bytesPerRow * height; - - return stripBytes; + return bytesPerRow * height; } /// diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs index 95d931b0e..6f8a81a82 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs @@ -18,12 +18,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// internal static class TiffDecoderMetadataCreator { - public static ImageMetadata Create(List> frames, List framesMetaData, bool ignoreMetadata, ByteOrder byteOrder) + public static ImageMetadata Create(List> frames, bool ignoreMetadata, ByteOrder byteOrder) where TPixel : unmanaged, IPixel { - DebugGuard.IsTrue(frames.Count == framesMetaData.Count, nameof(frames), "Image frames and frames metadata should be the same size."); - - if (framesMetaData.Count < 1) + if (frames.Count < 1) { TiffThrowHelper.ThrowImageFormatException("Expected at least one frame."); } @@ -58,13 +56,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff return imageMetaData; } - public static ImageMetadata Create(List framesMetaData, ByteOrder byteOrder, ExifProfile exifProfile) + public static ImageMetadata Create(ByteOrder byteOrder, ExifProfile exifProfile) { - if (framesMetaData.Count < 1) - { - TiffThrowHelper.ThrowImageFormatException("Expected at least one frame."); - } - var imageMetaData = new ImageMetadata(); SetResolution(imageMetaData, exifProfile); diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index f930b3c9e..055def11d 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -12,7 +12,6 @@ using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Formats.Tiff.Writers; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; @@ -110,18 +109,19 @@ namespace SixLabors.ImageSharp.Formats.Tiff Guard.NotNull(stream, nameof(stream)); this.configuration = image.GetConfiguration(); - ExifProfile rootFrameExifProfile = image.Frames.RootFrame.Metadata.ExifProfile; + TiffPhotometricInterpretation rootFramePhotometricInterpretation = GetRootFramePhotometricInterpretation(image); TiffPhotometricInterpretation photometricInterpretation = this.Mode == TiffEncodingMode.ColorPalette - ? TiffPhotometricInterpretation.PaletteColor : rootFramePhotometricInterpretation; - TiffBitsPerPixel? rootFrameBitsPerPixel = null; - if (rootFrameExifProfile != null) - { - rootFrameBitsPerPixel = new TiffFrameMetadata(rootFrameExifProfile).BitsPerPixel; - } + ? TiffPhotometricInterpretation.PaletteColor + : rootFramePhotometricInterpretation; - this.SetMode(rootFrameBitsPerPixel, photometricInterpretation); + TiffBitsPerPixel? rootFrameBitsPerPixel = image.Frames.RootFrame.Metadata.GetTiffMetadata().BitsPerPixel; + + // TODO: This isn't correct. + // We're overwriting explicit BPP based upon the Mode. It should be the other way around. + // BPP should also be nullable and based upon the current TPixel if not set. this.SetBitsPerPixel(rootFrameBitsPerPixel); + this.SetMode(photometricInterpretation); this.SetPhotometricInterpretation(); using (var writer = new TiffStreamWriter(stream)) @@ -144,9 +144,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff { writer.Write(ByteOrderMarker); writer.Write(TiffConstants.HeaderMagicNumber); - long firstIfdMarker = writer.PlaceMarker(); - - return firstIfdMarker; + return writer.PlaceMarker(); } /// @@ -206,7 +204,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff int rowsPerStrip = TiffConstants.DefaultStripSize / bytesPerRow; - return rowsPerStrip > 0 ? (rowsPerStrip < height ? rowsPerStrip : height) : 1; + if (rowsPerStrip > 0) + { + if (rowsPerStrip < height) + { + return rowsPerStrip; + } + + return height; + } + + return 1; } /// @@ -219,6 +227,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff { if (entries.Count == 0) { + // TODO: Perf. Throwhelper throw new ArgumentException("There must be at least one entry per IFD.", nameof(entries)); } @@ -268,7 +277,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff return nextIfdMarker; } - private void SetMode(TiffBitsPerPixel? rootFrameBitsPerPixel, TiffPhotometricInterpretation photometricInterpretation) + private void SetMode(TiffPhotometricInterpretation photometricInterpretation) { // Make sure, that the fax compressions are only used together with the BiColor mode. if (this.CompressionType == TiffCompression.CcittGroup3Fax || this.CompressionType == TiffCompression.Ccitt1D) @@ -286,19 +295,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff } } - if (this.Mode == TiffEncodingMode.Default && rootFrameBitsPerPixel.HasValue) - { - // Preserve input bits per pixel, if no encoding mode was specified and the input image has a bits per pixel set. - this.SetModeWithBitsPerPixel(rootFrameBitsPerPixel, photometricInterpretation); - - return; - } - - if (this.BitsPerPixel != null) - { - // The user has specified a bits per pixel, so use that to determine the encoding mode. - this.SetModeWithBitsPerPixel(this.BitsPerPixel, photometricInterpretation); - } + // Use the bits per pixel to determine the encoding mode. + this.SetModeWithBitsPerPixel(this.BitsPerPixel, photometricInterpretation); } private void SetModeWithBitsPerPixel(TiffBitsPerPixel? bitsPerPixel, TiffPhotometricInterpretation photometricInterpretation) @@ -383,11 +381,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff private static TiffPhotometricInterpretation GetRootFramePhotometricInterpretation(Image image) { ExifProfile exifProfile = image.Frames.RootFrame.Metadata.ExifProfile; - TiffPhotometricInterpretation rootFramePhotometricInterpretation = - exifProfile?.GetValue(ExifTag.PhotometricInterpretation) != null - ? (TiffPhotometricInterpretation)exifProfile?.GetValue(ExifTag.PhotometricInterpretation).Value - : TiffPhotometricInterpretation.WhiteIsZero; - return rootFramePhotometricInterpretation; + return exifProfile?.GetValue(ExifTag.PhotometricInterpretation) != null + ? (TiffPhotometricInterpretation)exifProfile?.GetValue(ExifTag.PhotometricInterpretation).Value + : TiffPhotometricInterpretation.WhiteIsZero; } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffFormat.cs b/src/ImageSharp/Formats/Tiff/TiffFormat.cs index bf0c4ebb1..2217ffb7f 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFormat.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFormat.cs @@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Encapsulates the means to encode and decode Tiff images. /// - public class TiffFormat : IImageFormat + public sealed class TiffFormat : IImageFormat { private TiffFormat() { @@ -35,5 +35,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// public TiffMetadata CreateDefaultFormatMetadata() => new TiffMetadata(); + + /// + public TiffFrameMetadata CreateDefaultFormatFrameMetadata() => new TiffFrameMetadata(); } } diff --git a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs index 7376b114b..3594ec388 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Provides Tiff specific metadata information for the frame. /// - internal class TiffFrameMetadata : IDeepCloneable + public class TiffFrameMetadata : IDeepCloneable { /// /// Initializes a new instance of the class. @@ -18,11 +18,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff { } - /// - /// Initializes a new instance of the class. - /// - /// The Tiff frame directory tags. - public TiffFrameMetadata(ExifProfile frameTags) => this.Initialize(frameTags ?? new ExifProfile()); + private TiffFrameMetadata(TiffFrameMetadata other) + { + this.BitsPerSample = other.BitsPerSample; + this.BitsPerPixel = other.BitsPerPixel; + } /// /// Gets or sets the number of bits per component. @@ -35,42 +35,54 @@ namespace SixLabors.ImageSharp.Formats.Tiff public TiffBitsPerPixel BitsPerPixel { get; set; } /// - /// Initializes a new instance of the class with a given ExifProfile. + /// Returns a new instance parsed from the given Exif profile. /// - /// The Tiff frame directory tags. - public void Initialize(ExifProfile frameTags) + /// The Exif profile containing tiff frame directory tags to parse. + /// If null, a new instance is created and parsed instead. + /// The . + internal static TiffFrameMetadata Parse(ExifProfile profile) { - TiffPhotometricInterpretation photometricInterpretation = frameTags.GetValue(ExifTag.PhotometricInterpretation) != null ? - (TiffPhotometricInterpretation)frameTags.GetValue(ExifTag.PhotometricInterpretation).Value : TiffPhotometricInterpretation.WhiteIsZero; + var meta = new TiffFrameMetadata(); + Parse(meta, profile); + return meta; + } - ushort[] bits = frameTags.GetValue(ExifTag.BitsPerSample)?.Value; + /// + /// Parses the given Exif profile to populate the properties of the tiff frame meta data.. + /// + /// The tiff frame meta data. + /// The Exif profile containing tiff frame directory tags. + internal static void Parse(TiffFrameMetadata meta, ExifProfile profile) + { + if (profile is null) + { + profile = new ExifProfile(); + } + + TiffPhotometricInterpretation photometricInterpretation = profile.GetValue(ExifTag.PhotometricInterpretation) != null + ? (TiffPhotometricInterpretation)profile.GetValue(ExifTag.PhotometricInterpretation).Value + : TiffPhotometricInterpretation.WhiteIsZero; + + ushort[] bits = profile.GetValue(ExifTag.BitsPerSample)?.Value; if (bits == null) { - if (photometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero || photometricInterpretation == TiffPhotometricInterpretation.BlackIsZero) + if (photometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero + || photometricInterpretation == TiffPhotometricInterpretation.BlackIsZero) { - this.BitsPerSample = TiffBitsPerSample.Bit1; + meta.BitsPerSample = TiffBitsPerSample.Bit1; } - this.BitsPerSample = null; + meta.BitsPerSample = null; } else { - this.BitsPerSample = bits.GetBitsPerSample(); + meta.BitsPerSample = bits.GetBitsPerSample(); } - this.BitsPerPixel = (TiffBitsPerPixel)this.BitsPerSample.GetValueOrDefault().BitsPerPixel(); + meta.BitsPerPixel = (TiffBitsPerPixel)meta.BitsPerSample.GetValueOrDefault().BitsPerPixel(); } /// - public IDeepCloneable DeepClone() - { - var clone = new TiffFrameMetadata - { - BitsPerSample = this.BitsPerSample, - BitsPerPixel = this.BitsPerPixel - }; - - return clone; - } + public IDeepCloneable DeepClone() => new TiffFrameMetadata(this); } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 2fdc66347..619c1b5c2 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff memStream.Position = 0; using var output = Image.Load(Configuration, memStream); ExifProfile exifProfile = output.Frames.RootFrame.Metadata.ExifProfile; - var frameMetaData = new TiffFrameMetadata(exifProfile); + var frameMetaData = TiffFrameMetadata.Parse(exifProfile); Assert.Equal(expectedBitsPerPixel, frameMetaData.BitsPerPixel); Assert.Equal(TiffCompression.None, (TiffCompression)exifProfile.GetValue(ExifTag.Compression).Value); } @@ -73,8 +73,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // assert memStream.Position = 0; using var output = Image.Load(Configuration, memStream); + ExifProfile exifProfile = output.Frames.RootFrame.Metadata.ExifProfile; - var frameMetaData = new TiffFrameMetadata(exifProfile); + TiffFrameMetadata frameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata(); Assert.Equal(bitsPerPixel, frameMetaData.BitsPerPixel); Assert.Equal(TiffCompression.None, (TiffCompression)exifProfile.GetValue(ExifTag.Compression).Value); } @@ -114,7 +115,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff memStream.Position = 0; using var output = Image.Load(Configuration, memStream); ExifProfile exifProfile = output.Frames.RootFrame.Metadata.ExifProfile; - var frameMetaData = new TiffFrameMetadata(exifProfile); + var frameMetaData = TiffFrameMetadata.Parse(exifProfile); Assert.Equal(expectedBitsPerPixel, frameMetaData.BitsPerPixel); Assert.Equal(expectedCompression, (TiffCompression)exifProfile.GetValue(ExifTag.Compression).Value); } @@ -141,7 +142,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff memStream.Position = 0; using var output = Image.Load(Configuration, memStream); ExifProfile exifProfile = output.Frames.RootFrame.Metadata.ExifProfile; - var frameMetaData = new TiffFrameMetadata(exifProfile); + var frameMetaData = TiffFrameMetadata.Parse(exifProfile); Assert.Equal(expectedBitsPerPixel, frameMetaData.BitsPerPixel); } @@ -165,7 +166,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff memStream.Position = 0; using var output = Image.Load(Configuration, memStream); ExifProfile exifProfile = output.Frames.RootFrame.Metadata.ExifProfile; - var frameMetaData = new TiffFrameMetadata(exifProfile); + var frameMetaData = TiffFrameMetadata.Parse(exifProfile); Assert.Equal(TiffBitsPerPixel.Bit1, frameMetaData.BitsPerPixel); Assert.Equal(expectedCompression, (TiffCompression)exifProfile.GetValue(ExifTag.Compression).Value); } @@ -341,7 +342,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff 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); + var inputMeta = TiffFrameMetadata.Parse(exifProfileInput); // act input.Save(memStream, tiffEncoder); @@ -350,7 +351,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff memStream.Position = 0; using var output = Image.Load(Configuration, memStream); ExifProfile exifProfileOutput = output.Frames.RootFrame.Metadata.ExifProfile; - var outputMeta = new TiffFrameMetadata(exifProfileOutput); + var outputMeta = TiffFrameMetadata.Parse(exifProfileOutput); ImageFrame rootFrame = output.Frames.RootFrame; Number rowsPerStrip = exifProfileOutput.GetValue(ExifTag.RowsPerStrip) != null ? exifProfileOutput.GetValue(ExifTag.RowsPerStrip).Value : TiffConstants.RowsPerStripInfinity; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index 3a6dea9aa..662c7c1f3 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff using (Image image = provider.GetImage(TiffDecoder)) { ExifProfile exifProfile = image.Frames.RootFrame.Metadata.ExifProfile; - var meta = new TiffFrameMetadata(exifProfile); + var meta = TiffFrameMetadata.Parse(exifProfile); var cloneSameAsSampleMetaData = (TiffFrameMetadata)meta.DeepClone(); Assert.NotNull(cloneSameAsSampleMetaData); Assert.Equal(TiffBitsPerSample.Bit4, cloneSameAsSampleMetaData.BitsPerSample); @@ -196,10 +196,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.NotNull(tiffMetaData); Assert.Equal(ByteOrder.LittleEndian, tiffMetaData.ByteOrder); - var frameMetaData = new TiffFrameMetadata(exifProfile); + var frameMetaData = TiffFrameMetadata.Parse(exifProfile); Assert.Equal(TiffBitsPerPixel.Bit4, frameMetaData.BitsPerPixel); - var tiffFrameMetadata = new TiffFrameMetadata(exifProfile); + var tiffFrameMetadata = TiffFrameMetadata.Parse(exifProfile); Assert.NotNull(frameMetaData); Assert.Equal(TiffBitsPerSample.Bit4, frameMetaData.BitsPerSample); } @@ -238,7 +238,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff using Image image = provider.GetImage(new TiffDecoder() { IgnoreMetadata = false }); ImageMetadata inputMetaData = image.Metadata; - var frameMetaInput = new TiffFrameMetadata(image.Frames.RootFrame.Metadata.ExifProfile); + var frameMetaInput = TiffFrameMetadata.Parse(image.Frames.RootFrame.Metadata.ExifProfile); ImageFrame rootFrameInput = image.Frames.RootFrame; byte[] xmpProfileInput = rootFrameInput.Metadata.XmpProfile; ExifProfile exifProfileInput = rootFrameInput.Metadata.ExifProfile; @@ -257,7 +257,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff ImageMetadata encodedImageMetaData = encodedImage.Metadata; ImageFrame rootFrameEncodedImage = encodedImage.Frames.RootFrame; - var tiffMetaDataEncodedRootFrame = new TiffFrameMetadata(rootFrameEncodedImage.Metadata.ExifProfile); + var tiffMetaDataEncodedRootFrame = TiffFrameMetadata.Parse(rootFrameEncodedImage.Metadata.ExifProfile); ExifProfile encodedImageExifProfile = rootFrameEncodedImage.Metadata.ExifProfile; byte[] encodedImageXmpProfile = rootFrameEncodedImage.Metadata.XmpProfile; From dcb3e6fa2fe99e8055f3566a78fc95d421f16bef Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 19 May 2021 19:48:59 +0200 Subject: [PATCH 244/275] Remove BitsPerSample from TiffFrameMetadata --- .../TiffPhotometricInterpretation.cs | 4 +- .../TiffColorType.cs | 16 +++--- .../Tiff/TiffBitsPerSampleExtensions.cs | 17 ------- .../Formats/Tiff/TiffDecoderOptionsParser.cs | 13 ++++- .../Formats/Tiff/TiffFrameMetadata.cs | 51 ++++++++----------- .../Formats/Tiff/TiffEncoderTests.cs | 4 +- .../Formats/Tiff/TiffMetadataTests.cs | 25 --------- 7 files changed, 46 insertions(+), 84 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs index d43ccb408..b39e1003a 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs @@ -9,12 +9,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Constants public enum TiffPhotometricInterpretation : ushort { /// - /// Bilevel and grayscale: 0 is imaged as white. The maximum value is imaged as black. + /// Bilevel and grayscale: 0 is imaged as white. The maximum value is imaged as black. /// WhiteIsZero = 0, /// - /// Bilevel and grayscale: 0 is imaged as black. The maximum value is imaged as white. + /// Bilevel and grayscale: 0 is imaged as black. The maximum value is imaged as white. /// BlackIsZero = 1, diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs index a9007b640..484d23163 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs @@ -9,42 +9,42 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation internal enum TiffColorType { /// - /// Grayscale: 0 is imaged as black. The maximum value is imaged as white. + /// Grayscale: 0 is imaged as black. The maximum value is imaged as white. /// BlackIsZero, /// - /// Grayscale: 0 is imaged as black. The maximum value is imaged as white. Optimized implementation for bilevel images. + /// Grayscale: 0 is imaged as black. The maximum value is imaged as white. Optimized implementation for bilevel images. /// BlackIsZero1, /// - /// Grayscale: 0 is imaged as black. The maximum value is imaged as white. Optimized implementation for 4-bit images. + /// Grayscale: 0 is imaged as black. The maximum value is imaged as white. Optimized implementation for 4-bit images. /// BlackIsZero4, /// - /// Grayscale: 0 is imaged as black. The maximum value is imaged as white. Optimized implementation for 8-bit images. + /// Grayscale: 0 is imaged as black. The maximum value is imaged as white. Optimized implementation for 8-bit images. /// BlackIsZero8, /// - /// Grayscale: 0 is imaged as white. The maximum value is imaged as black. + /// Grayscale: 0 is imaged as white. The maximum value is imaged as black. /// WhiteIsZero, /// - /// Grayscale: 0 is imaged as white. The maximum value is imaged as black. Optimized implementation for bilevel images. + /// Grayscale: 0 is imaged as white. The maximum value is imaged as black. Optimized implementation for bilevel images. /// WhiteIsZero1, /// - /// Grayscale: 0 is imaged as white. The maximum value is imaged as black. Optimized implementation for 4-bit images. + /// Grayscale: 0 is imaged as white. The maximum value is imaged as black. Optimized implementation for 4-bit images. /// WhiteIsZero4, /// - /// Grayscale: 0 is imaged as white. The maximum value is imaged as black. Optimized implementation for 8-bit images. + /// Grayscale: 0 is imaged as white. The maximum value is imaged as black. Optimized implementation for 8-bit images. /// WhiteIsZero8, diff --git a/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs b/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs index 33e13d08e..5c4c374be 100644 --- a/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs +++ b/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs @@ -71,22 +71,5 @@ namespace SixLabors.ImageSharp.Formats.Tiff return TiffBitsPerSample.Unknown; } - - /// - /// Gets the bits per pixel for the given bits per sample. - /// - /// The tiff bits per sample. - /// Bits per pixel. - public static int BitsPerPixel(this TiffBitsPerSample tiffBitsPerSample) - { - var bitsPerSample = tiffBitsPerSample.Bits(); - int bitsPerPixel = 0; - foreach (var bits in bitsPerSample) - { - bitsPerPixel += bits; - } - - return bitsPerPixel; - } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs index 9ce9ce8e6..7111f5d36 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs @@ -71,8 +71,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff 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(); + options.BitsPerPixel = entries.BitsPerPixel != null ? (int)entries.BitsPerPixel.Value : (int)TiffBitsPerPixel.Bit24; + options.BitsPerSample = GetBitsPerSample(entries.BitsPerPixel); ParseColorType(options, exifProfile); ParseCompression(options, exifProfile); @@ -273,5 +273,14 @@ namespace SixLabors.ImageSharp.Formats.Tiff } } } + + 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, + }; } } diff --git a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs index 3594ec388..c21238ad9 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Metadata.Profiles.Exif; namespace SixLabors.ImageSharp.Formats.Tiff @@ -18,21 +17,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff { } - private TiffFrameMetadata(TiffFrameMetadata other) - { - this.BitsPerSample = other.BitsPerSample; - this.BitsPerPixel = other.BitsPerPixel; - } - /// - /// Gets or sets the number of bits per component. + /// Initializes a new instance of the class. /// - public TiffBitsPerSample? BitsPerSample { get; set; } + /// The other tiff frame metadata. + private TiffFrameMetadata(TiffFrameMetadata other) => this.BitsPerPixel = other.BitsPerPixel; /// /// Gets or sets the bits per pixel. /// - public TiffBitsPerPixel BitsPerPixel { get; set; } + public TiffBitsPerPixel? BitsPerPixel { get; set; } /// /// Returns a new instance parsed from the given Exif profile. @@ -54,32 +48,31 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The Exif profile containing tiff frame directory tags. internal static void Parse(TiffFrameMetadata meta, ExifProfile profile) { - if (profile is null) - { - profile = new ExifProfile(); - } + profile ??= new ExifProfile(); - TiffPhotometricInterpretation photometricInterpretation = profile.GetValue(ExifTag.PhotometricInterpretation) != null - ? (TiffPhotometricInterpretation)profile.GetValue(ExifTag.PhotometricInterpretation).Value - : TiffPhotometricInterpretation.WhiteIsZero; + ushort[] bitsPerSample = profile.GetValue(ExifTag.BitsPerSample)?.Value; + meta.BitsPerPixel = BitsPerPixelFromBitsPerSample(bitsPerSample); + } - ushort[] bits = profile.GetValue(ExifTag.BitsPerSample)?.Value; - if (bits == null) + /// + /// Gets the bits per pixel for the given bits per sample. + /// + /// The tiff bits per sample. + /// Bits per pixel. + private static TiffBitsPerPixel BitsPerPixelFromBitsPerSample(ushort[] bitsPerSample) + { + if (bitsPerSample == null) { - if (photometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero - || photometricInterpretation == TiffPhotometricInterpretation.BlackIsZero) - { - meta.BitsPerSample = TiffBitsPerSample.Bit1; - } - - meta.BitsPerSample = null; + return TiffBitsPerPixel.Bit24; } - else + + int bitsPerPixel = 0; + foreach (ushort bits in bitsPerSample) { - meta.BitsPerSample = bits.GetBitsPerSample(); + bitsPerPixel += bits; } - meta.BitsPerPixel = (TiffBitsPerPixel)meta.BitsPerSample.GetValueOrDefault().BitsPerPixel(); + return (TiffBitsPerPixel)bitsPerPixel; } /// diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 619c1b5c2..fb8354b18 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -358,7 +358,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.True(output.Height > (int)rowsPerStrip); Assert.True(exifProfileOutput.GetValue(ExifTag.StripOffsets)?.Value.Length > 1); Number[] stripByteCounts = exifProfileOutput.GetValue(ExifTag.StripByteCounts)?.Value; + Assert.NotNull(stripByteCounts); Assert.True(stripByteCounts.Length > 1); + Assert.NotNull(outputMeta.BitsPerPixel); foreach (Number sz in stripByteCounts) { @@ -388,7 +390,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff private static void TestTiffEncoderCore( TestImageProvider provider, - TiffBitsPerPixel bitsPerPixel, + TiffBitsPerPixel? bitsPerPixel, TiffEncodingMode mode, TiffCompression compression = TiffCompression.None, TiffPredictor predictor = TiffPredictor.None, diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index 662c7c1f3..1e96e64fd 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -45,27 +45,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.False(meta.ByteOrder == clone.ByteOrder); } - [Theory] - [WithFile(SampleMetadata, PixelTypes.Rgba32)] - public void TiffFrameMetadata_CloneIsDeep(TestImageProvider provider) - where TPixel : unmanaged, IPixel - { - using (Image image = provider.GetImage(TiffDecoder)) - { - ExifProfile exifProfile = image.Frames.RootFrame.Metadata.ExifProfile; - var meta = TiffFrameMetadata.Parse(exifProfile); - var cloneSameAsSampleMetaData = (TiffFrameMetadata)meta.DeepClone(); - Assert.NotNull(cloneSameAsSampleMetaData); - Assert.Equal(TiffBitsPerSample.Bit4, cloneSameAsSampleMetaData.BitsPerSample); - - var clone = (TiffFrameMetadata)meta.DeepClone(); - - clone.BitsPerSample = TiffBitsPerSample.Bit1; - - Assert.False(meta.BitsPerSample == clone.BitsPerSample); - } - } - [Theory] [InlineData(Calliphora_BiColorUncompressed, 1)] [InlineData(GrayscaleUncompressed, 8)] @@ -198,10 +177,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff var frameMetaData = TiffFrameMetadata.Parse(exifProfile); Assert.Equal(TiffBitsPerPixel.Bit4, frameMetaData.BitsPerPixel); - - var tiffFrameMetadata = TiffFrameMetadata.Parse(exifProfile); - Assert.NotNull(frameMetaData); - Assert.Equal(TiffBitsPerSample.Bit4, frameMetaData.BitsPerSample); } } From 508844ad60c3f7fff94116bcc10463070fbd830b Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 19 May 2021 20:35:55 +0200 Subject: [PATCH 245/275] Fix failing tests --- .../Formats/Tiff/TiffEncoderCore.cs | 21 +++++++++++++++++-- .../Formats/Tiff/TiffMetadataTests.cs | 2 +- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 055def11d..878bd99a9 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -120,7 +120,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff // TODO: This isn't correct. // We're overwriting explicit BPP based upon the Mode. It should be the other way around. // BPP should also be nullable and based upon the current TPixel if not set. - this.SetBitsPerPixel(rootFrameBitsPerPixel); + this.SetBitsPerPixel(rootFrameBitsPerPixel, photometricInterpretation); this.SetMode(photometricInterpretation); this.SetPhotometricInterpretation(); @@ -286,6 +286,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff if (this.Mode == TiffEncodingMode.Default) { this.Mode = TiffEncodingMode.BiColor; + this.BitsPerPixel = TiffBitsPerPixel.Bit1; return; } @@ -321,9 +322,25 @@ namespace SixLabors.ImageSharp.Formats.Tiff } } - private void SetBitsPerPixel(TiffBitsPerPixel? rootFrameBitsPerPixel) + private void SetBitsPerPixel(TiffBitsPerPixel? rootFrameBitsPerPixel, TiffPhotometricInterpretation photometricInterpretation) { this.BitsPerPixel ??= rootFrameBitsPerPixel; + + if (photometricInterpretation == TiffPhotometricInterpretation.PaletteColor) + { + if (this.BitsPerPixel != TiffBitsPerPixel.Bit8 && this.BitsPerPixel != TiffBitsPerPixel.Bit4) + { + this.BitsPerPixel = TiffBitsPerPixel.Bit8; + } + + return; + } + + if (this.BitsPerPixel.HasValue) + { + return; + } + switch (this.Mode) { case TiffEncodingMode.BiColor: diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index 1e96e64fd..228eec078 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -236,7 +236,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff ExifProfile encodedImageExifProfile = rootFrameEncodedImage.Metadata.ExifProfile; byte[] encodedImageXmpProfile = rootFrameEncodedImage.Metadata.XmpProfile; - Assert.Equal(TiffBitsPerPixel.Bit24, tiffMetaDataEncodedRootFrame.BitsPerPixel); + Assert.Equal(TiffBitsPerPixel.Bit4, tiffMetaDataEncodedRootFrame.BitsPerPixel); Assert.Equal(TiffCompression.None, (TiffCompression)encodedImageExifProfile.GetValue(ExifTag.Compression).Value); Assert.Equal(inputMetaData.HorizontalResolution, encodedImageMetaData.HorizontalResolution); From d22692ee8fd787a5081fa7bfd1764c950899c060 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 20 May 2021 17:30:08 +0200 Subject: [PATCH 246/275] Change TiffEncoder to use TiffPhotometricInterpretation instead of EncodingMode --- .../Compressors/T4BitCompressor.cs | 4 +- .../TiffPhotometricInterpretation.cs | 20 +- .../Formats/Tiff/ITiffEncoderOptions.cs | 12 +- .../Formats/Tiff/TiffDecoderOptionsParser.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffEncoder.cs | 8 +- .../Formats/Tiff/TiffEncoderCore.cs | 170 +++++++------- .../Tiff/TiffEncoderEntriesCollector.cs | 29 ++- .../Formats/Tiff/TiffEncodingMode.cs | 36 --- .../Formats/Tiff/TiffThrowHelper.cs | 3 + .../Tiff/Writers/TiffBiColorWriter{TPixel}.cs | 15 +- .../Tiff/Writers/TiffColorWriterFactory.cs | 17 +- .../Codecs/EncodeTiff.cs | 14 +- .../Formats/Tiff/TiffEncoderTests.cs | 218 +++++++++++------- .../Formats/Tiff/TiffMetadataTests.cs | 4 +- 14 files changed, 302 insertions(+), 250 deletions(-) delete mode 100644 src/ImageSharp/Formats/Tiff/TiffEncodingMode.cs diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitCompressor.cs index a016fb712..3e9b7f4e6 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitCompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitCompressor.cs @@ -344,8 +344,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors { while (codeLength > 0) { - var bitNumber = (int)codeLength; - var bit = (code & (1 << (bitNumber - 1))) != 0; + int bitNumber = (int)codeLength; + bool bit = (code & (1 << (bitNumber - 1))) != 0; if (bit) { BitWriterUtils.WriteBit(compressedData, this.bytePosition, this.bitPosition); diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs index b39e1003a..6dab7de6e 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs @@ -10,6 +10,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Constants { /// /// Bilevel and grayscale: 0 is imaged as white. The maximum value is imaged as black. + /// + /// Not supported by the TiffEncoder. /// WhiteIsZero = 0, @@ -29,42 +31,58 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Constants PaletteColor = 3, /// - /// A transparency mask + /// A transparency mask. + /// + /// Not supported by the TiffEncoder. /// TransparencyMask = 4, /// /// Separated: usually CMYK (see Section 16 of the TIFF 6.0 specification). + /// + /// Not supported by the TiffEncoder. /// Separated = 5, /// /// YCbCr (see Section 21 of the TIFF 6.0 specification). + /// + /// Not supported by the TiffEncoder. /// YCbCr = 6, /// /// 1976 CIE L*a*b* (see Section 23 of the TIFF 6.0 specification). + /// + /// Not supported by the TiffEncoder. /// CieLab = 8, /// /// ICC L*a*b* (see TIFF Specification, supplement 1). + /// + /// Not supported by the TiffEncoder. /// IccLab = 9, /// /// ITU L*a*b* (see RFC2301). + /// + /// Not supported by the TiffEncoder. /// ItuLab = 10, /// /// Color Filter Array (see the DNG specification). + /// + /// Not supported by the TiffEncoder. /// ColorFilterArray = 32803, /// /// Linear Raw (see the DNG specification). + /// + /// Not supported by the TiffEncoder. /// LinearRaw = 34892 } diff --git a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs index 03c25ea13..d56a587df 100644 --- a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs +++ b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs @@ -20,24 +20,24 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Gets the compression type to use. /// - TiffCompression Compression { get; } + TiffCompression? Compression { get; } /// /// Gets the compression level 1-9 for the deflate compression mode. /// Defaults to . /// - DeflateCompressionLevel CompressionLevel { get; } + DeflateCompressionLevel? CompressionLevel { get; } /// - /// Gets the encoding mode to use. Possible options are RGB, RGB with a color palette, gray or BiColor. - /// If no mode is specified in the options, RGB will be used. + /// Gets the PhotometricInterpretation to use. Possible options are RGB, RGB with a color palette, gray or BiColor. + /// If no PhotometricInterpretation is specified or it is unsupported by the encoder, RGB will be used. /// - TiffEncodingMode Mode { get; } + TiffPhotometricInterpretation? PhotometricInterpretation { get; } /// /// Gets a value indicating which horizontal prediction to use. This can improve the compression ratio with deflate or lzw compression. /// - TiffPredictor HorizontalPredictor { get; } + TiffPredictor? HorizontalPredictor { get; } /// /// Gets the quantizer for creating a color palette image. diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs index 7111f5d36..88a2d194d 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs @@ -70,7 +70,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff 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; + (TiffPhotometricInterpretation)exifProfile.GetValue(ExifTag.PhotometricInterpretation).Value : TiffPhotometricInterpretation.BlackIsZero; options.BitsPerPixel = entries.BitsPerPixel != null ? (int)entries.BitsPerPixel.Value : (int)TiffBitsPerPixel.Bit24; options.BitsPerSample = GetBitsPerSample(entries.BitsPerPixel); diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs index b66ba339c..7d5ccdb94 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs @@ -22,16 +22,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff public TiffBitsPerPixel? BitsPerPixel { get; set; } /// - public TiffCompression Compression { get; set; } = TiffCompression.None; + public TiffCompression? Compression { get; set; } /// - public DeflateCompressionLevel CompressionLevel { get; set; } = DeflateCompressionLevel.DefaultCompression; + public DeflateCompressionLevel? CompressionLevel { get; set; } /// - public TiffEncodingMode Mode { get; set; } + public TiffPhotometricInterpretation? PhotometricInterpretation { get; set; } /// - public TiffPredictor HorizontalPredictor { get; set; } + public TiffPredictor? HorizontalPredictor { get; set; } /// public IQuantizer Quantizer { get; set; } diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 878bd99a9..f9402e4d4 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -12,6 +12,7 @@ using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Formats.Tiff.Writers; using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; @@ -61,34 +62,28 @@ namespace SixLabors.ImageSharp.Formats.Tiff public TiffEncoderCore(ITiffEncoderOptions options, MemoryAllocator memoryAllocator) { this.memoryAllocator = memoryAllocator; - this.Mode = options.Mode; + this.PhotometricInterpretation = options.PhotometricInterpretation; this.quantizer = options.Quantizer ?? KnownQuantizers.Octree; this.BitsPerPixel = options.BitsPerPixel; this.HorizontalPredictor = options.HorizontalPredictor; - this.CompressionType = options.Compression != TiffCompression.Invalid ? options.Compression : TiffCompression.None; - this.compressionLevel = options.CompressionLevel; + this.CompressionType = options.Compression; + this.compressionLevel = options.CompressionLevel ?? DeflateCompressionLevel.DefaultCompression; } /// /// Gets the photometric interpretation implementation to use when encoding the image. /// - internal TiffPhotometricInterpretation PhotometricInterpretation { get; private set; } + internal TiffPhotometricInterpretation? PhotometricInterpretation { get; private set; } /// /// Gets or sets the compression implementation to use when encoding the image. /// - internal TiffCompression CompressionType { get; set; } + internal TiffCompression? CompressionType { get; set; } /// - /// Gets the encoding mode to use. RGB, RGB with color palette or gray. - /// If no mode is specified in the options, RGB will be used. + /// Gets or sets a value indicating which horizontal predictor to use. This can improve the compression ratio with deflate compression. /// - internal TiffEncodingMode Mode { get; private set; } - - /// - /// Gets a value indicating which horizontal predictor to use. This can improve the compression ratio with deflate compression. - /// - internal TiffPredictor HorizontalPredictor { get; } + internal TiffPredictor? HorizontalPredictor { get; set; } /// /// Gets the bits per pixel. @@ -111,18 +106,27 @@ namespace SixLabors.ImageSharp.Formats.Tiff this.configuration = image.GetConfiguration(); TiffPhotometricInterpretation rootFramePhotometricInterpretation = GetRootFramePhotometricInterpretation(image); - TiffPhotometricInterpretation photometricInterpretation = this.Mode == TiffEncodingMode.ColorPalette + TiffPhotometricInterpretation photometricInterpretation = this.PhotometricInterpretation == TiffPhotometricInterpretation.PaletteColor ? TiffPhotometricInterpretation.PaletteColor : rootFramePhotometricInterpretation; - TiffBitsPerPixel? rootFrameBitsPerPixel = image.Frames.RootFrame.Metadata.GetTiffMetadata().BitsPerPixel; + ImageFrameMetadata rootFrameMetaData = image.Frames.RootFrame.Metadata; + ExifProfile rootFrameExifProfile = image.Frames.RootFrame.Metadata.ExifProfile; + TiffBitsPerPixel? rootFrameBitsPerPixel = rootFrameMetaData.GetTiffMetadata().BitsPerPixel; + + // If the user has not chosen a predictor or compression, set the values from the decoded image, if present. + if (!this.HorizontalPredictor.HasValue && rootFrameExifProfile?.GetValue(ExifTag.Predictor) != null) + { + this.HorizontalPredictor = (TiffPredictor)rootFrameExifProfile?.GetValue(ExifTag.Predictor).Value; + } + + if (!this.CompressionType.HasValue && rootFrameExifProfile?.GetValue(ExifTag.Compression) != null) + { + this.CompressionType = (TiffCompression)rootFrameExifProfile?.GetValue(ExifTag.Compression).Value; + } - // TODO: This isn't correct. - // We're overwriting explicit BPP based upon the Mode. It should be the other way around. - // BPP should also be nullable and based upon the current TPixel if not set. - this.SetBitsPerPixel(rootFrameBitsPerPixel, photometricInterpretation); - this.SetMode(photometricInterpretation); - this.SetPhotometricInterpretation(); + this.SetBitsPerPixel(rootFrameBitsPerPixel, image.PixelType.BitsPerPixel, photometricInterpretation); + this.SetPhotometricInterpretation(photometricInterpretation); using (var writer = new TiffStreamWriter(stream)) { @@ -159,20 +163,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff { var entriesCollector = new TiffEncoderEntriesCollector(); - // Write the image bytes to the steam. - uint imageDataStart = (uint)writer.Position; - using TiffBaseCompressor compressor = TiffCompressorFactory.Create( - this.CompressionType, + this.CompressionType ?? TiffCompression.None, writer.BaseStream, this.memoryAllocator, image.Width, (int)this.BitsPerPixel, this.compressionLevel, - this.HorizontalPredictor == TiffPredictor.Horizontal ? this.HorizontalPredictor : TiffPredictor.None); + this.HorizontalPredictor == TiffPredictor.Horizontal ? this.HorizontalPredictor.Value : TiffPredictor.None); using TiffBaseColorWriter colorWriter = TiffColorWriterFactory.Create( - this.Mode, + this.PhotometricInterpretation, image.Frames.RootFrame, this.quantizer, this.memoryAllocator, @@ -227,8 +228,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff { if (entries.Count == 0) { - // TODO: Perf. Throwhelper - throw new ArgumentException("There must be at least one entry per IFD.", nameof(entries)); + TiffThrowHelper.ThrowArgumentException("There must be at least one entry per IFD."); } uint dataOffset = (uint)writer.Position + (uint)(6 + (entries.Count * 12)); @@ -277,52 +277,73 @@ namespace SixLabors.ImageSharp.Formats.Tiff return nextIfdMarker; } - private void SetMode(TiffPhotometricInterpretation photometricInterpretation) + private void SetPhotometricInterpretation(TiffPhotometricInterpretation? photometricInterpretation) { - // Make sure, that the fax compressions are only used together with the BiColor mode. + // Make sure, that the fax compressions are only used together with the WhiteIsZero. if (this.CompressionType == TiffCompression.CcittGroup3Fax || this.CompressionType == TiffCompression.Ccitt1D) { - // Default means the user has not specified a preferred encoding mode. - if (this.Mode == TiffEncodingMode.Default) + // The user has not specified a preferred photometric interpretation. + if (this.PhotometricInterpretation == null) { - this.Mode = TiffEncodingMode.BiColor; + this.PhotometricInterpretation = TiffPhotometricInterpretation.WhiteIsZero; this.BitsPerPixel = TiffBitsPerPixel.Bit1; return; } - if (this.Mode != TiffEncodingMode.BiColor) + if (this.PhotometricInterpretation != TiffPhotometricInterpretation.WhiteIsZero && this.PhotometricInterpretation != TiffPhotometricInterpretation.BlackIsZero) { - TiffThrowHelper.ThrowImageFormatException($"The {this.CompressionType} compression and {this.Mode} aren't compatible. Please use {this.CompressionType} only with {TiffEncodingMode.BiColor} or {TiffEncodingMode.Default} mode."); + TiffThrowHelper.ThrowImageFormatException( + $"The {this.CompressionType} compression and {this.PhotometricInterpretation} aren't compatible. Please use {this.CompressionType} only with {TiffPhotometricInterpretation.BlackIsZero} or {TiffPhotometricInterpretation.WhiteIsZero}."); } + else + { + // The “normal” PhotometricInterpretation for bilevel CCITT compressed data is WhiteIsZero. + this.PhotometricInterpretation = TiffPhotometricInterpretation.WhiteIsZero; + } + + return; + } + + switch (this.PhotometricInterpretation) + { + // The currently supported values by the encoder for photometric interpretation: + case TiffPhotometricInterpretation.PaletteColor: + case TiffPhotometricInterpretation.BlackIsZero: + case TiffPhotometricInterpretation.WhiteIsZero: + break; + + default: + this.PhotometricInterpretation = TiffPhotometricInterpretation.Rgb; + break; } - // Use the bits per pixel to determine the encoding mode. - this.SetModeWithBitsPerPixel(this.BitsPerPixel, photometricInterpretation); + // Use the bits per pixel to determine the photometric interpretation. + this.SetPhotometricInterpretationWithBitsPerPixel(this.BitsPerPixel, photometricInterpretation); } - private void SetModeWithBitsPerPixel(TiffBitsPerPixel? bitsPerPixel, TiffPhotometricInterpretation photometricInterpretation) + private void SetPhotometricInterpretationWithBitsPerPixel(TiffBitsPerPixel? bitsPerPixel, TiffPhotometricInterpretation? photometricInterpretation) { switch (bitsPerPixel) { case TiffBitsPerPixel.Bit1: - this.Mode = TiffEncodingMode.BiColor; + this.PhotometricInterpretation = TiffPhotometricInterpretation.BlackIsZero; break; case TiffBitsPerPixel.Bit4: - this.Mode = TiffEncodingMode.ColorPalette; + this.PhotometricInterpretation = TiffPhotometricInterpretation.PaletteColor; break; case TiffBitsPerPixel.Bit8: - this.Mode = photometricInterpretation == TiffPhotometricInterpretation.PaletteColor - ? TiffEncodingMode.ColorPalette - : TiffEncodingMode.Gray; + this.PhotometricInterpretation = photometricInterpretation == TiffPhotometricInterpretation.PaletteColor + ? TiffPhotometricInterpretation.PaletteColor + : TiffPhotometricInterpretation.BlackIsZero; break; default: - this.Mode = TiffEncodingMode.Rgb; + this.PhotometricInterpretation = TiffPhotometricInterpretation.Rgb; break; } } - private void SetBitsPerPixel(TiffBitsPerPixel? rootFrameBitsPerPixel, TiffPhotometricInterpretation photometricInterpretation) + private void SetBitsPerPixel(TiffBitsPerPixel? rootFrameBitsPerPixel, int inputBitsPerPixel, TiffPhotometricInterpretation photometricInterpretation) { this.BitsPerPixel ??= rootFrameBitsPerPixel; @@ -341,56 +362,41 @@ namespace SixLabors.ImageSharp.Formats.Tiff return; } - switch (this.Mode) + if (this.PhotometricInterpretation == null && inputBitsPerPixel == 8) { - case TiffEncodingMode.BiColor: - this.BitsPerPixel = TiffBitsPerPixel.Bit1; - break; - case TiffEncodingMode.ColorPalette: - if (this.BitsPerPixel != TiffBitsPerPixel.Bit8 && this.BitsPerPixel != TiffBitsPerPixel.Bit4) - { - this.BitsPerPixel = TiffBitsPerPixel.Bit8; - } - - break; - case TiffEncodingMode.Gray: - this.BitsPerPixel = TiffBitsPerPixel.Bit8; - break; - case TiffEncodingMode.Rgb: - this.BitsPerPixel = TiffBitsPerPixel.Bit24; - break; - default: - this.Mode = TiffEncodingMode.Rgb; - this.BitsPerPixel = TiffBitsPerPixel.Bit24; - break; + this.BitsPerPixel = TiffBitsPerPixel.Bit8; + return; } - } - private void SetPhotometricInterpretation() - { - switch (this.Mode) + switch (this.PhotometricInterpretation) { - case TiffEncodingMode.ColorPalette: - this.PhotometricInterpretation = TiffPhotometricInterpretation.PaletteColor; - break; - case TiffEncodingMode.BiColor: - if (this.CompressionType == TiffCompression.CcittGroup3Fax || this.CompressionType == TiffCompression.Ccitt1D) + case TiffPhotometricInterpretation.BlackIsZero: + case TiffPhotometricInterpretation.WhiteIsZero: + if (this.CompressionType == TiffCompression.Ccitt1D || + this.CompressionType == TiffCompression.CcittGroup3Fax || + this.CompressionType == TiffCompression.CcittGroup4Fax) { - // The “normal” PhotometricInterpretation for bilevel CCITT compressed data is WhiteIsZero. - this.PhotometricInterpretation = TiffPhotometricInterpretation.WhiteIsZero; + this.BitsPerPixel = TiffBitsPerPixel.Bit1; } else { - this.PhotometricInterpretation = TiffPhotometricInterpretation.BlackIsZero; + this.BitsPerPixel = TiffBitsPerPixel.Bit8; } break; + case TiffPhotometricInterpretation.PaletteColor: + if (this.BitsPerPixel != TiffBitsPerPixel.Bit8 && this.BitsPerPixel != TiffBitsPerPixel.Bit4) + { + this.BitsPerPixel = TiffBitsPerPixel.Bit8; + } - case TiffEncodingMode.Gray: - this.PhotometricInterpretation = TiffPhotometricInterpretation.BlackIsZero; + break; + case TiffPhotometricInterpretation.Rgb: + this.BitsPerPixel = TiffBitsPerPixel.Bit24; break; default: this.PhotometricInterpretation = TiffPhotometricInterpretation.Rgb; + this.BitsPerPixel = TiffBitsPerPixel.Bit24; break; } } @@ -400,7 +406,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff ExifProfile exifProfile = image.Frames.RootFrame.Metadata.ExifProfile; return exifProfile?.GetValue(ExifTag.PhotometricInterpretation) != null ? (TiffPhotometricInterpretation)exifProfile?.GetValue(ExifTag.PhotometricInterpretation).Value - : TiffPhotometricInterpretation.WhiteIsZero; + : TiffPhotometricInterpretation.BlackIsZero; } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs index 391f7d541..09605bc69 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs @@ -281,7 +281,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff if (encoder.HorizontalPredictor == TiffPredictor.Horizontal) { - if (encoder.Mode == TiffEncodingMode.Rgb || encoder.Mode == TiffEncodingMode.Gray || encoder.Mode == TiffEncodingMode.ColorPalette) + if (encoder.PhotometricInterpretation == TiffPhotometricInterpretation.Rgb || + encoder.PhotometricInterpretation == TiffPhotometricInterpretation.PaletteColor || + encoder.PhotometricInterpretation == TiffPhotometricInterpretation.BlackIsZero) { var predictor = new ExifShort(ExifTagValue.Predictor) { Value = (ushort)TiffPredictor.Horizontal }; @@ -320,20 +322,23 @@ namespace SixLabors.ImageSharp.Formats.Tiff case TiffPhotometricInterpretation.Rgb: return TiffConstants.BitsPerSampleRgb8Bit; + case TiffPhotometricInterpretation.WhiteIsZero: - if (encoder.Mode == TiffEncodingMode.BiColor) + if (encoder.BitsPerPixel == TiffBitsPerPixel.Bit1) { return TiffConstants.BitsPerSample1Bit; } return TiffConstants.BitsPerSample8Bit; + case TiffPhotometricInterpretation.BlackIsZero: - if (encoder.Mode == TiffEncodingMode.BiColor) + if (encoder.BitsPerPixel == TiffBitsPerPixel.Bit1) { return TiffConstants.BitsPerSample1Bit; } return TiffConstants.BitsPerSample8Bit; + default: return TiffConstants.BitsPerSampleRgb8Bit; } @@ -350,7 +355,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff // PackBits is allowed for all modes. return (ushort)TiffCompression.PackBits; case TiffCompression.Lzw: - if (encoder.Mode == TiffEncodingMode.Rgb || encoder.Mode == TiffEncodingMode.Gray || encoder.Mode == TiffEncodingMode.ColorPalette) + if (encoder.PhotometricInterpretation == TiffPhotometricInterpretation.Rgb || + encoder.PhotometricInterpretation == TiffPhotometricInterpretation.PaletteColor || + encoder.PhotometricInterpretation == TiffPhotometricInterpretation.BlackIsZero) { return (ushort)TiffCompression.Lzw; } @@ -358,20 +365,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff break; case TiffCompression.CcittGroup3Fax: - if (encoder.Mode == TiffEncodingMode.BiColor) - { - return (ushort)TiffCompression.CcittGroup3Fax; - } - - break; + return (ushort)TiffCompression.CcittGroup3Fax; case TiffCompression.Ccitt1D: - if (encoder.Mode == TiffEncodingMode.BiColor) - { - return (ushort)TiffCompression.Ccitt1D; - } - - break; + return (ushort)TiffCompression.Ccitt1D; } return (ushort)TiffCompression.None; diff --git a/src/ImageSharp/Formats/Tiff/TiffEncodingMode.cs b/src/ImageSharp/Formats/Tiff/TiffEncodingMode.cs deleted file mode 100644 index 374505195..000000000 --- a/src/ImageSharp/Formats/Tiff/TiffEncodingMode.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -namespace SixLabors.ImageSharp.Formats.Tiff -{ - /// - /// Enum for the different tiff encoding options. - /// - public enum TiffEncodingMode - { - /// - /// No mode specified. Will preserve the bits per pixels of the input image. - /// - Default = 0, - - /// - /// The image will be encoded as RGB, 8 bit per channel. - /// - Rgb = 1, - - /// - /// The image will be encoded as RGB with a color palette. - /// - ColorPalette = 2, - - /// - /// The image will be encoded as 8 bit gray. - /// - Gray = 3, - - /// - /// The image will be written as a white and black image. - /// - BiColor = 4, - } -} diff --git a/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs b/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs index c5ebf481a..3c541a786 100644 --- a/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs +++ b/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs @@ -32,5 +32,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff [MethodImpl(InliningOptions.ColdPath)] public static void ThrowNotSupported(string message) => throw new NotSupportedException(message); + + [MethodImpl(InliningOptions.ColdPath)] + public static void ThrowArgumentException(string message) => throw new ArgumentException(message); } } diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter{TPixel}.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter{TPixel}.cs index e37cba7cf..be5c837ea 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter{TPixel}.cs @@ -36,16 +36,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers /// protected override void EncodeStrip(int y, int height, TiffBaseCompressor compressor) { - if (this.pixelsAsGray == null) - { - this.pixelsAsGray = this.MemoryAllocator.Allocate(height * this.Image.Width); - } + this.pixelsAsGray ??= this.MemoryAllocator.Allocate(height * this.Image.Width); Span pixelAsGraySpan = this.pixelsAsGray.Slice(0, height * this.Image.Width); - Span pixels = GetStripPixels(this.imageBlackWhite.GetRootFramePixelBuffer(), y, height); + Span pixelsBlackWhite = GetStripPixels(this.imageBlackWhite.GetRootFramePixelBuffer(), y, height); - PixelOperations.Instance.ToL8Bytes(this.Configuration, pixels, pixelAsGraySpan, pixels.Length); + PixelOperations.Instance.ToL8Bytes(this.Configuration, pixelsBlackWhite, pixelAsGraySpan, pixelsBlackWhite.Length); if (compressor.Method == TiffCompression.CcittGroup3Fax || compressor.Method == TiffCompression.Ccitt1D) { @@ -54,11 +51,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers } else { + // Write uncompressed image. int bytesPerStrip = this.BytesPerRow * height; - if (this.bitStrip == null) - { - this.bitStrip = this.MemoryAllocator.AllocateManagedByteBuffer(bytesPerStrip); - } + this.bitStrip ??= this.MemoryAllocator.AllocateManagedByteBuffer(bytesPerStrip); Span rows = this.bitStrip.Slice(0, bytesPerStrip); rows.Clear(); diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffColorWriterFactory.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffColorWriterFactory.cs index 01c1833f1..e53f4b420 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffColorWriterFactory.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffColorWriterFactory.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Quantization; @@ -10,7 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers internal static class TiffColorWriterFactory { public static TiffBaseColorWriter Create( - TiffEncodingMode mode, + TiffPhotometricInterpretation? photometricInterpretation, ImageFrame image, IQuantizer quantizer, MemoryAllocator memoryAllocator, @@ -19,14 +20,18 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers int bitsPerPixel) where TPixel : unmanaged, IPixel { - switch (mode) + switch (photometricInterpretation) { - case TiffEncodingMode.ColorPalette: + case TiffPhotometricInterpretation.PaletteColor: return new TiffPaletteWriter(image, quantizer, memoryAllocator, configuration, entriesCollector, bitsPerPixel); - case TiffEncodingMode.Gray: + case TiffPhotometricInterpretation.BlackIsZero: + case TiffPhotometricInterpretation.WhiteIsZero: + if (bitsPerPixel == 1) + { + return new TiffBiColorWriter(image, memoryAllocator, configuration, entriesCollector); + } + return new TiffGrayWriter(image, memoryAllocator, configuration, entriesCollector); - case TiffEncodingMode.BiColor: - return new TiffBiColorWriter(image, memoryAllocator, configuration, entriesCollector); default: return new TiffRgbWriter(image, memoryAllocator, configuration, entriesCollector); } diff --git a/tests/ImageSharp.Benchmarks/Codecs/EncodeTiff.cs b/tests/ImageSharp.Benchmarks/Codecs/EncodeTiff.cs index 3c318e229..7154b2310 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/EncodeTiff.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/EncodeTiff.cs @@ -61,8 +61,10 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs public void SystemDrawing() { ImageCodecInfo codec = FindCodecForType("image/tiff"); - using var parameters = new EncoderParameters(1); - parameters.Param[0] = new EncoderParameter(Encoder.Compression, (long)Cast(this.Compression)); + using var parameters = new EncoderParameters(1) + { + Param = {[0] = new EncoderParameter(Encoder.Compression, (long)Cast(this.Compression))} + }; using var memoryStream = new MemoryStream(); this.drawing.Save(memoryStream, codec, parameters); @@ -71,15 +73,15 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs [Benchmark(Description = "ImageSharp Tiff")] public void TiffCore() { - TiffEncodingMode mode = TiffEncodingMode.Default; + TiffPhotometricInterpretation photometricInterpretation = TiffPhotometricInterpretation.Rgb; - // workaround for 1-bit bug + // Workaround for 1-bit bug if (this.Compression == TiffCompression.CcittGroup3Fax || this.Compression == TiffCompression.Ccitt1D) { - mode = TiffEncodingMode.BiColor; + photometricInterpretation = TiffPhotometricInterpretation.WhiteIsZero; } - var encoder = new TiffEncoder() { Compression = this.Compression, Mode = mode }; + var encoder = new TiffEncoder() { Compression = this.Compression, PhotometricInterpretation = photometricInterpretation }; using var memoryStream = new MemoryStream(); this.core.SaveAsTiff(memoryStream, encoder); } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index fb8354b18..99a74182d 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -31,15 +31,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff } [Theory] - [InlineData(TiffEncodingMode.Default, TiffBitsPerPixel.Bit24)] - [InlineData(TiffEncodingMode.Rgb, TiffBitsPerPixel.Bit24)] - [InlineData(TiffEncodingMode.ColorPalette, TiffBitsPerPixel.Bit8)] - [InlineData(TiffEncodingMode.Gray, TiffBitsPerPixel.Bit8)] - [InlineData(TiffEncodingMode.BiColor, TiffBitsPerPixel.Bit1)] - public void EncoderOptions_SetEncodingMode_Works(TiffEncodingMode mode, TiffBitsPerPixel expectedBitsPerPixel) + [InlineData(null, TiffBitsPerPixel.Bit24)] + [InlineData(TiffPhotometricInterpretation.Rgb, TiffBitsPerPixel.Bit24)] + [InlineData(TiffPhotometricInterpretation.PaletteColor, TiffBitsPerPixel.Bit8)] + [InlineData(TiffPhotometricInterpretation.BlackIsZero, TiffBitsPerPixel.Bit8)] + public void EncoderOptions_SetPhotometricInterpretation_Works(TiffPhotometricInterpretation? photometricInterpretation, TiffBitsPerPixel expectedBitsPerPixel) { // arrange - var tiffEncoder = new TiffEncoder { Mode = mode }; + var tiffEncoder = new TiffEncoder { PhotometricInterpretation = photometricInterpretation }; using Image input = new Image(10, 10); using var memStream = new MemoryStream(); @@ -81,30 +80,29 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff } [Theory] - [InlineData(TiffEncodingMode.Default, TiffCompression.Deflate, TiffBitsPerPixel.Bit24, TiffCompression.Deflate)] - [InlineData(TiffEncodingMode.Rgb, TiffCompression.Deflate, TiffBitsPerPixel.Bit24, TiffCompression.Deflate)] - [InlineData(TiffEncodingMode.Gray, TiffCompression.Deflate, TiffBitsPerPixel.Bit8, TiffCompression.Deflate)] - [InlineData(TiffEncodingMode.BiColor, TiffCompression.Deflate, TiffBitsPerPixel.Bit1, TiffCompression.Deflate)] - [InlineData(TiffEncodingMode.ColorPalette, TiffCompression.Deflate, TiffBitsPerPixel.Bit8, TiffCompression.Deflate)] - [InlineData(TiffEncodingMode.Default, TiffCompression.PackBits, TiffBitsPerPixel.Bit24, TiffCompression.PackBits)] - [InlineData(TiffEncodingMode.Rgb, TiffCompression.PackBits, TiffBitsPerPixel.Bit24, TiffCompression.PackBits)] - [InlineData(TiffEncodingMode.ColorPalette, TiffCompression.PackBits, TiffBitsPerPixel.Bit8, TiffCompression.PackBits)] - [InlineData(TiffEncodingMode.Gray, TiffCompression.PackBits, TiffBitsPerPixel.Bit8, TiffCompression.PackBits)] - [InlineData(TiffEncodingMode.BiColor, TiffCompression.PackBits, TiffBitsPerPixel.Bit1, TiffCompression.PackBits)] - [InlineData(TiffEncodingMode.Rgb, TiffCompression.Lzw, TiffBitsPerPixel.Bit24, TiffCompression.Lzw)] - [InlineData(TiffEncodingMode.Gray, TiffCompression.Lzw, TiffBitsPerPixel.Bit8, TiffCompression.Lzw)] - [InlineData(TiffEncodingMode.ColorPalette, TiffCompression.Lzw, TiffBitsPerPixel.Bit8, TiffCompression.Lzw)] - [InlineData(TiffEncodingMode.BiColor, TiffCompression.CcittGroup3Fax, TiffBitsPerPixel.Bit1, TiffCompression.CcittGroup3Fax)] - [InlineData(TiffEncodingMode.BiColor, TiffCompression.Ccitt1D, TiffBitsPerPixel.Bit1, TiffCompression.Ccitt1D)] - [InlineData(TiffEncodingMode.Rgb, TiffCompression.ItuTRecT43, TiffBitsPerPixel.Bit24, TiffCompression.None)] - [InlineData(TiffEncodingMode.Rgb, TiffCompression.ItuTRecT82, TiffBitsPerPixel.Bit24, TiffCompression.None)] - [InlineData(TiffEncodingMode.Rgb, TiffCompression.Jpeg, TiffBitsPerPixel.Bit24, TiffCompression.None)] - [InlineData(TiffEncodingMode.Rgb, TiffCompression.OldDeflate, TiffBitsPerPixel.Bit24, TiffCompression.None)] - [InlineData(TiffEncodingMode.Rgb, TiffCompression.OldJpeg, TiffBitsPerPixel.Bit24, TiffCompression.None)] - public void EncoderOptions_SetEncodingModeAndCompression_Works(TiffEncodingMode mode, TiffCompression compression, TiffBitsPerPixel expectedBitsPerPixel, TiffCompression expectedCompression) + [InlineData(null, TiffCompression.Deflate, TiffBitsPerPixel.Bit24, TiffCompression.Deflate)] + [InlineData(TiffPhotometricInterpretation.Rgb, TiffCompression.Deflate, TiffBitsPerPixel.Bit24, TiffCompression.Deflate)] + [InlineData(TiffPhotometricInterpretation.BlackIsZero, TiffCompression.Deflate, TiffBitsPerPixel.Bit8, TiffCompression.Deflate)] + [InlineData(TiffPhotometricInterpretation.PaletteColor, TiffCompression.Deflate, TiffBitsPerPixel.Bit8, TiffCompression.Deflate)] + [InlineData(null, TiffCompression.PackBits, TiffBitsPerPixel.Bit24, TiffCompression.PackBits)] + [InlineData(TiffPhotometricInterpretation.Rgb, TiffCompression.PackBits, TiffBitsPerPixel.Bit24, TiffCompression.PackBits)] + [InlineData(TiffPhotometricInterpretation.PaletteColor, TiffCompression.PackBits, TiffBitsPerPixel.Bit8, TiffCompression.PackBits)] + [InlineData(TiffPhotometricInterpretation.BlackIsZero, TiffCompression.PackBits, TiffBitsPerPixel.Bit8, TiffCompression.PackBits)] + [InlineData(null, TiffCompression.Lzw, TiffBitsPerPixel.Bit24, TiffCompression.Lzw)] + [InlineData(TiffPhotometricInterpretation.Rgb, TiffCompression.Lzw, TiffBitsPerPixel.Bit24, TiffCompression.Lzw)] + [InlineData(TiffPhotometricInterpretation.BlackIsZero, TiffCompression.Lzw, TiffBitsPerPixel.Bit8, TiffCompression.Lzw)] + [InlineData(TiffPhotometricInterpretation.PaletteColor, TiffCompression.Lzw, TiffBitsPerPixel.Bit8, TiffCompression.Lzw)] + [InlineData(TiffPhotometricInterpretation.BlackIsZero, TiffCompression.CcittGroup3Fax, TiffBitsPerPixel.Bit1, TiffCompression.CcittGroup3Fax)] + [InlineData(TiffPhotometricInterpretation.BlackIsZero, TiffCompression.Ccitt1D, TiffBitsPerPixel.Bit1, TiffCompression.Ccitt1D)] + [InlineData(TiffPhotometricInterpretation.Rgb, TiffCompression.ItuTRecT43, TiffBitsPerPixel.Bit24, TiffCompression.None)] + [InlineData(TiffPhotometricInterpretation.Rgb, TiffCompression.ItuTRecT82, TiffBitsPerPixel.Bit24, TiffCompression.None)] + [InlineData(TiffPhotometricInterpretation.Rgb, TiffCompression.Jpeg, TiffBitsPerPixel.Bit24, TiffCompression.None)] + [InlineData(TiffPhotometricInterpretation.Rgb, TiffCompression.OldDeflate, TiffBitsPerPixel.Bit24, TiffCompression.None)] + [InlineData(TiffPhotometricInterpretation.Rgb, TiffCompression.OldJpeg, TiffBitsPerPixel.Bit24, TiffCompression.None)] + public void EncoderOptions_SetPhotometricInterpretationAndCompression_Works(TiffPhotometricInterpretation? photometricInterpretation, TiffCompression compression, TiffBitsPerPixel expectedBitsPerPixel, TiffCompression expectedCompression) { // arrange - var tiffEncoder = new TiffEncoder { Mode = mode, Compression = compression }; + var tiffEncoder = new TiffEncoder { PhotometricInterpretation = photometricInterpretation, Compression = compression }; using Image input = new Image(10, 10); using var memStream = new MemoryStream(); @@ -115,8 +113,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff memStream.Position = 0; using var output = Image.Load(Configuration, memStream); ExifProfile exifProfile = output.Frames.RootFrame.Metadata.ExifProfile; - var frameMetaData = TiffFrameMetadata.Parse(exifProfile); - Assert.Equal(expectedBitsPerPixel, frameMetaData.BitsPerPixel); + TiffFrameMetadata rootFrameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata(); + Assert.Equal(expectedBitsPerPixel, rootFrameMetaData.BitsPerPixel); Assert.Equal(expectedCompression, (TiffCompression)exifProfile.GetValue(ExifTag.Compression).Value); } @@ -146,6 +144,72 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal(expectedBitsPerPixel, frameMetaData.BitsPerPixel); } + [Fact] + public void TiffEncoder_PreservesBitsPerPixel_WhenInputIsL8() + { + // arrange + var tiffEncoder = new TiffEncoder(); + using Image input = new Image(10, 10); + using var memStream = new MemoryStream(); + var expectedBitsPerPixel = TiffBitsPerPixel.Bit8; + + // act + input.Save(memStream, tiffEncoder); + + // assert + memStream.Position = 0; + using var output = Image.Load(Configuration, memStream); + ExifProfile exifProfile = output.Frames.RootFrame.Metadata.ExifProfile; + var frameMetaData = TiffFrameMetadata.Parse(exifProfile); + Assert.Equal(expectedBitsPerPixel, frameMetaData.BitsPerPixel); + } + + [Theory] + [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffCompression.None)] + [WithFile(RgbLzwNoPredictor, PixelTypes.Rgba32, TiffCompression.Lzw)] + [WithFile(RgbDeflate, PixelTypes.Rgba32, TiffCompression.Deflate)] + [WithFile(RgbPackbits, PixelTypes.Rgba32, TiffCompression.PackBits)] + public void TiffEncoder_PreservesCompression(TestImageProvider provider, TiffCompression expectedCompression) + where TPixel : unmanaged, IPixel + { + // arrange + var tiffEncoder = new TiffEncoder(); + using Image input = provider.GetImage(); + using var memStream = new MemoryStream(); + + // act + input.Save(memStream, tiffEncoder); + + // assert + memStream.Position = 0; + using var output = Image.Load(Configuration, memStream); + ExifProfile exifProfile = output.Frames.RootFrame.Metadata.ExifProfile; + Assert.Equal(expectedCompression, (TiffCompression)exifProfile.GetValue(ExifTag.Compression).Value); + } + + [Theory] + [WithFile(RgbLzwNoPredictor, PixelTypes.Rgba32, TiffPredictor.None)] + [WithFile(RgbLzwPredictor, PixelTypes.Rgba32, TiffPredictor.Horizontal)] + [WithFile(RgbDeflate, PixelTypes.Rgba32, TiffPredictor.None)] + [WithFile(RgbDeflatePredictor, PixelTypes.Rgba32, TiffPredictor.Horizontal)] + public void TiffEncoder_PreservesPredictor(TestImageProvider provider, TiffPredictor expectedPredictor) + where TPixel : unmanaged, IPixel + { + // arrange + var tiffEncoder = new TiffEncoder(); + using Image input = provider.GetImage(); + using var memStream = new MemoryStream(); + + // act + input.Save(memStream, tiffEncoder); + + // assert + memStream.Position = 0; + using var output = Image.Load(Configuration, memStream); + ExifProfile exifProfile = output.Frames.RootFrame.Metadata.ExifProfile; + Assert.Equal(expectedPredictor, (TiffPredictor)exifProfile.GetValue(ExifTag.Predictor).Value); + } + [Theory] [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffCompression.CcittGroup3Fax, TiffCompression.CcittGroup3Fax)] [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffCompression.Ccitt1D, TiffCompression.Ccitt1D)] @@ -172,15 +236,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff } [Theory] - [InlineData(TiffEncodingMode.ColorPalette, TiffCompression.CcittGroup3Fax)] - [InlineData(TiffEncodingMode.ColorPalette, TiffCompression.Ccitt1D)] - [InlineData(TiffEncodingMode.Gray, TiffCompression.Ccitt1D)] - [InlineData(TiffEncodingMode.Rgb, TiffCompression.Ccitt1D)] - public void TiffEncoder_IncompatibilityOptions(TiffEncodingMode mode, TiffCompression compression) + [InlineData(TiffPhotometricInterpretation.PaletteColor, TiffCompression.CcittGroup3Fax)] + [InlineData(TiffPhotometricInterpretation.PaletteColor, TiffCompression.Ccitt1D)] + public void TiffEncoder_IncompatibilityOptions_ThrowsImageFormatException(TiffPhotometricInterpretation photometricInterpretation, TiffCompression compression) { // arrange using var input = new Image(10, 10); - var encoder = new TiffEncoder() { Mode = mode, Compression = compression }; + var encoder = new TiffEncoder() { PhotometricInterpretation = photometricInterpretation, Compression = compression }; using var memStream = new MemoryStream(); // act @@ -190,154 +252,154 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Theory] [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeRgb_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffEncodingMode.Rgb); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb); [Theory] [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeRgb_WithDeflateCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffEncodingMode.Rgb, TiffCompression.Deflate); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, TiffCompression.Deflate); [Theory] [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeRgb_WithDeflateCompressionAndPredictor_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffEncodingMode.Rgb, TiffCompression.Deflate, TiffPredictor.Horizontal); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, TiffCompression.Deflate, TiffPredictor.Horizontal); [Theory] [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeRgb_WithLzwCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffEncodingMode.Rgb, TiffCompression.Lzw); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, TiffCompression.Lzw); [Theory] [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeRgb_WithLzwCompressionAndPredictor_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffEncodingMode.Rgb, TiffCompression.Lzw, TiffPredictor.Horizontal); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, TiffCompression.Lzw, TiffPredictor.Horizontal); [Theory] [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeRgb_WithPackBitsCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffEncodingMode.Rgb, TiffCompression.PackBits); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, TiffCompression.PackBits); [Theory] [WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeGray_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.Gray); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffPhotometricInterpretation.BlackIsZero); [Theory] [WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeGray_WithDeflateCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.Gray, TiffCompression.Deflate); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffPhotometricInterpretation.BlackIsZero, TiffCompression.Deflate); [Theory] [WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeGray_WithDeflateCompressionAndPredictor_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.Gray, TiffCompression.Deflate, TiffPredictor.Horizontal); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffPhotometricInterpretation.BlackIsZero, TiffCompression.Deflate, TiffPredictor.Horizontal); [Theory] [WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeGray_WithLzwCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.Gray, TiffCompression.Lzw); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffPhotometricInterpretation.BlackIsZero, TiffCompression.Lzw); [Theory] [WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeGray_WithLzwCompressionAndPredictor_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.Gray, TiffCompression.Lzw, TiffPredictor.Horizontal); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffPhotometricInterpretation.BlackIsZero, TiffCompression.Lzw, TiffPredictor.Horizontal); [Theory] [WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeGray_WithPackBitsCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.Gray, TiffCompression.PackBits); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffPhotometricInterpretation.BlackIsZero, TiffCompression.PackBits); [Theory] [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => - TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.ColorPalette, useExactComparer: false, compareTolerance: 0.001f); + TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffPhotometricInterpretation.PaletteColor, useExactComparer: false, compareTolerance: 0.001f); [Theory] [WithFile(Rgb4BitPalette, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_With4Bit_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => //// Note: The magick reference decoder does not support 4 bit tiff's, so we use our TIFF decoder instead. - TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit4, TiffEncodingMode.ColorPalette, useExactComparer: false, compareTolerance: 0.001f, imageDecoder: new TiffDecoder()); + TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit4, TiffPhotometricInterpretation.PaletteColor, useExactComparer: false, compareTolerance: 0.001f, imageDecoder: new TiffDecoder()); [Theory] [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_WithPackBitsCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => - TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.ColorPalette, TiffCompression.PackBits, useExactComparer: false, compareTolerance: 0.001f); + TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffPhotometricInterpretation.PaletteColor, TiffCompression.PackBits, useExactComparer: false, compareTolerance: 0.001f); [Theory] [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_WithDeflateCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => - TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.ColorPalette, TiffCompression.Deflate, useExactComparer: false, compareTolerance: 0.001f); + TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffPhotometricInterpretation.PaletteColor, TiffCompression.Deflate, useExactComparer: false, compareTolerance: 0.001f); [Theory] [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_WithDeflateCompressionAndPredictor_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => - TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.ColorPalette, TiffCompression.Deflate, TiffPredictor.Horizontal, useExactComparer: false, compareTolerance: 0.001f); + TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffPhotometricInterpretation.PaletteColor, TiffCompression.Deflate, TiffPredictor.Horizontal, useExactComparer: false, compareTolerance: 0.001f); [Theory] [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_WithLzwCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => - TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.ColorPalette, TiffCompression.Lzw, useExactComparer: false, compareTolerance: 0.001f); + TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffPhotometricInterpretation.PaletteColor, TiffCompression.Lzw, useExactComparer: false, compareTolerance: 0.001f); [Theory] [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_WithLzwCompressionAndPredictor_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => - TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.ColorPalette, TiffCompression.Lzw, TiffPredictor.Horizontal, useExactComparer: false, compareTolerance: 0.001f); + TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffPhotometricInterpretation.PaletteColor, TiffCompression.Lzw, TiffPredictor.Horizontal, useExactComparer: false, compareTolerance: 0.001f); [Theory] [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeBiColor_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffEncodingMode.BiColor); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.BlackIsZero); [Theory] [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeBiColor_WithDeflateCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffEncodingMode.BiColor, TiffCompression.Deflate); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffPhotometricInterpretation.BlackIsZero, TiffCompression.Deflate); [Theory] [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeBiColor_WithPackBitsCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffEncodingMode.BiColor, TiffCompression.PackBits); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffPhotometricInterpretation.BlackIsZero, TiffCompression.PackBits); [Theory] [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeBiColor_WithCcittGroup3FaxCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffEncodingMode.BiColor, TiffCompression.CcittGroup3Fax); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffPhotometricInterpretation.BlackIsZero, TiffCompression.CcittGroup3Fax); [Theory] [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeBiColor_WithModifiedHuffmanCompression_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffEncodingMode.BiColor, TiffCompression.Ccitt1D); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffPhotometricInterpretation.BlackIsZero, TiffCompression.Ccitt1D); [Theory] - [WithFile(GrayscaleUncompressed, PixelTypes.L8, TiffEncodingMode.Gray, TiffCompression.PackBits)] - [WithFile(PaletteDeflateMultistrip, PixelTypes.L8, TiffEncodingMode.ColorPalette, TiffCompression.Lzw)] - [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffEncodingMode.Rgb, TiffCompression.Deflate)] - [WithFile(RgbUncompressed, PixelTypes.Rgb24, TiffEncodingMode.Rgb, TiffCompression.None)] - [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffEncodingMode.Rgb, TiffCompression.None)] - [WithFile(RgbUncompressed, PixelTypes.Rgb48, TiffEncodingMode.Rgb, TiffCompression.None)] - public void TiffEncoder_StripLength(TestImageProvider provider, TiffEncodingMode mode, TiffCompression compression) + [WithFile(GrayscaleUncompressed, PixelTypes.L8, TiffPhotometricInterpretation.BlackIsZero, TiffCompression.PackBits)] + [WithFile(PaletteDeflateMultistrip, PixelTypes.L8, TiffPhotometricInterpretation.PaletteColor, TiffCompression.Lzw)] + [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffPhotometricInterpretation.Rgb, TiffCompression.Deflate)] + [WithFile(RgbUncompressed, PixelTypes.Rgb24, TiffPhotometricInterpretation.Rgb, TiffCompression.None)] + [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffPhotometricInterpretation.Rgb, TiffCompression.None)] + [WithFile(RgbUncompressed, PixelTypes.Rgb48, TiffPhotometricInterpretation.Rgb, TiffCompression.None)] + public void TiffEncoder_StripLength(TestImageProvider provider, TiffPhotometricInterpretation photometricInterpretation, TiffCompression compression) where TPixel : unmanaged, IPixel => - TestStripLength(provider, mode, compression); + TestStripLength(provider, photometricInterpretation, compression); [Theory] - [WithFile(Calliphora_BiColorUncompressed, PixelTypes.L8, TiffEncodingMode.BiColor, TiffCompression.CcittGroup3Fax)] - public void TiffEncoder_StripLength_OutOfBounds(TestImageProvider provider, TiffEncodingMode mode, TiffCompression compression) + [WithFile(Calliphora_BiColorUncompressed, PixelTypes.L8, TiffPhotometricInterpretation.BlackIsZero, TiffCompression.CcittGroup3Fax)] + public void TiffEncoder_StripLength_OutOfBounds(TestImageProvider provider, TiffPhotometricInterpretation photometricInterpretation, TiffCompression compression) where TPixel : unmanaged, IPixel => - //// CcittGroup3Fax compressed data length can be larger than the original length - Assert.Throws(() => TestStripLength(provider, mode, compression)); + //// CcittGroup3Fax compressed data length can be larger than the original length. + Assert.Throws(() => TestStripLength(provider, photometricInterpretation, compression)); - private static void TestStripLength(TestImageProvider provider, TiffEncodingMode mode, TiffCompression compression) + private static void TestStripLength(TestImageProvider provider, TiffPhotometricInterpretation photometricInterpretation, TiffCompression compression) where TPixel : unmanaged, IPixel { // arrange - var tiffEncoder = new TiffEncoder() { Mode = mode, Compression = compression }; + var tiffEncoder = new TiffEncoder() { PhotometricInterpretation = photometricInterpretation, Compression = compression }; using Image input = provider.GetImage(); using var memStream = new MemoryStream(); ExifProfile exifProfileInput = input.Frames.RootFrame.Metadata.ExifProfile; @@ -384,14 +446,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff TestTiffEncoderCore( provider, inputMeta.BitsPerPixel, - mode, + photometricInterpretation, inputCompression); } private static void TestTiffEncoderCore( TestImageProvider provider, TiffBitsPerPixel? bitsPerPixel, - TiffEncodingMode mode, + TiffPhotometricInterpretation photometricInterpretation, TiffCompression compression = TiffCompression.None, TiffPredictor predictor = TiffPredictor.None, bool useExactComparer = true, @@ -402,7 +464,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff using Image image = provider.GetImage(); var encoder = new TiffEncoder { - Mode = mode, + PhotometricInterpretation = photometricInterpretation, BitsPerPixel = bitsPerPixel, Compression = compression, HorizontalPredictor = predictor diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index 228eec078..25f0521f9 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -222,7 +222,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal(TiffBitsPerPixel.Bit4, frameMetaInput.BitsPerPixel); // Save to Tiff - var tiffEncoder = new TiffEncoder() { Mode = TiffEncodingMode.Rgb }; + var tiffEncoder = new TiffEncoder() { PhotometricInterpretation = TiffPhotometricInterpretation.Rgb }; using var ms = new MemoryStream(); image.Save(ms, tiffEncoder); @@ -237,7 +237,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff byte[] encodedImageXmpProfile = rootFrameEncodedImage.Metadata.XmpProfile; Assert.Equal(TiffBitsPerPixel.Bit4, tiffMetaDataEncodedRootFrame.BitsPerPixel); - Assert.Equal(TiffCompression.None, (TiffCompression)encodedImageExifProfile.GetValue(ExifTag.Compression).Value); + Assert.Equal(TiffCompression.Lzw, (TiffCompression)encodedImageExifProfile.GetValue(ExifTag.Compression).Value); Assert.Equal(inputMetaData.HorizontalResolution, encodedImageMetaData.HorizontalResolution); Assert.Equal(inputMetaData.VerticalResolution, encodedImageMetaData.VerticalResolution); From 11a4e2027b22304b690bf96afb2eba1a3c607aa9 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 21 May 2021 19:08:41 +0200 Subject: [PATCH 247/275] Add Compression, PhotometricInterpretation and Predictor to TiffFrameMetadata --- .../Formats/Tiff/TiffDecoderOptionsParser.cs | 27 ++++----- .../Formats/Tiff/TiffEncoderCore.cs | 15 ++--- .../Formats/Tiff/TiffFrameMetadata.cs | 58 ++++++++++++++++++- .../Metadata/Profiles/Exif/ExifProfile.cs | 2 +- .../Formats/Tiff/TiffEncoderTests.cs | 34 +++++------ .../Formats/Tiff/TiffMetadataTests.cs | 57 +++++++++++++++--- 6 files changed, 134 insertions(+), 59 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs index 88a2d194d..64df61bf0 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs @@ -14,8 +14,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// internal static class TiffDecoderOptionsParser { - private const TiffPredictor DefaultPredictor = TiffPredictor.None; - private const TiffPlanarConfiguration DefaultPlanarConfiguration = TiffPlanarConfiguration.Chunky; /// @@ -23,8 +21,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// 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, ExifProfile exifProfile, TiffFrameMetadata entries) + /// The IFD entries container to read the image format information for current frame. + public static void VerifyAndParse(this TiffDecoderCore options, ExifProfile exifProfile, TiffFrameMetadata frameMetadata) { if (exifProfile.GetValue(ExifTag.TileOffsets)?.Value != null) { @@ -42,8 +40,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff TiffThrowHelper.ThrowNotSupported("The lower-order bits of the byte FillOrder is not supported."); } - TiffPredictor predictor = (TiffPredictor?)exifProfile.GetValue(ExifTag.Predictor)?.Value ?? DefaultPredictor; - if (predictor == TiffPredictor.FloatingPoint) + if (frameMetadata.Predictor == TiffPredictor.FloatingPoint) { TiffThrowHelper.ThrowNotSupported("TIFF images with FloatingPoint horizontal predictor are not supported."); } @@ -68,14 +65,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff VerifyRequiredFieldsArePresent(exifProfile); 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.BlackIsZero; - options.BitsPerPixel = entries.BitsPerPixel != null ? (int)entries.BitsPerPixel.Value : (int)TiffBitsPerPixel.Bit24; - options.BitsPerSample = GetBitsPerSample(entries.BitsPerPixel); - - ParseColorType(options, exifProfile); - ParseCompression(options, exifProfile); + options.Predictor = frameMetadata.Predictor ?? TiffFrameMetadata.DefaultPredictor; + options.PhotometricInterpretation = frameMetadata.PhotometricInterpretation ?? TiffFrameMetadata.DefaultPhotometricInterpretation; + options.BitsPerPixel = frameMetadata.BitsPerPixel != null ? (int)frameMetadata.BitsPerPixel.Value : (int)TiffFrameMetadata.DefaultBitsPerPixel; + options.BitsPerSample = GetBitsPerSample(frameMetadata.BitsPerPixel); + + options.ParseColorType(exifProfile); + options.ParseCompression(frameMetadata.Compression, exifProfile); } private static void VerifyRequiredFieldsArePresent(ExifProfile exifProfile) @@ -222,9 +218,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff } } - private static void ParseCompression(this TiffDecoderCore options, ExifProfile exifProfile) + private static void ParseCompression(this TiffDecoderCore options, TiffCompression? compression, ExifProfile exifProfile) { - TiffCompression compression = exifProfile.GetValue(ExifTag.Compression) != null ? (TiffCompression)exifProfile.GetValue(ExifTag.Compression).Value : TiffCompression.None; switch (compression) { case TiffCompression.None: diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index f9402e4d4..61dcd0625 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -111,19 +111,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff : rootFramePhotometricInterpretation; ImageFrameMetadata rootFrameMetaData = image.Frames.RootFrame.Metadata; - ExifProfile rootFrameExifProfile = image.Frames.RootFrame.Metadata.ExifProfile; - TiffBitsPerPixel? rootFrameBitsPerPixel = rootFrameMetaData.GetTiffMetadata().BitsPerPixel; + TiffFrameMetadata rootFrameTiffMetaData = rootFrameMetaData.GetTiffMetadata(); + TiffBitsPerPixel? rootFrameBitsPerPixel = rootFrameTiffMetaData.BitsPerPixel; // If the user has not chosen a predictor or compression, set the values from the decoded image, if present. - if (!this.HorizontalPredictor.HasValue && rootFrameExifProfile?.GetValue(ExifTag.Predictor) != null) - { - this.HorizontalPredictor = (TiffPredictor)rootFrameExifProfile?.GetValue(ExifTag.Predictor).Value; - } - - if (!this.CompressionType.HasValue && rootFrameExifProfile?.GetValue(ExifTag.Compression) != null) - { - this.CompressionType = (TiffCompression)rootFrameExifProfile?.GetValue(ExifTag.Compression).Value; - } + this.HorizontalPredictor ??= rootFrameTiffMetaData.Predictor; + this.CompressionType ??= rootFrameTiffMetaData.Compression; this.SetBitsPerPixel(rootFrameBitsPerPixel, image.PixelType.BitsPerPixel, photometricInterpretation); this.SetPhotometricInterpretation(photometricInterpretation); diff --git a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs index c21238ad9..baba5195d 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Metadata.Profiles.Exif; namespace SixLabors.ImageSharp.Formats.Tiff @@ -10,6 +11,26 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// public class TiffFrameMetadata : IDeepCloneable { + /// + /// 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 compression is None. + /// + public const TiffCompression DefaultCompression = TiffCompression.None; + + /// + /// The default photometric interpretation is BlackIsZero. + /// + public const TiffPhotometricInterpretation DefaultPhotometricInterpretation = TiffPhotometricInterpretation.BlackIsZero; + /// /// Initializes a new instance of the class. /// @@ -28,6 +49,21 @@ 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 color space of the image data. + /// + public TiffPhotometricInterpretation? PhotometricInterpretation { 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; } + /// /// Returns a new instance parsed from the given Exif profile. /// @@ -52,6 +88,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff ushort[] bitsPerSample = profile.GetValue(ExifTag.BitsPerSample)?.Value; meta.BitsPerPixel = BitsPerPixelFromBitsPerSample(bitsPerSample); + meta.Compression = (TiffCompression?)profile.GetValue(ExifTag.Compression)?.Value ?? DefaultCompression; + meta.PhotometricInterpretation = (TiffPhotometricInterpretation?)profile.GetValue(ExifTag.PhotometricInterpretation)?.Value ?? DefaultPhotometricInterpretation; + meta.Predictor = (TiffPredictor?)profile.GetValue(ExifTag.Predictor)?.Value ?? DefaultPredictor; + + profile.RemoveValue(ExifTag.Compression); + profile.RemoveValue(ExifTag.PhotometricInterpretation); + profile.RemoveValue(ExifTag.Predictor); } /// @@ -63,7 +106,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff { if (bitsPerSample == null) { - return TiffBitsPerPixel.Bit24; + return DefaultBitsPerPixel; } int bitsPerPixel = 0; @@ -76,6 +119,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff } /// - public IDeepCloneable DeepClone() => new TiffFrameMetadata(this); + public IDeepCloneable DeepClone() + { + var clone = new TiffFrameMetadata + { + BitsPerPixel = this.BitsPerPixel, + Compression = this.Compression, + PhotometricInterpretation = this.PhotometricInterpretation, + Predictor = this.Predictor + }; + + return clone; + } } } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs index 39c8c2293..9265314ed 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs @@ -167,7 +167,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// /// The tag of the EXIF value. /// - /// The . + /// True, if the value was removed, otherwise false. /// public bool RemoveValue(ExifTag tag) { diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 99a74182d..ce4b4f165 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -48,15 +48,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // assert memStream.Position = 0; using var output = Image.Load(Configuration, memStream); - ExifProfile exifProfile = output.Frames.RootFrame.Metadata.ExifProfile; - var frameMetaData = TiffFrameMetadata.Parse(exifProfile); + TiffFrameMetadata frameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata(); Assert.Equal(expectedBitsPerPixel, frameMetaData.BitsPerPixel); - Assert.Equal(TiffCompression.None, (TiffCompression)exifProfile.GetValue(ExifTag.Compression).Value); + Assert.Equal(TiffCompression.None, frameMetaData.Compression); } [Theory] [InlineData(TiffBitsPerPixel.Bit24)] - [InlineData(TiffBitsPerPixel.Bit8)] + [InlineData(TiffBitsPerPixel.Bit8)] [InlineData(TiffBitsPerPixel.Bit4)] [InlineData(TiffBitsPerPixel.Bit1)] public void EncoderOptions_SetBitPerPixel_Works(TiffBitsPerPixel bitsPerPixel) @@ -73,10 +72,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff memStream.Position = 0; using var output = Image.Load(Configuration, memStream); - ExifProfile exifProfile = output.Frames.RootFrame.Metadata.ExifProfile; TiffFrameMetadata frameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata(); Assert.Equal(bitsPerPixel, frameMetaData.BitsPerPixel); - Assert.Equal(TiffCompression.None, (TiffCompression)exifProfile.GetValue(ExifTag.Compression).Value); + Assert.Equal(TiffCompression.None, frameMetaData.Compression); } [Theory] @@ -112,10 +110,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // assert memStream.Position = 0; using var output = Image.Load(Configuration, memStream); - ExifProfile exifProfile = output.Frames.RootFrame.Metadata.ExifProfile; TiffFrameMetadata rootFrameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata(); Assert.Equal(expectedBitsPerPixel, rootFrameMetaData.BitsPerPixel); - Assert.Equal(expectedCompression, (TiffCompression)exifProfile.GetValue(ExifTag.Compression).Value); + Assert.Equal(expectedCompression, rootFrameMetaData.Compression); } [Theory] @@ -151,7 +148,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff var tiffEncoder = new TiffEncoder(); using Image input = new Image(10, 10); using var memStream = new MemoryStream(); - var expectedBitsPerPixel = TiffBitsPerPixel.Bit8; + TiffBitsPerPixel expectedBitsPerPixel = TiffBitsPerPixel.Bit8; // act input.Save(memStream, tiffEncoder); @@ -183,8 +180,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // assert memStream.Position = 0; using var output = Image.Load(Configuration, memStream); - ExifProfile exifProfile = output.Frames.RootFrame.Metadata.ExifProfile; - Assert.Equal(expectedCompression, (TiffCompression)exifProfile.GetValue(ExifTag.Compression).Value); + Assert.Equal(expectedCompression, output.Frames.RootFrame.Metadata.GetTiffMetadata().Compression); } [Theory] @@ -206,8 +202,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // assert memStream.Position = 0; using var output = Image.Load(Configuration, memStream); - ExifProfile exifProfile = output.Frames.RootFrame.Metadata.ExifProfile; - Assert.Equal(expectedPredictor, (TiffPredictor)exifProfile.GetValue(ExifTag.Predictor).Value); + TiffFrameMetadata frameMetadata = output.Frames.RootFrame.Metadata.GetTiffMetadata(); + Assert.Equal(expectedPredictor, frameMetadata.Predictor); } [Theory] @@ -229,10 +225,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // assert memStream.Position = 0; using var output = Image.Load(Configuration, memStream); - ExifProfile exifProfile = output.Frames.RootFrame.Metadata.ExifProfile; - var frameMetaData = TiffFrameMetadata.Parse(exifProfile); + TiffFrameMetadata frameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata(); Assert.Equal(TiffBitsPerPixel.Bit1, frameMetaData.BitsPerPixel); - Assert.Equal(expectedCompression, (TiffCompression)exifProfile.GetValue(ExifTag.Compression).Value); + Assert.Equal(expectedCompression, frameMetaData.Compression); } [Theory] @@ -402,9 +397,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff var tiffEncoder = new TiffEncoder() { PhotometricInterpretation = photometricInterpretation, Compression = compression }; 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 = TiffFrameMetadata.Parse(exifProfileInput); + TiffFrameMetadata inputMeta = input.Frames.RootFrame.Metadata.GetTiffMetadata(); + TiffCompression inputCompression = inputMeta.Compression ?? TiffCompression.None; // act input.Save(memStream, tiffEncoder); @@ -413,7 +407,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff memStream.Position = 0; using var output = Image.Load(Configuration, memStream); ExifProfile exifProfileOutput = output.Frames.RootFrame.Metadata.ExifProfile; - var outputMeta = TiffFrameMetadata.Parse(exifProfileOutput); + TiffFrameMetadata outputMeta = output.Frames.RootFrame.Metadata.GetTiffMetadata(); ImageFrame rootFrame = output.Frames.RootFrame; Number rowsPerStrip = exifProfileOutput.GetValue(ExifTag.RowsPerStrip) != null ? exifProfileOutput.GetValue(ExifTag.RowsPerStrip).Value : TiffConstants.RowsPerStripInfinity; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index 25f0521f9..7f799e73c 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -45,6 +45,41 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.False(meta.ByteOrder == clone.ByteOrder); } + [Theory] + [WithFile(SampleMetadata, PixelTypes.Rgba32)] + public void TiffFrameMetadata_CloneIsDeep(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TiffDecoder)) + { + TiffFrameMetadata meta = image.Frames.RootFrame.Metadata.GetTiffMetadata(); + var cloneSameAsSampleMetaData = (TiffFrameMetadata)meta.DeepClone(); + VerifyExpectedTiffFrameMetaDataIsPresent(cloneSameAsSampleMetaData); + + var clone = (TiffFrameMetadata)meta.DeepClone(); + + clone.BitsPerPixel = TiffBitsPerPixel.Bit8; + clone.Compression = TiffCompression.None; + clone.PhotometricInterpretation = TiffPhotometricInterpretation.CieLab; + clone.Predictor = TiffPredictor.Horizontal; + + Assert.False(meta.BitsPerPixel == clone.BitsPerPixel); + Assert.False(meta.Compression == clone.Compression); + Assert.False(meta.PhotometricInterpretation == clone.PhotometricInterpretation); + Assert.False(meta.Predictor == clone.Predictor); + } + } + + private static void VerifyExpectedTiffFrameMetaDataIsPresent(TiffFrameMetadata frameMetaData) + { + Assert.NotNull(frameMetaData); + Assert.NotNull(frameMetaData.BitsPerPixel); + Assert.Equal(TiffBitsPerSample.Bit4, (TiffBitsPerSample)frameMetaData.BitsPerPixel); + Assert.Equal(TiffCompression.Lzw, frameMetaData.Compression); + Assert.Equal(TiffPhotometricInterpretation.PaletteColor, frameMetaData.PhotometricInterpretation); + Assert.Equal(TiffPredictor.None, frameMetaData.Predictor); + } + [Theory] [InlineData(Calliphora_BiColorUncompressed, 1)] [InlineData(GrayscaleUncompressed, 8)] @@ -99,7 +134,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.NotNull(rootFrameMetaData.XmpProfile); Assert.NotNull(rootFrameMetaData.ExifProfile); Assert.Equal(2599, rootFrameMetaData.XmpProfile.Length); - Assert.Equal(30, rootFrameMetaData.ExifProfile.Values.Count); + Assert.Equal(27, rootFrameMetaData.ExifProfile.Values.Count); } } } @@ -132,9 +167,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal(2599, rootFrame.Metadata.XmpProfile.Length); ExifProfile exifProfile = rootFrame.Metadata.ExifProfile; + TiffFrameMetadata tiffFrameMetadata = rootFrame.Metadata.GetTiffMetadata(); Assert.NotNull(exifProfile); - Assert.Equal(30, exifProfile.Values.Count); - Assert.Equal(TiffCompression.Lzw, (TiffCompression)exifProfile.GetValue(ExifTag.Compression).Value); + + // The original exifProfile has 30 values, but 3 of those values will be stored in the TiffFrameMetaData + // and removed from the profile on decode. + Assert.Equal(27, exifProfile.Values.Count); + Assert.Equal(TiffCompression.Lzw, tiffFrameMetadata.Compression); 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); @@ -153,7 +192,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff 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(TiffPredictor.None, tiffFrameMetadata.Predictor); Assert.Equal(PixelResolutionUnit.PixelsPerInch, UnitConverter.ExifProfileToResolutionUnit(exifProfile)); ushort[] colorMap = exifProfile.GetValue(ExifTag.ColorMap)?.Value; Assert.NotNull(colorMap); @@ -162,7 +201,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff 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(TiffPhotometricInterpretation.PaletteColor, tiffFrameMetadata.PhotometricInterpretation); Assert.Equal(1u, exifProfile.GetValue(ExifTag.SamplesPerPixel).Value); ImageMetadata imageMetaData = image.Metadata; @@ -213,12 +252,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff using Image image = provider.GetImage(new TiffDecoder() { IgnoreMetadata = false }); ImageMetadata inputMetaData = image.Metadata; - var frameMetaInput = TiffFrameMetadata.Parse(image.Frames.RootFrame.Metadata.ExifProfile); ImageFrame rootFrameInput = image.Frames.RootFrame; + TiffFrameMetadata frameMetaInput = rootFrameInput.Metadata.GetTiffMetadata(); byte[] xmpProfileInput = rootFrameInput.Metadata.XmpProfile; ExifProfile exifProfileInput = rootFrameInput.Metadata.ExifProfile; - Assert.Equal(TiffCompression.Lzw, (TiffCompression)exifProfileInput.GetValue(ExifTag.Compression).Value); + Assert.Equal(TiffCompression.Lzw, frameMetaInput.Compression); Assert.Equal(TiffBitsPerPixel.Bit4, frameMetaInput.BitsPerPixel); // Save to Tiff @@ -232,12 +271,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff ImageMetadata encodedImageMetaData = encodedImage.Metadata; ImageFrame rootFrameEncodedImage = encodedImage.Frames.RootFrame; - var tiffMetaDataEncodedRootFrame = TiffFrameMetadata.Parse(rootFrameEncodedImage.Metadata.ExifProfile); + TiffFrameMetadata tiffMetaDataEncodedRootFrame = rootFrameEncodedImage.Metadata.GetTiffMetadata(); ExifProfile encodedImageExifProfile = rootFrameEncodedImage.Metadata.ExifProfile; byte[] encodedImageXmpProfile = rootFrameEncodedImage.Metadata.XmpProfile; Assert.Equal(TiffBitsPerPixel.Bit4, tiffMetaDataEncodedRootFrame.BitsPerPixel); - Assert.Equal(TiffCompression.Lzw, (TiffCompression)encodedImageExifProfile.GetValue(ExifTag.Compression).Value); + Assert.Equal(TiffCompression.Lzw, tiffMetaDataEncodedRootFrame.Compression); Assert.Equal(inputMetaData.HorizontalResolution, encodedImageMetaData.HorizontalResolution); Assert.Equal(inputMetaData.VerticalResolution, encodedImageMetaData.VerticalResolution); From ccc3f9b8815817dbefdee4bbd84a5eb7ff7dfe2b Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 23 May 2021 20:20:00 +0200 Subject: [PATCH 248/275] Rework setting tiff encoder parameters according to review --- .../Formats/Tiff/TiffDecoderOptionsParser.cs | 12 +- .../Formats/Tiff/TiffEncoderCore.cs | 172 +++++++++--------- .../Formats/Tiff/TiffFrameMetadata.cs | 70 +++---- .../Formats/Tiff/TiffEncoderTests.cs | 30 +-- .../Formats/Tiff/TiffMetadataTests.cs | 10 +- 5 files changed, 122 insertions(+), 172 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs index 64df61bf0..b5f3e7cf1 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs @@ -62,19 +62,19 @@ namespace SixLabors.ImageSharp.Formats.Tiff TiffThrowHelper.ThrowNotSupported("Variable-sized strips are not supported."); } - VerifyRequiredFieldsArePresent(exifProfile); + VerifyRequiredFieldsArePresent(exifProfile, frameMetadata); options.PlanarConfiguration = (TiffPlanarConfiguration?)exifProfile.GetValue(ExifTag.PlanarConfiguration)?.Value ?? DefaultPlanarConfiguration; - options.Predictor = frameMetadata.Predictor ?? TiffFrameMetadata.DefaultPredictor; - options.PhotometricInterpretation = frameMetadata.PhotometricInterpretation ?? TiffFrameMetadata.DefaultPhotometricInterpretation; - options.BitsPerPixel = frameMetadata.BitsPerPixel != null ? (int)frameMetadata.BitsPerPixel.Value : (int)TiffFrameMetadata.DefaultBitsPerPixel; + 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) + private static void VerifyRequiredFieldsArePresent(ExifProfile exifProfile, TiffFrameMetadata frameMetadata) { if (exifProfile.GetValue(ExifTag.StripOffsets) == null) { @@ -86,7 +86,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff TiffThrowHelper.ThrowImageFormatException("StripByteCounts are missing and are required for decoding the TIFF image!"); } - if (exifProfile.GetValue(ExifTag.BitsPerSample) == null) + if (frameMetadata.BitsPerPixel == null) { TiffThrowHelper.ThrowNotSupported("The TIFF BitsPerSample entry is missing which is required to decode the image!"); } diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 61dcd0625..6811601e3 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -54,6 +54,26 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// 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; + /// /// Initializes a new instance of the class. /// @@ -105,21 +125,33 @@ namespace SixLabors.ImageSharp.Formats.Tiff this.configuration = image.GetConfiguration(); - TiffPhotometricInterpretation rootFramePhotometricInterpretation = GetRootFramePhotometricInterpretation(image); - TiffPhotometricInterpretation photometricInterpretation = this.PhotometricInterpretation == TiffPhotometricInterpretation.PaletteColor - ? TiffPhotometricInterpretation.PaletteColor - : rootFramePhotometricInterpretation; - ImageFrameMetadata rootFrameMetaData = image.Frames.RootFrame.Metadata; TiffFrameMetadata rootFrameTiffMetaData = rootFrameMetaData.GetTiffMetadata(); - TiffBitsPerPixel? rootFrameBitsPerPixel = rootFrameTiffMetaData.BitsPerPixel; - // If the user has not chosen a predictor or compression, set the values from the decoded image, if present. - this.HorizontalPredictor ??= rootFrameTiffMetaData.Predictor; - this.CompressionType ??= rootFrameTiffMetaData.Compression; + // Determine the correct values to encode with. + // EncoderOptions > Metadata > Default. + TiffBitsPerPixel? bitsPerPixel = this.BitsPerPixel ?? rootFrameTiffMetaData.BitsPerPixel; + + TiffPhotometricInterpretation? photometricInterpretation = this.PhotometricInterpretation ?? rootFrameTiffMetaData.PhotometricInterpretation; + + TiffPredictor predictor = + this.HorizontalPredictor + ?? rootFrameTiffMetaData.Predictor + ?? DefaultPredictor; - this.SetBitsPerPixel(rootFrameBitsPerPixel, image.PixelType.BitsPerPixel, photometricInterpretation); - this.SetPhotometricInterpretation(photometricInterpretation); + TiffCompression compression = + this.CompressionType + ?? rootFrameTiffMetaData.Compression + ?? DefaultCompression; + + // Make sure, the bits per pixel and PhotometricInterpretation have values which makes sense in combination with the other chosen values. + bitsPerPixel = this.SanitizeBitsPerPixel(bitsPerPixel, image.PixelType.BitsPerPixel, photometricInterpretation, compression); + photometricInterpretation = this.SanitizePhotometricInterpretation(photometricInterpretation, bitsPerPixel, compression); + + this.BitsPerPixel = bitsPerPixel; + this.PhotometricInterpretation = photometricInterpretation; + this.CompressionType = compression; + this.HorizontalPredictor = predictor; using (var writer = new TiffStreamWriter(stream)) { @@ -270,98 +302,65 @@ namespace SixLabors.ImageSharp.Formats.Tiff return nextIfdMarker; } - private void SetPhotometricInterpretation(TiffPhotometricInterpretation? photometricInterpretation) + private TiffPhotometricInterpretation SanitizePhotometricInterpretation(TiffPhotometricInterpretation? photometricInterpretation, TiffBitsPerPixel? bitsPerPixel, TiffCompression compression) { // Make sure, that the fax compressions are only used together with the WhiteIsZero. - if (this.CompressionType == TiffCompression.CcittGroup3Fax || this.CompressionType == TiffCompression.Ccitt1D) + if (compression == TiffCompression.CcittGroup3Fax || compression == TiffCompression.Ccitt1D) { - // The user has not specified a preferred photometric interpretation. - if (this.PhotometricInterpretation == null) - { - this.PhotometricInterpretation = TiffPhotometricInterpretation.WhiteIsZero; - this.BitsPerPixel = TiffBitsPerPixel.Bit1; - return; - } - - if (this.PhotometricInterpretation != TiffPhotometricInterpretation.WhiteIsZero && this.PhotometricInterpretation != TiffPhotometricInterpretation.BlackIsZero) - { - TiffThrowHelper.ThrowImageFormatException( - $"The {this.CompressionType} compression and {this.PhotometricInterpretation} aren't compatible. Please use {this.CompressionType} only with {TiffPhotometricInterpretation.BlackIsZero} or {TiffPhotometricInterpretation.WhiteIsZero}."); - } - else - { - // The “normal” PhotometricInterpretation for bilevel CCITT compressed data is WhiteIsZero. - this.PhotometricInterpretation = TiffPhotometricInterpretation.WhiteIsZero; - } - - return; - } - - switch (this.PhotometricInterpretation) - { - // The currently supported values by the encoder for photometric interpretation: - case TiffPhotometricInterpretation.PaletteColor: - case TiffPhotometricInterpretation.BlackIsZero: - case TiffPhotometricInterpretation.WhiteIsZero: - break; - - default: - this.PhotometricInterpretation = TiffPhotometricInterpretation.Rgb; - break; + // The “normal” PhotometricInterpretation for bilevel CCITT compressed data is WhiteIsZero. + return TiffPhotometricInterpretation.WhiteIsZero; } // Use the bits per pixel to determine the photometric interpretation. - this.SetPhotometricInterpretationWithBitsPerPixel(this.BitsPerPixel, photometricInterpretation); - } - - private void SetPhotometricInterpretationWithBitsPerPixel(TiffBitsPerPixel? bitsPerPixel, TiffPhotometricInterpretation? photometricInterpretation) - { switch (bitsPerPixel) { case TiffBitsPerPixel.Bit1: - this.PhotometricInterpretation = TiffPhotometricInterpretation.BlackIsZero; - break; + return TiffPhotometricInterpretation.BlackIsZero; case TiffBitsPerPixel.Bit4: - this.PhotometricInterpretation = TiffPhotometricInterpretation.PaletteColor; - break; + return TiffPhotometricInterpretation.PaletteColor; case TiffBitsPerPixel.Bit8: - this.PhotometricInterpretation = photometricInterpretation == TiffPhotometricInterpretation.PaletteColor + return photometricInterpretation == TiffPhotometricInterpretation.PaletteColor ? TiffPhotometricInterpretation.PaletteColor : TiffPhotometricInterpretation.BlackIsZero; + } - break; - default: - this.PhotometricInterpretation = TiffPhotometricInterpretation.Rgb; - break; + if (photometricInterpretation.HasValue) + { + return photometricInterpretation.Value; } + + return DefaultPhotometricInterpretation; } - private void SetBitsPerPixel(TiffBitsPerPixel? rootFrameBitsPerPixel, int inputBitsPerPixel, TiffPhotometricInterpretation photometricInterpretation) + private TiffBitsPerPixel SanitizeBitsPerPixel(TiffBitsPerPixel? bitsPerPixel, int inputBitsPerPixel, TiffPhotometricInterpretation? photometricInterpretation, TiffCompression compression) { - this.BitsPerPixel ??= rootFrameBitsPerPixel; - + // Make sure Palette color is only used with 4 and 8 bit per pixel. if (photometricInterpretation == TiffPhotometricInterpretation.PaletteColor) { - if (this.BitsPerPixel != TiffBitsPerPixel.Bit8 && this.BitsPerPixel != TiffBitsPerPixel.Bit4) + if (bitsPerPixel != TiffBitsPerPixel.Bit8 && bitsPerPixel != TiffBitsPerPixel.Bit4) { - this.BitsPerPixel = TiffBitsPerPixel.Bit8; + return TiffBitsPerPixel.Bit8; } + } - return; + if (compression == TiffCompression.Ccitt1D || compression == TiffCompression.CcittGroup3Fax) + { + return TiffBitsPerPixel.Bit1; } - if (this.BitsPerPixel.HasValue) + if (bitsPerPixel.HasValue) { - return; + return bitsPerPixel.Value; } - if (this.PhotometricInterpretation == null && inputBitsPerPixel == 8) + // If no photometric interpretation was chosen, the input image bit per pixel should be preserved. + if (photometricInterpretation == null) { - this.BitsPerPixel = TiffBitsPerPixel.Bit8; - return; + // At the moment only 8 and 32 bits per pixel can be preserved by the tiff encoder. + return inputBitsPerPixel == 8 ? TiffBitsPerPixel.Bit8 : DefaultBitsPerPixel; } - switch (this.PhotometricInterpretation) + switch (photometricInterpretation) { case TiffPhotometricInterpretation.BlackIsZero: case TiffPhotometricInterpretation.WhiteIsZero: @@ -369,37 +368,28 @@ namespace SixLabors.ImageSharp.Formats.Tiff this.CompressionType == TiffCompression.CcittGroup3Fax || this.CompressionType == TiffCompression.CcittGroup4Fax) { - this.BitsPerPixel = TiffBitsPerPixel.Bit1; + return TiffBitsPerPixel.Bit1; } else { - this.BitsPerPixel = TiffBitsPerPixel.Bit8; + return TiffBitsPerPixel.Bit8; } - break; case TiffPhotometricInterpretation.PaletteColor: - if (this.BitsPerPixel != TiffBitsPerPixel.Bit8 && this.BitsPerPixel != TiffBitsPerPixel.Bit4) + if (bitsPerPixel != TiffBitsPerPixel.Bit8 && bitsPerPixel != TiffBitsPerPixel.Bit4) { - this.BitsPerPixel = TiffBitsPerPixel.Bit8; + return TiffBitsPerPixel.Bit8; + } + else + { + return bitsPerPixel.Value; } - break; case TiffPhotometricInterpretation.Rgb: - this.BitsPerPixel = TiffBitsPerPixel.Bit24; - break; - default: - this.PhotometricInterpretation = TiffPhotometricInterpretation.Rgb; - this.BitsPerPixel = TiffBitsPerPixel.Bit24; - break; + return TiffBitsPerPixel.Bit24; } - } - private static TiffPhotometricInterpretation GetRootFramePhotometricInterpretation(Image image) - { - ExifProfile exifProfile = image.Frames.RootFrame.Metadata.ExifProfile; - return exifProfile?.GetValue(ExifTag.PhotometricInterpretation) != null - ? (TiffPhotometricInterpretation)exifProfile?.GetValue(ExifTag.PhotometricInterpretation).Value - : TiffPhotometricInterpretation.BlackIsZero; + return DefaultBitsPerPixel; } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs index baba5195d..25a0578e9 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs @@ -11,26 +11,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// public class TiffFrameMetadata : IDeepCloneable { - /// - /// 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 compression is None. - /// - public const TiffCompression DefaultCompression = TiffCompression.None; - - /// - /// The default photometric interpretation is BlackIsZero. - /// - public const TiffPhotometricInterpretation DefaultPhotometricInterpretation = TiffPhotometricInterpretation.BlackIsZero; - /// /// Initializes a new instance of the class. /// @@ -42,7 +22,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// Initializes a new instance of the class. /// /// The other tiff frame metadata. - private TiffFrameMetadata(TiffFrameMetadata other) => this.BitsPerPixel = other.BitsPerPixel; + private TiffFrameMetadata(TiffFrameMetadata other) + { + this.BitsPerPixel = other.BitsPerPixel; + this.Compression = other.Compression; + this.PhotometricInterpretation = other.PhotometricInterpretation; + this.Predictor = other.Predictor; + } /// /// Gets or sets the bits per pixel. @@ -84,17 +70,20 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The Exif profile containing tiff frame directory tags. internal static void Parse(TiffFrameMetadata meta, ExifProfile profile) { - profile ??= new ExifProfile(); - - ushort[] bitsPerSample = profile.GetValue(ExifTag.BitsPerSample)?.Value; - meta.BitsPerPixel = BitsPerPixelFromBitsPerSample(bitsPerSample); - meta.Compression = (TiffCompression?)profile.GetValue(ExifTag.Compression)?.Value ?? DefaultCompression; - meta.PhotometricInterpretation = (TiffPhotometricInterpretation?)profile.GetValue(ExifTag.PhotometricInterpretation)?.Value ?? DefaultPhotometricInterpretation; - meta.Predictor = (TiffPredictor?)profile.GetValue(ExifTag.Predictor)?.Value ?? DefaultPredictor; - - profile.RemoveValue(ExifTag.Compression); - profile.RemoveValue(ExifTag.PhotometricInterpretation); - profile.RemoveValue(ExifTag.Predictor); + if (profile != null) + { + ushort[] bitsPerSample = profile.GetValue(ExifTag.BitsPerSample)?.Value; + meta.BitsPerPixel = BitsPerPixelFromBitsPerSample(bitsPerSample); + meta.Compression = (TiffCompression?)profile.GetValue(ExifTag.Compression)?.Value; + meta.PhotometricInterpretation = + (TiffPhotometricInterpretation?)profile.GetValue(ExifTag.PhotometricInterpretation)?.Value; + meta.Predictor = (TiffPredictor?)profile.GetValue(ExifTag.Predictor)?.Value; + + profile.RemoveValue(ExifTag.BitsPerSample); + profile.RemoveValue(ExifTag.Compression); + profile.RemoveValue(ExifTag.PhotometricInterpretation); + profile.RemoveValue(ExifTag.Predictor); + } } /// @@ -102,11 +91,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// The tiff bits per sample. /// Bits per pixel. - private static TiffBitsPerPixel BitsPerPixelFromBitsPerSample(ushort[] bitsPerSample) + private static TiffBitsPerPixel? BitsPerPixelFromBitsPerSample(ushort[] bitsPerSample) { if (bitsPerSample == null) { - return DefaultBitsPerPixel; + return null; } int bitsPerPixel = 0; @@ -119,17 +108,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff } /// - public IDeepCloneable DeepClone() - { - var clone = new TiffFrameMetadata - { - BitsPerPixel = this.BitsPerPixel, - Compression = this.Compression, - PhotometricInterpretation = this.PhotometricInterpretation, - Predictor = this.Predictor - }; - - return clone; - } + public IDeepCloneable DeepClone() => new TiffFrameMetadata(this); } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index ce4b4f165..bd61ac4b8 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Theory] [InlineData(TiffBitsPerPixel.Bit24)] - [InlineData(TiffBitsPerPixel.Bit8)] + [InlineData(TiffBitsPerPixel.Bit8)] [InlineData(TiffBitsPerPixel.Bit4)] [InlineData(TiffBitsPerPixel.Bit1)] public void EncoderOptions_SetBitPerPixel_Works(TiffBitsPerPixel bitsPerPixel) @@ -136,8 +136,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // assert memStream.Position = 0; using var output = Image.Load(Configuration, memStream); - ExifProfile exifProfile = output.Frames.RootFrame.Metadata.ExifProfile; - var frameMetaData = TiffFrameMetadata.Parse(exifProfile); + TiffFrameMetadata frameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata(); Assert.Equal(expectedBitsPerPixel, frameMetaData.BitsPerPixel); } @@ -156,8 +155,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // assert memStream.Position = 0; using var output = Image.Load(Configuration, memStream); - ExifProfile exifProfile = output.Frames.RootFrame.Metadata.ExifProfile; - var frameMetaData = TiffFrameMetadata.Parse(exifProfile); + var frameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata(); Assert.Equal(expectedBitsPerPixel, frameMetaData.BitsPerPixel); } @@ -184,11 +182,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff } [Theory] - [WithFile(RgbLzwNoPredictor, PixelTypes.Rgba32, TiffPredictor.None)] + [WithFile(RgbLzwNoPredictor, PixelTypes.Rgba32, null)] [WithFile(RgbLzwPredictor, PixelTypes.Rgba32, TiffPredictor.Horizontal)] - [WithFile(RgbDeflate, PixelTypes.Rgba32, TiffPredictor.None)] + [WithFile(RgbDeflate, PixelTypes.Rgba32, null)] [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 @@ -230,20 +228,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal(expectedCompression, frameMetaData.Compression); } - [Theory] - [InlineData(TiffPhotometricInterpretation.PaletteColor, TiffCompression.CcittGroup3Fax)] - [InlineData(TiffPhotometricInterpretation.PaletteColor, TiffCompression.Ccitt1D)] - public void TiffEncoder_IncompatibilityOptions_ThrowsImageFormatException(TiffPhotometricInterpretation photometricInterpretation, TiffCompression compression) - { - // arrange - using var input = new Image(10, 10); - var encoder = new TiffEncoder() { PhotometricInterpretation = photometricInterpretation, Compression = compression }; - using var memStream = new MemoryStream(); - - // act - Assert.Throws(() => input.Save(memStream, encoder)); - } - [Theory] [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeRgb_Works(TestImageProvider provider) @@ -350,7 +334,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Theory] [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeBiColor_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.BlackIsZero); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffPhotometricInterpretation.BlackIsZero); [Theory] [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index 7f799e73c..f52b74e83 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -134,7 +134,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.NotNull(rootFrameMetaData.XmpProfile); Assert.NotNull(rootFrameMetaData.ExifProfile); Assert.Equal(2599, rootFrameMetaData.XmpProfile.Length); - Assert.Equal(27, rootFrameMetaData.ExifProfile.Values.Count); + Assert.Equal(26, rootFrameMetaData.ExifProfile.Values.Count); } } } @@ -170,9 +170,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff TiffFrameMetadata tiffFrameMetadata = rootFrame.Metadata.GetTiffMetadata(); Assert.NotNull(exifProfile); - // The original exifProfile has 30 values, but 3 of those values will be stored in the TiffFrameMetaData + // The original exifProfile has 30 values, but 4 of those values will be stored in the TiffFrameMetaData // and removed from the profile on decode. - Assert.Equal(27, exifProfile.Values.Count); + Assert.Equal(26, exifProfile.Values.Count); + Assert.Equal(TiffBitsPerPixel.Bit4, tiffFrameMetadata.BitsPerPixel); Assert.Equal(TiffCompression.Lzw, tiffFrameMetadata.Compression); Assert.Equal("This is Название", exifProfile.GetValue(ExifTag.ImageDescription).Value); Assert.Equal("This is Изготовитель камеры", exifProfile.GetValue(ExifTag.Make).Value); @@ -213,9 +214,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff TiffMetadata tiffMetaData = image.Metadata.GetTiffMetadata(); Assert.NotNull(tiffMetaData); Assert.Equal(ByteOrder.LittleEndian, tiffMetaData.ByteOrder); - - var frameMetaData = TiffFrameMetadata.Parse(exifProfile); - Assert.Equal(TiffBitsPerPixel.Bit4, frameMetaData.BitsPerPixel); } } From 787d63000f97d0487db8dd914aed58bcffa9f03b Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 25 May 2021 15:00:47 +0200 Subject: [PATCH 249/275] Rework sanitize and set encoder options: BitsPerPixel should be the primary source of truth --- .../Formats/Tiff/TiffEncoderCore.cs | 118 ++++++++---------- .../Formats/Tiff/TiffEncoderTests.cs | 47 +++++-- 2 files changed, 92 insertions(+), 73 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 6811601e3..74c516f63 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -144,14 +144,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff ?? rootFrameTiffMetaData.Compression ?? DefaultCompression; - // Make sure, the bits per pixel and PhotometricInterpretation have values which makes sense in combination with the other chosen values. - bitsPerPixel = this.SanitizeBitsPerPixel(bitsPerPixel, image.PixelType.BitsPerPixel, photometricInterpretation, compression); - photometricInterpretation = this.SanitizePhotometricInterpretation(photometricInterpretation, bitsPerPixel, compression); - - this.BitsPerPixel = bitsPerPixel; - this.PhotometricInterpretation = photometricInterpretation; - this.CompressionType = compression; - this.HorizontalPredictor = predictor; + // Make sure, the Encoder options makes sense in combination with each other. + this.SanitizeAndSetEncoderOptions(bitsPerPixel, image.PixelType.BitsPerPixel, photometricInterpretation, compression, predictor); using (var writer = new TiffStreamWriter(stream)) { @@ -302,62 +296,49 @@ namespace SixLabors.ImageSharp.Formats.Tiff return nextIfdMarker; } - private TiffPhotometricInterpretation SanitizePhotometricInterpretation(TiffPhotometricInterpretation? photometricInterpretation, TiffBitsPerPixel? bitsPerPixel, TiffCompression compression) - { - // Make sure, that the fax compressions are only used together with the WhiteIsZero. - if (compression == TiffCompression.CcittGroup3Fax || compression == TiffCompression.Ccitt1D) - { - // The “normal” PhotometricInterpretation for bilevel CCITT compressed data is WhiteIsZero. - return TiffPhotometricInterpretation.WhiteIsZero; - } - - // Use the bits per pixel to determine the photometric interpretation. - switch (bitsPerPixel) - { - case TiffBitsPerPixel.Bit1: - return TiffPhotometricInterpretation.BlackIsZero; - case TiffBitsPerPixel.Bit4: - return TiffPhotometricInterpretation.PaletteColor; - case TiffBitsPerPixel.Bit8: - return photometricInterpretation == TiffPhotometricInterpretation.PaletteColor - ? TiffPhotometricInterpretation.PaletteColor - : TiffPhotometricInterpretation.BlackIsZero; - } - - if (photometricInterpretation.HasValue) - { - return photometricInterpretation.Value; - } - - return DefaultPhotometricInterpretation; - } - - private TiffBitsPerPixel SanitizeBitsPerPixel(TiffBitsPerPixel? bitsPerPixel, int inputBitsPerPixel, TiffPhotometricInterpretation? photometricInterpretation, TiffCompression compression) + private void SanitizeAndSetEncoderOptions(TiffBitsPerPixel? bitsPerPixel, int inputBitsPerPixel, TiffPhotometricInterpretation? photometricInterpretation, TiffCompression compression, TiffPredictor predictor) { - // Make sure Palette color is only used with 4 and 8 bit per pixel. - if (photometricInterpretation == TiffPhotometricInterpretation.PaletteColor) + // BitsPerPixel should be the primary source of truth for the encoder options. + if (bitsPerPixel.HasValue) { - if (bitsPerPixel != TiffBitsPerPixel.Bit8 && bitsPerPixel != TiffBitsPerPixel.Bit4) + switch (bitsPerPixel) { - return TiffBitsPerPixel.Bit8; + case TiffBitsPerPixel.Bit1: + if (compression == TiffCompression.Ccitt1D || compression == TiffCompression.CcittGroup3Fax || compression == TiffCompression.CcittGroup4Fax) + { + // 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; + default: + this.SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.Rgb, compression, predictor); + break; } - } - if (compression == TiffCompression.Ccitt1D || compression == TiffCompression.CcittGroup3Fax) - { - return TiffBitsPerPixel.Bit1; - } - - if (bitsPerPixel.HasValue) - { - return bitsPerPixel.Value; + return; } // If no photometric interpretation was chosen, the input image bit per pixel should be preserved. - if (photometricInterpretation == null) + if (!photometricInterpretation.HasValue) { // At the moment only 8 and 32 bits per pixel can be preserved by the tiff encoder. - return inputBitsPerPixel == 8 ? TiffBitsPerPixel.Bit8 : DefaultBitsPerPixel; + if (inputBitsPerPixel == 8) + { + this.SetEncoderOptions(TiffBitsPerPixel.Bit8, TiffPhotometricInterpretation.BlackIsZero, compression, predictor); + return; + } + + this.SetEncoderOptions(TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, compression, predictor); + return; } switch (photometricInterpretation) @@ -368,28 +349,33 @@ namespace SixLabors.ImageSharp.Formats.Tiff this.CompressionType == TiffCompression.CcittGroup3Fax || this.CompressionType == TiffCompression.CcittGroup4Fax) { - return TiffBitsPerPixel.Bit1; + this.SetEncoderOptions(TiffBitsPerPixel.Bit1, photometricInterpretation, compression, TiffPredictor.None); + return; } else { - return TiffBitsPerPixel.Bit8; + this.SetEncoderOptions(TiffBitsPerPixel.Bit8, photometricInterpretation, compression, predictor); + return; } case TiffPhotometricInterpretation.PaletteColor: - if (bitsPerPixel != TiffBitsPerPixel.Bit8 && bitsPerPixel != TiffBitsPerPixel.Bit4) - { - return TiffBitsPerPixel.Bit8; - } - else - { - return bitsPerPixel.Value; - } + this.SetEncoderOptions(TiffBitsPerPixel.Bit8, photometricInterpretation, compression, predictor); + return; case TiffPhotometricInterpretation.Rgb: - return TiffBitsPerPixel.Bit24; + this.SetEncoderOptions(TiffBitsPerPixel.Bit24, photometricInterpretation, compression, predictor); + return; } - return DefaultBitsPerPixel; + this.SetEncoderOptions(DefaultBitsPerPixel, DefaultPhotometricInterpretation, compression, predictor); + } + + private void SetEncoderOptions(TiffBitsPerPixel? bitsPerPixel, TiffPhotometricInterpretation? photometricInterpretation, TiffCompression compression, TiffPredictor predictor) + { + this.BitsPerPixel = bitsPerPixel; + this.PhotometricInterpretation = photometricInterpretation; + this.CompressionType = compression; + this.HorizontalPredictor = predictor; } } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index bd61ac4b8..a40ca04bd 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -35,6 +35,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [InlineData(TiffPhotometricInterpretation.Rgb, TiffBitsPerPixel.Bit24)] [InlineData(TiffPhotometricInterpretation.PaletteColor, TiffBitsPerPixel.Bit8)] [InlineData(TiffPhotometricInterpretation.BlackIsZero, TiffBitsPerPixel.Bit8)] + [InlineData(TiffPhotometricInterpretation.WhiteIsZero, TiffBitsPerPixel.Bit8)] + //// Unsupported TiffPhotometricInterpretation should default to 24 bits + [InlineData(TiffPhotometricInterpretation.CieLab, TiffBitsPerPixel.Bit24)] + [InlineData(TiffPhotometricInterpretation.ColorFilterArray, TiffBitsPerPixel.Bit24)] + [InlineData(TiffPhotometricInterpretation.ItuLab, TiffBitsPerPixel.Bit24)] + [InlineData(TiffPhotometricInterpretation.LinearRaw, TiffBitsPerPixel.Bit24)] + [InlineData(TiffPhotometricInterpretation.Separated, TiffBitsPerPixel.Bit24)] + [InlineData(TiffPhotometricInterpretation.TransparencyMask, TiffBitsPerPixel.Bit24)] public void EncoderOptions_SetPhotometricInterpretation_Works(TiffPhotometricInterpretation? photometricInterpretation, TiffBitsPerPixel expectedBitsPerPixel) { // arrange @@ -155,7 +163,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // assert memStream.Position = 0; using var output = Image.Load(Configuration, memStream); - var frameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata(); + TiffFrameMetadata frameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata(); Assert.Equal(expectedBitsPerPixel, frameMetaData.BitsPerPixel); } @@ -213,7 +221,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff where TPixel : unmanaged, IPixel { // arrange - var encoder = new TiffEncoder() { Compression = compression }; + var encoder = new TiffEncoder() { Compression = compression, BitsPerPixel = TiffBitsPerPixel.Bit1 }; using Image input = provider.GetImage(); using var memStream = new MemoryStream(); @@ -333,27 +341,52 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Theory] [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] - public void TiffEncoder_EncodeBiColor_Works(TestImageProvider provider) + public void TiffEncoder_EncodeBiColor_BlackIsZero_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffPhotometricInterpretation.BlackIsZero); [Theory] [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] - public void TiffEncoder_EncodeBiColor_WithDeflateCompression_Works(TestImageProvider provider) + public void TiffEncoder_EncodeBiColor_WhiteIsZero_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffPhotometricInterpretation.WhiteIsZero); + + [Theory] + [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeBiColor_WithDeflateCompression_BlackIsZero_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffPhotometricInterpretation.BlackIsZero, TiffCompression.Deflate); [Theory] [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] - public void TiffEncoder_EncodeBiColor_WithPackBitsCompression_Works(TestImageProvider provider) + public void TiffEncoder_EncodeBiColor_WithDeflateCompression_WhiteIsZero_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffPhotometricInterpretation.WhiteIsZero, TiffCompression.Deflate); + + [Theory] + [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeBiColor_WithPackBitsCompression_BlackIsZero_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffPhotometricInterpretation.BlackIsZero, TiffCompression.PackBits); [Theory] [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] - public void TiffEncoder_EncodeBiColor_WithCcittGroup3FaxCompression_Works(TestImageProvider provider) + public void TiffEncoder_EncodeBiColor_WithPackBitsCompression_WhiteIsZero_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffPhotometricInterpretation.WhiteIsZero, TiffCompression.PackBits); + + [Theory] + [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeBiColor_WithCcittGroup3FaxCompression_WhiteIsZero_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffPhotometricInterpretation.WhiteIsZero, TiffCompression.CcittGroup3Fax); + + [Theory] + [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeBiColor_WithCcittGroup3FaxCompression_BlackIsZero_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffPhotometricInterpretation.BlackIsZero, TiffCompression.CcittGroup3Fax); [Theory] [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] - public void TiffEncoder_EncodeBiColor_WithModifiedHuffmanCompression_Works(TestImageProvider provider) + public void TiffEncoder_EncodeBiColor_WithModifiedHuffmanCompression_WhiteIsZero_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffPhotometricInterpretation.WhiteIsZero, TiffCompression.Ccitt1D); + + [Theory] + [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeBiColor_WithModifiedHuffmanCompression_BlackIsZero_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffPhotometricInterpretation.BlackIsZero, TiffCompression.Ccitt1D); [Theory] From 2e87f7ac9ac20b85af10ee31aa13a311ffb00ead Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 25 May 2021 23:07:55 +0200 Subject: [PATCH 250/275] Add Tiff format to the default Configuration --- src/ImageSharp/Configuration.cs | 9 +++-- .../Formats/Tiff/ConfigurationExtensions.cs | 23 ----------- .../Codecs/DecodeTiff.cs | 4 +- .../Codecs/EncodeTiff.cs | 2 - tests/ImageSharp.Tests/ConfigurationTests.cs | 4 +- .../Formats/GeneralFormatTests.cs | 11 ++++- .../Formats/ImageFormatManagerTests.cs | 3 ++ .../Formats/Tiff/ImageExtensionsTest.cs | 40 ++++++++----------- .../Formats/Tiff/TiffDecoderTests.cs | 14 ++----- .../Formats/Tiff/TiffEncoderTests.cs | 26 +++++------- .../Formats/Tiff/TiffMetadataTests.cs | 14 ++----- 11 files changed, 53 insertions(+), 97 deletions(-) delete mode 100644 src/ImageSharp/Formats/Tiff/ConfigurationExtensions.cs diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index 8a13ad82d..49b7aa79b 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -10,6 +10,7 @@ using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Tga; +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Processing; @@ -39,7 +40,7 @@ namespace SixLabors.ImageSharp /// /// Initializes a new instance of the class. /// - /// A collection of configuration modules to register + /// A collection of configuration modules to register. public Configuration(params IConfigurationModule[] configurationModules) { if (configurationModules != null) @@ -77,7 +78,7 @@ namespace SixLabors.ImageSharp /// /// Gets or sets the size of the buffer to use when working with streams. - /// Intitialized with by default. + /// Initialized with by default. /// public int StreamProcessingBufferSize { @@ -180,6 +181,7 @@ namespace SixLabors.ImageSharp /// /// . /// . + /// . /// /// The default configuration of . internal static Configuration CreateDefaultInstance() @@ -189,7 +191,8 @@ namespace SixLabors.ImageSharp new JpegConfigurationModule(), new GifConfigurationModule(), new BmpConfigurationModule(), - new TgaConfigurationModule()); + new TgaConfigurationModule(), + new TiffConfigurationModule()); } } } diff --git a/src/ImageSharp/Formats/Tiff/ConfigurationExtensions.cs b/src/ImageSharp/Formats/Tiff/ConfigurationExtensions.cs deleted file mode 100644 index 49f87e090..000000000 --- a/src/ImageSharp/Formats/Tiff/ConfigurationExtensions.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -namespace SixLabors.ImageSharp.Formats.Tiff -{ - /// - /// Helper methods for the Configuration. - /// - public static class ConfigurationExtensions - { - /// - /// Registers the tiff format detector, encoder and decoder. - /// - /// The configuration. - public static void AddTiff(this Configuration configuration) - { - configuration.ImageFormatsManager.AddImageFormat(TiffFormat.Instance); - configuration.ImageFormatsManager.AddImageFormatDetector(new TiffImageFormatDetector()); - configuration.ImageFormatsManager.SetDecoder(TiffFormat.Instance, new TiffDecoder()); - configuration.ImageFormatsManager.SetEncoder(TiffFormat.Instance, new TiffEncoder()); - } - } -} diff --git a/tests/ImageSharp.Benchmarks/Codecs/DecodeTiff.cs b/tests/ImageSharp.Benchmarks/Codecs/DecodeTiff.cs index b388b5d70..e77bb8b3e 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/DecodeTiff.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/DecodeTiff.cs @@ -9,7 +9,6 @@ using System.IO; using BenchmarkDotNet.Attributes; -using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; @@ -23,7 +22,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs [Config(typeof(Config.ShortMultiFramework))] public class DecodeTiff { - private string prevImage = null; + private string prevImage; private byte[] data; @@ -68,7 +67,6 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs if (this.configuration == null) { this.configuration = new Configuration(); - this.configuration.AddTiff(); this.configuration.StreamProcessingBufferSize = BufferSize; } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/EncodeTiff.cs b/tests/ImageSharp.Benchmarks/Codecs/EncodeTiff.cs index 7154b2310..39055faf5 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/EncodeTiff.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/EncodeTiff.cs @@ -43,8 +43,6 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs if (this.core == null) { this.configuration = new Configuration(); - this.configuration.AddTiff(); - this.core = Image.Load(this.configuration, this.TestImageFullPath); this.drawing = System.Drawing.Image.FromFile(this.TestImageFullPath); } diff --git a/tests/ImageSharp.Tests/ConfigurationTests.cs b/tests/ImageSharp.Tests/ConfigurationTests.cs index 655e98c7f..3ad8ef2f8 100644 --- a/tests/ImageSharp.Tests/ConfigurationTests.cs +++ b/tests/ImageSharp.Tests/ConfigurationTests.cs @@ -21,11 +21,11 @@ namespace SixLabors.ImageSharp.Tests public Configuration DefaultConfiguration { get; } - private readonly int expectedDefaultConfigurationCount = 5; + private readonly int expectedDefaultConfigurationCount = 6; public ConfigurationTests() { - // the shallow copy of configuration should behave exactly like the default configuration, + // The shallow copy of configuration should behave exactly like the default configuration, // so by using the copy, we test both the default and the copy. this.DefaultConfiguration = Configuration.CreateDefaultInstance().Clone(); this.ConfigurationEmpty = new Configuration(); diff --git a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs index a171d6d52..f34fc9c78 100644 --- a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs +++ b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Tests.Formats public class GeneralFormatTests { /// - /// A collection made up of one file for each image format + /// A collection made up of one file for each image format. /// public static readonly IEnumerable DefaultFiles = new[] @@ -149,6 +149,11 @@ namespace SixLabors.ImageSharp.Tests.Formats { image.SaveAsTga(output); } + + using (FileStream output = File.OpenWrite(Path.Combine(path, $"{file.FileNameWithoutExtension}.tiff"))) + { + image.SaveAsTga(output); + } } } } @@ -196,6 +201,10 @@ namespace SixLabors.ImageSharp.Tests.Formats [InlineData(100, 100, "tga")] [InlineData(100, 10, "tga")] [InlineData(10, 100, "tga")] + [InlineData(100, 100, "tiff")] + [InlineData(100, 10, "tiff")] + [InlineData(10, 100, "tiff")] + public void CanIdentifyImageLoadedFromBytes(int width, int height, string extension) { using (var image = Image.LoadPixelData(new Rgba32[width * height], width, height)) diff --git a/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs b/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs index dea8c62e1..1e00bfff8 100644 --- a/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs +++ b/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs @@ -11,6 +11,7 @@ using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Tga; +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.PixelFormats; using Xunit; @@ -36,12 +37,14 @@ namespace SixLabors.ImageSharp.Tests.Formats Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType().Count()); Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType().Count()); Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType().Count()); + Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType().Count()); Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType().Count()); Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType().Count()); Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType().Count()); Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType().Count()); Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType().Count()); + Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType().Count()); } [Fact] diff --git a/tests/ImageSharp.Tests/Formats/Tiff/ImageExtensionsTest.cs b/tests/ImageSharp.Tests/Formats/Tiff/ImageExtensionsTest.cs index e2aeeb9e8..3365a1eb3 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/ImageExtensionsTest.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/ImageExtensionsTest.cs @@ -13,26 +13,18 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Trait("Format", "Tiff")] public class ImageExtensionsTest { - private readonly Configuration configuration; - - public ImageExtensionsTest() - { - this.configuration = new Configuration(); - this.configuration.AddTiff(); - } - [Fact] public void SaveAsTiff_Path() { string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest)); string file = Path.Combine(dir, "SaveAsTiff_Path.tiff"); - using (var image = new Image(this.configuration, 10, 10)) + using (var image = new Image(10, 10)) { image.SaveAsTiff(file); } - using (Image.Load(this.configuration, file, out IImageFormat mime)) + using (Image.Load(file, out IImageFormat mime)) { Assert.Equal("image/tiff", mime.DefaultMimeType); } @@ -44,12 +36,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest)); string file = Path.Combine(dir, "SaveAsTiffAsync_Path.tiff"); - using (var image = new Image(this.configuration, 10, 10)) + using (var image = new Image(10, 10)) { await image.SaveAsTiffAsync(file); } - using (Image.Load(this.configuration, file, out IImageFormat mime)) + using (Image.Load(file, out IImageFormat mime)) { Assert.Equal("image/tiff", mime.DefaultMimeType); } @@ -61,12 +53,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions)); string file = Path.Combine(dir, "SaveAsTiff_Path_Encoder.tiff"); - using (var image = new Image(this.configuration, 10, 10)) + using (var image = new Image(10, 10)) { image.SaveAsTiff(file, new TiffEncoder()); } - using (Image.Load(this.configuration, file, out IImageFormat mime)) + using (Image.Load(file, out IImageFormat mime)) { Assert.Equal("image/tiff", mime.DefaultMimeType); } @@ -78,12 +70,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions)); string file = Path.Combine(dir, "SaveAsTiffAsync_Path_Encoder.tiff"); - using (var image = new Image(this.configuration, 10, 10)) + using (var image = new Image(10, 10)) { await image.SaveAsTiffAsync(file, new TiffEncoder()); } - using (Image.Load(this.configuration, file, out IImageFormat mime)) + using (Image.Load(file, out IImageFormat mime)) { Assert.Equal("image/tiff", mime.DefaultMimeType); } @@ -94,14 +86,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { using var memoryStream = new MemoryStream(); - using (var image = new Image(this.configuration, 10, 10)) + using (var image = new Image(10, 10)) { image.SaveAsTiff(memoryStream); } memoryStream.Position = 0; - using (Image.Load(this.configuration, memoryStream, out IImageFormat mime)) + using (Image.Load(memoryStream, out IImageFormat mime)) { Assert.Equal("image/tiff", mime.DefaultMimeType); } @@ -112,14 +104,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { using var memoryStream = new MemoryStream(); - using (var image = new Image(this.configuration, 10, 10)) + using (var image = new Image(10, 10)) { await image.SaveAsTiffAsync(memoryStream); } memoryStream.Position = 0; - using (Image.Load(this.configuration, memoryStream, out IImageFormat mime)) + using (Image.Load(memoryStream, out IImageFormat mime)) { Assert.Equal("image/tiff", mime.DefaultMimeType); } @@ -130,14 +122,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { using var memoryStream = new MemoryStream(); - using (var image = new Image(this.configuration, 10, 10)) + using (var image = new Image(10, 10)) { image.SaveAsTiff(memoryStream, new TiffEncoder()); } memoryStream.Position = 0; - using (Image.Load(this.configuration, memoryStream, out IImageFormat mime)) + using (Image.Load(memoryStream, out IImageFormat mime)) { Assert.Equal("image/tiff", mime.DefaultMimeType); } @@ -148,14 +140,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { using var memoryStream = new MemoryStream(); - using (var image = new Image(this.configuration, 10, 10)) + using (var image = new Image(10, 10)) { await image.SaveAsTiffAsync(memoryStream, new TiffEncoder()); } memoryStream.Position = 0; - using (Image.Load(this.configuration, memoryStream, out IImageFormat mime)) + using (Image.Load(memoryStream, out IImageFormat mime)) { Assert.Equal("image/tiff", mime.DefaultMimeType); } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index bffb60302..c35311a2a 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -28,14 +28,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff private static MagickReferenceDecoder ReferenceDecoder => new MagickReferenceDecoder(); - private readonly Configuration configuration; - - public TiffDecoderTests() - { - this.configuration = new Configuration(); - this.configuration.AddTiff(); - } - [Theory] [WithFileCollection(nameof(NotSupportedImages), PixelTypes.Rgba32)] public void ThrowsNotSupported(TestImageProvider provider) @@ -50,7 +42,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff var testFile = TestFile.Create(imagePath); using (var stream = new MemoryStream(testFile.Bytes, false)) { - IImageInfo info = Image.Identify(this.configuration, stream); + IImageInfo info = Image.Identify(stream); Assert.Equal(expectedPixelSize, info.PixelType?.BitsPerPixel); Assert.Equal(expectedWidth, info.Width); @@ -70,14 +62,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff var testFile = TestFile.Create(imagePath); using (var stream = new MemoryStream(testFile.Bytes, false)) { - IImageInfo info = Image.Identify(this.configuration, stream); + IImageInfo info = Image.Identify(stream); Assert.NotNull(info.Metadata); Assert.Equal(expectedByteOrder, info.Metadata.GetTiffMetadata().ByteOrder); stream.Seek(0, SeekOrigin.Begin); - using var img = Image.Load(this.configuration, stream); + using var img = Image.Load(stream); Assert.Equal(expectedByteOrder, img.Metadata.GetTiffMetadata().ByteOrder); } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index a40ca04bd..546508ca5 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -22,14 +22,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { private static readonly IImageDecoder ReferenceDecoder = new MagickReferenceDecoder(); - private static readonly Configuration Configuration; - - static TiffEncoderTests() - { - Configuration = new Configuration(); - Configuration.AddTiff(); - } - [Theory] [InlineData(null, TiffBitsPerPixel.Bit24)] [InlineData(TiffPhotometricInterpretation.Rgb, TiffBitsPerPixel.Bit24)] @@ -55,7 +47,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // assert memStream.Position = 0; - using var output = Image.Load(Configuration, memStream); + using var output = Image.Load(memStream); TiffFrameMetadata frameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata(); Assert.Equal(expectedBitsPerPixel, frameMetaData.BitsPerPixel); Assert.Equal(TiffCompression.None, frameMetaData.Compression); @@ -78,7 +70,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // assert memStream.Position = 0; - using var output = Image.Load(Configuration, memStream); + using var output = Image.Load(memStream); TiffFrameMetadata frameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata(); Assert.Equal(bitsPerPixel, frameMetaData.BitsPerPixel); @@ -117,7 +109,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // assert memStream.Position = 0; - using var output = Image.Load(Configuration, memStream); + using var output = Image.Load(memStream); TiffFrameMetadata rootFrameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata(); Assert.Equal(expectedBitsPerPixel, rootFrameMetaData.BitsPerPixel); Assert.Equal(expectedCompression, rootFrameMetaData.Compression); @@ -143,7 +135,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // assert memStream.Position = 0; - using var output = Image.Load(Configuration, memStream); + using var output = Image.Load(memStream); TiffFrameMetadata frameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata(); Assert.Equal(expectedBitsPerPixel, frameMetaData.BitsPerPixel); } @@ -162,7 +154,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // assert memStream.Position = 0; - using var output = Image.Load(Configuration, memStream); + using var output = Image.Load(memStream); TiffFrameMetadata frameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata(); Assert.Equal(expectedBitsPerPixel, frameMetaData.BitsPerPixel); } @@ -185,7 +177,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // assert memStream.Position = 0; - using var output = Image.Load(Configuration, memStream); + using var output = Image.Load(memStream); Assert.Equal(expectedCompression, output.Frames.RootFrame.Metadata.GetTiffMetadata().Compression); } @@ -207,7 +199,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // assert memStream.Position = 0; - using var output = Image.Load(Configuration, memStream); + using var output = Image.Load(memStream); TiffFrameMetadata frameMetadata = output.Frames.RootFrame.Metadata.GetTiffMetadata(); Assert.Equal(expectedPredictor, frameMetadata.Predictor); } @@ -230,7 +222,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // assert memStream.Position = 0; - using var output = Image.Load(Configuration, memStream); + using var output = Image.Load(memStream); TiffFrameMetadata frameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata(); Assert.Equal(TiffBitsPerPixel.Bit1, frameMetaData.BitsPerPixel); Assert.Equal(expectedCompression, frameMetaData.Compression); @@ -422,7 +414,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // assert memStream.Position = 0; - using var output = Image.Load(Configuration, memStream); + using var output = Image.Load(memStream); ExifProfile exifProfileOutput = output.Frames.RootFrame.Metadata.ExifProfile; TiffFrameMetadata outputMeta = output.Frames.RootFrame.Metadata.GetTiffMetadata(); ImageFrame rootFrame = output.Frames.RootFrame; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index f52b74e83..3aded7b0e 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -20,16 +20,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Trait("Format", "Tiff")] public class TiffMetadataTests { - private readonly Configuration configuration; - private static TiffDecoder TiffDecoder => new TiffDecoder(); - public TiffMetadataTests() - { - this.configuration = new Configuration(); - this.configuration.AddTiff(); - } - [Fact] public void TiffMetadata_CloneIsDeep() { @@ -89,7 +81,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff var testFile = TestFile.Create(imagePath); using var stream = new MemoryStream(testFile.Bytes, false); - IImageInfo imageInfo = Image.Identify(this.configuration, stream); + IImageInfo imageInfo = Image.Identify(stream); Assert.NotNull(imageInfo); TiffMetadata tiffMetadata = imageInfo.Metadata.GetTiffMetadata(); @@ -105,7 +97,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff var testFile = TestFile.Create(imagePath); using var stream = new MemoryStream(testFile.Bytes, false); - IImageInfo imageInfo = Image.Identify(this.configuration, stream); + IImageInfo imageInfo = Image.Identify(stream); Assert.NotNull(imageInfo); TiffMetadata tiffMetadata = imageInfo.Metadata.GetTiffMetadata(); @@ -265,7 +257,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // Assert ms.Position = 0; - using var encodedImage = Image.Load(this.configuration, ms); + using var encodedImage = Image.Load(ms); ImageMetadata encodedImageMetaData = encodedImage.Metadata; ImageFrame rootFrameEncodedImage = encodedImage.Frames.RootFrame; From e06481c92b34549ce135424012d200c84b5dc392 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 26 May 2021 04:51:29 +0200 Subject: [PATCH 251/275] Add additional test categories --- .../ICC/DataReader/IccDataReaderCurvesTests.cs | 1 + .../Profiles/ICC/DataReader/IccDataReaderLutTests.cs | 1 + .../ICC/DataReader/IccDataReaderMatrixTests.cs | 1 + .../IccDataReaderMultiProcessElementTests.cs | 1 + .../ICC/DataReader/IccDataReaderNonPrimitivesTests.cs | 1 + .../ICC/DataReader/IccDataReaderPrimitivesTests.cs | 1 + .../ICC/DataReader/IccDataReaderTagDataEntryTests.cs | 1 + .../Profiles/ICC/DataReader/IccDataReaderTests.cs | 3 ++- .../ICC/DataWriter/IccDataWriterCurvesTests.cs | 1 + .../Profiles/ICC/DataWriter/IccDataWriterLutTests.cs | 1 + .../Profiles/ICC/DataWriter/IccDataWriterLutTests1.cs | 1 + .../Profiles/ICC/DataWriter/IccDataWriterLutTests2.cs | 1 + .../ICC/DataWriter/IccDataWriterMatrixTests.cs | 3 +-- .../IccDataWriterMultiProcessElementTests.cs | 1 + .../ICC/DataWriter/IccDataWriterNonPrimitivesTests.cs | 1 + .../ICC/DataWriter/IccDataWriterPrimitivesTests.cs | 1 + .../ICC/DataWriter/IccDataWriterTagDataEntryTests.cs | 1 + .../Profiles/ICC/DataWriter/IccDataWriterTests.cs | 1 + .../Metadata/Profiles/ICC/IccProfileTests.cs | 1 + .../Metadata/Profiles/ICC/IccReaderTests.cs | 1 + .../Metadata/Profiles/ICC/IccWriterTests.cs | 1 + .../Profiles/ICC/Various/IccProfileIdTests.cs | 5 +++-- .../Processing/Binarization/AdaptiveThresholdTests.cs | 9 +++++---- .../Processing/Binarization/BinaryThresholdTest.cs | 1 + .../Binarization/OrderedDitherFactoryTests.cs | 1 + .../Processing/Convolution/BoxBlurTest.cs | 5 +++-- .../Processing/Convolution/DetectEdgesTest.cs | 1 + .../Processing/Convolution/GaussianBlurTest.cs | 5 +++-- .../Processing/Convolution/GaussianSharpenTest.cs | 3 ++- .../Processing/Dithering/DitherTest.cs | 1 + .../Processing/Effects/BackgroundColorTest.cs | 2 +- .../Processing/Effects/OilPaintTest.cs | 1 + .../Processing/Effects/PixelateTest.cs | 3 ++- .../Processing/Filters/BlackWhiteTest.cs | 1 + .../Processing/Filters/BrightnessTest.cs | 1 + .../Processing/Filters/ColorBlindnessTest.cs | 1 + .../Processing/Filters/ContrastTest.cs | 6 +++--- .../ImageSharp.Tests/Processing/Filters/FilterTest.cs | 6 +++--- .../Processing/Filters/GrayscaleTest.cs | 1 + tests/ImageSharp.Tests/Processing/Filters/HueTest.cs | 5 +++-- .../ImageSharp.Tests/Processing/Filters/InvertTest.cs | 1 + .../Processing/Filters/KodachromeTest.cs | 1 + .../Processing/Filters/LightnessTest.cs | 1 + .../Processing/Filters/LomographTest.cs | 7 +++---- .../Processing/Filters/OpacityTest.cs | 5 +++-- .../Processing/Filters/PolaroidTest.cs | 1 + .../Processing/Filters/SaturateTest.cs | 5 +++-- .../ImageSharp.Tests/Processing/Filters/SepiaTest.cs | 1 + .../Normalization/HistogramEqualizationTests.cs | 1 + .../ImageSharp.Tests/Processing/Overlays/GlowTest.cs | 3 +-- .../Processing/Overlays/VignetteTest.cs | 2 +- .../Processors/Binarization/BinaryDitherTests.cs | 1 + .../Processors/Binarization/BinaryThresholdTest.cs | 2 +- .../Processors/Convolution/BokehBlurTest.cs | 1 + .../Processing/Processors/Convolution/BoxBlurTest.cs | 6 ++++-- .../Processors/Convolution/DetectEdgesTest.cs | 1 + .../Processors/Convolution/GaussianBlurTest.cs | 6 ++++-- .../Processors/Convolution/GaussianSharpenTest.cs | 4 +++- .../Processing/Processors/Dithering/DitherTests.cs | 1 + .../Processors/Effects/BackgroundColorTest.cs | 1 + .../Processing/Processors/Effects/OilPaintTest.cs | 1 + .../Processing/Processors/Effects/PixelShaderTest.cs | 1 + .../Processing/Processors/Effects/PixelateTest.cs | 3 ++- .../Processing/Processors/Filters/BlackWhiteTest.cs | 11 +++++------ .../Processing/Processors/Filters/BrightnessTest.cs | 7 +++---- .../Processors/Filters/ColorBlindnessTest.cs | 11 +++++------ .../Processing/Processors/Filters/ContrastTest.cs | 5 ++--- .../Processing/Processors/Filters/FilterTest.cs | 5 ++--- .../Processing/Processors/Filters/GrayscaleTest.cs | 5 ++--- .../Processing/Processors/Filters/HueTest.cs | 5 ++--- .../Processing/Processors/Filters/InvertTest.cs | 9 ++++----- .../Processing/Processors/Filters/KodachromeTest.cs | 9 ++++----- .../Processing/Processors/Filters/LightnessTest.cs | 7 +++---- .../Processing/Processors/Filters/LomographTest.cs | 9 ++++----- .../Processing/Processors/Filters/OpacityTest.cs | 5 ++--- .../Processing/Processors/Filters/PolaroidTest.cs | 9 ++++----- .../Processing/Processors/Filters/SaturateTest.cs | 5 ++--- .../Processing/Processors/Filters/SepiaTest.cs | 9 ++++----- .../Processing/Processors/Overlays/GlowTest.cs | 6 ++++-- .../Processing/Processors/Overlays/OverlayTestBase.cs | 1 + .../Processing/Processors/Overlays/VignetteTest.cs | 6 ++++-- .../Processors/Quantization/OctreeQuantizerTests.cs | 1 + .../Processors/Quantization/PaletteQuantizerTests.cs | 3 ++- .../Processors/Quantization/QuantizerTests.cs | 1 + .../Processors/Quantization/WuQuantizerTests.cs | 1 + .../Processors/Transforms/AffineTransformTests.cs | 1 + .../Processors/Transforms/AutoOrientTests.cs | 1 + .../Processing/Processors/Transforms/CropTest.cs | 5 +++-- .../Processors/Transforms/EntropyCropTest.cs | 1 + .../Processing/Processors/Transforms/FlipTests.cs | 1 + .../Processing/Processors/Transforms/PadTest.cs | 1 + .../Processors/Transforms/ResamplerTests.cs | 1 + .../Processors/Transforms/ResizeHelperTests.cs | 1 + .../ResizeKernelMapTests.ReferenceKernelMap.cs | 2 ++ .../Processors/Transforms/ResizeKernelMapTests.cs | 1 + .../Processing/Processors/Transforms/ResizeTests.cs | 2 +- .../Processors/Transforms/RotateFlipTests.cs | 9 ++++----- .../Processing/Processors/Transforms/RotateTests.cs | 5 +++-- .../Processing/Processors/Transforms/SkewTests.cs | 5 +++-- .../Processing/Processors/Transforms/SwizzleTests.cs | 1 + .../Processing/Transforms/AutoOrientTests.cs | 10 +++++----- .../Processing/Transforms/CropTest.cs | 5 +++-- .../Processing/Transforms/EntropyCropTest.cs | 5 +++-- .../Processing/Transforms/FlipTests.cs | 6 +++--- .../ImageSharp.Tests/Processing/Transforms/PadTest.cs | 1 + .../Transforms/ProjectiveTransformBuilderTests.cs | 3 ++- .../Processing/Transforms/ProjectiveTransformTests.cs | 1 + .../Processing/Transforms/ResizeTests.cs | 1 + .../Processing/Transforms/RotateFlipTests.cs | 5 +++-- .../Processing/Transforms/RotateTests.cs | 5 +++-- .../Processing/Transforms/SkewTest.cs | 5 +++-- .../Processing/Transforms/SwizzleTests.cs | 1 + .../Processing/Transforms/TransformBuilderTestBase.cs | 1 + .../Processing/Transforms/TransformsHelpersTest.cs | 1 + 114 files changed, 215 insertions(+), 141 deletions(-) diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderCurvesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderCurvesTests.cs index 5451cbf37..1f5a4b54e 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderCurvesTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderCurvesTests.cs @@ -6,6 +6,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Icc { + [Trait("Profile", "Icc")] public class IccDataReaderCurvesTests { [Theory] diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderLutTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderLutTests.cs index aa24c2673..d006b7651 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderLutTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderLutTests.cs @@ -6,6 +6,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Icc { + [Trait("Profile", "Icc")] public class IccDataReaderLutTests { [Theory] diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderMatrixTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderMatrixTests.cs index fe31d74ac..0ce9dc970 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderMatrixTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderMatrixTests.cs @@ -6,6 +6,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Icc { + [Trait("Profile", "Icc")] public class IccDataReaderMatrixTests { [Theory] diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderMultiProcessElementTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderMultiProcessElementTests.cs index 3fbef46de..33074edfb 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderMultiProcessElementTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderMultiProcessElementTests.cs @@ -6,6 +6,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Icc { + [Trait("Profile", "Icc")] public class IccDataReaderMultiProcessElementTests { [Theory] diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderNonPrimitivesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderNonPrimitivesTests.cs index cf4cf80d1..cb62992b0 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderNonPrimitivesTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderNonPrimitivesTests.cs @@ -8,6 +8,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Icc { + [Trait("Profile", "Icc")] public class IccDataReaderNonPrimitivesTests { [Theory] diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderPrimitivesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderPrimitivesTests.cs index 2b2b564a7..5daf21572 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderPrimitivesTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderPrimitivesTests.cs @@ -7,6 +7,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Icc { + [Trait("Profile", "Icc")] public class IccDataReaderPrimitivesTests { [Theory] diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderTagDataEntryTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderTagDataEntryTests.cs index ea77004ed..ad26f3df6 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderTagDataEntryTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderTagDataEntryTests.cs @@ -6,6 +6,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Icc { + [Trait("Profile", "Icc")] public class IccDataReaderTagDataEntryTests { [Theory] diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderTests.cs index 7c5070af1..e2981ce87 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System; @@ -7,6 +7,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Icc { + [Trait("Profile", "Icc")] public class IccDataReaderTests { [Fact] diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterCurvesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterCurvesTests.cs index 593eed97c..5944846a8 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterCurvesTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterCurvesTests.cs @@ -6,6 +6,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Icc { + [Trait("Profile", "Icc")] public class IccDataWriterCurvesTests { [Theory] diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests.cs index e48d89ddb..bb682e3c3 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests.cs @@ -6,6 +6,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Icc { + [Trait("Profile", "Icc")] public class IccDataWriterLutTests { [Theory] diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests1.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests1.cs index 711e3426d..45343a9e3 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests1.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests1.cs @@ -6,6 +6,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Icc { + [Trait("Profile", "Icc")] public class IccDataWriterLutTests1 { [Theory] diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests2.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests2.cs index ecfbad395..d6853e67b 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests2.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests2.cs @@ -6,6 +6,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Icc { + [Trait("Profile", "Icc")] public class IccDataWriterLutTests2 { [Theory] diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterMatrixTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterMatrixTests.cs index 4346265c7..b5bb53f2b 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterMatrixTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterMatrixTests.cs @@ -8,8 +8,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Icc { - using SixLabors.ImageSharp; - + [Trait("Profile", "Icc")] public class IccDataWriterMatrixTests { [Theory] diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterMultiProcessElementTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterMultiProcessElementTests.cs index bf8b7d069..98c0d9ce1 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterMultiProcessElementTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterMultiProcessElementTests.cs @@ -6,6 +6,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Icc { + [Trait("Profile", "Icc")] public class IccDataWriterMultiProcessElementTests { [Theory] diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterNonPrimitivesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterNonPrimitivesTests.cs index a918adc3f..01f831d91 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterNonPrimitivesTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterNonPrimitivesTests.cs @@ -8,6 +8,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Icc { + [Trait("Profile", "Icc")] public class IccDataWriterNonPrimitivesTests { [Theory] diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterPrimitivesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterPrimitivesTests.cs index 1dc37a195..eb908fbde 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterPrimitivesTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterPrimitivesTests.cs @@ -7,6 +7,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Icc { + [Trait("Profile", "Icc")] public class IccDataWriterPrimitivesTests { [Theory] diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterTagDataEntryTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterTagDataEntryTests.cs index 6325f26ce..3bd22c501 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterTagDataEntryTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterTagDataEntryTests.cs @@ -6,6 +6,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Icc { + [Trait("Profile", "Icc")] public class IccDataWriterTagDataEntryTests { [Theory] diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterTests.cs index 9fa3e644c..169a0b86b 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterTests.cs @@ -6,6 +6,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Icc { + [Trait("Profile", "Icc")] public class IccDataWriterTests { [Fact] diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccProfileTests.cs index a40082f78..e20d1d6b6 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccProfileTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccProfileTests.cs @@ -7,6 +7,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Icc { + [Trait("Profile", "Icc")] public class IccProfileTests { [Theory] diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccReaderTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccReaderTests.cs index e9d960ebb..b6ab9fc01 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccReaderTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccReaderTests.cs @@ -6,6 +6,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Icc { + [Trait("Profile", "Icc")] public class IccReaderTests { [Fact] diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccWriterTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccWriterTests.cs index 0d4495912..eee085c23 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccWriterTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccWriterTests.cs @@ -6,6 +6,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Icc { + [Trait("Profile", "Icc")] public class IccWriterTests { [Fact] diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/Various/IccProfileIdTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/Various/IccProfileIdTests.cs index b06a52964..11f8ef595 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/Various/IccProfileIdTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/Various/IccProfileIdTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Metadata.Profiles.Icc; @@ -6,6 +6,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Icc { + [Trait("Profile", "Icc")] public class IccProfileIdTests { [Fact] @@ -29,4 +30,4 @@ namespace SixLabors.ImageSharp.Tests.Icc Assert.Equal(4u, id.Part4); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Binarization/AdaptiveThresholdTests.cs b/tests/ImageSharp.Tests/Processing/Binarization/AdaptiveThresholdTests.cs index f4f800107..e6a34d1f0 100644 --- a/tests/ImageSharp.Tests/Processing/Binarization/AdaptiveThresholdTests.cs +++ b/tests/ImageSharp.Tests/Processing/Binarization/AdaptiveThresholdTests.cs @@ -9,13 +9,14 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Binarization { + [Trait("Category", "Processors")] public class AdaptiveThresholdTests : BaseImageOperationsExtensionTest { [Fact] public void AdaptiveThreshold_UsesDefaults_Works() { // arrange - var expectedThresholdLimit = .85f; + float expectedThresholdLimit = .85f; Color expectedUpper = Color.White; Color expectedLower = Color.Black; @@ -33,7 +34,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization public void AdaptiveThreshold_SettingThresholdLimit_Works() { // arrange - var expectedThresholdLimit = .65f; + float expectedThresholdLimit = .65f; // act this.operations.AdaptiveThreshold(expectedThresholdLimit); @@ -65,7 +66,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization public void AdaptiveThreshold_SettingUpperLowerWithThresholdLimit_Works() { // arrange - var expectedThresholdLimit = .77f; + float expectedThresholdLimit = .77f; Color expectedUpper = Color.HotPink; Color expectedLower = Color.Yellow; @@ -83,7 +84,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization public void AdaptiveThreshold_SettingUpperLowerWithThresholdLimit_WithRectangle_Works() { // arrange - var expectedThresholdLimit = .77f; + float expectedThresholdLimit = .77f; Color expectedUpper = Color.HotPink; Color expectedLower = Color.Yellow; diff --git a/tests/ImageSharp.Tests/Processing/Binarization/BinaryThresholdTest.cs b/tests/ImageSharp.Tests/Processing/Binarization/BinaryThresholdTest.cs index a2fb9f9ba..e241f729a 100644 --- a/tests/ImageSharp.Tests/Processing/Binarization/BinaryThresholdTest.cs +++ b/tests/ImageSharp.Tests/Processing/Binarization/BinaryThresholdTest.cs @@ -8,6 +8,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Binarization { + [Trait("Category", "Processors")] public class BinaryThresholdTest : BaseImageOperationsExtensionTest { [Fact] diff --git a/tests/ImageSharp.Tests/Processing/Binarization/OrderedDitherFactoryTests.cs b/tests/ImageSharp.Tests/Processing/Binarization/OrderedDitherFactoryTests.cs index 0bbb962fc..191b195b4 100644 --- a/tests/ImageSharp.Tests/Processing/Binarization/OrderedDitherFactoryTests.cs +++ b/tests/ImageSharp.Tests/Processing/Binarization/OrderedDitherFactoryTests.cs @@ -7,6 +7,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Binarization { + [Trait("Category", "Processors")] public class OrderedDitherFactoryTests { #pragma warning disable SA1025 // Code should not contain multiple whitespace in a row diff --git a/tests/ImageSharp.Tests/Processing/Convolution/BoxBlurTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/BoxBlurTest.cs index eb176f5f0..65d956424 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/BoxBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/BoxBlurTest.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing; @@ -7,6 +7,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Convolution { + [Trait("Category", "Processors")] public class BoxBlurTest : BaseImageOperationsExtensionTest { [Fact] @@ -36,4 +37,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution Assert.Equal(5, processor.Radius); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs index 3ffb8f4e3..56a73b3ff 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs @@ -8,6 +8,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Convolution { + [Trait("Category", "Processors")] [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1313:Parameter names should begin with lower-case letter", Justification = "OK. Used for TheoryData compatibility.")] public class DetectEdgesTest : BaseImageOperationsExtensionTest { diff --git a/tests/ImageSharp.Tests/Processing/Convolution/GaussianBlurTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/GaussianBlurTest.cs index 26454fcb6..ce21cab5f 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/GaussianBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/GaussianBlurTest.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing; @@ -7,6 +7,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Convolution { + [Trait("Category", "Processors")] public class GaussianBlurTest : BaseImageOperationsExtensionTest { [Fact] @@ -36,4 +37,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution Assert.Equal(.6f, processor.Sigma); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Convolution/GaussianSharpenTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/GaussianSharpenTest.cs index d264e82e1..e94683092 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/GaussianSharpenTest.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/GaussianSharpenTest.cs @@ -7,6 +7,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Convolution { + [Trait("Category", "Processors")] public class GaussianSharpenTest : BaseImageOperationsExtensionTest { [Fact] @@ -36,4 +37,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution Assert.Equal(.6f, processor.Sigma); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs b/tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs index 9f0a80453..029e549b9 100644 --- a/tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs +++ b/tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs @@ -8,6 +8,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Binarization { + [Trait("Category", "Processors")] public class DitherTest : BaseImageOperationsExtensionTest { private class Assert : Xunit.Assert diff --git a/tests/ImageSharp.Tests/Processing/Effects/BackgroundColorTest.cs b/tests/ImageSharp.Tests/Processing/Effects/BackgroundColorTest.cs index 5bc6256d9..11c90a507 100644 --- a/tests/ImageSharp.Tests/Processing/Effects/BackgroundColorTest.cs +++ b/tests/ImageSharp.Tests/Processing/Effects/BackgroundColorTest.cs @@ -3,11 +3,11 @@ using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Overlays; -using SixLabors.ImageSharp.Tests.TestUtilities; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Effects { + [Trait("Category", "Processors")] public class BackgroundColorTest : BaseImageOperationsExtensionTest { [Fact] diff --git a/tests/ImageSharp.Tests/Processing/Effects/OilPaintTest.cs b/tests/ImageSharp.Tests/Processing/Effects/OilPaintTest.cs index 2fd7ac7ef..c1c0dc07a 100644 --- a/tests/ImageSharp.Tests/Processing/Effects/OilPaintTest.cs +++ b/tests/ImageSharp.Tests/Processing/Effects/OilPaintTest.cs @@ -7,6 +7,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Effects { + [Trait("Category", "Processors")] public class OilPaintTest : BaseImageOperationsExtensionTest { [Fact] diff --git a/tests/ImageSharp.Tests/Processing/Effects/PixelateTest.cs b/tests/ImageSharp.Tests/Processing/Effects/PixelateTest.cs index 33061e1e4..e13b22a94 100644 --- a/tests/ImageSharp.Tests/Processing/Effects/PixelateTest.cs +++ b/tests/ImageSharp.Tests/Processing/Effects/PixelateTest.cs @@ -7,6 +7,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Effects { + [Trait("Category", "Processors")] public class PixelateTest : BaseImageOperationsExtensionTest { [Fact] @@ -36,4 +37,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Effects Assert.Equal(23, processor.Size); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Filters/BlackWhiteTest.cs b/tests/ImageSharp.Tests/Processing/Filters/BlackWhiteTest.cs index f87ace189..6eaa9937b 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/BlackWhiteTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/BlackWhiteTest.cs @@ -7,6 +7,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Filters { + [Trait("Category", "Processors")] public class BlackWhiteTest : BaseImageOperationsExtensionTest { [Fact] diff --git a/tests/ImageSharp.Tests/Processing/Filters/BrightnessTest.cs b/tests/ImageSharp.Tests/Processing/Filters/BrightnessTest.cs index 680a6afdc..8f0d19d9a 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/BrightnessTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/BrightnessTest.cs @@ -8,6 +8,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Effects { + [Trait("Category", "Processors")] public class BrightnessTest : BaseImageOperationsExtensionTest { [Fact] diff --git a/tests/ImageSharp.Tests/Processing/Filters/ColorBlindnessTest.cs b/tests/ImageSharp.Tests/Processing/Filters/ColorBlindnessTest.cs index e65b67815..237f132a4 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/ColorBlindnessTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/ColorBlindnessTest.cs @@ -12,6 +12,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Filters { + [Trait("Category", "Processors")] public class ColorBlindnessTest : BaseImageOperationsExtensionTest { public static IEnumerable TheoryData = new[] diff --git a/tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs b/tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs index e181999fa..3ef3cd0b2 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs @@ -1,13 +1,13 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Filters; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Effects { - using SixLabors.ImageSharp.Processing; - using SixLabors.ImageSharp.Processing.Processors.Filters; - + [Trait("Category", "Processors")] public class ContrastTest : BaseImageOperationsExtensionTest { [Fact] diff --git a/tests/ImageSharp.Tests/Processing/Filters/FilterTest.cs b/tests/ImageSharp.Tests/Processing/Filters/FilterTest.cs index 15945e468..3965d10c9 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/FilterTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/FilterTest.cs @@ -1,13 +1,13 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Filters; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Filters { - using SixLabors.ImageSharp.Processing; - using SixLabors.ImageSharp.Processing.Processors.Filters; - + [Trait("Category", "Processors")] public class FilterTest : BaseImageOperationsExtensionTest { [Fact] diff --git a/tests/ImageSharp.Tests/Processing/Filters/GrayscaleTest.cs b/tests/ImageSharp.Tests/Processing/Filters/GrayscaleTest.cs index 36c2ff769..2e56331a7 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/GrayscaleTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/GrayscaleTest.cs @@ -12,6 +12,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Filters { + [Trait("Category", "Processors")] public class GrayscaleTest : BaseImageOperationsExtensionTest { public static IEnumerable ModeTheoryData = new[] diff --git a/tests/ImageSharp.Tests/Processing/Filters/HueTest.cs b/tests/ImageSharp.Tests/Processing/Filters/HueTest.cs index 9d85af589..04fb3d599 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/HueTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/HueTest.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing; @@ -8,6 +8,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Filters { + [Trait("Category", "Processors")] public class HueTest : BaseImageOperationsExtensionTest { [Fact] @@ -28,4 +29,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Filters Assert.Equal(5f, processor.Degrees); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Filters/InvertTest.cs b/tests/ImageSharp.Tests/Processing/Filters/InvertTest.cs index e773a177f..c36e63330 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/InvertTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/InvertTest.cs @@ -7,6 +7,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Effects { + [Trait("Category", "Processors")] public class InvertTest : BaseImageOperationsExtensionTest { [Fact] diff --git a/tests/ImageSharp.Tests/Processing/Filters/KodachromeTest.cs b/tests/ImageSharp.Tests/Processing/Filters/KodachromeTest.cs index 798c0e055..72be60b39 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/KodachromeTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/KodachromeTest.cs @@ -7,6 +7,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Filters { + [Trait("Category", "Processors")] public class KodachromeTest : BaseImageOperationsExtensionTest { [Fact] diff --git a/tests/ImageSharp.Tests/Processing/Filters/LightnessTest.cs b/tests/ImageSharp.Tests/Processing/Filters/LightnessTest.cs index cbf44e4c6..1e0e0806f 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/LightnessTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/LightnessTest.cs @@ -7,6 +7,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Effects { + [Trait("Category", "Processors")] public class LightnessTest : BaseImageOperationsExtensionTest { [Fact] diff --git a/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs b/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs index e7d289ea5..a60ebbf80 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs @@ -1,15 +1,14 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Filters; using SixLabors.ImageSharp.Tests.Processing; - using Xunit; namespace SixLabors.ImageSharp.Tests { - using SixLabors.ImageSharp.Processing; - using SixLabors.ImageSharp.Processing.Processors.Filters; - + [Trait("Category", "Processors")] public class LomographTest : BaseImageOperationsExtensionTest { [Fact] diff --git a/tests/ImageSharp.Tests/Processing/Filters/OpacityTest.cs b/tests/ImageSharp.Tests/Processing/Filters/OpacityTest.cs index 8e8b4636c..f4e352061 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/OpacityTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/OpacityTest.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing; @@ -7,6 +7,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Effects { + [Trait("Category", "Processors")] public class OpacityTest : BaseImageOperationsExtensionTest { [Fact] @@ -27,4 +28,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Effects Assert.Equal(.6f, processor.Amount); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Filters/PolaroidTest.cs b/tests/ImageSharp.Tests/Processing/Filters/PolaroidTest.cs index 2cfd8519d..5601920e9 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/PolaroidTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/PolaroidTest.cs @@ -8,6 +8,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Filters { + [Trait("Category", "Processors")] public class PolaroidTest : BaseImageOperationsExtensionTest { [Fact] diff --git a/tests/ImageSharp.Tests/Processing/Filters/SaturateTest.cs b/tests/ImageSharp.Tests/Processing/Filters/SaturateTest.cs index b61a12102..e6542e79a 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/SaturateTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/SaturateTest.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing; @@ -7,6 +7,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Filters { + [Trait("Category", "Processors")] public class SaturateTest : BaseImageOperationsExtensionTest { [Fact] @@ -27,4 +28,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Filters Assert.Equal(5, processor.Amount); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Filters/SepiaTest.cs b/tests/ImageSharp.Tests/Processing/Filters/SepiaTest.cs index c7f85b732..7a9242cb3 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/SepiaTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/SepiaTest.cs @@ -7,6 +7,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Filters { + [Trait("Category", "Processors")] public class SepiaTest : BaseImageOperationsExtensionTest { [Fact] diff --git a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs index 4460f04fb..e64ae74c6 100644 --- a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs +++ b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs @@ -9,6 +9,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Normalization { + [Trait("Category", "Processors")] // ReSharper disable InconsistentNaming public class HistogramEqualizationTests { diff --git a/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs b/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs index 8bc0a2c97..a84751f5a 100644 --- a/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs +++ b/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs @@ -1,14 +1,13 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Overlays; -using SixLabors.ImageSharp.Tests.TestUtilities; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Overlays { + [Trait("Category", "Processors")] public class GlowTest : BaseImageOperationsExtensionTest { [Fact] diff --git a/tests/ImageSharp.Tests/Processing/Overlays/VignetteTest.cs b/tests/ImageSharp.Tests/Processing/Overlays/VignetteTest.cs index 32e8ba384..3e0c851d2 100644 --- a/tests/ImageSharp.Tests/Processing/Overlays/VignetteTest.cs +++ b/tests/ImageSharp.Tests/Processing/Overlays/VignetteTest.cs @@ -3,11 +3,11 @@ using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Overlays; -using SixLabors.ImageSharp.Tests.TestUtilities; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Overlays { + [Trait("Category", "Processors")] public class VignetteTest : BaseImageOperationsExtensionTest { [Fact] diff --git a/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryDitherTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryDitherTests.cs index 24e52d5d0..0103b138a 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryDitherTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryDitherTests.cs @@ -11,6 +11,7 @@ using Xunit; // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization { + [Trait("Category", "Processors")] public class BinaryDitherTests { public static readonly string[] CommonTestImages = diff --git a/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryThresholdTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryThresholdTest.cs index fd08eb2de..446ac70d4 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryThresholdTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryThresholdTest.cs @@ -4,12 +4,12 @@ using System.Globalization; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Binarization; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization { + [Trait("Category", "Processors")] public class BinaryThresholdTest { public static readonly TheoryData BinaryThresholdValues diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs index dbf59a29b..2351cbb91 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs @@ -16,6 +16,7 @@ using Xunit.Abstractions; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution { + [Trait("Category", "Processors")] public class BokehBlurTest { private static readonly string Components10x2 = @" diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs index 529a4b49c..eadee0c2e 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs @@ -1,10 +1,12 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing; +using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution { + [Trait("Category", "Processors")] [GroupOutput("Convolution")] public class BoxBlurTest : Basic1ParameterConvolutionTests { @@ -13,4 +15,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution protected override void Apply(IImageProcessingContext ctx, int value, Rectangle bounds) => ctx.BoxBlur(value, bounds); } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs index e468778de..cc28bf304 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs @@ -11,6 +11,7 @@ using Xunit; // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution { + [Trait("Category", "Processors")] [GroupOutput("Convolution")] [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1313:Parameter names should begin with lower-case letter", Justification = "OK. Used for TheoryData compatibility.")] public class DetectEdgesTest diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs index 31b3d20db..44fe673ec 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs @@ -1,10 +1,12 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing; +using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution { + [Trait("Category", "Processors")] [GroupOutput("Convolution")] public class GaussianBlurTest : Basic1ParameterConvolutionTests { @@ -13,4 +15,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution protected override void Apply(IImageProcessingContext ctx, int value, Rectangle bounds) => ctx.GaussianBlur(value, bounds); } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs index 7d3e91803..2b4f38e89 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs @@ -2,9 +2,11 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing; +using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution { + [Trait("Category", "Processors")] [GroupOutput("Convolution")] public class GaussianSharpenTest : Basic1ParameterConvolutionTests { @@ -13,4 +15,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution protected override void Apply(IImageProcessingContext ctx, int value, Rectangle bounds) => ctx.GaussianSharpen(value, bounds); } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs index 5c1b5da7f..4acc91bec 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs @@ -10,6 +10,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization { + [Trait("Category", "Processors")] public class DitherTests { public const PixelTypes CommonNonDefaultPixelTypes = diff --git a/tests/ImageSharp.Tests/Processing/Processors/Effects/BackgroundColorTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Effects/BackgroundColorTest.cs index b29e45221..acf2c0613 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Effects/BackgroundColorTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Effects/BackgroundColorTest.cs @@ -8,6 +8,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects { + [Trait("Category", "Processors")] [GroupOutput("Effects")] public class BackgroundColorTest { diff --git a/tests/ImageSharp.Tests/Processing/Processors/Effects/OilPaintTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Effects/OilPaintTest.cs index 0d68a860d..1dcd8181f 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Effects/OilPaintTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Effects/OilPaintTest.cs @@ -8,6 +8,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects { + [Trait("Category", "Processors")] [GroupOutput("Effects")] public class OilPaintTest { diff --git a/tests/ImageSharp.Tests/Processing/Processors/Effects/PixelShaderTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Effects/PixelShaderTest.cs index 919cb3137..6edde73cd 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Effects/PixelShaderTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Effects/PixelShaderTest.cs @@ -11,6 +11,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects { + [Trait("Category", "Processors")] [GroupOutput("Effects")] public class PixelShaderTest { diff --git a/tests/ImageSharp.Tests/Processing/Processors/Effects/PixelateTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Effects/PixelateTest.cs index 2173cbef8..e4de119fc 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Effects/PixelateTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Effects/PixelateTest.cs @@ -8,6 +8,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects { + [Trait("Category", "Processors")] [GroupOutput("Effects")] public class PixelateTest { @@ -30,4 +31,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects provider.RunRectangleConstrainedValidatingProcessorTest((x, rect) => x.Pixelate(value, rect), value); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/BlackWhiteTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/BlackWhiteTest.cs index fdcc3c6f7..0927a8b81 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Filters/BlackWhiteTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/BlackWhiteTest.cs @@ -1,15 +1,14 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; - +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters { - using SixLabors.ImageSharp.Processing; - using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; - + [Trait("Category", "Processors")] [GroupOutput("Filters")] public class BlackWhiteTest { @@ -21,4 +20,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters provider.RunValidatingProcessorTest(ctx => ctx.BlackWhite(), comparer: ImageComparer.TolerantPercentage(0.002f)); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/BrightnessTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/BrightnessTest.cs index d7e5b13cc..3527d6bbd 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Filters/BrightnessTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/BrightnessTest.cs @@ -2,14 +2,13 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; - +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects { - using SixLabors.ImageSharp.Processing; - using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; - + [Trait("Category", "Processors")] [GroupOutput("Filters")] public class BrightnessTest { diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/ColorBlindnessTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/ColorBlindnessTest.cs index a007f7194..f86858c84 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Filters/ColorBlindnessTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/ColorBlindnessTest.cs @@ -1,15 +1,14 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; - +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters { - using SixLabors.ImageSharp.Processing; - using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; - + [Trait("Category", "Processors")] [GroupOutput("Filters")] public class ColorBlindnessTest { @@ -33,4 +32,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters public void ApplyColorBlindnessFilter(TestImageProvider provider, ColorBlindnessMode colorBlindness) where TPixel : unmanaged, IPixel => provider.RunValidatingProcessorTest(x => x.ColorBlindness(colorBlindness), colorBlindness.ToString(), this.imageComparer); } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/ContrastTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/ContrastTest.cs index 25fe9c84c..720408ad0 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Filters/ContrastTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/ContrastTest.cs @@ -2,13 +2,12 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; - +using SixLabors.ImageSharp.Processing; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects { - using SixLabors.ImageSharp.Processing; - + [Trait("Category", "Processors")] [GroupOutput("Filters")] public class ContrastTest { diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/FilterTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/FilterTest.cs index 535179cb1..b5c0e583c 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Filters/FilterTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/FilterTest.cs @@ -2,14 +2,13 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters { - using SixLabors.ImageSharp; - using SixLabors.ImageSharp.Processing; - + [Trait("Category", "Processors")] [GroupOutput("Filters")] public class FilterTest { diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/GrayscaleTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/GrayscaleTest.cs index 279b699ee..c568188fb 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Filters/GrayscaleTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/GrayscaleTest.cs @@ -2,13 +2,12 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; - +using SixLabors.ImageSharp.Processing; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters { - using SixLabors.ImageSharp.Processing; - + [Trait("Category", "Processors")] [GroupOutput("Filters")] public class GrayscaleTest { diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/HueTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/HueTest.cs index 3538f0dba..0c2b455e3 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Filters/HueTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/HueTest.cs @@ -2,13 +2,12 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; - +using SixLabors.ImageSharp.Processing; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters { - using SixLabors.ImageSharp.Processing; - + [Trait("Category", "Processors")] [GroupOutput("Filters")] public class HueTest { diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/InvertTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/InvertTest.cs index a2e0b0b4b..f57034508 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Filters/InvertTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/InvertTest.cs @@ -1,14 +1,13 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; - +using SixLabors.ImageSharp.Processing; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects { - using SixLabors.ImageSharp.Processing; - + [Trait("Category", "Processors")] [GroupOutput("Filters")] public class InvertTest { @@ -20,4 +19,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects provider.RunValidatingProcessorTest(x => x.Invert()); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/KodachromeTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/KodachromeTest.cs index f21d45836..2fecac32c 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Filters/KodachromeTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/KodachromeTest.cs @@ -1,14 +1,13 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; - +using SixLabors.ImageSharp.Processing; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters { - using SixLabors.ImageSharp.Processing; - + [Trait("Category", "Processors")] [GroupOutput("Filters")] public class KodachromeTest { @@ -20,4 +19,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters provider.RunValidatingProcessorTest(x => x.Kodachrome()); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/LightnessTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/LightnessTest.cs index c924ddc4f..78e379916 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Filters/LightnessTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/LightnessTest.cs @@ -2,14 +2,13 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; - +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects { - using SixLabors.ImageSharp.Processing; - using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; - + [Trait("Category", "Processors")] [GroupOutput("Filters")] public class LightnessTest { diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/LomographTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/LomographTest.cs index a7ef2f862..e5621b592 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Filters/LomographTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/LomographTest.cs @@ -1,14 +1,13 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; - +using SixLabors.ImageSharp.Processing; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters { - using SixLabors.ImageSharp.Processing; - + [Trait("Category", "Processors")] [GroupOutput("Filters")] public class LomographTest { @@ -20,4 +19,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters provider.RunValidatingProcessorTest(x => x.Lomograph()); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/OpacityTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/OpacityTest.cs index 64025a6fb..3a218544e 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Filters/OpacityTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/OpacityTest.cs @@ -2,13 +2,12 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; - +using SixLabors.ImageSharp.Processing; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects { - using SixLabors.ImageSharp.Processing; - + [Trait("Category", "Processors")] [GroupOutput("Filters")] public class OpacityTest { diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/PolaroidTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/PolaroidTest.cs index 8be43efa9..8077051cd 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Filters/PolaroidTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/PolaroidTest.cs @@ -1,14 +1,13 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; - +using SixLabors.ImageSharp.Processing; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters { - using SixLabors.ImageSharp.Processing; - + [Trait("Category", "Processors")] [GroupOutput("Filters")] public class PolaroidTest { @@ -20,4 +19,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters provider.RunValidatingProcessorTest(x => x.Polaroid()); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/SaturateTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/SaturateTest.cs index 91c6e4af8..e10243289 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Filters/SaturateTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/SaturateTest.cs @@ -2,13 +2,12 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; - +using SixLabors.ImageSharp.Processing; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters { - using SixLabors.ImageSharp.Processing; - + [Trait("Category", "Processors")] [GroupOutput("Filters")] public class SaturateTest { diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/SepiaTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/SepiaTest.cs index af2c2136a..86e3050c2 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Filters/SepiaTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/SepiaTest.cs @@ -1,14 +1,13 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; - +using SixLabors.ImageSharp.Processing; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters { - using SixLabors.ImageSharp.Processing; - + [Trait("Category", "Processors")] [GroupOutput("Filters")] public class SepiaTest { @@ -20,4 +19,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters provider.RunValidatingProcessorTest(x => x.Sepia()); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Processors/Overlays/GlowTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Overlays/GlowTest.cs index f0d6b784b..0a2b9921c 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Overlays/GlowTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Overlays/GlowTest.cs @@ -1,10 +1,12 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing; +using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Overlays { + [Trait("Category", "Processors")] [GroupOutput("Overlays")] public class GlowTest : OverlayTestBase { @@ -15,4 +17,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Overlays protected override void Apply(IImageProcessingContext ctx, Rectangle rect) => ctx.Glow(rect); } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Processors/Overlays/OverlayTestBase.cs b/tests/ImageSharp.Tests/Processing/Processors/Overlays/OverlayTestBase.cs index fa4d422b1..6814a9132 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Overlays/OverlayTestBase.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Overlays/OverlayTestBase.cs @@ -9,6 +9,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Overlays { + [Trait("Category", "Processors")] [GroupOutput("Overlays")] public abstract class OverlayTestBase { diff --git a/tests/ImageSharp.Tests/Processing/Processors/Overlays/VignetteTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Overlays/VignetteTest.cs index 6eccde4bc..3a6c8a11a 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Overlays/VignetteTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Overlays/VignetteTest.cs @@ -1,10 +1,12 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing; +using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Overlays { + [Trait("Category", "Processors")] [GroupOutput("Overlays")] public class VignetteTest : OverlayTestBase { @@ -15,4 +17,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Overlays protected override void Apply(IImageProcessingContext ctx, Rectangle rect) => ctx.Vignette(rect); } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Processors/Quantization/OctreeQuantizerTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Quantization/OctreeQuantizerTests.cs index 2b4460429..cccb77e86 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Quantization/OctreeQuantizerTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Quantization/OctreeQuantizerTests.cs @@ -8,6 +8,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Quantization { + [Trait("Category", "Processors")] public class OctreeQuantizerTests { [Fact] diff --git a/tests/ImageSharp.Tests/Processing/Processors/Quantization/PaletteQuantizerTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Quantization/PaletteQuantizerTests.cs index 0df498cd1..991a2bcb7 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Quantization/PaletteQuantizerTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Quantization/PaletteQuantizerTests.cs @@ -8,9 +8,10 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Quantization { + [Trait("Category", "Processors")] public class PaletteQuantizerTests { - private static readonly Color[] Palette = new Color[] { Color.Red, Color.Green, Color.Blue }; + private static readonly Color[] Palette = { Color.Red, Color.Green, Color.Blue }; [Fact] public void PaletteQuantizerConstructor() diff --git a/tests/ImageSharp.Tests/Processing/Processors/Quantization/QuantizerTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Quantization/QuantizerTests.cs index 3f4656d41..af1d7f3f3 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Quantization/QuantizerTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Quantization/QuantizerTests.cs @@ -11,6 +11,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Quantization { + [Trait("Category", "Processors")] public class QuantizerTests { /// diff --git a/tests/ImageSharp.Tests/Processing/Processors/Quantization/WuQuantizerTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Quantization/WuQuantizerTests.cs index 8881aa9ad..639f8fd2d 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Quantization/WuQuantizerTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Quantization/WuQuantizerTests.cs @@ -8,6 +8,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Quantization { + [Trait("Category", "Processors")] public class WuQuantizerTests { [Fact] diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/AffineTransformTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/AffineTransformTests.cs index 49a443d92..d2d2fcc1f 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/AffineTransformTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/AffineTransformTests.cs @@ -14,6 +14,7 @@ using Xunit.Abstractions; namespace SixLabors.ImageSharp.Tests.Processing.Transforms { + [Trait("Category", "Processors")] public class AffineTransformTests { private readonly ITestOutputHelper output; diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs index 44f88c3a2..38fde5060 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs @@ -11,6 +11,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms { + [Trait("Category", "Processors")] [GroupOutput("Transforms")] public class AutoOrientTests { diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/CropTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/CropTest.cs index 78c35fa9b..52f3b65de 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/CropTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/CropTest.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System; @@ -11,6 +11,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms { + [Trait("Category", "Processors")] [GroupOutput("Transforms")] public class CropTest { @@ -29,4 +30,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms comparer: ImageComparer.Exact); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/EntropyCropTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/EntropyCropTest.cs index 16668fb20..4e8a65ddc 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/EntropyCropTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/EntropyCropTest.cs @@ -7,6 +7,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms { + [Trait("Category", "Processors")] [GroupOutput("Transforms")] public class EntropyCropTest { diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/FlipTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/FlipTests.cs index c094febc9..b9f0fb9e8 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/FlipTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/FlipTests.cs @@ -9,6 +9,7 @@ using Xunit; // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms { + [Trait("Category", "Processors")] [GroupOutput("Transforms")] public class FlipTests { diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/PadTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/PadTest.cs index 2ea833640..b1441d109 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/PadTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/PadTest.cs @@ -7,6 +7,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms { + [Trait("Category", "Processors")] public class PadTest { public static readonly string[] CommonTestImages = diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResamplerTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResamplerTests.cs index 4691fc82b..43fe196f7 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResamplerTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResamplerTests.cs @@ -8,6 +8,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms { + [Trait("Category", "Processors")] public class ResamplerTests { [Theory] diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeHelperTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeHelperTests.cs index ceee3e7e0..253d29eea 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeHelperTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeHelperTests.cs @@ -8,6 +8,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms { + [Trait("Category", "Processors")] public class ResizeHelperTests { [Theory] diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.ReferenceKernelMap.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.ReferenceKernelMap.cs index da567f18c..dfab25b11 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.ReferenceKernelMap.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.ReferenceKernelMap.cs @@ -5,9 +5,11 @@ using System.Collections.Generic; using System.Linq; using SixLabors.ImageSharp.Processing.Processors.Transforms; +using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms { + [Trait("Category", "Processors")] public partial class ResizeKernelMapTests { /// diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs index 991bca80e..f15a6242d 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs @@ -13,6 +13,7 @@ using Xunit.Abstractions; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms { + [Trait("Category", "Processors")] public partial class ResizeKernelMapTests { private ITestOutputHelper Output { get; } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs index 58b7fd12e..42cf1e3c1 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs @@ -5,7 +5,6 @@ using System; using System.Linq; using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Transforms; @@ -16,6 +15,7 @@ using Xunit; // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms { + [Trait("Category", "Processors")] public class ResizeTests { private const PixelTypes CommonNonDefaultPixelTypes = diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateFlipTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateFlipTests.cs index 398039e43..0648c48b4 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateFlipTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateFlipTests.cs @@ -1,14 +1,13 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; - +using SixLabors.ImageSharp.Processing; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms { - using SixLabors.ImageSharp.Processing; - + [Trait("Category", "Processors")] public class RotateFlipTests { public static readonly string[] FlipFiles = { TestImages.Bmp.F }; @@ -36,4 +35,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms } } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateTests.cs index 1e888a51a..61b63d064 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; @@ -7,6 +7,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms { + [Trait("Category", "Processors")] [GroupOutput("Transforms")] public class RotateTests { @@ -43,4 +44,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms provider.RunValidatingProcessorTest(ctx => ctx.Rotate(value), value, appendPixelTypeToFileName: false); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/SkewTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/SkewTests.cs index 2fd87de29..05d5095af 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/SkewTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/SkewTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; @@ -10,6 +10,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms { + [Trait("Category", "Processors")] [GroupOutput("Transforms")] public class SkewTests { @@ -64,4 +65,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms appendPixelTypeToFileName: false); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/SwizzleTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/SwizzleTests.cs index f508744fa..61af13ea3 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/SwizzleTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/SwizzleTests.cs @@ -10,6 +10,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms { + [Trait("Category", "Processors")] [GroupOutput("Transforms")] public class SwizzleTests { diff --git a/tests/ImageSharp.Tests/Processing/Transforms/AutoOrientTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/AutoOrientTests.cs index 50fff725b..1b681a82f 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/AutoOrientTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/AutoOrientTests.cs @@ -1,13 +1,13 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Transforms; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Transforms { - using SixLabors.ImageSharp.Processing; - using SixLabors.ImageSharp.Processing.Processors.Transforms; - + [Trait("Category", "Processors")] public class AutoOrientTests : BaseImageOperationsExtensionTest { [Fact] @@ -17,4 +17,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms this.Verify(); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Transforms/CropTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/CropTest.cs index 9fa75448b..ed56f681c 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/CropTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/CropTest.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System; @@ -9,6 +9,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Transforms { + [Trait("Category", "Processors")] public class CropTest : BaseImageOperationsExtensionTest { [Theory] @@ -41,4 +42,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms Assert.Throws(() => this.operations.Crop(cropRectangle)); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Transforms/EntropyCropTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/EntropyCropTest.cs index f2ca8dee5..53fa02edb 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/EntropyCropTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/EntropyCropTest.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing; @@ -7,6 +7,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Transforms { + [Trait("Category", "Processors")] public class EntropyCropTest : BaseImageOperationsExtensionTest { [Theory] @@ -20,4 +21,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms Assert.Equal(threshold, processor.Threshold); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Transforms/FlipTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/FlipTests.cs index 3f6e26b8e..843cd3040 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/FlipTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/FlipTests.cs @@ -1,13 +1,13 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Transforms; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Transforms { - using SixLabors.ImageSharp.Processing; - using SixLabors.ImageSharp.Processing.Processors.Transforms; - + [Trait("Category", "Processors")] public class FlipTests : BaseImageOperationsExtensionTest { [Theory] diff --git a/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs index 3f49b0f02..227e470d4 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs @@ -8,6 +8,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Transforms { + [Trait("Category", "Processors")] public class PadTest : BaseImageOperationsExtensionTest { [Fact] diff --git a/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformBuilderTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformBuilderTests.cs index d95992d6b..2f0f8f6ac 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformBuilderTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformBuilderTests.cs @@ -1,12 +1,13 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using System; using System.Numerics; using SixLabors.ImageSharp.Processing; +using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Transforms { + [Trait("Category", "Processors")] public class ProjectiveTransformBuilderTests : TransformBuilderTestBase { protected override ProjectiveTransformBuilder CreateBuilder() diff --git a/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformTests.cs index 2fd5f2a7d..ef8e03763 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformTests.cs @@ -14,6 +14,7 @@ using Xunit.Abstractions; // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests.Processing.Transforms { + [Trait("Category", "Processors")] public class ProjectiveTransformTests { private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.03f, 3); diff --git a/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs index 5f426083c..60f7aaa0b 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs @@ -9,6 +9,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Transforms { + [Trait("Category", "Processors")] public class ResizeTests : BaseImageOperationsExtensionTest { [Fact] diff --git a/tests/ImageSharp.Tests/Processing/Transforms/RotateFlipTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/RotateFlipTests.cs index 379d39966..90a96972a 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/RotateFlipTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/RotateFlipTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing; @@ -7,6 +7,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Transforms { + [Trait("Category", "Processors")] public class RotateFlipTests : BaseImageOperationsExtensionTest { [Theory] @@ -32,4 +33,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms Assert.Equal(flip, flipProcessor.FlipMode); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Transforms/RotateTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/RotateTests.cs index 6f7dbd9de..b79bb29eb 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/RotateTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/RotateTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing; @@ -8,6 +8,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Transforms { + [Trait("Category", "Processors")] public class RotateTests : BaseImageOperationsExtensionTest { [Theory] @@ -34,4 +35,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms Assert.Equal(expectedAngle, processor.Degrees); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Transforms/SkewTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/SkewTest.cs index de276b427..06282494a 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/SkewTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/SkewTest.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing; @@ -8,6 +8,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Transforms { + [Trait("Category", "Processors")] public class SkewTest : BaseImageOperationsExtensionTest { [Fact] @@ -20,4 +21,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms Assert.Equal(20, processor.DegreesY); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Transforms/SwizzleTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/SwizzleTests.cs index cde6aeca3..a6d032335 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/SwizzleTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/SwizzleTests.cs @@ -7,6 +7,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Transforms { + [Trait("Category", "Processors")] public class SwizzleTests : BaseImageOperationsExtensionTest { private struct InvertXAndYSwizzler : ISwizzler diff --git a/tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs b/tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs index 2e0dfd59e..d4540e433 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs @@ -9,6 +9,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Transforms { + [Trait("Category", "Processors")] public abstract class TransformBuilderTestBase { private static readonly ApproximateFloatComparer Comparer = new ApproximateFloatComparer(1e-6f); diff --git a/tests/ImageSharp.Tests/Processing/Transforms/TransformsHelpersTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/TransformsHelpersTest.cs index 81c415c06..869162b38 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/TransformsHelpersTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/TransformsHelpersTest.cs @@ -8,6 +8,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Transforms { + [Trait("Category", "Processors")] public class TransformsHelpersTest { [Fact] From 2931619bc5da13cd3aae6563aeb03284755f604f Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 26 May 2021 05:05:06 +0200 Subject: [PATCH 252/275] Fix namespaces --- .../Metadata/Profiles/ICC/DataReader/IccDataReader.cs | 2 +- .../Formats/Gif/Sections/GifGraphicControlExtensionTests.cs | 6 +++--- .../Formats/Gif/Sections/GifImageDescriptorTests.cs | 6 +++--- .../Formats/Gif/Sections/GifLogicalScreenDescriptorTests.cs | 6 +++--- tests/ImageSharp.Tests/Formats/Png/PngFilterTests.cs | 2 -- .../Formats/Png/ReferenceImplementations.cs | 2 +- .../Metadata/Profiles/Exif/ExifProfileTests.cs | 2 +- .../Metadata/Profiles/Exif/ExifReaderTests.cs | 2 +- .../Profiles/Exif/ExifTagDescriptionAttributeTests.cs | 2 +- .../Metadata/Profiles/Exif/ExifValueTests.cs | 4 ++-- .../Profiles/ICC/DataReader/IccDataReaderCurvesTests.cs | 2 +- .../Profiles/ICC/DataReader/IccDataReaderLutTests.cs | 2 +- .../Profiles/ICC/DataReader/IccDataReaderMatrixTests.cs | 2 +- .../ICC/DataReader/IccDataReaderMultiProcessElementTests.cs | 2 +- .../ICC/DataReader/IccDataReaderNonPrimitivesTests.cs | 2 +- .../Profiles/ICC/DataReader/IccDataReaderPrimitivesTests.cs | 2 +- .../ICC/DataReader/IccDataReaderTagDataEntryTests.cs | 2 +- .../Metadata/Profiles/ICC/DataReader/IccDataReaderTests.cs | 2 +- .../Profiles/ICC/DataWriter/IccDataWriterCurvesTests.cs | 2 +- .../Profiles/ICC/DataWriter/IccDataWriterLutTests.cs | 2 +- .../Profiles/ICC/DataWriter/IccDataWriterLutTests1.cs | 2 +- .../Profiles/ICC/DataWriter/IccDataWriterLutTests2.cs | 2 +- .../Profiles/ICC/DataWriter/IccDataWriterMatrixTests.cs | 2 +- .../ICC/DataWriter/IccDataWriterMultiProcessElementTests.cs | 2 +- .../ICC/DataWriter/IccDataWriterNonPrimitivesTests.cs | 2 +- .../Profiles/ICC/DataWriter/IccDataWriterPrimitivesTests.cs | 2 +- .../ICC/DataWriter/IccDataWriterTagDataEntryTests.cs | 2 +- .../Metadata/Profiles/ICC/DataWriter/IccDataWriterTests.cs | 2 +- .../Metadata/Profiles/ICC/IccProfileTests.cs | 2 +- .../Metadata/Profiles/ICC/IccReaderTests.cs | 2 +- .../Metadata/Profiles/ICC/IccWriterTests.cs | 2 +- .../Metadata/Profiles/ICC/Various/IccProfileIdTests.cs | 2 +- .../Metadata/Profiles/IPTC/IptcProfileTests.cs | 4 ++-- .../Processing/BaseImageOperationsExtensionTest.cs | 1 - .../Convolution/Processors/LaplacianKernelFactoryTests.cs | 2 +- tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs | 2 +- tests/ImageSharp.Tests/Processing/Filters/BrightnessTest.cs | 2 +- tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs | 2 +- tests/ImageSharp.Tests/Processing/Filters/InvertTest.cs | 2 +- tests/ImageSharp.Tests/Processing/Filters/LightnessTest.cs | 2 +- tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs | 3 +-- tests/ImageSharp.Tests/Processing/Filters/OpacityTest.cs | 2 +- tests/ImageSharp.Tests/Processing/IntegralImageTests.cs | 2 +- .../Processing/Processors/Dithering/DitherTests.cs | 2 +- .../Processing/Processors/Filters/BrightnessTest.cs | 2 +- .../Processing/Processors/Filters/ContrastTest.cs | 2 +- .../Processing/Processors/Filters/InvertTest.cs | 2 +- .../Processing/Processors/Filters/LightnessTest.cs | 2 +- .../Processing/Processors/Filters/OpacityTest.cs | 2 +- 49 files changed, 55 insertions(+), 59 deletions(-) diff --git a/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.cs index 8e9cad563..2751c7b3e 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.Metadata.Profiles.Icc +namespace SixLabors.ImageSharp.Metadata.Profiles.ICC.DataReader { /// /// Provides methods to read ICC data types diff --git a/tests/ImageSharp.Tests/Formats/Gif/Sections/GifGraphicControlExtensionTests.cs b/tests/ImageSharp.Tests/Formats/Gif/Sections/GifGraphicControlExtensionTests.cs index 6ec1162c4..c4be71d2a 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/Sections/GifGraphicControlExtensionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/Sections/GifGraphicControlExtensionTests.cs @@ -1,11 +1,11 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Formats.Gif; using Xunit; -namespace SixLabors.ImageSharp.Tests.Formats.Gif +namespace SixLabors.ImageSharp.Tests.Formats.Gif.Sections { public class GifGraphicControlExtensionTests { @@ -18,4 +18,4 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif Assert.Equal(14, GifGraphicControlExtension.GetPackedValue(GifDisposalMethod.RestoreToPrevious, true, false)); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Formats/Gif/Sections/GifImageDescriptorTests.cs b/tests/ImageSharp.Tests/Formats/Gif/Sections/GifImageDescriptorTests.cs index db88cf5b3..41ec1c7e8 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/Sections/GifImageDescriptorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/Sections/GifImageDescriptorTests.cs @@ -1,11 +1,11 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Formats.Gif; using Xunit; -namespace SixLabors.ImageSharp.Tests.Formats.Gif +namespace SixLabors.ImageSharp.Tests.Formats.Gif.Sections { public class GifImageDescriptorTests { @@ -21,4 +21,4 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif Assert.Equal(232, GifImageDescriptor.GetPackedValue(true, true, true, 8)); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Formats/Gif/Sections/GifLogicalScreenDescriptorTests.cs b/tests/ImageSharp.Tests/Formats/Gif/Sections/GifLogicalScreenDescriptorTests.cs index 9773bcd61..6efa680c8 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/Sections/GifLogicalScreenDescriptorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/Sections/GifLogicalScreenDescriptorTests.cs @@ -1,11 +1,11 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Formats.Gif; using Xunit; -namespace SixLabors.ImageSharp.Tests.Formats.Gif +namespace SixLabors.ImageSharp.Tests.Formats.Gif.Sections { public class GifLogicalScreenDescriptorTests { @@ -20,4 +20,4 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif Assert.Equal(55, GifLogicalScreenDescriptor.GetPackedValue(false, 3, false, 7)); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Formats/Png/PngFilterTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngFilterTests.cs index 5f7b4f832..80bfd3497 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngFilterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngFilterTests.cs @@ -7,7 +7,6 @@ using System; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Png.Filters; -using SixLabors.ImageSharp.Tests.Formats.Png.Utils; using SixLabors.ImageSharp.Tests.TestUtilities; using Xunit; using Xunit.Abstractions; @@ -142,7 +141,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png HwIntrinsics.DisableSIMD); } - [Fact] public void UpAvx2() { diff --git a/tests/ImageSharp.Tests/Formats/Png/ReferenceImplementations.cs b/tests/ImageSharp.Tests/Formats/Png/ReferenceImplementations.cs index dd8ecc096..a9b53e16e 100644 --- a/tests/ImageSharp.Tests/Formats/Png/ReferenceImplementations.cs +++ b/tests/ImageSharp.Tests/Formats/Png/ReferenceImplementations.cs @@ -6,7 +6,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Tests.Formats.Png.Utils +namespace SixLabors.ImageSharp.Tests.Formats.Png { /// /// This class contains reference implementations to produce verification data for unit tests diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs index cac46fd60..9d2c4b174 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs @@ -12,7 +12,7 @@ using SixLabors.ImageSharp.PixelFormats; using Xunit; -namespace SixLabors.ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif { [Trait("Profile", "Exif")] public class ExifProfileTests diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifReaderTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifReaderTests.cs index 4ff37eb6b..7cd7da44e 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifReaderTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifReaderTests.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using Xunit; -namespace SixLabors.ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif { [Trait("Profile", "Exif")] public class ExifReaderTests diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifTagDescriptionAttributeTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifTagDescriptionAttributeTests.cs index 42a3b72a6..2fec828ad 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifTagDescriptionAttributeTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifTagDescriptionAttributeTests.cs @@ -4,7 +4,7 @@ using SixLabors.ImageSharp.Metadata.Profiles.Exif; using Xunit; -namespace SixLabors.ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif { [Trait("Profile", "Exif")] public class ExifTagDescriptionAttributeTests diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifValueTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifValueTests.cs index a2c01ea61..0a816bb21 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifValueTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifValueTests.cs @@ -5,12 +5,12 @@ using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; using Xunit; -namespace SixLabors.ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif { [Trait("Profile", "Exif")] public class ExifValueTests { - private ExifProfile profile; + private readonly ExifProfile profile; public ExifValueTests() { diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderCurvesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderCurvesTests.cs index 1f5a4b54e..bfbb47a94 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderCurvesTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderCurvesTests.cs @@ -4,7 +4,7 @@ using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; -namespace SixLabors.ImageSharp.Tests.Icc +namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader { [Trait("Profile", "Icc")] public class IccDataReaderCurvesTests diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderLutTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderLutTests.cs index d006b7651..10a62976e 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderLutTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderLutTests.cs @@ -4,7 +4,7 @@ using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; -namespace SixLabors.ImageSharp.Tests.Icc +namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader { [Trait("Profile", "Icc")] public class IccDataReaderLutTests diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderMatrixTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderMatrixTests.cs index 0ce9dc970..4739211bb 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderMatrixTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderMatrixTests.cs @@ -4,7 +4,7 @@ using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; -namespace SixLabors.ImageSharp.Tests.Icc +namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader { [Trait("Profile", "Icc")] public class IccDataReaderMatrixTests diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderMultiProcessElementTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderMultiProcessElementTests.cs index 33074edfb..aa91e87f3 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderMultiProcessElementTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderMultiProcessElementTests.cs @@ -4,7 +4,7 @@ using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; -namespace SixLabors.ImageSharp.Tests.Icc +namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader { [Trait("Profile", "Icc")] public class IccDataReaderMultiProcessElementTests diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderNonPrimitivesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderNonPrimitivesTests.cs index cb62992b0..15652dd95 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderNonPrimitivesTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderNonPrimitivesTests.cs @@ -6,7 +6,7 @@ using System.Numerics; using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; -namespace SixLabors.ImageSharp.Tests.Icc +namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader { [Trait("Profile", "Icc")] public class IccDataReaderNonPrimitivesTests diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderPrimitivesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderPrimitivesTests.cs index 5daf21572..b4532ff47 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderPrimitivesTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderPrimitivesTests.cs @@ -5,7 +5,7 @@ using System; using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; -namespace SixLabors.ImageSharp.Tests.Icc +namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader { [Trait("Profile", "Icc")] public class IccDataReaderPrimitivesTests diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderTagDataEntryTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderTagDataEntryTests.cs index ad26f3df6..cc7b06b29 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderTagDataEntryTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderTagDataEntryTests.cs @@ -4,7 +4,7 @@ using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; -namespace SixLabors.ImageSharp.Tests.Icc +namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader { [Trait("Profile", "Icc")] public class IccDataReaderTagDataEntryTests diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderTests.cs index e2981ce87..6e2bd05ee 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderTests.cs @@ -5,7 +5,7 @@ using System; using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; -namespace SixLabors.ImageSharp.Tests.Icc +namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader { [Trait("Profile", "Icc")] public class IccDataReaderTests diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterCurvesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterCurvesTests.cs index 5944846a8..9cd50064c 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterCurvesTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterCurvesTests.cs @@ -4,7 +4,7 @@ using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; -namespace SixLabors.ImageSharp.Tests.Icc +namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter { [Trait("Profile", "Icc")] public class IccDataWriterCurvesTests diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests.cs index bb682e3c3..bca454818 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests.cs @@ -4,7 +4,7 @@ using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; -namespace SixLabors.ImageSharp.Tests.Icc +namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter { [Trait("Profile", "Icc")] public class IccDataWriterLutTests diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests1.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests1.cs index 45343a9e3..cb436c3b8 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests1.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests1.cs @@ -4,7 +4,7 @@ using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; -namespace SixLabors.ImageSharp.Tests.Icc +namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter { [Trait("Profile", "Icc")] public class IccDataWriterLutTests1 diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests2.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests2.cs index d6853e67b..a17b80819 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests2.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests2.cs @@ -4,7 +4,7 @@ using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; -namespace SixLabors.ImageSharp.Tests.Icc +namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter { [Trait("Profile", "Icc")] public class IccDataWriterLutTests2 diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterMatrixTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterMatrixTests.cs index b5bb53f2b..349f37df0 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterMatrixTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterMatrixTests.cs @@ -6,7 +6,7 @@ using System.Numerics; using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; -namespace SixLabors.ImageSharp.Tests.Icc +namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter { [Trait("Profile", "Icc")] public class IccDataWriterMatrixTests diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterMultiProcessElementTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterMultiProcessElementTests.cs index 98c0d9ce1..9cd10190f 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterMultiProcessElementTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterMultiProcessElementTests.cs @@ -4,7 +4,7 @@ using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; -namespace SixLabors.ImageSharp.Tests.Icc +namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter { [Trait("Profile", "Icc")] public class IccDataWriterMultiProcessElementTests diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterNonPrimitivesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterNonPrimitivesTests.cs index 01f831d91..d7c1da83a 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterNonPrimitivesTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterNonPrimitivesTests.cs @@ -6,7 +6,7 @@ using System.Numerics; using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; -namespace SixLabors.ImageSharp.Tests.Icc +namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter { [Trait("Profile", "Icc")] public class IccDataWriterNonPrimitivesTests diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterPrimitivesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterPrimitivesTests.cs index eb908fbde..a44c1595b 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterPrimitivesTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterPrimitivesTests.cs @@ -5,7 +5,7 @@ using System; using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; -namespace SixLabors.ImageSharp.Tests.Icc +namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter { [Trait("Profile", "Icc")] public class IccDataWriterPrimitivesTests diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterTagDataEntryTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterTagDataEntryTests.cs index 3bd22c501..b5d39aa4d 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterTagDataEntryTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterTagDataEntryTests.cs @@ -4,7 +4,7 @@ using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; -namespace SixLabors.ImageSharp.Tests.Icc +namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter { [Trait("Profile", "Icc")] public class IccDataWriterTagDataEntryTests diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterTests.cs index 169a0b86b..59abe29b7 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterTests.cs @@ -4,7 +4,7 @@ using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; -namespace SixLabors.ImageSharp.Tests.Icc +namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter { [Trait("Profile", "Icc")] public class IccDataWriterTests diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccProfileTests.cs index e20d1d6b6..ad2619f03 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccProfileTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccProfileTests.cs @@ -5,7 +5,7 @@ using System; using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; -namespace SixLabors.ImageSharp.Tests.Icc +namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Icc { [Trait("Profile", "Icc")] public class IccProfileTests diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccReaderTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccReaderTests.cs index b6ab9fc01..c2b57d0ba 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccReaderTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccReaderTests.cs @@ -4,7 +4,7 @@ using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; -namespace SixLabors.ImageSharp.Tests.Icc +namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Icc { [Trait("Profile", "Icc")] public class IccReaderTests diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccWriterTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccWriterTests.cs index eee085c23..bd9f55d3e 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccWriterTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccWriterTests.cs @@ -4,7 +4,7 @@ using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; -namespace SixLabors.ImageSharp.Tests.Icc +namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Icc { [Trait("Profile", "Icc")] public class IccWriterTests diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/Various/IccProfileIdTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/Various/IccProfileIdTests.cs index 11f8ef595..aa24d191b 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/Various/IccProfileIdTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/Various/IccProfileIdTests.cs @@ -4,7 +4,7 @@ using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Xunit; -namespace SixLabors.ImageSharp.Tests.Icc +namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.Various { [Trait("Profile", "Icc")] public class IccProfileIdTests diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs index b5bb53b5b..2930f02ed 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs @@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.IPTC // arrange var profile = new IptcProfile(); var value = new string('s', tag.MaxLength() + 1); - var expectedLength = tag.MaxLength(); + int expectedLength = tag.MaxLength(); // act profile.SetValue(tag, value); @@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.IPTC // arrange var profile = new IptcProfile(); var value = new string('s', tag.MaxLength() + 1); - var expectedLength = value.Length; + int expectedLength = value.Length; // act profile.SetValue(tag, value, false); diff --git a/tests/ImageSharp.Tests/Processing/BaseImageOperationsExtensionTest.cs b/tests/ImageSharp.Tests/Processing/BaseImageOperationsExtensionTest.cs index ae9befba0..e983577a2 100644 --- a/tests/ImageSharp.Tests/Processing/BaseImageOperationsExtensionTest.cs +++ b/tests/ImageSharp.Tests/Processing/BaseImageOperationsExtensionTest.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using System.ComponentModel.DataAnnotations; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; diff --git a/tests/ImageSharp.Tests/Processing/Convolution/Processors/LaplacianKernelFactoryTests.cs b/tests/ImageSharp.Tests/Processing/Convolution/Processors/LaplacianKernelFactoryTests.cs index 73f6a3f47..31a1fc2d4 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/Processors/LaplacianKernelFactoryTests.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/Processors/LaplacianKernelFactoryTests.cs @@ -5,7 +5,7 @@ using System; using SixLabors.ImageSharp.Processing.Processors.Convolution; using Xunit; -namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution +namespace SixLabors.ImageSharp.Tests.Processing.Convolution.Processors { public class LaplacianKernelFactoryTests { diff --git a/tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs b/tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs index 029e549b9..71cee8f7f 100644 --- a/tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs +++ b/tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs @@ -6,7 +6,7 @@ using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Dithering; using Xunit; -namespace SixLabors.ImageSharp.Tests.Processing.Binarization +namespace SixLabors.ImageSharp.Tests.Processing.Dithering { [Trait("Category", "Processors")] public class DitherTest : BaseImageOperationsExtensionTest diff --git a/tests/ImageSharp.Tests/Processing/Filters/BrightnessTest.cs b/tests/ImageSharp.Tests/Processing/Filters/BrightnessTest.cs index 8f0d19d9a..7f0330148 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/BrightnessTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/BrightnessTest.cs @@ -6,7 +6,7 @@ using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Filters; using Xunit; -namespace SixLabors.ImageSharp.Tests.Processing.Effects +namespace SixLabors.ImageSharp.Tests.Processing.Filters { [Trait("Category", "Processors")] public class BrightnessTest : BaseImageOperationsExtensionTest diff --git a/tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs b/tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs index 3ef3cd0b2..b968e023f 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs @@ -5,7 +5,7 @@ using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Filters; using Xunit; -namespace SixLabors.ImageSharp.Tests.Processing.Effects +namespace SixLabors.ImageSharp.Tests.Processing.Filters { [Trait("Category", "Processors")] public class ContrastTest : BaseImageOperationsExtensionTest diff --git a/tests/ImageSharp.Tests/Processing/Filters/InvertTest.cs b/tests/ImageSharp.Tests/Processing/Filters/InvertTest.cs index c36e63330..ed1c729e6 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/InvertTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/InvertTest.cs @@ -5,7 +5,7 @@ using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Filters; using Xunit; -namespace SixLabors.ImageSharp.Tests.Processing.Effects +namespace SixLabors.ImageSharp.Tests.Processing.Filters { [Trait("Category", "Processors")] public class InvertTest : BaseImageOperationsExtensionTest diff --git a/tests/ImageSharp.Tests/Processing/Filters/LightnessTest.cs b/tests/ImageSharp.Tests/Processing/Filters/LightnessTest.cs index 1e0e0806f..2b8a8be88 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/LightnessTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/LightnessTest.cs @@ -5,7 +5,7 @@ using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Filters; using Xunit; -namespace SixLabors.ImageSharp.Tests.Processing.Effects +namespace SixLabors.ImageSharp.Tests.Processing.Filters { [Trait("Category", "Processors")] public class LightnessTest : BaseImageOperationsExtensionTest diff --git a/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs b/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs index a60ebbf80..f28601fe3 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs @@ -3,10 +3,9 @@ using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Filters; -using SixLabors.ImageSharp.Tests.Processing; using Xunit; -namespace SixLabors.ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.Processing.Filters { [Trait("Category", "Processors")] public class LomographTest : BaseImageOperationsExtensionTest diff --git a/tests/ImageSharp.Tests/Processing/Filters/OpacityTest.cs b/tests/ImageSharp.Tests/Processing/Filters/OpacityTest.cs index f4e352061..526fd9a2d 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/OpacityTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/OpacityTest.cs @@ -5,7 +5,7 @@ using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Filters; using Xunit; -namespace SixLabors.ImageSharp.Tests.Processing.Effects +namespace SixLabors.ImageSharp.Tests.Processing.Filters { [Trait("Category", "Processors")] public class OpacityTest : BaseImageOperationsExtensionTest diff --git a/tests/ImageSharp.Tests/Processing/IntegralImageTests.cs b/tests/ImageSharp.Tests/Processing/IntegralImageTests.cs index 481463f47..285535b9f 100644 --- a/tests/ImageSharp.Tests/Processing/IntegralImageTests.cs +++ b/tests/ImageSharp.Tests/Processing/IntegralImageTests.cs @@ -6,7 +6,7 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using Xunit; -namespace SixLabors.ImageSharp.Tests.Processing.Transforms +namespace SixLabors.ImageSharp.Tests.Processing { public class IntegralImageTests : BaseImageOperationsExtensionTest { diff --git a/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs index 4acc91bec..36ce5029c 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs @@ -8,7 +8,7 @@ using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using Xunit; -namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Dithering { [Trait("Category", "Processors")] public class DitherTests diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/BrightnessTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/BrightnessTest.cs index 3527d6bbd..97f04440b 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Filters/BrightnessTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/BrightnessTest.cs @@ -6,7 +6,7 @@ using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using Xunit; -namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters { [Trait("Category", "Processors")] [GroupOutput("Filters")] diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/ContrastTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/ContrastTest.cs index 720408ad0..81a7e24ff 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Filters/ContrastTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/ContrastTest.cs @@ -5,7 +5,7 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using Xunit; -namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters { [Trait("Category", "Processors")] [GroupOutput("Filters")] diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/InvertTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/InvertTest.cs index f57034508..8c435d23a 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Filters/InvertTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/InvertTest.cs @@ -5,7 +5,7 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using Xunit; -namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters { [Trait("Category", "Processors")] [GroupOutput("Filters")] diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/LightnessTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/LightnessTest.cs index 78e379916..69fa8cdea 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Filters/LightnessTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/LightnessTest.cs @@ -6,7 +6,7 @@ using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using Xunit; -namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters { [Trait("Category", "Processors")] [GroupOutput("Filters")] diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/OpacityTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/OpacityTest.cs index 3a218544e..645746a21 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Filters/OpacityTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/OpacityTest.cs @@ -5,7 +5,7 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using Xunit; -namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters { [Trait("Category", "Processors")] [GroupOutput("Filters")] From ba752bcf70d17ef2ed2ac1cb089127d282b1838c Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 26 May 2021 05:41:17 +0200 Subject: [PATCH 253/275] Add missing usings / dispose --- .../Profiles/Exif/ExifProfileTests.cs | 21 ++++++++++++------- .../Profiles/IPTC/IptcProfileTests.cs | 2 +- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs index 9d2c4b174..1f23838ab 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs @@ -63,6 +63,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif Assert.NotNull(value); Assert.Equal(expected, value.Value); + image.Dispose(); } [Fact] @@ -157,6 +158,8 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif IExifValue value2 = image.Metadata.ExifProfile.GetValue(ExifTag.FlashEnergy); Assert.NotNull(value2); Assert.Equal(new Rational(double.PositiveInfinity), value2.Value); + + image.Dispose(); } [Theory] @@ -231,6 +234,8 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif latitude = image.Metadata.ExifProfile.GetValue(ExifTag.GPSLatitude); Assert.Equal(expectedLatitude, latitude.Value); + + image.Dispose(); } [Theory] @@ -252,13 +257,15 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif { Assert.True(ExifTags.GetPart(exifProfileValue.Tag) == ExifParts.ExifTags); } + + image.Dispose(); } [Fact] public void RemoveEntry_Works() { // Arrange - Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateRgba32Image(); + using Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateRgba32Image(); int profileCount = image.Metadata.ExifProfile.Values.Count; // Assert @@ -311,7 +318,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif TestProfile(profile); - Image thumbnail = profile.CreateThumbnail(); + using Image thumbnail = profile.CreateThumbnail(); Assert.NotNull(thumbnail); Assert.Equal(256, thumbnail.Width); Assert.Equal(170, thumbnail.Height); @@ -337,7 +344,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif image.Metadata.ExifProfile = expectedProfile; // Act - Image reloadedImage = WriteAndRead(image, TestImageWriteFormat.Jpeg); + using Image reloadedImage = WriteAndRead(image, TestImageWriteFormat.Jpeg); // Assert ExifProfile actualProfile = reloadedImage.Metadata.ExifProfile; @@ -361,7 +368,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif { // This image contains an 802 byte EXIF profile // It has a tag with an index offset of 18,481,152 bytes (overrunning the data) - Image image = TestFile.Create(TestImages.Jpeg.Progressive.Bad.ExifUndefType).CreateRgba32Image(); + using Image image = TestFile.Create(TestImages.Jpeg.Progressive.Bad.ExifUndefType).CreateRgba32Image(); Assert.NotNull(image); ExifProfile profile = image.Metadata.ExifProfile; @@ -381,7 +388,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif public void TestArrayValueWithUnspecifiedSize() { // This images contains array in the exif profile that has zero components. - Image image = TestFile.Create(TestImages.Jpeg.Issues.InvalidCast520).CreateRgba32Image(); + using Image image = TestFile.Create(TestImages.Jpeg.Issues.InvalidCast520).CreateRgba32Image(); ExifProfile profile = image.Metadata.ExifProfile; Assert.NotNull(profile); @@ -409,7 +416,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif image.Metadata.ExifProfile = CreateExifProfile(); // Act - Image reloadedImage = WriteAndRead(image, imageFormat); + using Image reloadedImage = WriteAndRead(image, imageFormat); // Assert ExifProfile actual = reloadedImage.Metadata.ExifProfile; @@ -460,7 +467,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif internal static ExifProfile GetExifProfile() { - Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateRgba32Image(); + using Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateRgba32Image(); ExifProfile profile = image.Metadata.ExifProfile; Assert.NotNull(profile); diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs index 2930f02ed..3c60f4526 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs @@ -228,7 +228,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.IPTC image.Metadata.IptcProfile.SetValue(IptcTag.Caption, expectedCaption); // act - Image reloadedImage = WriteAndReadJpeg(image); + using Image reloadedImage = WriteAndReadJpeg(image); // assert IptcProfile actual = reloadedImage.Metadata.IptcProfile; From e2b745a1515cc3cfae7bc87ad407c16bf405ee8d Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 26 May 2021 09:06:17 +0200 Subject: [PATCH 254/275] Make CreateReader static --- .../Profiles/ICC/DataReader/IccDataReader.cs | 2 +- .../DataReader/IccDataReaderCurvesTests.cs | 14 ++-- .../ICC/DataReader/IccDataReaderLutTests.cs | 14 ++-- .../DataReader/IccDataReaderMatrixTests.cs | 6 +- .../IccDataReaderMultiProcessElementTests.cs | 10 +-- .../IccDataReaderNonPrimitivesTests.cs | 22 +++--- .../IccDataReaderPrimitivesTests.cs | 16 ++--- .../IccDataReaderTagDataEntryTests.cs | 68 +++++++++---------- 8 files changed, 76 insertions(+), 76 deletions(-) diff --git a/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.cs index 2751c7b3e..8e9cad563 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.Metadata.Profiles.ICC.DataReader +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// /// Provides methods to read ICC data types diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderCurvesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderCurvesTests.cs index bfbb47a94..dff370124 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderCurvesTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderCurvesTests.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader [MemberData(nameof(IccTestDataCurves.OneDimensionalCurveTestData), MemberType = typeof(IccTestDataCurves))] internal void ReadOneDimensionalCurve(byte[] data, IccOneDimensionalCurve expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccOneDimensionalCurve output = reader.ReadOneDimensionalCurve(); @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader [MemberData(nameof(IccTestDataCurves.ResponseCurveTestData), MemberType = typeof(IccTestDataCurves))] internal void ReadResponseCurve(byte[] data, IccResponseCurve expected, int channelCount) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccResponseCurve output = reader.ReadResponseCurve(channelCount); @@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader [MemberData(nameof(IccTestDataCurves.ParametricCurveTestData), MemberType = typeof(IccTestDataCurves))] internal void ReadParametricCurve(byte[] data, IccParametricCurve expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccParametricCurve output = reader.ReadParametricCurve(); @@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader [MemberData(nameof(IccTestDataCurves.CurveSegmentTestData), MemberType = typeof(IccTestDataCurves))] internal void ReadCurveSegment(byte[] data, IccCurveSegment expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccCurveSegment output = reader.ReadCurveSegment(); @@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader [MemberData(nameof(IccTestDataCurves.FormulaCurveSegmentTestData), MemberType = typeof(IccTestDataCurves))] internal void ReadFormulaCurveElement(byte[] data, IccFormulaCurveElement expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccFormulaCurveElement output = reader.ReadFormulaCurveElement(); @@ -68,14 +68,14 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader [MemberData(nameof(IccTestDataCurves.SampledCurveSegmentTestData), MemberType = typeof(IccTestDataCurves))] internal void ReadSampledCurveElement(byte[] data, IccSampledCurveElement expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccSampledCurveElement output = reader.ReadSampledCurveElement(); Assert.Equal(expected, output); } - private IccDataReader CreateReader(byte[] data) + private static IccDataReader CreateReader(byte[] data) { return new IccDataReader(data); } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderLutTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderLutTests.cs index 10a62976e..411738158 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderLutTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderLutTests.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader [MemberData(nameof(IccTestDataLut.ClutTestData), MemberType = typeof(IccTestDataLut))] internal void ReadClut(byte[] data, IccClut expected, int inChannelCount, int outChannelCount, bool isFloat) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccClut output = reader.ReadClut(inChannelCount, outChannelCount, isFloat); @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader [MemberData(nameof(IccTestDataLut.Clut8TestData), MemberType = typeof(IccTestDataLut))] internal void ReadClut8(byte[] data, IccClut expected, int inChannelCount, int outChannelCount, byte[] gridPointCount) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccClut output = reader.ReadClut8(inChannelCount, outChannelCount, gridPointCount); @@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader [MemberData(nameof(IccTestDataLut.Clut16TestData), MemberType = typeof(IccTestDataLut))] internal void ReadClut16(byte[] data, IccClut expected, int inChannelCount, int outChannelCount, byte[] gridPointCount) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccClut output = reader.ReadClut16(inChannelCount, outChannelCount, gridPointCount); @@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader [MemberData(nameof(IccTestDataLut.ClutF32TestData), MemberType = typeof(IccTestDataLut))] internal void ReadClutF32(byte[] data, IccClut expected, int inChannelCount, int outChannelCount, byte[] gridPointCount) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccClut output = reader.ReadClutF32(inChannelCount, outChannelCount, gridPointCount); @@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader [MemberData(nameof(IccTestDataLut.Lut8TestData), MemberType = typeof(IccTestDataLut))] internal void ReadLut8(byte[] data, IccLut expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccLut output = reader.ReadLut8(); @@ -68,14 +68,14 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader [MemberData(nameof(IccTestDataLut.Lut16TestData), MemberType = typeof(IccTestDataLut))] internal void ReadLut16(byte[] data, IccLut expected, int count) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccLut output = reader.ReadLut16(count); Assert.Equal(expected, output); } - private IccDataReader CreateReader(byte[] data) + private static IccDataReader CreateReader(byte[] data) { return new IccDataReader(data); } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderMatrixTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderMatrixTests.cs index 4739211bb..49e0ea262 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderMatrixTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderMatrixTests.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader [MemberData(nameof(IccTestDataMatrix.Matrix2D_FloatArrayTestData), MemberType = typeof(IccTestDataMatrix))] public void ReadMatrix2D(byte[] data, int xCount, int yCount, bool isSingle, float[,] expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); float[,] output = reader.ReadMatrix(xCount, yCount, isSingle); @@ -24,14 +24,14 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader [MemberData(nameof(IccTestDataMatrix.Matrix1D_ArrayTestData), MemberType = typeof(IccTestDataMatrix))] public void ReadMatrix1D(byte[] data, int yCount, bool isSingle, float[] expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); float[] output = reader.ReadMatrix(yCount, isSingle); Assert.Equal(expected, output); } - private IccDataReader CreateReader(byte[] data) + private static IccDataReader CreateReader(byte[] data) { return new IccDataReader(data); } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderMultiProcessElementTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderMultiProcessElementTests.cs index aa91e87f3..5673ba75b 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderMultiProcessElementTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderMultiProcessElementTests.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader [MemberData(nameof(IccTestDataMultiProcessElements.MultiProcessElementTestData), MemberType = typeof(IccTestDataMultiProcessElements))] internal void ReadMultiProcessElement(byte[] data, IccMultiProcessElement expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccMultiProcessElement output = reader.ReadMultiProcessElement(); @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader [MemberData(nameof(IccTestDataMultiProcessElements.CurveSetTestData), MemberType = typeof(IccTestDataMultiProcessElements))] internal void ReadCurveSetProcessElement(byte[] data, IccCurveSetProcessElement expected, int inChannelCount, int outChannelCount) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccCurveSetProcessElement output = reader.ReadCurveSetProcessElement(inChannelCount, outChannelCount); @@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader [MemberData(nameof(IccTestDataMultiProcessElements.MatrixTestData), MemberType = typeof(IccTestDataMultiProcessElements))] internal void ReadMatrixProcessElement(byte[] data, IccMatrixProcessElement expected, int inChannelCount, int outChannelCount) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccMatrixProcessElement output = reader.ReadMatrixProcessElement(inChannelCount, outChannelCount); @@ -46,14 +46,14 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader [MemberData(nameof(IccTestDataMultiProcessElements.ClutTestData), MemberType = typeof(IccTestDataMultiProcessElements))] internal void ReadClutProcessElement(byte[] data, IccClutProcessElement expected, int inChannelCount, int outChannelCount) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccClutProcessElement output = reader.ReadClutProcessElement(inChannelCount, outChannelCount); Assert.Equal(expected, output); } - private IccDataReader CreateReader(byte[] data) + private static IccDataReader CreateReader(byte[] data) { return new IccDataReader(data); } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderNonPrimitivesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderNonPrimitivesTests.cs index 15652dd95..7d1d07743 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderNonPrimitivesTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderNonPrimitivesTests.cs @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader [MemberData(nameof(IccTestDataNonPrimitives.DateTimeTestData), MemberType = typeof(IccTestDataNonPrimitives))] public void ReadDateTime(byte[] data, DateTime expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); DateTime output = reader.ReadDateTime(); @@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader [MemberData(nameof(IccTestDataNonPrimitives.VersionNumberTestData), MemberType = typeof(IccTestDataNonPrimitives))] public void ReadVersionNumber(byte[] data, IccVersion expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccVersion output = reader.ReadVersionNumber(); @@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader [MemberData(nameof(IccTestDataNonPrimitives.XyzNumberTestData), MemberType = typeof(IccTestDataNonPrimitives))] public void ReadXyzNumber(byte[] data, Vector3 expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); Vector3 output = reader.ReadXyzNumber(); @@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader [MemberData(nameof(IccTestDataNonPrimitives.ProfileIdTestData), MemberType = typeof(IccTestDataNonPrimitives))] internal void ReadProfileId(byte[] data, IccProfileId expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccProfileId output = reader.ReadProfileId(); @@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader [MemberData(nameof(IccTestDataNonPrimitives.PositionNumberTestData), MemberType = typeof(IccTestDataNonPrimitives))] internal void ReadPositionNumber(byte[] data, IccPositionNumber expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccPositionNumber output = reader.ReadPositionNumber(); @@ -70,7 +70,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader [MemberData(nameof(IccTestDataNonPrimitives.ResponseNumberTestData), MemberType = typeof(IccTestDataNonPrimitives))] internal void ReadResponseNumber(byte[] data, IccResponseNumber expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccResponseNumber output = reader.ReadResponseNumber(); @@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader [MemberData(nameof(IccTestDataNonPrimitives.NamedColorTestData), MemberType = typeof(IccTestDataNonPrimitives))] internal void ReadNamedColor(byte[] data, IccNamedColor expected, uint coordinateCount) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccNamedColor output = reader.ReadNamedColor(coordinateCount); @@ -92,7 +92,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader [MemberData(nameof(IccTestDataNonPrimitives.ProfileDescriptionReadTestData), MemberType = typeof(IccTestDataNonPrimitives))] internal void ReadProfileDescription(byte[] data, IccProfileDescription expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccProfileDescription output = reader.ReadProfileDescription(); @@ -103,7 +103,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader [MemberData(nameof(IccTestDataNonPrimitives.ColorantTableEntryTestData), MemberType = typeof(IccTestDataNonPrimitives))] internal void ReadColorantTableEntry(byte[] data, IccColorantTableEntry expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccColorantTableEntry output = reader.ReadColorantTableEntry(); @@ -114,14 +114,14 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader [MemberData(nameof(IccTestDataNonPrimitives.ScreeningChannelTestData), MemberType = typeof(IccTestDataNonPrimitives))] internal void ReadScreeningChannel(byte[] data, IccScreeningChannel expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccScreeningChannel output = reader.ReadScreeningChannel(); Assert.Equal(expected, output); } - private IccDataReader CreateReader(byte[] data) + private static IccDataReader CreateReader(byte[] data) { return new IccDataReader(data); } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderPrimitivesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderPrimitivesTests.cs index b4532ff47..feff5e496 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderPrimitivesTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderPrimitivesTests.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader [MemberData(nameof(IccTestDataPrimitives.AsciiTestData), MemberType = typeof(IccTestDataPrimitives))] public void ReadAsciiString(byte[] textBytes, int length, string expected) { - IccDataReader reader = this.CreateReader(textBytes); + IccDataReader reader = CreateReader(textBytes); string output = reader.ReadAsciiString(length); @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader [Fact] public void ReadAsciiStringWithNegativeLengthThrowsArgumentException() { - IccDataReader reader = this.CreateReader(new byte[4]); + IccDataReader reader = CreateReader(new byte[4]); Assert.Throws(() => reader.ReadAsciiString(-1)); } @@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader [Fact] public void ReadUnicodeStringWithNegativeLengthThrowsArgumentException() { - IccDataReader reader = this.CreateReader(new byte[4]); + IccDataReader reader = CreateReader(new byte[4]); Assert.Throws(() => reader.ReadUnicodeString(-1)); } @@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader [MemberData(nameof(IccTestDataPrimitives.Fix16TestData), MemberType = typeof(IccTestDataPrimitives))] public void ReadFix16(byte[] data, float expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); float output = reader.ReadFix16(); @@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader [MemberData(nameof(IccTestDataPrimitives.UFix16TestData), MemberType = typeof(IccTestDataPrimitives))] public void ReadUFix16(byte[] data, float expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); float output = reader.ReadUFix16(); @@ -63,7 +63,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader [MemberData(nameof(IccTestDataPrimitives.U1Fix15TestData), MemberType = typeof(IccTestDataPrimitives))] public void ReadU1Fix15(byte[] data, float expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); float output = reader.ReadU1Fix15(); @@ -74,14 +74,14 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader [MemberData(nameof(IccTestDataPrimitives.UFix8TestData), MemberType = typeof(IccTestDataPrimitives))] public void ReadUFix8(byte[] data, float expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); float output = reader.ReadUFix8(); Assert.Equal(expected, output); } - private IccDataReader CreateReader(byte[] data) + private static IccDataReader CreateReader(byte[] data) { return new IccDataReader(data); } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderTagDataEntryTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderTagDataEntryTests.cs index cc7b06b29..45ad6ce49 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderTagDataEntryTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderTagDataEntryTests.cs @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadUnknownTagDataEntry(byte[] data, IccUnknownTagDataEntry expected, uint size) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccUnknownTagDataEntry output = reader.ReadUnknownTagDataEntry(size); @@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadChromaticityTagDataEntry(byte[] data, IccChromaticityTagDataEntry expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccChromaticityTagDataEntry output = reader.ReadChromaticityTagDataEntry(); @@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadColorantOrderTagDataEntry(byte[] data, IccColorantOrderTagDataEntry expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccColorantOrderTagDataEntry output = reader.ReadColorantOrderTagDataEntry(); @@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadColorantTableTagDataEntry(byte[] data, IccColorantTableTagDataEntry expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccColorantTableTagDataEntry output = reader.ReadColorantTableTagDataEntry(); @@ -67,7 +67,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadCurveTagDataEntry(byte[] data, IccCurveTagDataEntry expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccCurveTagDataEntry output = reader.ReadCurveTagDataEntry(); @@ -80,7 +80,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadDataTagDataEntry(byte[] data, IccDataTagDataEntry expected, uint size) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccDataTagDataEntry output = reader.ReadDataTagDataEntry(size); @@ -93,7 +93,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadDateTimeTagDataEntry(byte[] data, IccDateTimeTagDataEntry expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccDateTimeTagDataEntry output = reader.ReadDateTimeTagDataEntry(); @@ -106,7 +106,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadLut16TagDataEntry(byte[] data, IccLut16TagDataEntry expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccLut16TagDataEntry output = reader.ReadLut16TagDataEntry(); @@ -119,7 +119,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadLut8TagDataEntry(byte[] data, IccLut8TagDataEntry expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccLut8TagDataEntry output = reader.ReadLut8TagDataEntry(); @@ -132,7 +132,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadLutAToBTagDataEntry(byte[] data, IccLutAToBTagDataEntry expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccLutAToBTagDataEntry output = reader.ReadLutAtoBTagDataEntry(); @@ -145,7 +145,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadLutBToATagDataEntry(byte[] data, IccLutBToATagDataEntry expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccLutBToATagDataEntry output = reader.ReadLutBtoATagDataEntry(); @@ -158,7 +158,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadMeasurementTagDataEntry(byte[] data, IccMeasurementTagDataEntry expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccMeasurementTagDataEntry output = reader.ReadMeasurementTagDataEntry(); @@ -171,7 +171,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadMultiLocalizedUnicodeTagDataEntry(byte[] data, IccMultiLocalizedUnicodeTagDataEntry expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccMultiLocalizedUnicodeTagDataEntry output = reader.ReadMultiLocalizedUnicodeTagDataEntry(); @@ -184,7 +184,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadMultiProcessElementsTagDataEntry(byte[] data, IccMultiProcessElementsTagDataEntry expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccMultiProcessElementsTagDataEntry output = reader.ReadMultiProcessElementsTagDataEntry(); @@ -197,7 +197,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadNamedColor2TagDataEntry(byte[] data, IccNamedColor2TagDataEntry expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccNamedColor2TagDataEntry output = reader.ReadNamedColor2TagDataEntry(); @@ -210,7 +210,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadParametricCurveTagDataEntry(byte[] data, IccParametricCurveTagDataEntry expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccParametricCurveTagDataEntry output = reader.ReadParametricCurveTagDataEntry(); @@ -223,7 +223,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadProfileSequenceDescTagDataEntry(byte[] data, IccProfileSequenceDescTagDataEntry expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccProfileSequenceDescTagDataEntry output = reader.ReadProfileSequenceDescTagDataEntry(); @@ -238,7 +238,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader byte[] data, IccProfileSequenceIdentifierTagDataEntry expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccProfileSequenceIdentifierTagDataEntry output = reader.ReadProfileSequenceIdentifierTagDataEntry(); @@ -251,7 +251,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadResponseCurveSet16TagDataEntry(byte[] data, IccResponseCurveSet16TagDataEntry expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccResponseCurveSet16TagDataEntry output = reader.ReadResponseCurveSet16TagDataEntry(); @@ -264,7 +264,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadFix16ArrayTagDataEntry(byte[] data, IccFix16ArrayTagDataEntry expected, uint size) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccFix16ArrayTagDataEntry output = reader.ReadFix16ArrayTagDataEntry(size); @@ -277,7 +277,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadSignatureTagDataEntry(byte[] data, IccSignatureTagDataEntry expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccSignatureTagDataEntry output = reader.ReadSignatureTagDataEntry(); @@ -290,7 +290,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadTextTagDataEntry(byte[] data, IccTextTagDataEntry expected, uint size) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccTextTagDataEntry output = reader.ReadTextTagDataEntry(size); @@ -303,7 +303,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadUFix16ArrayTagDataEntry(byte[] data, IccUFix16ArrayTagDataEntry expected, uint size) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccUFix16ArrayTagDataEntry output = reader.ReadUFix16ArrayTagDataEntry(size); @@ -316,7 +316,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadUInt16ArrayTagDataEntry(byte[] data, IccUInt16ArrayTagDataEntry expected, uint size) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccUInt16ArrayTagDataEntry output = reader.ReadUInt16ArrayTagDataEntry(size); @@ -329,7 +329,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadUInt32ArrayTagDataEntry(byte[] data, IccUInt32ArrayTagDataEntry expected, uint size) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccUInt32ArrayTagDataEntry output = reader.ReadUInt32ArrayTagDataEntry(size); @@ -342,7 +342,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadUInt64ArrayTagDataEntry(byte[] data, IccUInt64ArrayTagDataEntry expected, uint size) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccUInt64ArrayTagDataEntry output = reader.ReadUInt64ArrayTagDataEntry(size); @@ -355,7 +355,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadUInt8ArrayTagDataEntry(byte[] data, IccUInt8ArrayTagDataEntry expected, uint size) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccUInt8ArrayTagDataEntry output = reader.ReadUInt8ArrayTagDataEntry(size); @@ -368,7 +368,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadViewingConditionsTagDataEntry(byte[] data, IccViewingConditionsTagDataEntry expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccViewingConditionsTagDataEntry output = reader.ReadViewingConditionsTagDataEntry(); @@ -381,7 +381,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadXyzTagDataEntry(byte[] data, IccXyzTagDataEntry expected, uint size) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccXyzTagDataEntry output = reader.ReadXyzTagDataEntry(size); @@ -394,7 +394,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadTextDescriptionTagDataEntry(byte[] data, IccTextDescriptionTagDataEntry expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccTextDescriptionTagDataEntry output = reader.ReadTextDescriptionTagDataEntry(); @@ -407,7 +407,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadCrdInfoTagDataEntry(byte[] data, IccCrdInfoTagDataEntry expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccCrdInfoTagDataEntry output = reader.ReadCrdInfoTagDataEntry(); @@ -420,7 +420,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadScreeningTagDataEntry(byte[] data, IccScreeningTagDataEntry expected) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccScreeningTagDataEntry output = reader.ReadScreeningTagDataEntry(); @@ -433,14 +433,14 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadUcrBgTagDataEntry(byte[] data, IccUcrBgTagDataEntry expected, uint size) { - IccDataReader reader = this.CreateReader(data); + IccDataReader reader = CreateReader(data); IccUcrBgTagDataEntry output = reader.ReadUcrBgTagDataEntry(size); Assert.Equal(expected, output); } - private IccDataReader CreateReader(byte[] data) + private static IccDataReader CreateReader(byte[] data) { return new IccDataReader(data); } From b49313e1dc9f0a46d847761fff3cbf4ee8b32ba3 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 26 May 2021 14:13:00 +0200 Subject: [PATCH 255/275] Use StringComparison.Ordinal and fix some minor warnings --- .../Formats/Jpg/JpegDecoderTests.cs | 10 ++--- .../Formats/Png/PngMetadataTests.cs | 40 +++++++++---------- .../ImageFrameCollectionTests.Generic.cs | 22 +++++----- .../Image/ImageTests.Identify.cs | 20 +++++----- .../Memory/Allocators/BufferTestSuite.cs | 38 +++++++++--------- .../PixelOperations/PixelOperationsTests.cs | 2 +- .../BaseImageOperationsExtensionTest.cs | 5 ++- .../Processing/ImageOperationTests.cs | 2 +- .../Processing/ImageProcessingContextTests.cs | 5 ++- .../HistogramEqualizationTests.cs | 22 +++++----- tests/ImageSharp.Tests/TestFormat.cs | 34 ++++++++-------- .../Attributes/ImageDataAttributeBase.cs | 2 +- .../Tests/SemaphoreReadMemoryStreamTests.cs | 5 +-- 13 files changed, 106 insertions(+), 101 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 3910b2c49..fe57b3840 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -141,9 +141,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg TestEnvironment.InputImagesDirectoryFullPath, fileName); - const int NumberOfRuns = 5; + const int numberOfRuns = 5; - for (int i = 0; i < NumberOfRuns; i++) + for (int i = 0; i < numberOfRuns; i++) { var cts = new CancellationTokenSource(); if (cancellationDelayMs == 0) @@ -157,16 +157,16 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg try { - using var image = await Image.LoadAsync(hugeFile, cts.Token); + using Image image = await Image.LoadAsync(hugeFile, cts.Token); } catch (TaskCanceledException) { - // Succesfully observed a cancellation + // Successfully observed a cancellation return; } } - throw new Exception($"No cancellation happened out of {NumberOfRuns} runs!"); + throw new Exception($"No cancellation happened out of {numberOfRuns} runs!"); } [Theory(Skip = "Identify is too fast, doesn't work reliably.")] diff --git a/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs index f9ff41df1..ba6e71935 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs @@ -90,12 +90,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png using (Image image = provider.GetImage(new PngDecoder())) { PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance); - Assert.DoesNotContain(meta.TextData, m => m.Value.Equals("leading space")); - Assert.DoesNotContain(meta.TextData, m => m.Value.Equals("trailing space")); - Assert.DoesNotContain(meta.TextData, m => m.Value.Equals("space")); - Assert.DoesNotContain(meta.TextData, m => m.Value.Equals("empty")); - Assert.DoesNotContain(meta.TextData, m => m.Value.Equals("invalid characters")); - Assert.DoesNotContain(meta.TextData, m => m.Value.Equals("too large")); + Assert.DoesNotContain(meta.TextData, m => m.Value.Equals("leading space", System.StringComparison.Ordinal)); + Assert.DoesNotContain(meta.TextData, m => m.Value.Equals("trailing space", System.StringComparison.Ordinal)); + Assert.DoesNotContain(meta.TextData, m => m.Value.Equals("space", System.StringComparison.Ordinal)); + Assert.DoesNotContain(meta.TextData, m => m.Value.Equals("empty", System.StringComparison.Ordinal)); + Assert.DoesNotContain(meta.TextData, m => m.Value.Equals("invalid characters", System.StringComparison.Ordinal)); + Assert.DoesNotContain(meta.TextData, m => m.Value.Equals("too large", System.StringComparison.Ordinal)); } } @@ -277,20 +277,20 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png private static void VerifyTextDataIsPresent(PngMetadata meta) { Assert.NotNull(meta); - Assert.Contains(meta.TextData, m => m.Keyword.Equals("Comment") && m.Value.Equals("comment")); - Assert.Contains(meta.TextData, m => m.Keyword.Equals("Author") && m.Value.Equals("ImageSharp")); - Assert.Contains(meta.TextData, m => m.Keyword.Equals("Copyright") && m.Value.Equals("ImageSharp")); - Assert.Contains(meta.TextData, m => m.Keyword.Equals("Title") && m.Value.Equals("unittest")); - Assert.Contains(meta.TextData, m => m.Keyword.Equals("Description") && m.Value.Equals("compressed-text")); - Assert.Contains(meta.TextData, m => m.Keyword.Equals("International") && m.Value.Equals("'e', mu'tlheghvam, ghaH yu'") && - m.LanguageTag.Equals("x-klingon") && m.TranslatedKeyword.Equals("warning")); - Assert.Contains(meta.TextData, m => m.Keyword.Equals("International2") && m.Value.Equals("ИМАГЕШАРП") && m.LanguageTag.Equals("rus")); - Assert.Contains(meta.TextData, m => m.Keyword.Equals("CompressedInternational") && m.Value.Equals("la plume de la mante") && - m.LanguageTag.Equals("fra") && m.TranslatedKeyword.Equals("foobar")); - Assert.Contains(meta.TextData, m => m.Keyword.Equals("CompressedInternational2") && m.Value.Equals("這是一個考驗") && - m.LanguageTag.Equals("chinese")); - Assert.Contains(meta.TextData, m => m.Keyword.Equals("NoLang") && m.Value.Equals("this text chunk is missing a language tag")); - Assert.Contains(meta.TextData, m => m.Keyword.Equals("NoTranslatedKeyword") && m.Value.Equals("dieser chunk hat kein übersetztes Schlüßelwort")); + Assert.Contains(meta.TextData, m => m.Keyword.Equals("Comment", System.StringComparison.Ordinal) && m.Value.Equals("comment", System.StringComparison.Ordinal)); + Assert.Contains(meta.TextData, m => m.Keyword.Equals("Author", System.StringComparison.Ordinal) && m.Value.Equals("ImageSharp", System.StringComparison.Ordinal)); + Assert.Contains(meta.TextData, m => m.Keyword.Equals("Copyright", System.StringComparison.Ordinal) && m.Value.Equals("ImageSharp", System.StringComparison.Ordinal)); + Assert.Contains(meta.TextData, m => m.Keyword.Equals("Title", System.StringComparison.Ordinal) && m.Value.Equals("unittest", System.StringComparison.Ordinal)); + Assert.Contains(meta.TextData, m => m.Keyword.Equals("Description", System.StringComparison.Ordinal) && m.Value.Equals("compressed-text", System.StringComparison.Ordinal)); + Assert.Contains(meta.TextData, m => m.Keyword.Equals("International", System.StringComparison.Ordinal) && m.Value.Equals("'e', mu'tlheghvam, ghaH yu'", System.StringComparison.Ordinal) && + m.LanguageTag.Equals("x-klingon", System.StringComparison.Ordinal) && m.TranslatedKeyword.Equals("warning", System.StringComparison.Ordinal)); + Assert.Contains(meta.TextData, m => m.Keyword.Equals("International2", System.StringComparison.Ordinal) && m.Value.Equals("ИМАГЕШАРП", System.StringComparison.Ordinal) && m.LanguageTag.Equals("rus", System.StringComparison.Ordinal)); + Assert.Contains(meta.TextData, m => m.Keyword.Equals("CompressedInternational", System.StringComparison.Ordinal) && m.Value.Equals("la plume de la mante", System.StringComparison.Ordinal) && + m.LanguageTag.Equals("fra", System.StringComparison.Ordinal) && m.TranslatedKeyword.Equals("foobar", System.StringComparison.Ordinal)); + Assert.Contains(meta.TextData, m => m.Keyword.Equals("CompressedInternational2", System.StringComparison.Ordinal) && m.Value.Equals("這是一個考驗", System.StringComparison.Ordinal) && + m.LanguageTag.Equals("chinese", System.StringComparison.Ordinal)); + Assert.Contains(meta.TextData, m => m.Keyword.Equals("NoLang", System.StringComparison.Ordinal) && m.Value.Equals("this text chunk is missing a language tag", System.StringComparison.Ordinal)); + Assert.Contains(meta.TextData, m => m.Keyword.Equals("NoTranslatedKeyword", System.StringComparison.Ordinal) && m.Value.Equals("dieser chunk hat kein übersetztes Schlüßelwort", System.StringComparison.Ordinal)); } } } diff --git a/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs index ecbc331b2..14f6ed8df 100644 --- a/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs +++ b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs @@ -66,7 +66,7 @@ namespace SixLabors.ImageSharp.Tests ArgumentOutOfRangeException ex = Assert.Throws( () => { - this.Collection.AddFrame(new Rgba32[0]); + this.Collection.AddFrame(Array.Empty()); }); Assert.StartsWith($"Parameter \"data\" ({typeof(int)}) must be greater than or equal to {100}, was {0}", ex.Message); @@ -246,7 +246,7 @@ namespace SixLabors.ImageSharp.Tests public void AddFrameFromPixelData() { Assert.True(this.Image.Frames.RootFrame.TryGetSinglePixelSpan(out Span imgSpan)); - var pixelData = imgSpan.ToArray(); + Rgba32[] pixelData = imgSpan.ToArray(); this.Image.Frames.AddFrame(pixelData); Assert.Equal(2, this.Image.Frames.Count); } @@ -276,46 +276,46 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void MoveFrame_LeavesFrameInCorrectLocation() { - for (var i = 0; i < 9; i++) + for (int i = 0; i < 9; i++) { this.Image.Frames.CreateFrame(); } - var frame = this.Image.Frames[4]; + ImageFrame frame = this.Image.Frames[4]; this.Image.Frames.MoveFrame(4, 7); - var newIndex = this.Image.Frames.IndexOf(frame); + int newIndex = this.Image.Frames.IndexOf(frame); Assert.Equal(7, newIndex); } [Fact] public void IndexOf_ReturnsCorrectIndex() { - for (var i = 0; i < 9; i++) + for (int i = 0; i < 9; i++) { this.Image.Frames.CreateFrame(); } - var frame = this.Image.Frames[4]; - var index = this.Image.Frames.IndexOf(frame); + ImageFrame frame = this.Image.Frames[4]; + int index = this.Image.Frames.IndexOf(frame); Assert.Equal(4, index); } [Fact] public void Contains_TrueIfMember() { - for (var i = 0; i < 9; i++) + for (int i = 0; i < 9; i++) { this.Image.Frames.CreateFrame(); } - var frame = this.Image.Frames[4]; + ImageFrame frame = this.Image.Frames[4]; Assert.True(this.Image.Frames.Contains(frame)); } [Fact] public void Contains_FalseIfNonMember() { - for (var i = 0; i < 9; i++) + for (int i = 0; i < 9; i++) { this.Image.Frames.CreateFrame(); } diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Identify.cs b/tests/ImageSharp.Tests/Image/ImageTests.Identify.cs index 3fbe1f70d..271aa30cf 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.Identify.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.Identify.cs @@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Tests private static readonly Size ExpectedImageSize = new Size(108, 202); - private byte[] ActualImageBytes => TestFile.Create(TestImages.Bmp.F).Bytes; + private static byte[] ActualImageBytes => TestFile.Create(TestImages.Bmp.F).Bytes; private IImageInfo LocalImageInfo => this.localImageInfoMock.Object; @@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void FromBytes_GlobalConfiguration() { - IImageInfo info = Image.Identify(this.ActualImageBytes, out IImageFormat type); + IImageInfo info = Image.Identify(ActualImageBytes, out IImageFormat type); Assert.Equal(ExpectedImageSize, info.Size()); Assert.Equal(ExpectedGlobalFormat, type); @@ -69,7 +69,7 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void FromStream_GlobalConfiguration() { - using (var stream = new MemoryStream(this.ActualImageBytes)) + using (var stream = new MemoryStream(ActualImageBytes)) { IImageInfo info = Image.Identify(stream, out IImageFormat type); @@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void FromStream_GlobalConfiguration_NoFormat() { - using (var stream = new MemoryStream(this.ActualImageBytes)) + using (var stream = new MemoryStream(ActualImageBytes)) { IImageInfo info = Image.Identify(stream); @@ -92,7 +92,7 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void FromNonSeekableStream_GlobalConfiguration() { - using var stream = new MemoryStream(this.ActualImageBytes); + using var stream = new MemoryStream(ActualImageBytes); using var nonSeekableStream = new NonSeekableStream(stream); IImageInfo info = Image.Identify(nonSeekableStream, out IImageFormat type); @@ -104,7 +104,7 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void FromNonSeekableStream_GlobalConfiguration_NoFormat() { - using var stream = new MemoryStream(this.ActualImageBytes); + using var stream = new MemoryStream(ActualImageBytes); using var nonSeekableStream = new NonSeekableStream(stream); IImageInfo info = Image.Identify(nonSeekableStream); @@ -141,7 +141,7 @@ namespace SixLabors.ImageSharp.Tests [Fact] public async Task FromStreamAsync_GlobalConfiguration_NoFormat() { - using (var stream = new MemoryStream(this.ActualImageBytes)) + using (var stream = new MemoryStream(ActualImageBytes)) { var asyncStream = new AsyncStreamWrapper(stream, () => false); IImageInfo info = await Image.IdentifyAsync(asyncStream); @@ -153,7 +153,7 @@ namespace SixLabors.ImageSharp.Tests [Fact] public async Task FromStreamAsync_GlobalConfiguration() { - using (var stream = new MemoryStream(this.ActualImageBytes)) + using (var stream = new MemoryStream(ActualImageBytes)) { var asyncStream = new AsyncStreamWrapper(stream, () => false); (IImageInfo ImageInfo, IImageFormat Format) res = await Image.IdentifyWithFormatAsync(asyncStream); @@ -166,7 +166,7 @@ namespace SixLabors.ImageSharp.Tests [Fact] public async Task FromNonSeekableStreamAsync_GlobalConfiguration_NoFormat() { - using var stream = new MemoryStream(this.ActualImageBytes); + using var stream = new MemoryStream(ActualImageBytes); using var nonSeekableStream = new NonSeekableStream(stream); var asyncStream = new AsyncStreamWrapper(nonSeekableStream, () => false); @@ -178,7 +178,7 @@ namespace SixLabors.ImageSharp.Tests [Fact] public async Task FromNonSeekableStreamAsync_GlobalConfiguration() { - using var stream = new MemoryStream(this.ActualImageBytes); + using var stream = new MemoryStream(ActualImageBytes); using var nonSeekableStream = new NonSeekableStream(stream); var asyncStream = new AsyncStreamWrapper(nonSeekableStream, () => false); diff --git a/tests/ImageSharp.Tests/Memory/Allocators/BufferTestSuite.cs b/tests/ImageSharp.Tests/Memory/Allocators/BufferTestSuite.cs index 1124b6439..1cadf1653 100644 --- a/tests/ImageSharp.Tests/Memory/Allocators/BufferTestSuite.cs +++ b/tests/ImageSharp.Tests/Memory/Allocators/BufferTestSuite.cs @@ -60,24 +60,24 @@ namespace SixLabors.ImageSharp.Tests.Memory.Allocators } } - public static readonly TheoryData LenthValues = new TheoryData { 0, 1, 7, 1023, 1024 }; + public static readonly TheoryData LengthValues = new TheoryData { 0, 1, 7, 1023, 1024 }; [Theory] - [MemberData(nameof(LenthValues))] + [MemberData(nameof(LengthValues))] public void HasCorrectLength_byte(int desiredLength) { this.TestHasCorrectLength(desiredLength); } [Theory] - [MemberData(nameof(LenthValues))] + [MemberData(nameof(LengthValues))] public void HasCorrectLength_float(int desiredLength) { this.TestHasCorrectLength(desiredLength); } [Theory] - [MemberData(nameof(LenthValues))] + [MemberData(nameof(LengthValues))] public void HasCorrectLength_CustomStruct(int desiredLength) { this.TestHasCorrectLength(desiredLength); @@ -93,7 +93,7 @@ namespace SixLabors.ImageSharp.Tests.Memory.Allocators } [Theory] - [MemberData(nameof(LenthValues))] + [MemberData(nameof(LengthValues))] public void CanAllocateCleanBuffer_byte(int desiredLength) { this.TestCanAllocateCleanBuffer(desiredLength, false); @@ -101,14 +101,14 @@ namespace SixLabors.ImageSharp.Tests.Memory.Allocators } [Theory] - [MemberData(nameof(LenthValues))] + [MemberData(nameof(LengthValues))] public void CanAllocateCleanBuffer_double(int desiredLength) { this.TestCanAllocateCleanBuffer(desiredLength); } [Theory] - [MemberData(nameof(LenthValues))] + [MemberData(nameof(LengthValues))] public void CanAllocateCleanBuffer_CustomStruct(int desiredLength) { this.TestCanAllocateCleanBuffer(desiredLength); @@ -145,14 +145,14 @@ namespace SixLabors.ImageSharp.Tests.Memory.Allocators } [Theory] - [MemberData(nameof(LenthValues))] + [MemberData(nameof(LengthValues))] public void SpanPropertyIsAlwaysTheSame_int(int desiredLength) { this.TestSpanPropertyIsAlwaysTheSame(desiredLength); } [Theory] - [MemberData(nameof(LenthValues))] + [MemberData(nameof(LengthValues))] public void SpanPropertyIsAlwaysTheSame_byte(int desiredLength) { this.TestSpanPropertyIsAlwaysTheSame(desiredLength, false); @@ -174,18 +174,18 @@ namespace SixLabors.ImageSharp.Tests.Memory.Allocators } [Theory] - [MemberData(nameof(LenthValues))] + [MemberData(nameof(LengthValues))] public void WriteAndReadElements_float(int desiredLength) { - this.TestWriteAndReadElements(desiredLength, x => x * 1.2f); + this.TestWriteAndReadElements(desiredLength, x => x * 1.2f); } [Theory] - [MemberData(nameof(LenthValues))] + [MemberData(nameof(LengthValues))] public void WriteAndReadElements_byte(int desiredLength) { - this.TestWriteAndReadElements(desiredLength, x => (byte)(x + 1), false); - this.TestWriteAndReadElements(desiredLength, x => (byte)(x + 1), true); + this.TestWriteAndReadElements(desiredLength, x => (byte)(x + 1), false); + this.TestWriteAndReadElements(desiredLength, x => (byte)(x + 1), true); } private void TestWriteAndReadElements(int desiredLength, Func getExpectedValue, bool testManagedByteBuffer = false) @@ -193,7 +193,7 @@ namespace SixLabors.ImageSharp.Tests.Memory.Allocators { using (IMemoryOwner buffer = this.Allocate(desiredLength, AllocationOptions.None, testManagedByteBuffer)) { - T[] expectedVals = new T[buffer.Length()]; + var expectedVals = new T[buffer.Length()]; for (int i = 0; i < buffer.Length(); i++) { @@ -211,7 +211,7 @@ namespace SixLabors.ImageSharp.Tests.Memory.Allocators } [Theory] - [MemberData(nameof(LenthValues))] + [MemberData(nameof(LengthValues))] public void IndexingSpan_WhenOutOfRange_Throws_byte(int desiredLength) { this.TestIndexOutOfRangeShouldThrow(desiredLength, false); @@ -219,14 +219,14 @@ namespace SixLabors.ImageSharp.Tests.Memory.Allocators } [Theory] - [MemberData(nameof(LenthValues))] + [MemberData(nameof(LengthValues))] public void IndexingSpan_WhenOutOfRange_Throws_long(int desiredLength) { this.TestIndexOutOfRangeShouldThrow(desiredLength); } [Theory] - [MemberData(nameof(LenthValues))] + [MemberData(nameof(LengthValues))] public void IndexingSpan_WhenOutOfRange_Throws_CustomStruct(int desiredLength) { this.TestIndexOutOfRangeShouldThrow(desiredLength); @@ -316,4 +316,4 @@ namespace SixLabors.ImageSharp.Tests.Memory.Allocators } } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs index cc7f32bef..a2688359f 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs @@ -110,7 +110,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations [Fact] public void PixelTypeInfoHasCorrectBitsPerPixel() { - var bits = this.Operations.GetPixelTypeInfo().BitsPerPixel; + int bits = this.Operations.GetPixelTypeInfo().BitsPerPixel; Assert.Equal(Unsafe.SizeOf() * 8, bits); } diff --git a/tests/ImageSharp.Tests/Processing/BaseImageOperationsExtensionTest.cs b/tests/ImageSharp.Tests/Processing/BaseImageOperationsExtensionTest.cs index e983577a2..d144c876f 100644 --- a/tests/ImageSharp.Tests/Processing/BaseImageOperationsExtensionTest.cs +++ b/tests/ImageSharp.Tests/Processing/BaseImageOperationsExtensionTest.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; @@ -9,7 +10,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing { - public abstract class BaseImageOperationsExtensionTest + public abstract class BaseImageOperationsExtensionTest : IDisposable { protected readonly IImageProcessingContext operations; private readonly FakeImageOperationsProvider.FakeImageOperations internalOperations; @@ -58,5 +59,7 @@ namespace SixLabors.ImageSharp.Tests.Processing return Assert.IsType(operation.GenericProcessor); } + + public void Dispose() => this.source?.Dispose(); } } diff --git a/tests/ImageSharp.Tests/Processing/ImageOperationTests.cs b/tests/ImageSharp.Tests/Processing/ImageOperationTests.cs index cd0a65ad5..d85495b92 100644 --- a/tests/ImageSharp.Tests/Processing/ImageOperationTests.cs +++ b/tests/ImageSharp.Tests/Processing/ImageOperationTests.cs @@ -171,7 +171,7 @@ namespace SixLabors.ImageSharp.Tests.Processing private static void CheckThrowsCorrectObjectDisposedException(Action action) { - var ex = Assert.Throws(action); + ObjectDisposedException ex = Assert.Throws(action); Assert.Equal(ExpectedExceptionMessage, ex.Message); } } diff --git a/tests/ImageSharp.Tests/Processing/ImageProcessingContextTests.cs b/tests/ImageSharp.Tests/Processing/ImageProcessingContextTests.cs index c206938a2..220bd5f05 100644 --- a/tests/ImageSharp.Tests/Processing/ImageProcessingContextTests.cs +++ b/tests/ImageSharp.Tests/Processing/ImageProcessingContextTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; using Moq; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; @@ -12,7 +13,7 @@ namespace SixLabors.ImageSharp.Tests.Processing /// /// Contains test cases for default implementation. /// - public class ImageProcessingContextTests + public class ImageProcessingContextTests : IDisposable { private readonly Image image = new Image(10, 10); @@ -195,5 +196,7 @@ namespace SixLabors.ImageSharp.Tests.Processing .Setup(p => p.CreatePixelSpecificProcessor(Configuration.Default, It.IsAny>(), It.IsAny())) .Returns(this.cloningProcessorImpl.Object); } + + public void Dispose() => this.image?.Dispose(); } } diff --git a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs index e64ae74c6..ab3a1d760 100644 --- a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs +++ b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs @@ -9,8 +9,8 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Normalization { - [Trait("Category", "Processors")] // ReSharper disable InconsistentNaming + [Trait("Category", "Processors")] public class HistogramEqualizationTests { private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.0456F); @@ -18,10 +18,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization [Theory] [InlineData(256)] [InlineData(65536)] - public void GlobalHistogramEqualization_WithDifferentLumanceLevels(int luminanceLevels) + public void GlobalHistogramEqualization_WithDifferentLuminanceLevels(int luminanceLevels) { // Arrange - var pixels = new byte[] + byte[] pixels = { 52, 55, 61, 59, 70, 61, 76, 61, 62, 59, 55, 104, 94, 85, 59, 71, @@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization } } - var expected = new byte[] + byte[] expected = { 0, 12, 53, 32, 146, 53, 174, 53, 57, 32, 12, 227, 219, 202, 32, 154, @@ -150,13 +150,13 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization using (Image image = provider.GetImage()) { var options = new HistogramEqualizationOptions() - { - Method = HistogramEqualizationMethod.AdaptiveTileInterpolation, - LuminanceLevels = 256, - ClipHistogram = true, - ClipLimit = 5, - NumberOfTiles = 10 - }; + { + Method = HistogramEqualizationMethod.AdaptiveTileInterpolation, + LuminanceLevels = 256, + ClipHistogram = true, + ClipLimit = 5, + NumberOfTiles = 10 + }; image.Mutate(x => x.HistogramEqualization(options)); image.DebugSave(provider); image.CompareToReferenceOutput(ValidatorComparer, provider); diff --git a/tests/ImageSharp.Tests/TestFormat.cs b/tests/ImageSharp.Tests/TestFormat.cs index 7273a65f7..c8d0633d7 100644 --- a/tests/ImageSharp.Tests/TestFormat.cs +++ b/tests/ImageSharp.Tests/TestFormat.cs @@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.Tests public TestDecoder Decoder { get; } - private byte[] header = Guid.NewGuid().ToByteArray(); + private readonly byte[] header = Guid.NewGuid().ToByteArray(); public MemoryStream CreateStream(byte[] marker = null) { @@ -119,16 +119,16 @@ namespace SixLabors.ImageSharp.Tests public IEnumerable FileExtensions => this.SupportedExtensions; - public bool IsSupportedFileFormat(ReadOnlySpan header) + public bool IsSupportedFileFormat(ReadOnlySpan fileHeader) { - if (header.Length < this.header.Length) + if (fileHeader.Length < this.header.Length) { return false; } for (int i = 0; i < this.header.Length; i++) { - if (header[i] != this.header[i]) + if (fileHeader[i] != this.header[i]) { return false; } @@ -137,11 +137,11 @@ namespace SixLabors.ImageSharp.Tests return true; } - public void Configure(Configuration host) + public void Configure(Configuration configuration) { - host.ImageFormatsManager.AddImageFormatDetector(new TestHeader(this)); - host.ImageFormatsManager.SetEncoder(this, new TestEncoder(this)); - host.ImageFormatsManager.SetDecoder(this, new TestDecoder(this)); + configuration.ImageFormatsManager.AddImageFormatDetector(new TestHeader(this)); + configuration.ImageFormatsManager.SetEncoder(this, new TestEncoder(this)); + configuration.ImageFormatsManager.SetDecoder(this, new TestDecoder(this)); } public struct DecodeOperation @@ -177,7 +177,7 @@ namespace SixLabors.ImageSharp.Tests public class TestHeader : IImageFormatDetector { - private TestFormat testFormat; + private readonly TestFormat testFormat; public int HeaderSize => this.testFormat.HeaderSize; @@ -199,7 +199,7 @@ namespace SixLabors.ImageSharp.Tests public class TestDecoder : IImageDecoder, IImageInfoDetector { - private TestFormat testFormat; + private readonly TestFormat testFormat; public TestDecoder(TestFormat testFormat) { @@ -212,20 +212,20 @@ namespace SixLabors.ImageSharp.Tests public int HeaderSize => this.testFormat.HeaderSize; - public Image Decode(Configuration config, Stream stream) + public Image Decode(Configuration configuration, Stream stream) where TPixel : unmanaged, IPixel - => this.DecodeImpl(config, stream, default).GetAwaiter().GetResult(); + => this.DecodeImpl(configuration, stream, default).GetAwaiter().GetResult(); - public Task> DecodeAsync(Configuration config, Stream stream, CancellationToken cancellationToken) + public Task> DecodeAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel - => this.DecodeImpl(config, stream, cancellationToken); + => this.DecodeImpl(configuration, stream, cancellationToken); private async Task> DecodeImpl(Configuration config, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { var ms = new MemoryStream(); await stream.CopyToAsync(ms, config.StreamProcessingBufferSize, cancellationToken); - var marker = ms.ToArray().Skip(this.testFormat.header.Length).ToArray(); + byte[] marker = ms.ToArray().Skip(this.testFormat.header.Length).ToArray(); this.testFormat.DecodeCalls.Add(new DecodeOperation { Marker = marker, @@ -251,9 +251,9 @@ namespace SixLabors.ImageSharp.Tests => await this.DecodeImpl(configuration, stream, cancellationToken); } - public class TestEncoder : ImageSharp.Formats.IImageEncoder + public class TestEncoder : IImageEncoder { - private TestFormat testFormat; + private readonly TestFormat testFormat; public TestEncoder(TestFormat testFormat) { diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/ImageDataAttributeBase.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/ImageDataAttributeBase.cs index 0cf76a389..12db71e66 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/ImageDataAttributeBase.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/ImageDataAttributeBase.cs @@ -73,7 +73,7 @@ namespace SixLabors.ImageSharp.Tests if (!addedRows.Any()) { - addedRows = new[] { new object[0] }; + addedRows = new[] { Array.Empty() }; } bool firstIsProvider = this.FirstIsProvider(testMethod); diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/SemaphoreReadMemoryStreamTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/SemaphoreReadMemoryStreamTests.cs index 87f8cb8c1..92f972941 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/SemaphoreReadMemoryStreamTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/SemaphoreReadMemoryStreamTests.cs @@ -1,13 +1,12 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System.IO; using System.Threading; using System.Threading.Tasks; -using SixLabors.ImageSharp.Tests.TestUtilities; using Xunit; -namespace SixLabors.ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests { public class SemaphoreReadMemoryStreamTests { From 86adfa588dfd7321291b547cf155fa53007a1ee8 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 26 May 2021 15:48:52 +0200 Subject: [PATCH 256/275] Add missing usings --- .../Formats/Jpg/JpegDecoderTests.cs | 2 +- .../ImageFrameCollectionTests.Generic.cs | 82 +++++++++++-------- .../ImageSharp.Tests/Image/ImageTests.Save.cs | 15 ++-- .../Image/ImageTests.SaveAsync.cs | 16 ++-- .../DataWriter/IccDataWriterCurvesTests.cs | 14 ++-- .../ICC/DataWriter/IccDataWriterLutTests.cs | 14 ++-- .../ICC/DataWriter/IccDataWriterLutTests1.cs | 14 ++-- .../ICC/DataWriter/IccDataWriterLutTests2.cs | 14 ++-- .../DataWriter/IccDataWriterMatrixTests.cs | 12 +-- .../IccDataWriterMultiProcessElementTests.cs | 10 +-- .../IccDataWriterNonPrimitivesTests.cs | 20 ++--- .../IccDataWriterPrimitivesTests.cs | 20 ++--- .../IccDataWriterTagDataEntryTests.cs | 68 +++++++-------- .../ICC/DataWriter/IccDataWriterTests.cs | 18 ++-- .../ImageProviders/TestPatternProvider.cs | 3 +- .../TestUtilities/TestUtils.cs | 3 +- 16 files changed, 167 insertions(+), 158 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index fe57b3840..27d70fd18 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -117,7 +117,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [Theory] [WithFile(TestImages.Jpeg.Baseline.Floorplan, PixelTypes.Rgba32)] [WithFile(TestImages.Jpeg.Progressive.Festzug, PixelTypes.Rgba32)] - public async Task DecodeAsnc_DegenerateMemoryRequest_ShouldTranslateTo_ImageFormatException(TestImageProvider provider) + public async Task DecodeAsync_DegenerateMemoryRequest_ShouldTranslateTo_ImageFormatException(TestImageProvider provider) where TPixel : unmanaged, IPixel { provider.LimitAllocatorBufferCapacity().InBytesSqrt(10); diff --git a/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs index 14f6ed8df..a00f190db 100644 --- a/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs +++ b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs @@ -28,7 +28,8 @@ namespace SixLabors.ImageSharp.Tests ArgumentException ex = Assert.Throws( () => { - this.Collection.AddFrame(new ImageFrame(Configuration.Default, 1, 1)); + using var frame = new ImageFrame(Configuration.Default, 1, 1); + using ImageFrame addedFrame = this.Collection.AddFrame(frame); }); Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message); @@ -40,7 +41,7 @@ namespace SixLabors.ImageSharp.Tests ArgumentNullException ex = Assert.Throws( () => { - this.Collection.AddFrame((ImageFrame)null); + using ImageFrame addedFrame = this.Collection.AddFrame((ImageFrame)null); }); Assert.StartsWith("Parameter \"frame\" must be not null.", ex.Message); @@ -54,7 +55,7 @@ namespace SixLabors.ImageSharp.Tests ArgumentNullException ex = Assert.Throws( () => { - this.Collection.AddFrame(data); + using ImageFrame addedFrame = this.Collection.AddFrame(data); }); Assert.StartsWith("Parameter \"source\" must be not null.", ex.Message); @@ -66,7 +67,7 @@ namespace SixLabors.ImageSharp.Tests ArgumentOutOfRangeException ex = Assert.Throws( () => { - this.Collection.AddFrame(Array.Empty()); + using ImageFrame addedFrame = this.Collection.AddFrame(Array.Empty()); }); Assert.StartsWith($"Parameter \"data\" ({typeof(int)}) must be greater than or equal to {100}, was {0}", ex.Message); @@ -78,7 +79,8 @@ namespace SixLabors.ImageSharp.Tests ArgumentException ex = Assert.Throws( () => { - this.Collection.InsertFrame(1, new ImageFrame(Configuration.Default, 1, 1)); + using var frame = new ImageFrame(Configuration.Default, 1, 1); + using ImageFrame insertedFrame = this.Collection.InsertFrame(1, frame); }); Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message); @@ -90,7 +92,7 @@ namespace SixLabors.ImageSharp.Tests ArgumentNullException ex = Assert.Throws( () => { - this.Collection.InsertFrame(1, null); + using ImageFrame insertedFrame = this.Collection.InsertFrame(1, null); }); Assert.StartsWith("Parameter \"frame\" must be not null.", ex.Message); @@ -102,9 +104,11 @@ namespace SixLabors.ImageSharp.Tests ArgumentException ex = Assert.Throws( () => { + using var imageFrame1 = new ImageFrame(Configuration.Default, 10, 10); + using var imageFrame2 = new ImageFrame(Configuration.Default, 1, 1); new ImageFrameCollection( this.Image, - new[] { new ImageFrame(Configuration.Default, 10, 10), new ImageFrame(Configuration.Default, 1, 1) }); + new[] { imageFrame1, imageFrame2 }); }); Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message); @@ -113,24 +117,24 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void RemoveAtFrame_ThrowIfRemovingLastFrame() { + using var imageFrame = new ImageFrame(Configuration.Default, 10, 10); var collection = new ImageFrameCollection( this.Image, - new[] { new ImageFrame(Configuration.Default, 10, 10) }); + new[] { imageFrame }); InvalidOperationException ex = Assert.Throws( - () => - { - collection.RemoveFrame(0); - }); + () => collection.RemoveFrame(0)); Assert.Equal("Cannot remove last frame.", ex.Message); } [Fact] public void RemoveAtFrame_CanRemoveFrameZeroIfMultipleFramesExist() { + using var imageFrame1 = new ImageFrame(Configuration.Default, 10, 10); + using var imageFrame2 = new ImageFrame(Configuration.Default, 10, 10); var collection = new ImageFrameCollection( this.Image, - new[] { new ImageFrame(Configuration.Default, 10, 10), new ImageFrame(Configuration.Default, 10, 10) }); + new[] { imageFrame1, imageFrame2 }); collection.RemoveFrame(0); Assert.Equal(1, collection.Count); @@ -139,9 +143,11 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void RootFrameIsFrameAtIndexZero() { + using var imageFrame1 = new ImageFrame(Configuration.Default, 10, 10); + using var imageFrame2 = new ImageFrame(Configuration.Default, 10, 10); var collection = new ImageFrameCollection( this.Image, - new[] { new ImageFrame(Configuration.Default, 10, 10), new ImageFrame(Configuration.Default, 10, 10) }); + new[] { imageFrame1, imageFrame2 }); Assert.Equal(collection.RootFrame, collection[0]); } @@ -149,9 +155,11 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void ConstructorPopulatesFrames() { + using var imageFrame1 = new ImageFrame(Configuration.Default, 10, 10); + using var imageFrame2 = new ImageFrame(Configuration.Default, 10, 10); var collection = new ImageFrameCollection( this.Image, - new[] { new ImageFrame(Configuration.Default, 10, 10), new ImageFrame(Configuration.Default, 10, 10) }); + new[] { imageFrame1, imageFrame2 }); Assert.Equal(2, collection.Count); } @@ -159,9 +167,11 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void DisposeClearsCollection() { + using var imageFrame1 = new ImageFrame(Configuration.Default, 10, 10); + using var imageFrame2 = new ImageFrame(Configuration.Default, 10, 10); var collection = new ImageFrameCollection( this.Image, - new[] { new ImageFrame(Configuration.Default, 10, 10), new ImageFrame(Configuration.Default, 10, 10) }); + new[] { imageFrame1, imageFrame2 }); collection.Dispose(); @@ -171,9 +181,11 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void Dispose_DisposesAllInnerFrames() { + using var imageFrame1 = new ImageFrame(Configuration.Default, 10, 10); + using var imageFrame2 = new ImageFrame(Configuration.Default, 10, 10); var collection = new ImageFrameCollection( this.Image, - new[] { new ImageFrame(Configuration.Default, 10, 10), new ImageFrame(Configuration.Default, 10, 10) }); + new[] { imageFrame1, imageFrame2 }); IPixelSource[] framesSnapShot = collection.OfType>().ToArray(); collection.Dispose(); @@ -194,7 +206,8 @@ namespace SixLabors.ImageSharp.Tests { using (Image img = provider.GetImage()) { - img.Frames.AddFrame(new ImageFrame(Configuration.Default, 10, 10)); // add a frame anyway + using var imageFrame = new ImageFrame(Configuration.Default, 10, 10); + using ImageFrame addedFrame = img.Frames.AddFrame(imageFrame); // add a frame anyway using (Image cloned = img.Frames.CloneFrame(0)) { Assert.Equal(2, img.Frames.Count); @@ -215,7 +228,8 @@ namespace SixLabors.ImageSharp.Tests Assert.True(img.TryGetSinglePixelSpan(out Span imgSpan)); TPixel[] sourcePixelData = imgSpan.ToArray(); - img.Frames.AddFrame(new ImageFrame(Configuration.Default, 10, 10)); + using var imageFrame = new ImageFrame(Configuration.Default, 10, 10); + using ImageFrame addedFrame = img.Frames.AddFrame(imageFrame); using (Image cloned = img.Frames.ExportFrame(0)) { Assert.Equal(1, img.Frames.Count); @@ -227,19 +241,21 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void CreateFrame_Default() { - this.Image.Frames.CreateFrame(); - - Assert.Equal(2, this.Image.Frames.Count); - this.Image.Frames[1].ComparePixelBufferTo(default(Rgba32)); + using (this.Image.Frames.CreateFrame()) + { + Assert.Equal(2, this.Image.Frames.Count); + this.Image.Frames[1].ComparePixelBufferTo(default(Rgba32)); + } } [Fact] public void CreateFrame_CustomFillColor() { - this.Image.Frames.CreateFrame(Color.HotPink); - - Assert.Equal(2, this.Image.Frames.Count); - this.Image.Frames[1].ComparePixelBufferTo(Color.HotPink); + using (this.Image.Frames.CreateFrame(Color.HotPink)) + { + Assert.Equal(2, this.Image.Frames.Count); + this.Image.Frames[1].ComparePixelBufferTo(Color.HotPink); + } } [Fact] @@ -247,15 +263,15 @@ namespace SixLabors.ImageSharp.Tests { Assert.True(this.Image.Frames.RootFrame.TryGetSinglePixelSpan(out Span imgSpan)); Rgba32[] pixelData = imgSpan.ToArray(); - this.Image.Frames.AddFrame(pixelData); + using ImageFrame addedFrame = this.Image.Frames.AddFrame(pixelData); Assert.Equal(2, this.Image.Frames.Count); } [Fact] public void AddFrame_clones_sourceFrame() { - var otherFrame = new ImageFrame(Configuration.Default, 10, 10); - ImageFrame addedFrame = this.Image.Frames.AddFrame(otherFrame); + using var otherFrame = new ImageFrame(Configuration.Default, 10, 10); + using ImageFrame addedFrame = this.Image.Frames.AddFrame(otherFrame); Assert.True(otherFrame.TryGetSinglePixelSpan(out Span otherFrameSpan)); addedFrame.ComparePixelBufferTo(otherFrameSpan); @@ -265,8 +281,8 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void InsertFrame_clones_sourceFrame() { - var otherFrame = new ImageFrame(Configuration.Default, 10, 10); - ImageFrame addedFrame = this.Image.Frames.InsertFrame(0, otherFrame); + using var otherFrame = new ImageFrame(Configuration.Default, 10, 10); + using ImageFrame addedFrame = this.Image.Frames.InsertFrame(0, otherFrame); Assert.True(otherFrame.TryGetSinglePixelSpan(out Span otherFrameSpan)); addedFrame.ComparePixelBufferTo(otherFrameSpan); @@ -320,7 +336,7 @@ namespace SixLabors.ImageSharp.Tests this.Image.Frames.CreateFrame(); } - var frame = new ImageFrame(Configuration.Default, 10, 10); + using var frame = new ImageFrame(Configuration.Default, 10, 10); Assert.False(this.Image.Frames.Contains(frame)); } } diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Save.cs b/tests/ImageSharp.Tests/Image/ImageTests.Save.cs index ee46807e5..fe3df1721 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.Save.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.Save.cs @@ -3,9 +3,8 @@ using System; using System.IO; - using Moq; - +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.PixelFormats; using Xunit; @@ -13,8 +12,6 @@ using Xunit; // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests { - using SixLabors.ImageSharp.Formats; - public partial class ImageTests { public class Save @@ -23,7 +20,7 @@ namespace SixLabors.ImageSharp.Tests public void DetectedEncoding() { string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageTests)); - string file = System.IO.Path.Combine(dir, "DetectedEncoding.png"); + string file = Path.Combine(dir, "DetectedEncoding.png"); using (var image = new Image(10, 10)) { @@ -40,7 +37,7 @@ namespace SixLabors.ImageSharp.Tests public void WhenExtensionIsUnknown_Throws() { string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageTests)); - string file = System.IO.Path.Combine(dir, "UnknownExtensionsEncoding_Throws.tmp"); + string file = Path.Combine(dir, "UnknownExtensionsEncoding_Throws.tmp"); Assert.Throws( () => @@ -56,14 +53,14 @@ namespace SixLabors.ImageSharp.Tests public void SetEncoding() { string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageTests)); - string file = System.IO.Path.Combine(dir, "SetEncoding.dat"); + string file = Path.Combine(dir, "SetEncoding.dat"); using (var image = new Image(10, 10)) { image.Save(file, new PngEncoder()); } - using (Image.Load(file, out var mime)) + using (Image.Load(file, out IImageFormat mime)) { Assert.Equal("image/png", mime.DefaultMimeType); } @@ -72,7 +69,7 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void ThrowsWhenDisposed() { - var image = new Image(5, 5); + using var image = new Image(5, 5); image.Dispose(); IImageEncoder encoder = Mock.Of(); using (var stream = new MemoryStream()) diff --git a/tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs b/tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs index 4e6b002d0..825bd55e4 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs @@ -4,20 +4,18 @@ using System; using System.IO; using System.Threading; +using System.Threading.Tasks; using Moq; -using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities; using Xunit; // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests { - using System.Threading.Tasks; - using SixLabors.ImageSharp.Advanced; - using SixLabors.ImageSharp.Formats; - using SixLabors.ImageSharp.Tests.TestUtilities; - public partial class ImageTests { public class SaveAsync @@ -43,7 +41,7 @@ namespace SixLabors.ImageSharp.Tests public async Task WhenExtensionIsUnknown_Throws() { string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageTests)); - string file = System.IO.Path.Combine(dir, "UnknownExtensionsEncoding_Throws.tmp"); + string file = Path.Combine(dir, "UnknownExtensionsEncoding_Throws.tmp"); await Assert.ThrowsAsync( async () => @@ -59,14 +57,14 @@ namespace SixLabors.ImageSharp.Tests public async Task SetEncoding() { string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageTests)); - string file = System.IO.Path.Combine(dir, "SetEncoding.dat"); + string file = Path.Combine(dir, "SetEncoding.dat"); using (var image = new Image(10, 10)) { await image.SaveAsync(file, new PngEncoder()); } - using (Image.Load(file, out var mime)) + using (Image.Load(file, out IImageFormat mime)) { Assert.Equal("image/png", mime.DefaultMimeType); } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterCurvesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterCurvesTests.cs index 9cd50064c..3bb2ebc41 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterCurvesTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterCurvesTests.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataCurves.OneDimensionalCurveTestData), MemberType = typeof(IccTestDataCurves))] internal void WriteOneDimensionalCurve(byte[] expected, IccOneDimensionalCurve data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteOneDimensionalCurve(data); byte[] output = writer.GetData(); @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataCurves.ResponseCurveTestData), MemberType = typeof(IccTestDataCurves))] internal void WriteResponseCurve(byte[] expected, IccResponseCurve data, int channelCount) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteResponseCurve(data); byte[] output = writer.GetData(); @@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataCurves.ParametricCurveTestData), MemberType = typeof(IccTestDataCurves))] internal void WriteParametricCurve(byte[] expected, IccParametricCurve data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteParametricCurve(data); byte[] output = writer.GetData(); @@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataCurves.CurveSegmentTestData), MemberType = typeof(IccTestDataCurves))] internal void WriteCurveSegment(byte[] expected, IccCurveSegment data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteCurveSegment(data); byte[] output = writer.GetData(); @@ -61,7 +61,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataCurves.FormulaCurveSegmentTestData), MemberType = typeof(IccTestDataCurves))] internal void WriteFormulaCurveElement(byte[] expected, IccFormulaCurveElement data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteFormulaCurveElement(data); byte[] output = writer.GetData(); @@ -73,7 +73,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataCurves.SampledCurveSegmentTestData), MemberType = typeof(IccTestDataCurves))] internal void WriteSampledCurveElement(byte[] expected, IccSampledCurveElement data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteSampledCurveElement(data); byte[] output = writer.GetData(); @@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter Assert.Equal(expected, output); } - private IccDataWriter CreateWriter() + private static IccDataWriter CreateWriter() { return new IccDataWriter(); } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests.cs index bca454818..23ea921ae 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataLut.ClutTestData), MemberType = typeof(IccTestDataLut))] internal void WriteClutAll(byte[] expected, IccClut data, int inChannelCount, int outChannelCount, bool isFloat) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteClut(data); byte[] output = writer.GetData(); @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataLut.Clut8TestData), MemberType = typeof(IccTestDataLut))] internal void WriteClut8(byte[] expected, IccClut data, int inChannelCount, int outChannelCount, byte[] gridPointCount) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteClut8(data); byte[] output = writer.GetData(); @@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataLut.Clut16TestData), MemberType = typeof(IccTestDataLut))] internal void WriteClut16(byte[] expected, IccClut data, int inChannelCount, int outChannelCount, byte[] gridPointCount) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteClut16(data); byte[] output = writer.GetData(); @@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataLut.ClutF32TestData), MemberType = typeof(IccTestDataLut))] internal void WriteClutF32(byte[] expected, IccClut data, int inChannelCount, int outChannelCount, byte[] gridPointCount) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteClutF32(data); byte[] output = writer.GetData(); @@ -61,7 +61,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataLut.Lut8TestData), MemberType = typeof(IccTestDataLut))] internal void WriteLut8(byte[] expected, IccLut data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteLut8(data); byte[] output = writer.GetData(); @@ -73,7 +73,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataLut.Lut16TestData), MemberType = typeof(IccTestDataLut))] internal void WriteLut16(byte[] expected, IccLut data, int count) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteLut16(data); byte[] output = writer.GetData(); @@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter Assert.Equal(expected, output); } - private IccDataWriter CreateWriter() + private static IccDataWriter CreateWriter() { return new IccDataWriter(); } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests1.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests1.cs index cb436c3b8..463804671 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests1.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests1.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataLut.ClutTestData), MemberType = typeof(IccTestDataLut))] internal void WriteClutAll(byte[] expected, IccClut data, int inChannelCount, int outChannelCount, bool isFloat) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteClut(data); byte[] output = writer.GetData(); @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataLut.Clut8TestData), MemberType = typeof(IccTestDataLut))] internal void WriteClut8(byte[] expected, IccClut data, int inChannelCount, int outChannelCount, byte[] gridPointCount) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteClut8(data); byte[] output = writer.GetData(); @@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataLut.Clut16TestData), MemberType = typeof(IccTestDataLut))] internal void WriteClut16(byte[] expected, IccClut data, int inChannelCount, int outChannelCount, byte[] gridPointCount) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteClut16(data); byte[] output = writer.GetData(); @@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataLut.ClutF32TestData), MemberType = typeof(IccTestDataLut))] internal void WriteClutF32(byte[] expected, IccClut data, int inChannelCount, int outChannelCount, byte[] gridPointCount) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteClutF32(data); byte[] output = writer.GetData(); @@ -61,7 +61,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataLut.Lut8TestData), MemberType = typeof(IccTestDataLut))] internal void WriteLut8(byte[] expected, IccLut data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteLut8(data); byte[] output = writer.GetData(); @@ -73,7 +73,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataLut.Lut16TestData), MemberType = typeof(IccTestDataLut))] internal void WriteLut16(byte[] expected, IccLut data, int count) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteLut16(data); byte[] output = writer.GetData(); @@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter Assert.Equal(expected, output); } - private IccDataWriter CreateWriter() + private static IccDataWriter CreateWriter() { return new IccDataWriter(); } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests2.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests2.cs index a17b80819..b81dba24d 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests2.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests2.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataLut.ClutTestData), MemberType = typeof(IccTestDataLut))] internal void WriteClutAll(byte[] expected, IccClut data, int inChannelCount, int outChannelCount, bool isFloat) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteClut(data); byte[] output = writer.GetData(); @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataLut.Clut8TestData), MemberType = typeof(IccTestDataLut))] internal void WriteClut8(byte[] expected, IccClut data, int inChannelCount, int outChannelCount, byte[] gridPointCount) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteClut8(data); byte[] output = writer.GetData(); @@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataLut.Clut16TestData), MemberType = typeof(IccTestDataLut))] internal void WriteClut16(byte[] expected, IccClut data, int inChannelCount, int outChannelCount, byte[] gridPointCount) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteClut16(data); byte[] output = writer.GetData(); @@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataLut.ClutF32TestData), MemberType = typeof(IccTestDataLut))] internal void WriteClutF32(byte[] expected, IccClut data, int inChannelCount, int outChannelCount, byte[] gridPointCount) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteClutF32(data); byte[] output = writer.GetData(); @@ -61,7 +61,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataLut.Lut8TestData), MemberType = typeof(IccTestDataLut))] internal void WriteLut8(byte[] expected, IccLut data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteLut8(data); byte[] output = writer.GetData(); @@ -73,7 +73,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataLut.Lut16TestData), MemberType = typeof(IccTestDataLut))] internal void WriteLut16(byte[] expected, IccLut data, int count) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteLut16(data); byte[] output = writer.GetData(); @@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter Assert.Equal(expected, output); } - private IccDataWriter CreateWriter() + private static IccDataWriter CreateWriter() { return new IccDataWriter(); } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterMatrixTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterMatrixTests.cs index 349f37df0..30e8da2da 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterMatrixTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterMatrixTests.cs @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataMatrix.Matrix2D_FloatArrayTestData), MemberType = typeof(IccTestDataMatrix))] public void WriteMatrix2D_Array(byte[] expected, int xCount, int yCount, bool isSingle, float[,] data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteMatrix(data, isSingle); byte[] output = writer.GetData(); @@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataMatrix.Matrix2D_Matrix4x4TestData), MemberType = typeof(IccTestDataMatrix))] public void WriteMatrix2D_Matrix4x4(byte[] expected, int xCount, int yCount, bool isSingle, Matrix4x4 data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteMatrix(data, isSingle); byte[] output = writer.GetData(); @@ -39,7 +39,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataMatrix.Matrix2D_DenseMatrixTestData), MemberType = typeof(IccTestDataMatrix))] internal void WriteMatrix2D_DenseMatrix(byte[] expected, int xCount, int yCount, bool isSingle, in DenseMatrix data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteMatrix(data, isSingle); byte[] output = writer.GetData(); @@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataMatrix.Matrix1D_ArrayTestData), MemberType = typeof(IccTestDataMatrix))] public void WriteMatrix1D_Array(byte[] expected, int yCount, bool isSingle, float[] data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteMatrix(data, isSingle); byte[] output = writer.GetData(); @@ -63,7 +63,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataMatrix.Matrix1D_Vector3TestData), MemberType = typeof(IccTestDataMatrix))] public void WriteMatrix1D_Vector3(byte[] expected, int yCount, bool isSingle, Vector3 data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteMatrix(data, isSingle); byte[] output = writer.GetData(); @@ -71,7 +71,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter Assert.Equal(expected, output); } - private IccDataWriter CreateWriter() + private static IccDataWriter CreateWriter() { return new IccDataWriter(); } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterMultiProcessElementTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterMultiProcessElementTests.cs index 9cd10190f..78826bb4d 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterMultiProcessElementTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterMultiProcessElementTests.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataMultiProcessElements.MultiProcessElementTestData), MemberType = typeof(IccTestDataMultiProcessElements))] internal void WriteMultiProcessElement(byte[] expected, IccMultiProcessElement data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteMultiProcessElement(data); byte[] output = writer.GetData(); @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataMultiProcessElements.CurveSetTestData), MemberType = typeof(IccTestDataMultiProcessElements))] internal void WriteCurveSetProcessElement(byte[] expected, IccCurveSetProcessElement data, int inChannelCount, int outChannelCount) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteCurveSetProcessElement(data); byte[] output = writer.GetData(); @@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataMultiProcessElements.MatrixTestData), MemberType = typeof(IccTestDataMultiProcessElements))] internal void WriteMatrixProcessElement(byte[] expected, IccMatrixProcessElement data, int inChannelCount, int outChannelCount) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteMatrixProcessElement(data); byte[] output = writer.GetData(); @@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataMultiProcessElements.ClutTestData), MemberType = typeof(IccTestDataMultiProcessElements))] internal void WriteClutProcessElement(byte[] expected, IccClutProcessElement data, int inChannelCount, int outChannelCount) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteClutProcessElement(data); byte[] output = writer.GetData(); @@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter Assert.Equal(expected, output); } - private IccDataWriter CreateWriter() + private static IccDataWriter CreateWriter() { return new IccDataWriter(); } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterNonPrimitivesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterNonPrimitivesTests.cs index d7c1da83a..aa51b149d 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterNonPrimitivesTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterNonPrimitivesTests.cs @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataNonPrimitives.DateTimeTestData), MemberType = typeof(IccTestDataNonPrimitives))] public void WriteDateTime(byte[] expected, DateTime data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteDateTime(data); byte[] output = writer.GetData(); @@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataNonPrimitives.VersionNumberTestData), MemberType = typeof(IccTestDataNonPrimitives))] public void WriteVersionNumber(byte[] expected, IccVersion data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteVersionNumber(data); byte[] output = writer.GetData(); @@ -39,7 +39,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataNonPrimitives.XyzNumberTestData), MemberType = typeof(IccTestDataNonPrimitives))] public void WriteXyzNumber(byte[] expected, Vector3 data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteXyzNumber(data); byte[] output = writer.GetData(); @@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataNonPrimitives.ProfileIdTestData), MemberType = typeof(IccTestDataNonPrimitives))] internal void WriteProfileId(byte[] expected, IccProfileId data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteProfileId(data); byte[] output = writer.GetData(); @@ -63,7 +63,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataNonPrimitives.PositionNumberTestData), MemberType = typeof(IccTestDataNonPrimitives))] internal void WritePositionNumber(byte[] expected, IccPositionNumber data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WritePositionNumber(data); byte[] output = writer.GetData(); @@ -75,7 +75,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataNonPrimitives.ResponseNumberTestData), MemberType = typeof(IccTestDataNonPrimitives))] internal void WriteResponseNumber(byte[] expected, IccResponseNumber data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteResponseNumber(data); byte[] output = writer.GetData(); @@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataNonPrimitives.NamedColorTestData), MemberType = typeof(IccTestDataNonPrimitives))] internal void WriteNamedColor(byte[] expected, IccNamedColor data, uint coordinateCount) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteNamedColor(data); byte[] output = writer.GetData(); @@ -99,7 +99,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataNonPrimitives.ProfileDescriptionWriteTestData), MemberType = typeof(IccTestDataNonPrimitives))] internal void WriteProfileDescription(byte[] expected, IccProfileDescription data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteProfileDescription(data); byte[] output = writer.GetData(); @@ -111,7 +111,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataNonPrimitives.ScreeningChannelTestData), MemberType = typeof(IccTestDataNonPrimitives))] internal void WriteScreeningChannel(byte[] expected, IccScreeningChannel data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteScreeningChannel(data); byte[] output = writer.GetData(); @@ -119,7 +119,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter Assert.Equal(expected, output); } - private IccDataWriter CreateWriter() + private static IccDataWriter CreateWriter() { return new IccDataWriter(); } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterPrimitivesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterPrimitivesTests.cs index a44c1595b..9946d55f9 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterPrimitivesTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterPrimitivesTests.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataPrimitives.AsciiWriteTestData), MemberType = typeof(IccTestDataPrimitives))] public void WriteAsciiString(byte[] expected, string data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteAsciiString(data); byte[] output = writer.GetData(); @@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataPrimitives.AsciiPaddingTestData), MemberType = typeof(IccTestDataPrimitives))] public void WriteAsciiStringPadded(byte[] expected, int length, string data, bool ensureNullTerminator) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteAsciiString(data, length, ensureNullTerminator); byte[] output = writer.GetData(); @@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [Fact] public void WriteAsciiStringWithNullWritesEmpty() { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); int count = writer.WriteAsciiString(null); byte[] output = writer.GetData(); @@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [Fact] public void WriteAsciiStringWithNegativeLengthThrowsArgumentException() { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); Assert.Throws(() => writer.WriteAsciiString("abcd", -1, false)); } @@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [Fact] public void WriteUnicodeStringWithNullWritesEmpty() { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); int count = writer.WriteUnicodeString(null); byte[] output = writer.GetData(); @@ -70,7 +70,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataPrimitives.Fix16TestData), MemberType = typeof(IccTestDataPrimitives))] public void WriteFix16(byte[] expected, float data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteFix16(data); byte[] output = writer.GetData(); @@ -82,7 +82,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataPrimitives.UFix16TestData), MemberType = typeof(IccTestDataPrimitives))] public void WriteUFix16(byte[] expected, float data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteUFix16(data); byte[] output = writer.GetData(); @@ -94,7 +94,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataPrimitives.U1Fix15TestData), MemberType = typeof(IccTestDataPrimitives))] public void WriteU1Fix15(byte[] expected, float data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteU1Fix15(data); byte[] output = writer.GetData(); @@ -106,7 +106,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataPrimitives.UFix8TestData), MemberType = typeof(IccTestDataPrimitives))] public void WriteUFix8(byte[] expected, float data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteUFix8(data); byte[] output = writer.GetData(); @@ -114,7 +114,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter Assert.Equal(expected, output); } - private IccDataWriter CreateWriter() + private static IccDataWriter CreateWriter() { return new IccDataWriter(); } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterTagDataEntryTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterTagDataEntryTests.cs index b5d39aa4d..7fd79994a 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterTagDataEntryTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterTagDataEntryTests.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataTagDataEntry.UnknownTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] internal void WriteUnknownTagDataEntry(byte[] expected, IccUnknownTagDataEntry data, uint size) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteUnknownTagDataEntry(data); byte[] output = writer.GetData(); @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataTagDataEntry.ChromaticityTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] internal void WriteChromaticityTagDataEntry(byte[] expected, IccChromaticityTagDataEntry data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteChromaticityTagDataEntry(data); byte[] output = writer.GetData(); @@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataTagDataEntry.ColorantOrderTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] internal void WriteColorantOrderTagDataEntry(byte[] expected, IccColorantOrderTagDataEntry data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteColorantOrderTagDataEntry(data); byte[] output = writer.GetData(); @@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataTagDataEntry.ColorantTableTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] internal void WriteColorantTableTagDataEntry(byte[] expected, IccColorantTableTagDataEntry data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteColorantTableTagDataEntry(data); byte[] output = writer.GetData(); @@ -61,7 +61,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataTagDataEntry.CurveTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] internal void WriteCurveTagDataEntry(byte[] expected, IccCurveTagDataEntry data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteCurveTagDataEntry(data); byte[] output = writer.GetData(); @@ -73,7 +73,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataTagDataEntry.DataTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] internal void WriteDataTagDataEntry(byte[] expected, IccDataTagDataEntry data, uint size) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteDataTagDataEntry(data); byte[] output = writer.GetData(); @@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataTagDataEntry.DateTimeTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] internal void WriteDateTimeTagDataEntry(byte[] expected, IccDateTimeTagDataEntry data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteDateTimeTagDataEntry(data); byte[] output = writer.GetData(); @@ -97,7 +97,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataTagDataEntry.Lut16TagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] internal void WriteLut16TagDataEntry(byte[] expected, IccLut16TagDataEntry data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteLut16TagDataEntry(data); byte[] output = writer.GetData(); @@ -109,7 +109,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataTagDataEntry.Lut8TagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] internal void WriteLut8TagDataEntry(byte[] expected, IccLut8TagDataEntry data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteLut8TagDataEntry(data); byte[] output = writer.GetData(); @@ -121,7 +121,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataTagDataEntry.LutAToBTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] internal void WriteLutAToBTagDataEntry(byte[] expected, IccLutAToBTagDataEntry data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteLutAtoBTagDataEntry(data); byte[] output = writer.GetData(); @@ -133,7 +133,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataTagDataEntry.LutBToATagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] internal void WriteLutBToATagDataEntry(byte[] expected, IccLutBToATagDataEntry data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteLutBtoATagDataEntry(data); byte[] output = writer.GetData(); @@ -145,7 +145,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataTagDataEntry.MeasurementTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] internal void WriteMeasurementTagDataEntry(byte[] expected, IccMeasurementTagDataEntry data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteMeasurementTagDataEntry(data); byte[] output = writer.GetData(); @@ -157,7 +157,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataTagDataEntry.MultiLocalizedUnicodeTagDataEntryTestData_Write), MemberType = typeof(IccTestDataTagDataEntry))] internal void WriteMultiLocalizedUnicodeTagDataEntry(byte[] expected, IccMultiLocalizedUnicodeTagDataEntry data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteMultiLocalizedUnicodeTagDataEntry(data); byte[] output = writer.GetData(); @@ -169,7 +169,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataTagDataEntry.MultiProcessElementsTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] internal void WriteMultiProcessElementsTagDataEntry(byte[] expected, IccMultiProcessElementsTagDataEntry data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteMultiProcessElementsTagDataEntry(data); byte[] output = writer.GetData(); @@ -181,7 +181,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataTagDataEntry.NamedColor2TagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] internal void WriteNamedColor2TagDataEntry(byte[] expected, IccNamedColor2TagDataEntry data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteNamedColor2TagDataEntry(data); byte[] output = writer.GetData(); @@ -193,7 +193,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataTagDataEntry.ParametricCurveTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] internal void WriteParametricCurveTagDataEntry(byte[] expected, IccParametricCurveTagDataEntry data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteParametricCurveTagDataEntry(data); byte[] output = writer.GetData(); @@ -205,7 +205,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataTagDataEntry.ProfileSequenceDescTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] internal void WriteProfileSequenceDescTagDataEntry(byte[] expected, IccProfileSequenceDescTagDataEntry data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteProfileSequenceDescTagDataEntry(data); byte[] output = writer.GetData(); @@ -217,7 +217,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataTagDataEntry.ProfileSequenceIdentifierTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] internal void WriteProfileSequenceIdentifierTagDataEntry(byte[] expected, IccProfileSequenceIdentifierTagDataEntry data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteProfileSequenceIdentifierTagDataEntry(data); byte[] output = writer.GetData(); @@ -229,7 +229,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataTagDataEntry.ResponseCurveSet16TagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] internal void WriteResponseCurveSet16TagDataEntry(byte[] expected, IccResponseCurveSet16TagDataEntry data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteResponseCurveSet16TagDataEntry(data); byte[] output = writer.GetData(); @@ -241,7 +241,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataTagDataEntry.Fix16ArrayTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] internal void WriteFix16ArrayTagDataEntry(byte[] expected, IccFix16ArrayTagDataEntry data, uint size) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteFix16ArrayTagDataEntry(data); byte[] output = writer.GetData(); @@ -253,7 +253,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataTagDataEntry.SignatureTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] internal void WriteSignatureTagDataEntry(byte[] expected, IccSignatureTagDataEntry data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteSignatureTagDataEntry(data); byte[] output = writer.GetData(); @@ -265,7 +265,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataTagDataEntry.TextTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] internal void WriteTextTagDataEntry(byte[] expected, IccTextTagDataEntry data, uint size) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteTextTagDataEntry(data); byte[] output = writer.GetData(); @@ -277,7 +277,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataTagDataEntry.UFix16ArrayTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] internal void WriteUFix16ArrayTagDataEntry(byte[] expected, IccUFix16ArrayTagDataEntry data, uint size) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteUFix16ArrayTagDataEntry(data); byte[] output = writer.GetData(); @@ -289,7 +289,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataTagDataEntry.UInt16ArrayTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] internal void WriteUInt16ArrayTagDataEntry(byte[] expected, IccUInt16ArrayTagDataEntry data, uint size) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteUInt16ArrayTagDataEntry(data); byte[] output = writer.GetData(); @@ -301,7 +301,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataTagDataEntry.UInt32ArrayTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] internal void WriteUInt32ArrayTagDataEntry(byte[] expected, IccUInt32ArrayTagDataEntry data, uint size) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteUInt32ArrayTagDataEntry(data); byte[] output = writer.GetData(); @@ -313,7 +313,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataTagDataEntry.UInt64ArrayTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] internal void WriteUInt64ArrayTagDataEntry(byte[] expected, IccUInt64ArrayTagDataEntry data, uint size) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteUInt64ArrayTagDataEntry(data); byte[] output = writer.GetData(); @@ -325,7 +325,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataTagDataEntry.UInt8ArrayTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] internal void WriteUInt8ArrayTagDataEntry(byte[] expected, IccUInt8ArrayTagDataEntry data, uint size) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteUInt8ArrayTagDataEntry(data); byte[] output = writer.GetData(); @@ -337,7 +337,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataTagDataEntry.ViewingConditionsTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] internal void WriteViewingConditionsTagDataEntry(byte[] expected, IccViewingConditionsTagDataEntry data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteViewingConditionsTagDataEntry(data); byte[] output = writer.GetData(); @@ -349,7 +349,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataTagDataEntry.XYZTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] internal void WriteXyzTagDataEntry(byte[] expected, IccXyzTagDataEntry data, uint size) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteXyzTagDataEntry(data); byte[] output = writer.GetData(); @@ -361,7 +361,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataTagDataEntry.TextDescriptionTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] internal void WriteTextDescriptionTagDataEntry(byte[] expected, IccTextDescriptionTagDataEntry data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteTextDescriptionTagDataEntry(data); byte[] output = writer.GetData(); @@ -373,7 +373,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataTagDataEntry.CrdInfoTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] internal void WriteCrdInfoTagDataEntry(byte[] expected, IccCrdInfoTagDataEntry data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteCrdInfoTagDataEntry(data); byte[] output = writer.GetData(); @@ -385,7 +385,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataTagDataEntry.ScreeningTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] internal void WriteScreeningTagDataEntry(byte[] expected, IccScreeningTagDataEntry data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteScreeningTagDataEntry(data); byte[] output = writer.GetData(); @@ -397,7 +397,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataTagDataEntry.UcrBgTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] internal void WriteUcrBgTagDataEntry(byte[] expected, IccUcrBgTagDataEntry data, uint size) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteUcrBgTagDataEntry(data); byte[] output = writer.GetData(); @@ -405,7 +405,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter Assert.Equal(expected, output); } - private IccDataWriter CreateWriter() + private static IccDataWriter CreateWriter() { return new IccDataWriter(); } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterTests.cs index 59abe29b7..325eac146 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterTests.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [Fact] public void WriteEmpty() { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteEmpty(4); byte[] output = writer.GetData(); @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [InlineData(4, 4)] public void WritePadding(int writePosition, int expectedLength) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteEmpty(writePosition); writer.WritePadding(); @@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataArray.UInt8TestData), MemberType = typeof(IccTestDataArray))] public void WriteArrayUInt8(byte[] data, byte[] expected) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteArray(data); byte[] output = writer.GetData(); @@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataArray.UInt16TestData), MemberType = typeof(IccTestDataArray))] public void WriteArrayUInt16(byte[] expected, ushort[] data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteArray(data); byte[] output = writer.GetData(); @@ -62,7 +62,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataArray.Int16TestData), MemberType = typeof(IccTestDataArray))] public void WriteArrayInt16(byte[] expected, short[] data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteArray(data); byte[] output = writer.GetData(); @@ -74,7 +74,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataArray.UInt32TestData), MemberType = typeof(IccTestDataArray))] public void WriteArrayUInt32(byte[] expected, uint[] data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteArray(data); byte[] output = writer.GetData(); @@ -86,7 +86,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataArray.Int32TestData), MemberType = typeof(IccTestDataArray))] public void WriteArrayInt32(byte[] expected, int[] data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteArray(data); byte[] output = writer.GetData(); @@ -98,7 +98,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter [MemberData(nameof(IccTestDataArray.UInt64TestData), MemberType = typeof(IccTestDataArray))] public void WriteArrayUInt64(byte[] expected, ulong[] data) { - IccDataWriter writer = this.CreateWriter(); + using IccDataWriter writer = CreateWriter(); writer.WriteArray(data); byte[] output = writer.GetData(); @@ -106,7 +106,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter Assert.Equal(expected, output); } - private IccDataWriter CreateWriter() + private static IccDataWriter CreateWriter() { return new IccDataWriter(); } diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs index f186ed318..7612b663a 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Numerics; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -65,7 +64,7 @@ namespace SixLabors.ImageSharp.Tests /// /// Draws the test pattern on an image by drawing 4 other patterns in the for quadrants of the image. /// - /// The image to rdaw on. + /// The image to draw on. private static void DrawTestPattern(Image image) { // first lets split the image into 4 quadrants diff --git a/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs b/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs index 5f41021a0..32b5eaf18 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs @@ -7,7 +7,6 @@ using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; @@ -354,7 +353,7 @@ namespace SixLabors.ImageSharp.Tests } } - public static string AsInvariantString(this FormattableString formattable) => System.FormattableString.Invariant(formattable); + public static string AsInvariantString(this FormattableString formattable) => FormattableString.Invariant(formattable); public static IResampler GetResampler(string name) { From 31c679998e88aee79b291da9e4837a73a0bceaa8 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 26 May 2021 16:16:24 +0200 Subject: [PATCH 257/275] Use Path.DirectorySeparatorChar --- tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs | 2 +- tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs index a171d6d52..8943d7795 100644 --- a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs +++ b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs @@ -171,7 +171,7 @@ namespace SixLabors.ImageSharp.Tests.Formats using (var image2 = Image.Load(serialized)) { - image2.Save($"{path}/{file.FileName}"); + image2.Save($"{path}{Path.DirectorySeparatorChar}{file.FileName}"); } } } diff --git a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs index 8a038a691..07acabccf 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs @@ -97,7 +97,7 @@ namespace SixLabors.ImageSharp.Tests details = '_' + details; } - return TestUtils.AsInvariantString($"{this.GetTestOutputDir()}/{this.TestName}{pixName}{fn}{details}{extension}"); + return TestUtils.AsInvariantString($"{this.GetTestOutputDir()}{Path.DirectorySeparatorChar}{this.TestName}{pixName}{fn}{details}{extension}"); } /// From a29653f75f1dd84837c3dc831fefe432dc9e44e7 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 27 May 2021 08:12:45 +0200 Subject: [PATCH 258/275] Simplify comparing to expected strings --- .../Formats/Png/PngMetadataTests.cs | 37 +++++++++---------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs index ba6e71935..b4307af5d 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs @@ -90,12 +90,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png using (Image image = provider.GetImage(new PngDecoder())) { PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance); - Assert.DoesNotContain(meta.TextData, m => m.Value.Equals("leading space", System.StringComparison.Ordinal)); - Assert.DoesNotContain(meta.TextData, m => m.Value.Equals("trailing space", System.StringComparison.Ordinal)); - Assert.DoesNotContain(meta.TextData, m => m.Value.Equals("space", System.StringComparison.Ordinal)); - Assert.DoesNotContain(meta.TextData, m => m.Value.Equals("empty", System.StringComparison.Ordinal)); - Assert.DoesNotContain(meta.TextData, m => m.Value.Equals("invalid characters", System.StringComparison.Ordinal)); - Assert.DoesNotContain(meta.TextData, m => m.Value.Equals("too large", System.StringComparison.Ordinal)); + Assert.DoesNotContain(meta.TextData, m => m.Value is "leading space"); + Assert.DoesNotContain(meta.TextData, m => m.Value is "trailing space"); + Assert.DoesNotContain(meta.TextData, m => m.Value is "space"); + Assert.DoesNotContain(meta.TextData, m => m.Value is "empty"); + Assert.DoesNotContain(meta.TextData, m => m.Value is "invalid characters"); + Assert.DoesNotContain(meta.TextData, m => m.Value is "too large"); } } @@ -277,20 +277,17 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png private static void VerifyTextDataIsPresent(PngMetadata meta) { Assert.NotNull(meta); - Assert.Contains(meta.TextData, m => m.Keyword.Equals("Comment", System.StringComparison.Ordinal) && m.Value.Equals("comment", System.StringComparison.Ordinal)); - Assert.Contains(meta.TextData, m => m.Keyword.Equals("Author", System.StringComparison.Ordinal) && m.Value.Equals("ImageSharp", System.StringComparison.Ordinal)); - Assert.Contains(meta.TextData, m => m.Keyword.Equals("Copyright", System.StringComparison.Ordinal) && m.Value.Equals("ImageSharp", System.StringComparison.Ordinal)); - Assert.Contains(meta.TextData, m => m.Keyword.Equals("Title", System.StringComparison.Ordinal) && m.Value.Equals("unittest", System.StringComparison.Ordinal)); - Assert.Contains(meta.TextData, m => m.Keyword.Equals("Description", System.StringComparison.Ordinal) && m.Value.Equals("compressed-text", System.StringComparison.Ordinal)); - Assert.Contains(meta.TextData, m => m.Keyword.Equals("International", System.StringComparison.Ordinal) && m.Value.Equals("'e', mu'tlheghvam, ghaH yu'", System.StringComparison.Ordinal) && - m.LanguageTag.Equals("x-klingon", System.StringComparison.Ordinal) && m.TranslatedKeyword.Equals("warning", System.StringComparison.Ordinal)); - Assert.Contains(meta.TextData, m => m.Keyword.Equals("International2", System.StringComparison.Ordinal) && m.Value.Equals("ИМАГЕШАРП", System.StringComparison.Ordinal) && m.LanguageTag.Equals("rus", System.StringComparison.Ordinal)); - Assert.Contains(meta.TextData, m => m.Keyword.Equals("CompressedInternational", System.StringComparison.Ordinal) && m.Value.Equals("la plume de la mante", System.StringComparison.Ordinal) && - m.LanguageTag.Equals("fra", System.StringComparison.Ordinal) && m.TranslatedKeyword.Equals("foobar", System.StringComparison.Ordinal)); - Assert.Contains(meta.TextData, m => m.Keyword.Equals("CompressedInternational2", System.StringComparison.Ordinal) && m.Value.Equals("這是一個考驗", System.StringComparison.Ordinal) && - m.LanguageTag.Equals("chinese", System.StringComparison.Ordinal)); - Assert.Contains(meta.TextData, m => m.Keyword.Equals("NoLang", System.StringComparison.Ordinal) && m.Value.Equals("this text chunk is missing a language tag", System.StringComparison.Ordinal)); - Assert.Contains(meta.TextData, m => m.Keyword.Equals("NoTranslatedKeyword", System.StringComparison.Ordinal) && m.Value.Equals("dieser chunk hat kein übersetztes Schlüßelwort", System.StringComparison.Ordinal)); + Assert.Contains(meta.TextData, m => m.Keyword is "Comment" && m.Value is "comment"); + Assert.Contains(meta.TextData, m => m.Keyword is "Author" && m.Value is "ImageSharp"); + Assert.Contains(meta.TextData, m => m.Keyword is "Copyright" && m.Value is "ImageSharp"); + Assert.Contains(meta.TextData, m => m.Keyword is "Title" && m.Value is "unittest"); + Assert.Contains(meta.TextData, m => m.Keyword is "Description" && m.Value is "compressed-text"); + Assert.Contains(meta.TextData, m => m.Keyword is "International" && m.Value is "'e', mu'tlheghvam, ghaH yu'" && m.LanguageTag is "x-klingon" && m.TranslatedKeyword is "warning"); + Assert.Contains(meta.TextData, m => m.Keyword is "International2" && m.Value is "ИМАГЕШАРП" && m.LanguageTag is "rus"); + Assert.Contains(meta.TextData, m => m.Keyword is "CompressedInternational" && m.Value is "la plume de la mante" && m.LanguageTag is "fra" && m.TranslatedKeyword is "foobar"); + Assert.Contains(meta.TextData, m => m.Keyword is "CompressedInternational2" && m.Value is "這是一個考驗" && m.LanguageTag is "chinese"); + Assert.Contains(meta.TextData, m => m.Keyword is "NoLang" && m.Value is "this text chunk is missing a language tag"); + Assert.Contains(meta.TextData, m => m.Keyword is "NoTranslatedKeyword" && m.Value is "dieser chunk hat kein übersetztes Schlüßelwort"); } } } From 93f232229f031772a8e8d7d3dcec09250720b73f Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 27 May 2021 14:39:47 +0100 Subject: [PATCH 259/275] Update ImageSharp.Tests.ProfilingSandbox.csproj --- .../ImageSharp.Tests.ProfilingSandbox.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj b/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj index 915947532..fe3b16450 100644 --- a/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj +++ b/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj @@ -13,6 +13,7 @@ false false Debug;Release;Release-InnerLoop;Debug-InnerLoop + false From 5ceba7116cadfb29af12275b57dc9a8997a3cf99 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 28 May 2021 13:42:14 +0100 Subject: [PATCH 260/275] Ensure cdfMinSpan is cleared before use. --- .../AdaptiveHistogramEqualizationProcessor.cs | 9 +--- ...eHistogramEqualizationProcessor{TPixel}.cs | 13 ++++-- .../HistogramEqualizationProcessor.cs | 44 ++++--------------- .../HistogramEqualizationProcessor{TPixel}.cs | 1 + .../HistogramEqualizationTests.cs | 31 +++++++++++++ .../Issue1640_L16_TestPattern5120x9234.png | 3 ++ 6 files changed, 56 insertions(+), 45 deletions(-) create mode 100644 tests/Images/External/ReferenceOutput/HistogramEqualizationTests/Issue1640_L16_TestPattern5120x9234.png diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor.cs index 56593acb8..9b28a8fdd 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor.cs @@ -22,10 +22,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization bool clipHistogram, int clipLimit, int numberOfTiles) - : base(luminanceLevels, clipHistogram, clipLimit) - { - this.NumberOfTiles = numberOfTiles; - } + : base(luminanceLevels, clipHistogram, clipLimit) => this.NumberOfTiles = numberOfTiles; /// /// Gets the number of tiles the image is split into (horizontal and vertically) for the adaptive histogram equalization. @@ -34,8 +31,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// public override IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) - { - return new AdaptiveHistogramEqualizationProcessor( + => new AdaptiveHistogramEqualizationProcessor( configuration, this.LuminanceLevels, this.ClipHistogram, @@ -43,6 +39,5 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization this.NumberOfTiles, source, sourceRectangle); - } } } diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs index 14687426d..317db83b4 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs @@ -459,10 +459,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization private readonly Configuration configuration; private readonly MemoryAllocator memoryAllocator; - // Used for storing the minimum value for each CDF entry. + /// + /// Used for storing the minimum value for each CDF entry. + /// private readonly Buffer2D cdfMinBuffer2D; - // Used for storing the LUT for each CDF entry. + /// + /// Used for storing the LUT for each CDF entry. + /// private readonly Buffer2D cdfLutBuffer2D; private readonly int pixelsInTile; private readonly int sourceWidth; @@ -596,6 +600,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization int y = this.tileYStartPositions[index].y; int endY = Math.Min(y + this.tileHeight, this.sourceHeight); Span cdfMinSpan = this.cdfMinBuffer2D.GetRowSpan(cdfY); + cdfMinSpan.Clear(); using IMemoryOwner histogramBuffer = this.allocator.Allocate(this.luminanceLevels); Span histogram = histogramBuffer.GetSpan(); @@ -614,7 +619,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization for (int dx = x; dx < xlimit; dx++) { int luminance = GetLuminance(rowSpan[dx], this.luminanceLevels); - histogram[luminance]++; + + // This is safe. The index maxes out to the span length. + Unsafe.Add(ref histogramBase, luminance)++; } } diff --git a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs index 60686f401..f93334beb 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs @@ -49,44 +49,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// /// The . /// The . - public static HistogramEqualizationProcessor FromOptions(HistogramEqualizationOptions options) + public static HistogramEqualizationProcessor FromOptions(HistogramEqualizationOptions options) => options.Method switch { - HistogramEqualizationProcessor processor; + HistogramEqualizationMethod.Global + => new GlobalHistogramEqualizationProcessor(options.LuminanceLevels, options.ClipHistogram, options.ClipLimit), - switch (options.Method) - { - case HistogramEqualizationMethod.Global: - processor = new GlobalHistogramEqualizationProcessor( - options.LuminanceLevels, - options.ClipHistogram, - options.ClipLimit); - break; + HistogramEqualizationMethod.AdaptiveTileInterpolation + => new AdaptiveHistogramEqualizationProcessor(options.LuminanceLevels, options.ClipHistogram, options.ClipLimit, options.NumberOfTiles), - case HistogramEqualizationMethod.AdaptiveTileInterpolation: - processor = new AdaptiveHistogramEqualizationProcessor( - options.LuminanceLevels, - options.ClipHistogram, - options.ClipLimit, - options.NumberOfTiles); - break; + HistogramEqualizationMethod.AdaptiveSlidingWindow + => new AdaptiveHistogramEqualizationSlidingWindowProcessor(options.LuminanceLevels, options.ClipHistogram, options.ClipLimit, options.NumberOfTiles), - case HistogramEqualizationMethod.AdaptiveSlidingWindow: - processor = new AdaptiveHistogramEqualizationSlidingWindowProcessor( - options.LuminanceLevels, - options.ClipHistogram, - options.ClipLimit, - options.NumberOfTiles); - break; - - default: - processor = new GlobalHistogramEqualizationProcessor( - options.LuminanceLevels, - options.ClipHistogram, - options.ClipLimit); - break; - } - - return processor; - } + _ => new GlobalHistogramEqualizationProcessor(options.LuminanceLevels, options.ClipHistogram, options.ClipLimit), + }; } } diff --git a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor{TPixel}.cs index 59df3058d..9227cb0c0 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor{TPixel}.cs @@ -142,6 +142,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization [MethodImpl(InliningOptions.ShortMethod)] public static int GetLuminance(TPixel sourcePixel, int luminanceLevels) { + // TODO: We need a bulk per span equivalent. var vector = sourcePixel.ToVector4(); return ColorNumerics.GetBT709Luminance(ref vector, luminanceLevels); } diff --git a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs index ab3a1d760..a24910f9d 100644 --- a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs +++ b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs @@ -141,6 +141,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization /// See: https://github.com/SixLabors/ImageSharp/pull/984 /// /// The pixel type of the image. + /// The test image provider. [Theory] [WithTestPatternImages(110, 110, PixelTypes.Rgb24)] [WithTestPatternImages(170, 170, PixelTypes.Rgb24)] @@ -162,5 +163,35 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization image.CompareToReferenceOutput(ValidatorComparer, provider); } } + + [Theory] + [WithTestPatternImages(5120, 9234, PixelTypes.L16)] + public unsafe void Issue1640(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(); + + // https://github.com/SixLabors/ImageSharp/discussions/1640 + for (int i = 0; i < 2; i++) + { + var options = new HistogramEqualizationOptions + { + Method = HistogramEqualizationMethod.AdaptiveTileInterpolation, + LuminanceLevels = 4096, + ClipHistogram = false, + ClipLimit = 350, + NumberOfTiles = 8 + }; + + Image processed = image.Clone(ctx => + { + ctx.HistogramEqualization(options); + ctx.Resize(image.Width / 4, image.Height / 4, KnownResamplers.Bicubic); + }); + + processed.DebugSave(provider); + processed.CompareToReferenceOutput(ValidatorComparer, provider); + } + } } } diff --git a/tests/Images/External/ReferenceOutput/HistogramEqualizationTests/Issue1640_L16_TestPattern5120x9234.png b/tests/Images/External/ReferenceOutput/HistogramEqualizationTests/Issue1640_L16_TestPattern5120x9234.png new file mode 100644 index 000000000..b3bbcf794 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/HistogramEqualizationTests/Issue1640_L16_TestPattern5120x9234.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2e6bff82eaedcd43932a5bd11d1feeea2143f00ab2ee5fe0654a403bba9ba2de +size 424844 From 0110ff23ef534b3e278e2deb6e7f7ef9656b49c9 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 28 May 2021 13:48:10 +0100 Subject: [PATCH 261/275] 64 bit only. Huge image --- .../Processing/Normalization/HistogramEqualizationTests.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs index a24910f9d..e48504e7b 100644 --- a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs +++ b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs @@ -169,6 +169,11 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization public unsafe void Issue1640(TestImageProvider provider) where TPixel : unmanaged, IPixel { + if (!TestEnvironment.Is64BitProcess) + { + return; + } + using Image image = provider.GetImage(); // https://github.com/SixLabors/ImageSharp/discussions/1640 From 2ce4e2166c7efbd221ed711e73adfe156df91f71 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 28 May 2021 14:39:02 +0100 Subject: [PATCH 262/275] Fix using --- .../Processing/Normalization/HistogramEqualizationTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs index e48504e7b..902b4086a 100644 --- a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs +++ b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs @@ -188,7 +188,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization NumberOfTiles = 8 }; - Image processed = image.Clone(ctx => + using Image processed = image.Clone(ctx => { ctx.HistogramEqualization(options); ctx.Resize(image.Width / 4, image.Height / 4, KnownResamplers.Bicubic); From 8834788490c1ba053840594b618948875e9b4828 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 28 May 2021 14:44:55 +0100 Subject: [PATCH 263/275] Revert unsafe indexer --- .../AdaptiveHistogramEqualizationProcessor{TPixel}.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs index 317db83b4..91ed9f5de 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs @@ -619,9 +619,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization for (int dx = x; dx < xlimit; dx++) { int luminance = GetLuminance(rowSpan[dx], this.luminanceLevels); - - // This is safe. The index maxes out to the span length. - Unsafe.Add(ref histogramBase, luminance)++; + histogram[luminance]++; } } From a8d269f35ad90b8f613e95573a03f3cc96790933 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 28 May 2021 15:20:25 +0100 Subject: [PATCH 264/275] Better test --- .../HistogramEqualizationTests.cs | 41 ++++++++++--------- .../Issue1640_L16_TestPattern5120x9234.png | 3 -- 2 files changed, 22 insertions(+), 22 deletions(-) delete mode 100644 tests/Images/External/ReferenceOutput/HistogramEqualizationTests/Issue1640_L16_TestPattern5120x9234.png diff --git a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs index 902b4086a..85b753024 100644 --- a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs +++ b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs @@ -174,29 +174,32 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization return; } - using Image image = provider.GetImage(); - // https://github.com/SixLabors/ImageSharp/discussions/1640 - for (int i = 0; i < 2; i++) + // Test using isolated memory to ensure clean buffers for reference + provider.Configuration = Configuration.CreateDefaultInstance(); + var options = new HistogramEqualizationOptions { - var options = new HistogramEqualizationOptions - { - Method = HistogramEqualizationMethod.AdaptiveTileInterpolation, - LuminanceLevels = 4096, - ClipHistogram = false, - ClipLimit = 350, - NumberOfTiles = 8 - }; + Method = HistogramEqualizationMethod.AdaptiveTileInterpolation, + LuminanceLevels = 4096, + ClipHistogram = false, + ClipLimit = 350, + NumberOfTiles = 8 + }; - using Image processed = image.Clone(ctx => - { - ctx.HistogramEqualization(options); - ctx.Resize(image.Width / 4, image.Height / 4, KnownResamplers.Bicubic); - }); + using Image image = provider.GetImage(); + using Image referenceResult = image.Clone(ctx => + { + ctx.HistogramEqualization(options); + ctx.Resize(image.Width / 4, image.Height / 4, KnownResamplers.Bicubic); + }); - processed.DebugSave(provider); - processed.CompareToReferenceOutput(ValidatorComparer, provider); - } + using Image processed = image.Clone(ctx => + { + ctx.HistogramEqualization(options); + ctx.Resize(image.Width / 4, image.Height / 4, KnownResamplers.Bicubic); + }); + + ValidatorComparer.VerifySimilarity(referenceResult, processed); } } } diff --git a/tests/Images/External/ReferenceOutput/HistogramEqualizationTests/Issue1640_L16_TestPattern5120x9234.png b/tests/Images/External/ReferenceOutput/HistogramEqualizationTests/Issue1640_L16_TestPattern5120x9234.png deleted file mode 100644 index b3bbcf794..000000000 --- a/tests/Images/External/ReferenceOutput/HistogramEqualizationTests/Issue1640_L16_TestPattern5120x9234.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2e6bff82eaedcd43932a5bd11d1feeea2143f00ab2ee5fe0654a403bba9ba2de -size 424844 From 9886969a305511119cdd31bf12376f652b26339e Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 28 May 2021 16:24:11 +0100 Subject: [PATCH 265/275] Skip flaky test --- tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs index 9a1d423a6..a03ceefaf 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs @@ -309,7 +309,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg Assert.Equal(values.Entries, actual.Entries); } - [Theory] + [Theory(Skip = "TODO: Too Flaky")] [InlineData(JpegSubsample.Ratio420, 0)] [InlineData(JpegSubsample.Ratio420, 3)] [InlineData(JpegSubsample.Ratio420, 10)] From b6f00c1597ee053f28612b5185b7b1218a4f050d Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Fri, 28 May 2021 21:27:20 +0100 Subject: [PATCH 266/275] Stop using timing to test cancellation token compliance. --- .../Formats/Jpg/JpegDecoderTests.cs | 83 +++++----- .../Formats/Jpg/JpegEncoderTests.cs | 39 ++--- .../Image/ImageTests.SaveAsync.cs | 20 ++- .../AsyncLocalSwitchableFilesystem.cs | 51 +++++++ .../TestUtilities/PausedStream.cs | 142 ++++++++++++++++++ 5 files changed, 269 insertions(+), 66 deletions(-) create mode 100644 tests/ImageSharp.Tests/TestUtilities/AsyncLocalSwitchableFilesystem.cs create mode 100644 tests/ImageSharp.Tests/TestUtilities/PausedStream.cs diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 27d70fd18..1b561c20d 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -12,6 +12,7 @@ using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; +using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using Xunit; @@ -126,61 +127,53 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg Assert.IsType(ex.InnerException); } - [Theory] - [InlineData(TestImages.Jpeg.Baseline.Jpeg420Small, 0)] - [InlineData(TestImages.Jpeg.Issues.ExifGetString750Transform, 1)] - [InlineData(TestImages.Jpeg.Issues.ExifGetString750Transform, 15)] - [InlineData(TestImages.Jpeg.Issues.ExifGetString750Transform, 30)] - [InlineData(TestImages.Jpeg.Issues.BadRstProgressive518, 1)] - [InlineData(TestImages.Jpeg.Issues.BadRstProgressive518, 15)] - [InlineData(TestImages.Jpeg.Issues.BadRstProgressive518, 30)] - public async Task Decode_IsCancellable(string fileName, int cancellationDelayMs) + [Fact] + public async Task Decode_IsCancellable() { - // Decoding these huge files took 300ms on i7-8650U in 2020. 30ms should be safe for cancellation delay. - string hugeFile = Path.Combine( - TestEnvironment.InputImagesDirectoryFullPath, - fileName); - - const int numberOfRuns = 5; + var file = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImages.Jpeg.Baseline.Jpeg420Small); + using var pausedStream = new PausedStream(file); + var cts = new CancellationTokenSource(); - for (int i = 0; i < numberOfRuns; i++) + var testTask = Task.Run(async () => { - var cts = new CancellationTokenSource(); - if (cancellationDelayMs == 0) - { - cts.Cancel(); - } - else - { - cts.CancelAfter(cancellationDelayMs); - } + AsyncLocalSwitchableFilesystem.ConfigureFileSystemStream(pausedStream); - try - { - using Image image = await Image.LoadAsync(hugeFile, cts.Token); - } - catch (TaskCanceledException) + return await Assert.ThrowsAsync(async () => { - // Successfully observed a cancellation - return; - } - } + using Image image = await Image.LoadAsync("someFakeFile", cts.Token); + }); + }); + + await pausedStream.FirstWaitReached; + cts.Cancel(); - throw new Exception($"No cancellation happened out of {numberOfRuns} runs!"); + // allow testTask to try and continue now we know we have started but canceled + pausedStream.Release(); + + await testTask; } - [Theory(Skip = "Identify is too fast, doesn't work reliably.")] - [InlineData(TestImages.Jpeg.Baseline.Exif)] - [InlineData(TestImages.Jpeg.Progressive.Bad.ExifUndefType)] - public async Task Identify_IsCancellable(string fileName) + [Fact] + public async Task Identify_IsCancellable() { - string file = Path.Combine( - TestEnvironment.InputImagesDirectoryFullPath, - fileName); - + var file = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImages.Jpeg.Baseline.Jpeg420Small); + using var pausedStream = new PausedStream(file); var cts = new CancellationTokenSource(); - cts.CancelAfter(TimeSpan.FromTicks(1)); - await Assert.ThrowsAsync(() => Image.IdentifyAsync(file, cts.Token)); + + var testTask = Task.Run(async () => + { + AsyncLocalSwitchableFilesystem.ConfigureFileSystemStream(pausedStream); + + return await Assert.ThrowsAsync(async () => await Image.IdentifyAsync("someFakeFile", cts.Token)); + }); + + await pausedStream.FirstWaitReached; + cts.Cancel(); + + // allow testTask to try and continue now we know we have started but canceled + pausedStream.Release(); + + await testTask; } // DEBUG ONLY! diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs index 9a1d423a6..d7e77eabe 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs @@ -13,6 +13,7 @@ using SixLabors.ImageSharp.Metadata.Profiles.Icc; using SixLabors.ImageSharp.Metadata.Profiles.Iptc; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using Xunit; @@ -310,28 +311,30 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg } [Theory] - [InlineData(JpegSubsample.Ratio420, 0)] - [InlineData(JpegSubsample.Ratio420, 3)] - [InlineData(JpegSubsample.Ratio420, 10)] - [InlineData(JpegSubsample.Ratio444, 0)] - [InlineData(JpegSubsample.Ratio444, 3)] - [InlineData(JpegSubsample.Ratio444, 10)] - public async Task Encode_IsCancellable(JpegSubsample subsample, int cancellationDelayMs) + [InlineData(JpegSubsample.Ratio420)] + [InlineData(JpegSubsample.Ratio444)] + public async Task Encode_IsCancellable(JpegSubsample subsample) { - using var image = new Image(5000, 5000); - using var stream = new MemoryStream(); + using var pausedStream = new PausedStream(new MemoryStream()); var cts = new CancellationTokenSource(); - if (cancellationDelayMs == 0) - { - cts.Cancel(); - } - else + + var testTask = Task.Run(async () => { - cts.CancelAfter(cancellationDelayMs); - } + using var image = new Image(5000, 5000); + return await Assert.ThrowsAsync(async () => + { + var encoder = new JpegEncoder() { Subsample = subsample }; + await image.SaveAsync(pausedStream, encoder, cts.Token); + }); + }); + + await pausedStream.FirstWaitReached; + cts.Cancel(); + + // allow testTask to try and continue now we know we have started but canceled + pausedStream.Release(); - var encoder = new JpegEncoder() { Subsample = subsample }; - await Assert.ThrowsAsync(() => image.SaveAsync(stream, encoder, cts.Token)); + await testTask; } } } diff --git a/tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs b/tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs index 825bd55e4..f34b74f89 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs @@ -139,11 +139,25 @@ namespace SixLabors.ImageSharp.Tests var encoder = new PngEncoder() { CompressionLevel = PngCompressionLevel.BestCompression }; using var stream = new MemoryStream(); var asyncStream = new AsyncStreamWrapper(stream, () => false); + var pausedStream = new PausedStream(asyncStream); + var cts = new CancellationTokenSource(); - cts.CancelAfter(TimeSpan.FromTicks(1)); - await Assert.ThrowsAnyAsync(() => - image.SaveAsync(asyncStream, encoder, cts.Token)); + var testTask = Task.Run(async () => + { + AsyncLocalSwitchableFilesystem.ConfigureFileSystemStream(pausedStream); + + using var image = new Image(5000, 5000); + return await Assert.ThrowsAsync(async () => await image.SaveAsync(pausedStream, encoder, cts.Token)); + }); + + await pausedStream.FirstWaitReached; + cts.Cancel(); + + // allow testTask to try and continue now we know we have started but canceled + pausedStream.Release(); + + await testTask; } } } diff --git a/tests/ImageSharp.Tests/TestUtilities/AsyncLocalSwitchableFilesystem.cs b/tests/ImageSharp.Tests/TestUtilities/AsyncLocalSwitchableFilesystem.cs new file mode 100644 index 000000000..d53af7a43 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/AsyncLocalSwitchableFilesystem.cs @@ -0,0 +1,51 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System.IO; +using System.Threading; +using SixLabors.ImageSharp.IO; + +namespace SixLabors.ImageSharp.Tests.TestUtilities +{ + public class AsyncLocalSwitchableFilesystem : IFileSystem + { + private static readonly LocalFileSystem LocalFile = new LocalFileSystem(); + + private static readonly AsyncLocalSwitchableFilesystem Instance = new AsyncLocalSwitchableFilesystem(); + + internal static void ConfigureDefaultFileSystem(IFileSystem fileSystem) + { + Configuration.Default.FileSystem = Instance; + Instance.FileSystem = fileSystem; + } + + internal static void ConfigureFileSystemStream(Stream stream) + { + Configuration.Default.FileSystem = Instance; + Instance.FileSystem = new SingleStreamFileSystem(stream); + } + + private readonly AsyncLocal asyncLocal = new AsyncLocal(); + + private IFileSystem FileSystem + { + get => this.asyncLocal.Value ?? LocalFile; + set => this.asyncLocal.Value = value; + } + + public Stream Create(string path) => this.FileSystem.Create(path); + + public Stream OpenRead(string path) => this.FileSystem.OpenRead(path); + + public class SingleStreamFileSystem : IFileSystem + { + private readonly Stream stream; + + public SingleStreamFileSystem(Stream stream) => this.stream = stream; + + Stream IFileSystem.Create(string path) => this.stream; + + Stream IFileSystem.OpenRead(string path) => this.stream; + } + } +} diff --git a/tests/ImageSharp.Tests/TestUtilities/PausedStream.cs b/tests/ImageSharp.Tests/TestUtilities/PausedStream.cs new file mode 100644 index 000000000..20c4cf0e3 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/PausedStream.cs @@ -0,0 +1,142 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace SixLabors.ImageSharp.Tests.TestUtilities +{ + public class PausedStream : Stream + { + private readonly SemaphoreSlim slim = new SemaphoreSlim(0); + + private readonly CancellationTokenSource cancelationTokenSource = new CancellationTokenSource(); + private readonly TaskCompletionSource waitReached = new TaskCompletionSource(); + + private readonly Stream innerStream; + + public Task FirstWaitReached => this.waitReached.Task; + + public void Release() + { + this.slim.Release(); + this.cancelationTokenSource.Cancel(); + } + + public void Next() => this.slim.Release(); + + private void Wait() + { + this.waitReached.TrySetResult(null); + + if (this.cancelationTokenSource.IsCancellationRequested) + { + return; + } + + try + { + this.slim.Wait(this.cancelationTokenSource.Token); + } + catch (OperationCanceledException) + { + // ignore this as its just used to unlock any waits in progress + } + } + + private async Task Await(Func action) + { + await Task.Yield(); + this.Wait(); + await action(); + } + + private async Task Await(Func> action) + { + await Task.Yield(); + this.Wait(); + return await action(); + } + + private T Await(Func action) + { + this.Wait(); + return action(); + } + + private void Await(Action action) + { + this.Wait(); + action(); + } + + public PausedStream(byte[] data) + : this(new MemoryStream(data)) + { + } + + public PausedStream(string filePath) + : this(File.OpenRead(filePath)) + { + } + + public PausedStream(Stream innerStream) => this.innerStream = innerStream; + + public override bool CanTimeout => this.innerStream.CanTimeout; + + public override void Close() => this.Await(() => this.innerStream.Close()); + + public override void CopyTo(Stream destination, int bufferSize) => this.Await(() => this.innerStream.CopyTo(destination, bufferSize)); + + public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) => this.Await(() => this.innerStream.CopyToAsync(destination, bufferSize, cancellationToken)); + + public override bool CanRead => this.innerStream.CanRead; + + public override bool CanSeek => this.innerStream.CanSeek; + + public override bool CanWrite => this.innerStream.CanWrite; + + public override long Length => this.Await(() => this.innerStream.Length); + + public override long Position { get => this.Await(() => this.innerStream.Position); set => this.Await(() => this.innerStream.Position = value); } + + public override void Flush() => this.Await(() => this.innerStream.Flush()); + + public override int Read(byte[] buffer, int offset, int count) => this.Await(() => this.innerStream.Read(buffer, offset, count)); + + public override long Seek(long offset, SeekOrigin origin) => this.Await(() => this.innerStream.Seek(offset, origin)); + + public override void SetLength(long value) => this.Await(() => this.innerStream.SetLength(value)); + + public override void Write(byte[] buffer, int offset, int count) => this.Await(() => this.innerStream.Write(buffer, offset, count)); + + public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) => this.Await(() => this.innerStream.ReadAsync(buffer, offset, count, cancellationToken)); + + public override int Read(Span buffer) + { + this.Wait(); + return this.innerStream.Read(buffer); + } + + public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) => this.Await(() => this.innerStream.ReadAsync(buffer, cancellationToken)); + + public override void Write(ReadOnlySpan buffer) + { + this.Wait(); + this.innerStream.Write(buffer); + } + + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) => this.Await(() => this.innerStream.WriteAsync(buffer, offset, count, cancellationToken)); + + public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) => this.Await(() => this.innerStream.WriteAsync(buffer, cancellationToken)); + + public override void WriteByte(byte value) => this.Await(() => this.innerStream.WriteByte(value)); + + public override int ReadByte() => this.Await(() => this.innerStream.ReadByte()); + + protected override void Dispose(bool disposing) => this.innerStream.Dispose(); + public override ValueTask DisposeAsync() => this.innerStream.DisposeAsync(); + } +} From 2a8b1da925f13753f181966895702ac413a64e35 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Fri, 28 May 2021 21:32:34 +0100 Subject: [PATCH 267/275] style cop my old foe --- tests/ImageSharp.Tests/TestUtilities/PausedStream.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/ImageSharp.Tests/TestUtilities/PausedStream.cs b/tests/ImageSharp.Tests/TestUtilities/PausedStream.cs index 20c4cf0e3..bba4c61dc 100644 --- a/tests/ImageSharp.Tests/TestUtilities/PausedStream.cs +++ b/tests/ImageSharp.Tests/TestUtilities/PausedStream.cs @@ -137,6 +137,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities public override int ReadByte() => this.Await(() => this.innerStream.ReadByte()); protected override void Dispose(bool disposing) => this.innerStream.Dispose(); + public override ValueTask DisposeAsync() => this.innerStream.DisposeAsync(); } } From 5ccfec917e891e73904f9d541a864ae1ee4f416a Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Fri, 28 May 2021 21:56:58 +0100 Subject: [PATCH 268/275] allow finer grained details logic on when to release the pause. --- .../Formats/Jpg/JpegDecoderTests.cs | 56 +++++++++---------- .../Formats/Jpg/JpegEncoderTests.cs | 33 ++++++----- .../Image/ImageTests.SaveAsync.cs | 19 ++----- .../TestUtilities/PausedStream.cs | 10 ++-- 4 files changed, 57 insertions(+), 61 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 1b561c20d..478b0f3ff 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -127,53 +127,53 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg Assert.IsType(ex.InnerException); } - [Fact] - public async Task Decode_IsCancellable() + [Theory] + [InlineData(0)] + [InlineData(0.5)] + [InlineData(0.9)] + public async Task Decode_IsCancellable(int percentageOfStreamReadToCancel) { + var cts = new CancellationTokenSource(); var file = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImages.Jpeg.Baseline.Jpeg420Small); using var pausedStream = new PausedStream(file); - var cts = new CancellationTokenSource(); - - var testTask = Task.Run(async () => + pausedStream.OnWaiting(s => { - AsyncLocalSwitchableFilesystem.ConfigureFileSystemStream(pausedStream); - - return await Assert.ThrowsAsync(async () => + if (s.Position >= s.Length * percentageOfStreamReadToCancel) { - using Image image = await Image.LoadAsync("someFakeFile", cts.Token); - }); + cts.Cancel(); + pausedStream.Release(); + } + else + { + // allows this/next wait to unblock + pausedStream.Next(); + } }); - await pausedStream.FirstWaitReached; - cts.Cancel(); - - // allow testTask to try and continue now we know we have started but canceled - pausedStream.Release(); + AsyncLocalSwitchableFilesystem.ConfigureFileSystemStream(pausedStream); - await testTask; + await Assert.ThrowsAsync(async () => + { + using Image image = await Image.LoadAsync("someFakeFile", cts.Token); + }); } [Fact] public async Task Identify_IsCancellable() { - var file = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImages.Jpeg.Baseline.Jpeg420Small); - using var pausedStream = new PausedStream(file); var cts = new CancellationTokenSource(); - var testTask = Task.Run(async () => + var file = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImages.Jpeg.Baseline.Jpeg420Small); + using var pausedStream = new PausedStream(file); + pausedStream.OnWaiting(s => { - AsyncLocalSwitchableFilesystem.ConfigureFileSystemStream(pausedStream); - - return await Assert.ThrowsAsync(async () => await Image.IdentifyAsync("someFakeFile", cts.Token)); + cts.Cancel(); + pausedStream.Release(); }); - await pausedStream.FirstWaitReached; - cts.Cancel(); - - // allow testTask to try and continue now we know we have started but canceled - pausedStream.Release(); + AsyncLocalSwitchableFilesystem.ConfigureFileSystemStream(pausedStream); - await testTask; + await Assert.ThrowsAsync(async () => await Image.IdentifyAsync("someFakeFile", cts.Token)); } // DEBUG ONLY! diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs index d7e77eabe..3c48865c7 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs @@ -315,26 +315,29 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [InlineData(JpegSubsample.Ratio444)] public async Task Encode_IsCancellable(JpegSubsample subsample) { - using var pausedStream = new PausedStream(new MemoryStream()); var cts = new CancellationTokenSource(); - - var testTask = Task.Run(async () => + using var pausedStream = new PausedStream(new MemoryStream()); + pausedStream.OnWaiting(s => { - using var image = new Image(5000, 5000); - return await Assert.ThrowsAsync(async () => + // after some writing + if (s.Position >= 500) + { + cts.Cancel(); + pausedStream.Release(); + } + else { - var encoder = new JpegEncoder() { Subsample = subsample }; - await image.SaveAsync(pausedStream, encoder, cts.Token); - }); + // allows this/next wait to unblock + pausedStream.Next(); + } }); - await pausedStream.FirstWaitReached; - cts.Cancel(); - - // allow testTask to try and continue now we know we have started but canceled - pausedStream.Release(); - - await testTask; + using var image = new Image(5000, 5000); + await Assert.ThrowsAsync(async () => + { + var encoder = new JpegEncoder() { Subsample = subsample }; + await image.SaveAsync(pausedStream, encoder, cts.Token); + }); } } } diff --git a/tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs b/tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs index f34b74f89..8bb121349 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs @@ -139,25 +139,16 @@ namespace SixLabors.ImageSharp.Tests var encoder = new PngEncoder() { CompressionLevel = PngCompressionLevel.BestCompression }; using var stream = new MemoryStream(); var asyncStream = new AsyncStreamWrapper(stream, () => false); - var pausedStream = new PausedStream(asyncStream); - var cts = new CancellationTokenSource(); - var testTask = Task.Run(async () => + var pausedStream = new PausedStream(asyncStream); + pausedStream.OnWaiting(s => { - AsyncLocalSwitchableFilesystem.ConfigureFileSystemStream(pausedStream); - - using var image = new Image(5000, 5000); - return await Assert.ThrowsAsync(async () => await image.SaveAsync(pausedStream, encoder, cts.Token)); + cts.Cancel(); + pausedStream.Release(); }); - await pausedStream.FirstWaitReached; - cts.Cancel(); - - // allow testTask to try and continue now we know we have started but canceled - pausedStream.Release(); - - await testTask; + await Assert.ThrowsAsync(async () => await image.SaveAsync(pausedStream, encoder, cts.Token)); } } } diff --git a/tests/ImageSharp.Tests/TestUtilities/PausedStream.cs b/tests/ImageSharp.Tests/TestUtilities/PausedStream.cs index bba4c61dc..c6902b06a 100644 --- a/tests/ImageSharp.Tests/TestUtilities/PausedStream.cs +++ b/tests/ImageSharp.Tests/TestUtilities/PausedStream.cs @@ -13,11 +13,13 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities private readonly SemaphoreSlim slim = new SemaphoreSlim(0); private readonly CancellationTokenSource cancelationTokenSource = new CancellationTokenSource(); - private readonly TaskCompletionSource waitReached = new TaskCompletionSource(); private readonly Stream innerStream; + private Action onWaitingCallback; - public Task FirstWaitReached => this.waitReached.Task; + public void OnWaiting(Action onWaitingCallback) => this.onWaitingCallback = onWaitingCallback; + + public void OnWaiting(Action onWaitingCallback) => this.OnWaiting(_ => onWaitingCallback()); public void Release() { @@ -29,13 +31,13 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities private void Wait() { - this.waitReached.TrySetResult(null); - if (this.cancelationTokenSource.IsCancellationRequested) { return; } + this.onWaitingCallback?.Invoke(this.innerStream); + try { this.slim.Wait(this.cancelationTokenSource.Token); From 0dd0f0f01ca31e725e47c99cab5e1b94efebdfde Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Fri, 28 May 2021 22:09:56 +0100 Subject: [PATCH 269/275] skipping `DisposeAsync()` as it not in netcoreapp2.1 its not used anyway in our code so doesn't matter. --- tests/ImageSharp.Tests/TestUtilities/PausedStream.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/ImageSharp.Tests/TestUtilities/PausedStream.cs b/tests/ImageSharp.Tests/TestUtilities/PausedStream.cs index c6902b06a..5887c533d 100644 --- a/tests/ImageSharp.Tests/TestUtilities/PausedStream.cs +++ b/tests/ImageSharp.Tests/TestUtilities/PausedStream.cs @@ -139,7 +139,5 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities public override int ReadByte() => this.Await(() => this.innerStream.ReadByte()); protected override void Dispose(bool disposing) => this.innerStream.Dispose(); - - public override ValueTask DisposeAsync() => this.innerStream.DisposeAsync(); } } From 654901555d2d79b9d8f18372a296aec23b8638c0 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Fri, 28 May 2021 22:21:35 +0100 Subject: [PATCH 270/275] skip some overrides on full framework --- .../TestUtilities/PausedStream.cs | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/tests/ImageSharp.Tests/TestUtilities/PausedStream.cs b/tests/ImageSharp.Tests/TestUtilities/PausedStream.cs index 5887c533d..b6ab039b1 100644 --- a/tests/ImageSharp.Tests/TestUtilities/PausedStream.cs +++ b/tests/ImageSharp.Tests/TestUtilities/PausedStream.cs @@ -90,8 +90,6 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities public override void Close() => this.Await(() => this.innerStream.Close()); - public override void CopyTo(Stream destination, int bufferSize) => this.Await(() => this.innerStream.CopyTo(destination, bufferSize)); - public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) => this.Await(() => this.innerStream.CopyToAsync(destination, bufferSize, cancellationToken)); public override bool CanRead => this.innerStream.CanRead; @@ -116,6 +114,17 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) => this.Await(() => this.innerStream.ReadAsync(buffer, offset, count, cancellationToken)); + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) => this.Await(() => this.innerStream.WriteAsync(buffer, offset, count, cancellationToken)); + + public override void WriteByte(byte value) => this.Await(() => this.innerStream.WriteByte(value)); + + public override int ReadByte() => this.Await(() => this.innerStream.ReadByte()); + + protected override void Dispose(bool disposing) => this.innerStream.Dispose(); + +#if NETCOREAPP + public override void CopyTo(Stream destination, int bufferSize) => this.Await(() => this.innerStream.CopyTo(destination, bufferSize)); + public override int Read(Span buffer) { this.Wait(); @@ -130,14 +139,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities this.innerStream.Write(buffer); } - public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) => this.Await(() => this.innerStream.WriteAsync(buffer, offset, count, cancellationToken)); - public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) => this.Await(() => this.innerStream.WriteAsync(buffer, cancellationToken)); - - public override void WriteByte(byte value) => this.Await(() => this.innerStream.WriteByte(value)); - - public override int ReadByte() => this.Await(() => this.innerStream.ReadByte()); - - protected override void Dispose(bool disposing) => this.innerStream.Dispose(); +#endif } } From e66e31947c6c50699e5dcce2263f550494c13e43 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Fri, 28 May 2021 22:27:56 +0100 Subject: [PATCH 271/275] rename semaphore --- tests/ImageSharp.Tests/TestUtilities/PausedStream.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/ImageSharp.Tests/TestUtilities/PausedStream.cs b/tests/ImageSharp.Tests/TestUtilities/PausedStream.cs index b6ab039b1..4d3646301 100644 --- a/tests/ImageSharp.Tests/TestUtilities/PausedStream.cs +++ b/tests/ImageSharp.Tests/TestUtilities/PausedStream.cs @@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities { public class PausedStream : Stream { - private readonly SemaphoreSlim slim = new SemaphoreSlim(0); + private readonly SemaphoreSlim semaphore = new SemaphoreSlim(0); private readonly CancellationTokenSource cancelationTokenSource = new CancellationTokenSource(); @@ -23,11 +23,11 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities public void Release() { - this.slim.Release(); + this.semaphore.Release(); this.cancelationTokenSource.Cancel(); } - public void Next() => this.slim.Release(); + public void Next() => this.semaphore.Release(); private void Wait() { @@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities try { - this.slim.Wait(this.cancelationTokenSource.Token); + this.semaphore.Wait(this.cancelationTokenSource.Token); } catch (OperationCanceledException) { From 695cfd3ef70e8a4a224999f39941856dd79a1321 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Fri, 28 May 2021 22:56:30 +0100 Subject: [PATCH 272/275] use overloads taking configuration --- .../Formats/Jpg/JpegDecoderTests.cs | 11 ++-- .../AsyncLocalSwitchableFilesystem.cs | 51 ------------------- .../TestUtilities/SingleStreamFileSystem.cs | 19 +++++++ 3 files changed, 25 insertions(+), 56 deletions(-) delete mode 100644 tests/ImageSharp.Tests/TestUtilities/AsyncLocalSwitchableFilesystem.cs create mode 100644 tests/ImageSharp.Tests/TestUtilities/SingleStreamFileSystem.cs diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 478b0f3ff..67df6a881 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -150,11 +150,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg } }); - AsyncLocalSwitchableFilesystem.ConfigureFileSystemStream(pausedStream); - + var config = Configuration.CreateDefaultInstance(); + config.FileSystem = new SingleStreamFileSystem(pausedStream); await Assert.ThrowsAsync(async () => { - using Image image = await Image.LoadAsync("someFakeFile", cts.Token); + using Image image = await Image.LoadAsync(config, "someFakeFile", cts.Token); }); } @@ -171,9 +171,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg pausedStream.Release(); }); - AsyncLocalSwitchableFilesystem.ConfigureFileSystemStream(pausedStream); + var config = Configuration.CreateDefaultInstance(); + config.FileSystem = new SingleStreamFileSystem(pausedStream); - await Assert.ThrowsAsync(async () => await Image.IdentifyAsync("someFakeFile", cts.Token)); + await Assert.ThrowsAsync(async () => await Image.IdentifyAsync(config, "someFakeFile", cts.Token)); } // DEBUG ONLY! diff --git a/tests/ImageSharp.Tests/TestUtilities/AsyncLocalSwitchableFilesystem.cs b/tests/ImageSharp.Tests/TestUtilities/AsyncLocalSwitchableFilesystem.cs deleted file mode 100644 index d53af7a43..000000000 --- a/tests/ImageSharp.Tests/TestUtilities/AsyncLocalSwitchableFilesystem.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -using System.IO; -using System.Threading; -using SixLabors.ImageSharp.IO; - -namespace SixLabors.ImageSharp.Tests.TestUtilities -{ - public class AsyncLocalSwitchableFilesystem : IFileSystem - { - private static readonly LocalFileSystem LocalFile = new LocalFileSystem(); - - private static readonly AsyncLocalSwitchableFilesystem Instance = new AsyncLocalSwitchableFilesystem(); - - internal static void ConfigureDefaultFileSystem(IFileSystem fileSystem) - { - Configuration.Default.FileSystem = Instance; - Instance.FileSystem = fileSystem; - } - - internal static void ConfigureFileSystemStream(Stream stream) - { - Configuration.Default.FileSystem = Instance; - Instance.FileSystem = new SingleStreamFileSystem(stream); - } - - private readonly AsyncLocal asyncLocal = new AsyncLocal(); - - private IFileSystem FileSystem - { - get => this.asyncLocal.Value ?? LocalFile; - set => this.asyncLocal.Value = value; - } - - public Stream Create(string path) => this.FileSystem.Create(path); - - public Stream OpenRead(string path) => this.FileSystem.OpenRead(path); - - public class SingleStreamFileSystem : IFileSystem - { - private readonly Stream stream; - - public SingleStreamFileSystem(Stream stream) => this.stream = stream; - - Stream IFileSystem.Create(string path) => this.stream; - - Stream IFileSystem.OpenRead(string path) => this.stream; - } - } -} diff --git a/tests/ImageSharp.Tests/TestUtilities/SingleStreamFileSystem.cs b/tests/ImageSharp.Tests/TestUtilities/SingleStreamFileSystem.cs new file mode 100644 index 000000000..ddd1ec750 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/SingleStreamFileSystem.cs @@ -0,0 +1,19 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System.IO; +using SixLabors.ImageSharp.IO; + +namespace SixLabors.ImageSharp.Tests.TestUtilities +{ + internal class SingleStreamFileSystem : IFileSystem + { + private readonly Stream stream; + + public SingleStreamFileSystem(Stream stream) => this.stream = stream; + + Stream IFileSystem.Create(string path) => this.stream; + + Stream IFileSystem.OpenRead(string path) => this.stream; + } +} From 881bb51f217bc722fd847f1c3fe1357b90b8f90f Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 1 Jun 2021 01:12:44 +0200 Subject: [PATCH 273/275] Make sure encoding 4bit paletted tiff rows are byte aligned --- .../Formats/Tiff/TiffDecoderCore.cs | 2 +- .../Tiff/Writers/TiffPaletteWriter{TPixel}.cs | 33 ++++++++++++++----- .../Formats/Tiff/TiffDecoderTests.cs | 23 +++++++++++-- .../Formats/Tiff/TiffEncoderTests.cs | 6 ++-- tests/ImageSharp.Tests/TestImages.cs | 2 ++ .../Input/Tiff/flower-minisblack-04.tiff | 3 ++ .../Images/Input/Tiff/flower-palette-04.tiff | 3 ++ 7 files changed, 57 insertions(+), 15 deletions(-) create mode 100644 tests/Images/Input/Tiff/flower-minisblack-04.tiff create mode 100644 tests/Images/Input/Tiff/flower-palette-04.tiff diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index b7b764007..50882c007 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -202,7 +202,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff if (this.PlanarConfiguration == TiffPlanarConfiguration.Chunky) { - DebugGuard.IsTrue(plane == -1, "Excepted Chunky planar."); + DebugGuard.IsTrue(plane == -1, "Expected Chunky planar."); bitsPerPixel = this.BitsPerPixel; } else diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter{TPixel}.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter{TPixel}.cs index 712578f81..fe614c55e 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter{TPixel}.cs @@ -55,23 +55,38 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers /// protected override void EncodeStrip(int y, int height, TiffBaseCompressor compressor) { - Span pixels = GetStripPixels(((IPixelSource)this.quantizedImage).PixelBuffer, y, height); + Span indexedPixels = GetStripPixels(((IPixelSource)this.quantizedImage).PixelBuffer, y, height); if (this.BitsPerPixel == 4) { - using IMemoryOwner rows4bitBuffer = this.MemoryAllocator.Allocate(pixels.Length / 2); + int width = this.Image.Width; + int excess = (width % 2) * height; + int rows4BitBufferLength = indexedPixels.Length + excess; + using IMemoryOwner rows4bitBuffer = this.MemoryAllocator.Allocate(rows4BitBufferLength); Span rows4bit = rows4bitBuffer.GetSpan(); - int idx = 0; - for (int i = 0; i < rows4bit.Length; i++) + int idxPixels = 0; + int idx4bitRows = 0; + int halfWidth = width / 2; + for (int row = 0; row < height; row++) { - rows4bit[i] = (byte)((pixels[idx] << 4) | (pixels[idx + 1] & 0xF)); - idx += 2; + for (int x = 0; x < halfWidth; x++) + { + rows4bit[idx4bitRows] = (byte)((indexedPixels[idxPixels] << 4) | (indexedPixels[idxPixels + 1] & 0xF)); + idxPixels += 2; + idx4bitRows++; + } + + // Make sure rows are byte-aligned. + if (width % 2 != 0) + { + rows4bit[idx4bitRows++] = (byte)(indexedPixels[idxPixels++] << 4); + } } - compressor.CompressStrip(rows4bit, height); + compressor.CompressStrip(rows4bit.Slice(0, idx4bitRows), height); } else { - compressor.CompressStrip(pixels, height); + compressor.CompressStrip(indexedPixels, height); } } @@ -91,7 +106,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers PixelOperations.Instance.ToRgb48(this.Configuration, quantizedColors, quantizedColorRgb48); // It can happen that the quantized colors are less than the expected maximum per channel. - var diffToMaxColors = this.maxColors - quantizedColors.Length; + int diffToMaxColors = this.maxColors - quantizedColors.Length; // In a TIFF ColorMap, all the Red values come first, followed by the Green values, // then the Blue values. Convert the quantized palette to this format. diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index c35311a2a..1a72046fb 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -4,7 +4,7 @@ // ReSharper disable InconsistentNaming using System; using System.IO; - +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; @@ -37,6 +37,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [InlineData(RgbUncompressed, 24, 256, 256, 300, 300, PixelResolutionUnit.PixelsPerInch)] [InlineData(SmallRgbDeflate, 24, 32, 32, 96, 96, PixelResolutionUnit.PixelsPerInch)] [InlineData(Calliphora_GrayscaleUncompressed, 8, 804, 1198, 96, 96, PixelResolutionUnit.PixelsPerInch)] + [InlineData(Flower4BitPalette, 4, 73, 43, 72, 72, PixelResolutionUnit.PixelsPerInch)] public void Identify(string imagePath, int expectedPixelSize, int expectedWidth, int expectedHeight, double expectedHResolution, double expectedVResolution, PixelResolutionUnit expectedResolutionUnit) { var testFile = TestFile.Create(imagePath); @@ -91,6 +92,19 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public void TiffDecoder_CanDecode_WithPalette(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); + [Theory] + [WithFile(Rgb4BitPalette, PixelTypes.Rgba32)] + [WithFile(Flower4BitPalette, PixelTypes.Rgba32)] + [WithFile(Flower4BitPaletteGray, PixelTypes.Rgba32)] + public void TiffDecoder_CanDecode_4Bit_WithPalette(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + if (TestEnvironment.IsWindows) + { + TestTiffDecoder(provider, new SystemDrawingReferenceDecoder(), useExactComparer: false, 0.01f); + } + } + [Theory] [WithFile(GrayscaleDeflateMultistrip, PixelTypes.Rgba32)] [WithFile(RgbDeflateMultistrip, PixelTypes.Rgba32)] @@ -155,12 +169,15 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff image.CompareToOriginalMultiFrame(provider, ImageComparer.Exact, ReferenceDecoder); } - private static void TestTiffDecoder(TestImageProvider provider) + private static void TestTiffDecoder(TestImageProvider provider, IImageDecoder referenceDecoder = null, bool useExactComparer = true, float compareTolerance = 0.001f) where TPixel : unmanaged, IPixel { using Image image = provider.GetImage(TiffDecoder); image.DebugSave(provider); - image.CompareToOriginal(provider, ImageComparer.Exact, ReferenceDecoder); + image.CompareToOriginal( + provider, + useExactComparer ? ImageComparer.Exact : ImageComparer.Tolerant(compareTolerance), + referenceDecoder ?? ReferenceDecoder); } } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 546508ca5..105514c98 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -296,10 +296,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Theory] [WithFile(Rgb4BitPalette, PixelTypes.Rgba32)] + [WithFile(Flower4BitPalette, PixelTypes.Rgba32)] + [WithFile(Flower4BitPaletteGray, PixelTypes.Rgba32)] public void TiffEncoder_EncodeColorPalette_With4Bit_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => //// Note: The magick reference decoder does not support 4 bit tiff's, so we use our TIFF decoder instead. - TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit4, TiffPhotometricInterpretation.PaletteColor, useExactComparer: false, compareTolerance: 0.001f, imageDecoder: new TiffDecoder()); + TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit4, TiffPhotometricInterpretation.PaletteColor, useExactComparer: false, compareTolerance: 0.003f, imageDecoder: new TiffDecoder()); [Theory] [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] @@ -460,7 +462,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff TiffCompression compression = TiffCompression.None, TiffPredictor predictor = TiffPredictor.None, bool useExactComparer = true, - float compareTolerance = 0.01f, + float compareTolerance = 0.001f, IImageDecoder imageDecoder = null) where TPixel : unmanaged, IPixel { diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 49d0e759c..09394d4ea 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -558,6 +558,8 @@ 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 SmallRgbDeflate = "Tiff/rgb_small_deflate.tiff"; public const string SmallRgbLzw = "Tiff/rgb_small_lzw.tiff"; diff --git a/tests/Images/Input/Tiff/flower-minisblack-04.tiff b/tests/Images/Input/Tiff/flower-minisblack-04.tiff new file mode 100644 index 000000000..e6d1e1336 --- /dev/null +++ b/tests/Images/Input/Tiff/flower-minisblack-04.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:18991fca75a89b3d15c7f93dee0454e3943920b595ba16145ebc1fd8bd45b1f5 +size 1905 diff --git a/tests/Images/Input/Tiff/flower-palette-04.tiff b/tests/Images/Input/Tiff/flower-palette-04.tiff new file mode 100644 index 000000000..8594a0b00 --- /dev/null +++ b/tests/Images/Input/Tiff/flower-palette-04.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:700ec8103b4197c415ba90d983a7d5f471f155fd5b1c952d86ee9becba898a1a +size 2010 From e8a9e54eef0582ab728bffe9f100d4c52dec2403 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 1 Jun 2021 11:14:59 +0200 Subject: [PATCH 274/275] Fix length of 4 bit row buffer --- .../Formats/Tiff/Writers/TiffPaletteWriter{TPixel}.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter{TPixel}.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter{TPixel}.cs index fe614c55e..b851122a6 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter{TPixel}.cs @@ -60,7 +60,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers { int width = this.Image.Width; int excess = (width % 2) * height; - int rows4BitBufferLength = indexedPixels.Length + excess; + int rows4BitBufferLength = (width / 2 * height) + excess; using IMemoryOwner rows4bitBuffer = this.MemoryAllocator.Allocate(rows4BitBufferLength); Span rows4bit = rows4bitBuffer.GetSpan(); int idxPixels = 0; From d5c5d678ab5213559acc1d6024d954e544679e3a Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 1 Jun 2021 16:23:28 +0200 Subject: [PATCH 275/275] Review changes --- .../Formats/Tiff/Writers/TiffPaletteWriter{TPixel}.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter{TPixel}.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter{TPixel}.cs index b851122a6..d1a3dd1ea 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter{TPixel}.cs @@ -59,13 +59,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers if (this.BitsPerPixel == 4) { int width = this.Image.Width; - int excess = (width % 2) * height; - int rows4BitBufferLength = (width / 2 * height) + excess; + int halfWidth = width >> 1; + int excess = (width & 1) * height; // (width % 2) * height + int rows4BitBufferLength = (halfWidth * height) + excess; using IMemoryOwner rows4bitBuffer = this.MemoryAllocator.Allocate(rows4BitBufferLength); Span rows4bit = rows4bitBuffer.GetSpan(); int idxPixels = 0; int idx4bitRows = 0; - int halfWidth = width / 2; for (int row = 0; row < height; row++) { for (int x = 0; x < halfWidth; x++)