From 1b568ceedd5190a1ea23b5a5f758d8e921ef54a1 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Sun, 26 Feb 2017 20:47:20 +0000 Subject: [PATCH 01/63] 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 02/63] 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 03/63] 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 04/63] 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 05/63] 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 06/63] 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 07/63] 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 08/63] 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 09/63] 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 10/63] 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 11/63] 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 12/63] 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 13/63] 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 14/63] 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 15/63] 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 16/63] 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 17/63] 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 18/63] 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 19/63] 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 20/63] 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 21/63] 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 22/63] 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 23/63] 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 24/63] 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 25/63] 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 26/63] 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 27/63] 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 28/63] 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 29/63] 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 30/63] 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 31/63] 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 32/63] 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 33/63] 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 34/63] 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 35/63] 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 36/63] 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 37/63] 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 38/63] 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 39/63] 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 40/63] 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 41/63] 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 42/63] 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 43/63] 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 44/63] 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 45/63] 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 46/63] 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 47/63] 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 48/63] 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 49/63] 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 50/63] 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 51/63] 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 52/63] 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 53/63] 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 54/63] 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 55/63] 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 56/63] 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 57/63] 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 58/63] 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 59/63] 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 60/63] 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 61/63] 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 62/63] 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 63/63] 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