From fdfb547936fdcb55ca71707699e67e8d7b3d0301 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sat, 11 Sep 2021 22:14:11 +0300 Subject: [PATCH 01/62] Support long8 types for exif reader --- src/ImageSharp/Common/Helpers/Numerics.cs | 32 ++++ .../Metadata/Profiles/Exif/ExifDataType.cs | 17 +- .../Metadata/Profiles/Exif/ExifDataTypes.cs | 5 +- .../Metadata/Profiles/Exif/ExifReader.cs | 147 +++++++++++++++++- .../Profiles/Exif/Values/ExifLong8.cs | 53 +++++++ .../Profiles/Exif/Values/ExifLong8Array.cs | 27 ++++ .../Profiles/Exif/Values/ExifNumber.cs | 17 ++ .../Profiles/Exif/Values/ExifNumberArray.cs | 37 +++++ .../Profiles/Exif/Values/ExifSignedLong8.cs | 26 ++++ .../Exif/Values/ExifSignedLong8Array.cs | 22 +++ .../Profiles/Exif/Values/ExifValues.cs | 4 +- src/ImageSharp/Primitives/Number.cs | 75 +++++++-- 12 files changed, 442 insertions(+), 20 deletions(-) create mode 100644 src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8.cs create mode 100644 src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs create mode 100644 src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedLong8.cs create mode 100644 src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedLong8Array.cs diff --git a/src/ImageSharp/Common/Helpers/Numerics.cs b/src/ImageSharp/Common/Helpers/Numerics.cs index ba5c588ca..885443e86 100644 --- a/src/ImageSharp/Common/Helpers/Numerics.cs +++ b/src/ImageSharp/Common/Helpers/Numerics.cs @@ -226,6 +226,38 @@ namespace SixLabors.ImageSharp return value; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong Clamp(ulong value, ulong min, ulong max) + { + if (value > max) + { + return max; + } + + if (value < min) + { + return min; + } + + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long Clamp(long value, long min, long max) + { + if (value > max) + { + return max; + } + + if (value < min) + { + return min; + } + + return value; + } + /// /// Returns the value clamped to the inclusive range of min and max. /// diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifDataType.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifDataType.cs index 733eb4a79..0185afb50 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifDataType.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifDataType.cs @@ -80,6 +80,21 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// /// Reference to an IFD (32-bit (4-byte) unsigned integer). /// - Ifd = 13 + Ifd = 13, + + /// + /// A 64-bit (8-byte) unsigned integer. + /// + Long8 = 16, + + /// + /// A 64-bit (8-byte) signed integer (2's complement notation). + /// + SignedLong8 = 17, + + /// + /// Reference to an IFD (64-bit (8-byte) unsigned integer). + /// + Ifd8 = 18, } } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifDataTypes.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifDataTypes.cs index 4f75999bb..ee30c6b08 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifDataTypes.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifDataTypes.cs @@ -32,10 +32,13 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif case ExifDataType.Long: case ExifDataType.SignedLong: case ExifDataType.SingleFloat: + case ExifDataType.Ifd: return 4; case ExifDataType.DoubleFloat: case ExifDataType.Rational: - case ExifDataType.SignedRational: + case ExifDataType.Long8: + case ExifDataType.SignedLong8: + case ExifDataType.Ifd8: return 8; default: throw new NotSupportedException(dataType.ToString()); diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs index 6e671b3ec..5c26fde7b 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs @@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return values; } - protected override void RegisterExtLoader(uint offset, Action loader) => this.loaders.Add(loader); + protected override void RegisterExtLoader(ulong offset, Action loader) => this.loaders.Add(loader); private void GetThumbnail(uint offset) { @@ -87,6 +87,8 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif internal abstract class BaseExifReader { private readonly byte[] offsetBuffer = new byte[4]; + private readonly byte[] offsetBuffer8 = new byte[8]; + private readonly byte[] buf8 = new byte[8]; private readonly byte[] buf4 = new byte[4]; private readonly byte[] buf2 = new byte[2]; @@ -119,7 +121,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif public bool IsBigEndian { get; protected set; } - protected abstract void RegisterExtLoader(uint offset, Action loader); + protected abstract void RegisterExtLoader(ulong offset, Action loader); /// /// Reads the values to the values collection. @@ -155,6 +157,35 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif } } + protected void ReadValues64(List values, ulong offset) + { + if (offset > (ulong)this.data.Length) + { + return; + } + + this.Seek(offset); + ulong count = this.ReadUInt64(); + + for (ulong i = 0; i < count; i++) + { + this.ReadValue64(values); + } + } + + protected void ReadSubIfd64(List values) + { + if (this.exifOffset != 0) + { + this.ReadValues64(values, this.exifOffset); + } + + if (this.gpsOffset != 0) + { + this.ReadValues64(values, this.gpsOffset); + } + } + private static TDataType[] ToArray(ExifDataType dataType, ReadOnlySpan data, ConverterMethod converter) { int dataTypeSize = (int)ExifDataTypes.GetSize(dataType); @@ -186,7 +217,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return Encoding.UTF8.GetString(buffer); } - private object ConvertValue(ExifDataType dataType, ReadOnlySpan buffer, uint numberOfComponents) + private object ConvertValue(ExifDataType dataType, ReadOnlySpan buffer, ulong numberOfComponents) { if (buffer.Length == 0) { @@ -269,6 +300,20 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif } return ToArray(dataType, buffer, this.ConvertToSingle); + case ExifDataType.Long8: + if (numberOfComponents == 1) + { + return this.ConvertToUInt64(buffer); + } + + return ToArray(dataType, buffer, this.ConvertToUInt64); + case ExifDataType.SignedLong8: + if (numberOfComponents == 1) + { + return this.ConvertToInt64(buffer); + } + + return ToArray(dataType, buffer, this.ConvertToUInt64); case ExifDataType.Undefined: if (numberOfComponents == 1) { @@ -349,6 +394,69 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif } } + private void ReadValue64(List values) + { + if ((this.data.Length - this.data.Position) < 20) + { + return; + } + + var tag = (ExifTagValue)this.ReadUInt16(); + ExifDataType dataType = EnumUtils.Parse(this.ReadUInt16(), ExifDataType.Unknown); + + ulong numberOfComponents = this.ReadUInt64(); + + this.TryReadSpan(this.offsetBuffer8); + + if (dataType == ExifDataType.Unknown) + { + return; + } + + if (dataType == ExifDataType.Undefined && numberOfComponents == 0) + { + numberOfComponents = 8; + } + + // The StripOffsets, StripByteCounts, TileOffsets, and TileByteCounts tags are allowed to have the datatype TIFF_LONG8 in BigTIFF. + // Old datatypes TIFF_LONG, and TIFF_SHORT where allowed in the TIFF 6.0 specification, are still valid in BigTIFF, too. + // https://www.awaresystems.be/imaging/tiff/bigtiff.html + ExifValue exifValue = exifValue = ExifValues.Create(tag) ?? ExifValues.Create(tag, dataType, numberOfComponents); + + if (exifValue is null) + { + this.AddInvalidTag(new UnkownExifTag(tag)); + return; + } + + ulong size = numberOfComponents * ExifDataTypes.GetSize(dataType); + if (size > 8) + { + ulong newOffset = this.ConvertToUInt64(this.offsetBuffer8); + if (newOffset > ulong.MaxValue || (newOffset + size) > (ulong)this.data.Length) + { + this.AddInvalidTag(new UnkownExifTag(tag)); + return; + } + + this.RegisterExtLoader(newOffset, () => + { + byte[] dataBuffer = new byte[size]; + this.Seek(newOffset); + if (this.TryReadSpan(dataBuffer)) + { + object value = this.ConvertValue(dataType, dataBuffer, numberOfComponents); + this.Add(values, exifValue, value); + } + }); + } + else + { + object value = this.ConvertValue(dataType, ((Span)this.offsetBuffer8).Slice(0, (int)size), numberOfComponents); + this.Add(values, exifValue, value); + } + } + private void Add(IList values, IExifValue exif, object value) { if (!exif.TrySetValue(value)) @@ -383,8 +491,8 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif private void AddInvalidTag(ExifTag tag) => (this.invalidTags ??= new List()).Add(tag); - private void Seek(long pos) - => this.data.Seek(pos, SeekOrigin.Begin); + private void Seek(ulong pos) + => this.data.Seek((long)pos, SeekOrigin.Begin); private bool TryReadSpan(Span span) { @@ -398,6 +506,11 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return read == length; } + protected ulong ReadUInt64() => + this.TryReadSpan(this.buf8) + ? this.ConvertToUInt64(this.buf8) + : default; + // Known as Long in Exif Specification. protected uint ReadUInt32() => this.TryReadSpan(this.buf4) @@ -408,6 +521,30 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif ? this.ConvertToShort(this.buf2) : default; + private long ConvertToInt64(ReadOnlySpan buffer) + { + if (buffer.Length < 8) + { + return default; + } + + return this.IsBigEndian + ? BinaryPrimitives.ReadInt64BigEndian(buffer) + : BinaryPrimitives.ReadInt64LittleEndian(buffer); + } + + private ulong ConvertToUInt64(ReadOnlySpan buffer) + { + if (buffer.Length < 8) + { + return default; + } + + return this.IsBigEndian + ? BinaryPrimitives.ReadUInt64BigEndian(buffer) + : BinaryPrimitives.ReadUInt64LittleEndian(buffer); + } + private double ConvertToDouble(ReadOnlySpan buffer) { if (buffer.Length < 8) diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8.cs new file mode 100644 index 000000000..47da962f3 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8.cs @@ -0,0 +1,53 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System.Globalization; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifLong8 : ExifValue + { + public ExifLong8(ExifTag tag) + : base(tag) + { + } + + public ExifLong8(ExifTagValue tag) + : base(tag) + { + } + + private ExifLong8(ExifLong8 value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.Long8; + + protected override string StringValue => this.Value.ToString(CultureInfo.InvariantCulture); + + public override bool TrySetValue(object value) + { + if (base.TrySetValue(value)) + { + return true; + } + + switch (value) + { + case long intValue: + if (intValue >= 0) + { + this.Value = (ulong)intValue; + return true; + } + + return false; + default: + return false; + } + } + + public override IExifValue DeepClone() => new ExifLong8(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs new file mode 100644 index 000000000..3cf59adac --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs @@ -0,0 +1,27 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifLong8Array : ExifArrayValue + { + public ExifLong8Array(ExifTag tag) + : base(tag) + { + } + + public ExifLong8Array(ExifTagValue tag) + : base(tag) + { + } + + private ExifLong8Array(ExifLong8Array value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.Long8; + + public override IExifValue DeepClone() => new ExifLong8Array(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumber.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumber.cs index 9e206b23d..d16de4d22 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumber.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumber.cs @@ -21,6 +21,11 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif { get { + if (this.Value > uint.MaxValue) + { + return ExifDataType.Long8; + } + if (this.Value > ushort.MaxValue) { return ExifDataType.Long; @@ -41,6 +46,18 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif switch (value) { + case long longValue: + if (longValue >= 0) + { + this.Value = (ulong)longValue; + return true; + } + + return false; + case ulong ulongValue: + this.Value = ulongValue; + + return true; case int intValue: if (intValue >= uint.MinValue) { diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs index 2d3a93aed..15b29dcec 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs @@ -26,6 +26,11 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif for (int i = 0; i < this.Value.Length; i++) { + if (this.Value[i] > uint.MaxValue) + { + return ExifDataType.Long8; + } + if (this.Value[i] > ushort.MaxValue) { return ExifDataType.Long; @@ -45,6 +50,10 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif switch (value) { + case long val: + return this.SetSingle(val); + case ulong val: + return this.SetSingle(val); case int val: return this.SetSingle(val); case uint val: @@ -53,6 +62,10 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return this.SetSingle(val); case ushort val: return this.SetSingle(val); + case long[] array: + return this.SetArray(array); + case ulong[] array: + return this.SetArray(array); case int[] array: return this.SetArray(array); case uint[] array: @@ -74,6 +87,30 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return true; } + private bool SetArray(long[] values) + { + var numbers = new Number[values.Length]; + for (int i = 0; i < values.Length; i++) + { + numbers[i] = values[i]; + } + + this.Value = numbers; + return true; + } + + private bool SetArray(ulong[] values) + { + var numbers = new Number[values.Length]; + for (int i = 0; i < values.Length; i++) + { + numbers[i] = values[i]; + } + + this.Value = numbers; + return true; + } + private bool SetArray(int[] values) { var numbers = new Number[values.Length]; diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedLong8.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedLong8.cs new file mode 100644 index 000000000..8362dcf2c --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedLong8.cs @@ -0,0 +1,26 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System.Globalization; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifSignedLong8 : ExifValue + { + public ExifSignedLong8(ExifTagValue tag) + : base(tag) + { + } + + private ExifSignedLong8(ExifSignedLong8 value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.SignedLong8; + + protected override string StringValue => this.Value.ToString(CultureInfo.InvariantCulture); + + public override IExifValue DeepClone() => new ExifSignedLong8(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedLong8Array.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedLong8Array.cs new file mode 100644 index 000000000..34ce20c8f --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedLong8Array.cs @@ -0,0 +1,22 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifSignedLong8Array : ExifArrayValue + { + public ExifSignedLong8Array(ExifTagValue tag) + : base(tag) + { + } + + private ExifSignedLong8Array(ExifSignedLong8Array value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.SignedLong8; + + public override IExifValue DeepClone() => new ExifSignedLong8Array(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs index af1eee2dc..7e5b35f49 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif public static ExifValue Create(ExifTag tag) => (ExifValue)CreateValue((ExifTagValue)(ushort)tag); - public static ExifValue Create(ExifTagValue tag, ExifDataType dataType, uint numberOfComponents) => Create(tag, dataType, numberOfComponents != 1); + public static ExifValue Create(ExifTagValue tag, ExifDataType dataType, ulong numberOfComponents) => Create(tag, dataType, numberOfComponents != 1); public static ExifValue Create(ExifTagValue tag, ExifDataType dataType, bool isArray) { @@ -19,10 +19,12 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif case ExifDataType.DoubleFloat: return isArray ? (ExifValue)new ExifDoubleArray(tag) : new ExifDouble(tag); case ExifDataType.SingleFloat: return isArray ? (ExifValue)new ExifFloatArray(tag) : new ExifFloat(tag); case ExifDataType.Long: return isArray ? (ExifValue)new ExifLongArray(tag) : new ExifLong(tag); + case ExifDataType.Long8: return isArray ? (ExifValue)new ExifLong8Array(tag) : new ExifLong8(tag); case ExifDataType.Rational: return isArray ? (ExifValue)new ExifRationalArray(tag) : new ExifRational(tag); case ExifDataType.Short: return isArray ? (ExifValue)new ExifShortArray(tag) : new ExifShort(tag); case ExifDataType.SignedByte: return isArray ? (ExifValue)new ExifSignedByteArray(tag) : new ExifSignedByte(tag); case ExifDataType.SignedLong: return isArray ? (ExifValue)new ExifSignedLongArray(tag) : new ExifSignedLong(tag); + case ExifDataType.SignedLong8: return isArray ? (ExifValue)new ExifSignedLong8Array(tag) : new ExifSignedLong8(tag); case ExifDataType.SignedRational: return isArray ? (ExifValue)new ExifSignedRationalArray(tag) : new ExifSignedRational(tag); case ExifDataType.SignedShort: return isArray ? (ExifValue)new ExifSignedShortArray(tag) : new ExifSignedShort(tag); case ExifDataType.Ascii: return new ExifString(tag); diff --git a/src/ImageSharp/Primitives/Number.cs b/src/ImageSharp/Primitives/Number.cs index e33d2344a..690b72d79 100644 --- a/src/ImageSharp/Primitives/Number.cs +++ b/src/ImageSharp/Primitives/Number.cs @@ -14,19 +14,19 @@ namespace SixLabors.ImageSharp public struct Number : IEquatable, IComparable { [FieldOffset(0)] - private readonly int signedValue; + private readonly long signedValue; [FieldOffset(0)] - private readonly uint unsignedValue; + private readonly ulong unsignedValue; - [FieldOffset(4)] + [FieldOffset(8)] private readonly bool isSigned; /// /// Initializes a new instance of the struct. /// /// The value of the number. - public Number(int value) + public Number(long value) : this() { this.signedValue = value; @@ -37,30 +37,70 @@ namespace SixLabors.ImageSharp /// Initializes a new instance of the struct. /// /// The value of the number. - public Number(uint value) + public Number(ulong value) : this() { this.unsignedValue = value; this.isSigned = false; } + /// + /// Converts the specified to an instance of this type. + /// + /// The value. + public static implicit operator Number(long value) => new Number(value); + + /// + /// Converts the specified to an instance of this type. + /// + /// The value. + public static implicit operator Number(ulong value) => new Number(value); + /// /// Converts the specified to an instance of this type. /// /// The value. - public static implicit operator Number(int value) => new Number(value); + public static implicit operator Number(int value) => new Number((long)value); /// /// Converts the specified to an instance of this type. /// /// The value. - public static implicit operator Number(uint value) => new Number(value); + public static implicit operator Number(uint value) => new Number((ulong)value); + + /// + /// Converts the specified to an instance of this type. + /// + /// The value. + public static implicit operator Number(short value) => new Number((long)value); /// /// Converts the specified to an instance of this type. /// /// The value. - public static implicit operator Number(ushort value) => new Number((uint)value); + public static implicit operator Number(ushort value) => new Number((ulong)value); + + /// + /// Converts the specified to an instance of this type. + /// + /// The to convert. + public static explicit operator long(Number number) + { + return number.isSigned + ? number.signedValue + : (long)Numerics.Clamp(number.unsignedValue, 0, long.MaxValue); + } + + /// + /// Converts the specified to an instance of this type. + /// + /// The to convert. + public static explicit operator ulong(Number number) + { + return number.isSigned + ? (ulong)Numerics.Clamp(number.signedValue, 0, long.MaxValue) + : number.unsignedValue; + } /// /// Converts the specified to a . @@ -69,8 +109,8 @@ namespace SixLabors.ImageSharp public static explicit operator int(Number number) { return number.isSigned - ? number.signedValue - : (int)Numerics.Clamp(number.unsignedValue, 0, int.MaxValue); + ? (int)Numerics.Clamp(number.signedValue, int.MinValue, int.MaxValue) + : (int)Numerics.Clamp(number.unsignedValue, 0, (uint)int.MaxValue); } /// @@ -80,8 +120,19 @@ namespace SixLabors.ImageSharp public static explicit operator uint(Number number) { return number.isSigned - ? (uint)Numerics.Clamp(number.signedValue, 0, int.MaxValue) - : number.unsignedValue; + ? (uint)Numerics.Clamp(number.signedValue, uint.MinValue, uint.MaxValue) + : (uint)Numerics.Clamp(number.unsignedValue, uint.MinValue, uint.MaxValue); + } + + /// + /// Converts the specified to a . + /// + /// The to convert. + public static explicit operator short(Number number) + { + return number.isSigned + ? (short)Numerics.Clamp(number.signedValue, short.MinValue, short.MaxValue) + : (short)Numerics.Clamp(number.unsignedValue, 0, (ushort)short.MaxValue); } /// From 4b3ae005a6eca9783a8b238bbb72b6192d371b4c Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sat, 11 Sep 2021 22:26:32 +0300 Subject: [PATCH 02/62] Implement support BigTiff format decoding --- .../Tiff/Compression/TiffBaseDecompressor.cs | 10 ++-- .../Formats/Tiff/Constants/TiffConstants.cs | 10 ++++ .../Formats/Tiff/Ifd/DirectoryReader.cs | 24 ++++---- .../Formats/Tiff/Ifd/EntryReader.cs | 58 +++++++++++++------ .../Formats/Tiff/TiffDecoderCore.cs | 5 ++ .../Formats/Tiff/TiffDecoderOptionsParser.cs | 13 +++-- 6 files changed, 81 insertions(+), 39 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseDecompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseDecompressor.cs index 28459d0c5..f1faa6c67 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseDecompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseDecompressor.cs @@ -35,17 +35,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression /// The number of bytes to read from the input stream. /// The height of the strip. /// The output buffer for uncompressed data. - public void Decompress(BufferedReadStream stream, uint stripOffset, uint stripByteCount, int stripHeight, Span buffer) + public void Decompress(BufferedReadStream stream, ulong stripOffset, ulong stripByteCount, int stripHeight, Span buffer) { - if (stripByteCount > int.MaxValue) + if (stripOffset > long.MaxValue || stripByteCount > long.MaxValue) { - TiffThrowHelper.ThrowImageFormatException("The StripByteCount value is too big."); + TiffThrowHelper.ThrowImageFormatException("The StripOffset or StripByteCount value is too big."); } - stream.Seek(stripOffset, SeekOrigin.Begin); + stream.Seek((long)stripOffset, SeekOrigin.Begin); this.Decompress(stream, (int)stripByteCount, stripHeight, buffer); - if (stripOffset + stripByteCount < stream.Position) + if ((long)stripOffset + (long)stripByteCount < stream.Position) { TiffThrowHelper.ThrowImageFormatException("Out of range when reading a strip."); } diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs index b54545141..b5548c707 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs @@ -35,6 +35,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Constants /// public const ushort HeaderMagicNumber = 42; + /// + /// The big tiff header magic number + /// + public const ushort BigTiffHeaderMagicNumber = 43; + + /// + /// The big tiff bytesize of offsets value. + /// + public const ushort BigTiffBytesize = 8; + /// /// RowsPerStrip default value, which is effectively infinity. /// diff --git a/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs index 8b2c6bd3a..bd673cf3a 100644 --- a/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs +++ b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs @@ -16,11 +16,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff { private readonly Stream stream; - private uint nextIfdOffset; + private ulong nextIfdOffset; // used for sequential read big values (actual for multiframe big files) // todo: different tags can link to the same data (stream offset) - investigate - private readonly SortedList lazyLoaders = new SortedList(new DuplicateKeyComparer()); + private readonly SortedList lazyLoaders = new SortedList(new DuplicateKeyComparer()); public DirectoryReader(Stream stream) => this.stream = stream; @@ -36,13 +36,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff public IEnumerable Read() { this.ByteOrder = ReadByteOrder(this.stream); - this.nextIfdOffset = new HeaderReader(this.stream, this.ByteOrder).ReadFileHeader(); - return this.ReadIfds(); + var headerReader = new HeaderReader(this.stream, this.ByteOrder); + headerReader.ReadFileHeader(); + + this.nextIfdOffset = headerReader.FirstIfdOffset; + + return this.ReadIfds(headerReader.IsBigTiff); } private static ByteOrder ReadByteOrder(Stream stream) { - var headerBytes = new byte[2]; + byte[] headerBytes = new byte[2]; stream.Read(headerBytes, 0, 2); if (headerBytes[0] == TiffConstants.ByteOrderLittleEndian && headerBytes[1] == TiffConstants.ByteOrderLittleEndian) { @@ -56,13 +60,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff throw TiffThrowHelper.ThrowInvalidHeader(); } - private IEnumerable ReadIfds() + private IEnumerable ReadIfds(bool isBigTiff) { var readers = new List(); - while (this.nextIfdOffset != 0 && this.nextIfdOffset < this.stream.Length) + while (this.nextIfdOffset != 0 && this.nextIfdOffset < (ulong)this.stream.Length) { - var reader = new EntryReader(this.stream, this.ByteOrder, this.nextIfdOffset, this.lazyLoaders); - reader.ReadTags(); + var reader = new EntryReader(this.stream, this.ByteOrder, this.lazyLoaders); + reader.ReadTags(isBigTiff, this.nextIfdOffset); this.nextIfdOffset = reader.NextIfdOffset; @@ -75,7 +79,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff loader(); } - var list = new List(); + var list = new List(readers.Count); foreach (EntryReader reader in readers) { var profile = new ExifProfile(reader.Values, reader.InvalidTags); diff --git a/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs b/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs index 123a64cc1..7884242ef 100644 --- a/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs +++ b/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs @@ -12,31 +12,38 @@ namespace SixLabors.ImageSharp.Formats.Tiff { internal class EntryReader : BaseExifReader { - private readonly uint startOffset; + private readonly SortedList lazyLoaders; - private readonly SortedList lazyLoaders; - - public EntryReader(Stream stream, ByteOrder byteOrder, uint ifdOffset, SortedList lazyLoaders) + public EntryReader(Stream stream, ByteOrder byteOrder, SortedList lazyLoaders) : base(stream) { this.IsBigEndian = byteOrder == ByteOrder.BigEndian; - this.startOffset = ifdOffset; this.lazyLoaders = lazyLoaders; } public List Values { get; } = new List(); - public uint NextIfdOffset { get; private set; } + public ulong NextIfdOffset { get; private set; } - public void ReadTags() + public void ReadTags(bool isBigTiff, ulong ifdOffset) { - this.ReadValues(this.Values, this.startOffset); - this.NextIfdOffset = this.ReadUInt32(); + if (!isBigTiff) + { + this.ReadValues(this.Values, (uint)ifdOffset); + this.NextIfdOffset = this.ReadUInt32(); + + this.ReadSubIfd(this.Values); + } + else + { + this.ReadValues64(this.Values, ifdOffset); + this.NextIfdOffset = this.ReadUInt64(); - this.ReadSubIfd(this.Values); + this.ReadSubIfd64(this.Values); + } } - protected override void RegisterExtLoader(uint offset, Action reader) => + protected override void RegisterExtLoader(ulong offset, Action reader) => this.lazyLoaders.Add(offset, reader); } @@ -46,20 +53,35 @@ namespace SixLabors.ImageSharp.Formats.Tiff : base(stream) => this.IsBigEndian = byteOrder == ByteOrder.BigEndian; - public uint FirstIfdOffset { get; private set; } + public bool IsBigTiff { get; private set; } + + public ulong FirstIfdOffset { get; private set; } - public uint ReadFileHeader() + public void ReadFileHeader() { ushort magic = this.ReadUInt16(); - if (magic != TiffConstants.HeaderMagicNumber) + if (magic == TiffConstants.HeaderMagicNumber) { - TiffThrowHelper.ThrowInvalidHeader(); + this.IsBigTiff = false; + this.FirstIfdOffset = this.ReadUInt32(); + return; + } + else if (magic == TiffConstants.BigTiffHeaderMagicNumber) + { + this.IsBigTiff = true; + + ushort bytesize = this.ReadUInt16(); + ushort reserve = this.ReadUInt16(); + if (bytesize == TiffConstants.BigTiffBytesize && reserve == 0) + { + this.FirstIfdOffset = this.ReadUInt64(); + return; + } } - this.FirstIfdOffset = this.ReadUInt32(); - return this.FirstIfdOffset; + TiffThrowHelper.ThrowInvalidHeader(); } - protected override void RegisterExtLoader(uint offset, Action reader) => throw new NotSupportedException(); + protected override void RegisterExtLoader(ulong offset, Action reader) => throw new NotSupportedException(); } } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 55af87005..922c1f174 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -432,6 +432,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff TiffThrowHelper.ThrowImageFormatException("The TIFF image frame is missing the ImageWidth"); } + if (((ulong)width.Value) > int.MaxValue) + { + TiffThrowHelper.ThrowImageFormatException("Too big ImageWidth value"); + } + return (int)width.Value; } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs index a8420fabb..a187d444a 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs @@ -24,12 +24,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The IFD entries container to read the image format information for current frame. public static void VerifyAndParse(this TiffDecoderCore options, ExifProfile exifProfile, TiffFrameMetadata frameMetadata) { - if (exifProfile.GetValue(ExifTag.TileOffsets)?.Value != null) + if (exifProfile.GetValueInternal(ExifTag.TileOffsets) is not null || exifProfile.GetValueInternal(ExifTag.TileByteCounts) is not null) { TiffThrowHelper.ThrowNotSupported("Tiled images are not supported."); } - if (exifProfile.GetValue(ExifTag.ExtraSamples)?.Value != null) + if (exifProfile.GetValueInternal(ExifTag.ExtraSamples) is not null) { TiffThrowHelper.ThrowNotSupported("ExtraSamples is not supported."); } @@ -95,12 +95,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff private static void VerifyRequiredFieldsArePresent(ExifProfile exifProfile, TiffFrameMetadata frameMetadata) { - if (exifProfile.GetValue(ExifTag.StripOffsets) == null) + if (exifProfile.GetValueInternal(ExifTag.StripOffsets) is null) { TiffThrowHelper.ThrowImageFormatException("StripOffsets are missing and are required for decoding the TIFF image!"); } - if (exifProfile.GetValue(ExifTag.StripByteCounts) == null) + if (exifProfile.GetValueInternal(ExifTag.StripByteCounts) is null) { TiffThrowHelper.ThrowImageFormatException("StripByteCounts are missing and are required for decoding the TIFF image!"); } @@ -384,7 +384,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff private static void ParseCompression(this TiffDecoderCore options, TiffCompression? compression, ExifProfile exifProfile) { - switch (compression) + // Default 1 (No compression) https://www.awaresystems.be/imaging/tiff/tifftags/compression.html + switch (compression ?? TiffCompression.None) { case TiffCompression.None: { @@ -441,7 +442,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff default: { - TiffThrowHelper.ThrowNotSupported($"The specified TIFF compression format {compression} is not supported"); + TiffThrowHelper.ThrowNotSupported($"The specified TIFF compression format '{compression}' is not supported"); break; } } From ae577bba8738a3c497915958de7948768fb63945 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sat, 11 Sep 2021 22:27:03 +0300 Subject: [PATCH 03/62] Add BigTiff decoder tests --- .../Formats/Tiff/BigTiffDecoderTests.cs | 35 +++ .../Formats/Tiff/TiffDecoderBaseTester.cs | 32 +++ .../Formats/Tiff/TiffDecoderTests.cs | 22 +- tests/ImageSharp.Tests/TestImages.cs | 15 ++ tests/Images/Input/Tiff/BigTiff/BigTIFF.tif | 3 + .../Images/Input/Tiff/BigTiff/BigTIFFLong.tif | 3 + .../Input/Tiff/BigTiff/BigTIFFLong8.tif | 3 + .../Input/Tiff/BigTiff/BigTIFFLong8Tiles.tif | 3 + .../Input/Tiff/BigTiff/BigTIFFMotorola.tif | 3 + .../BigTiff/BigTIFFMotorolaLongStrips.tif | 3 + .../Input/Tiff/BigTiff/BigTIFFSamples.html | 239 ++++++++++++++++++ .../Input/Tiff/BigTiff/BigTIFFSubIFD4.tif | 3 + .../Input/Tiff/BigTiff/BigTIFFSubIFD8.tif | 3 + tests/Images/Input/Tiff/BigTiff/Classic.tif | 3 + tests/Images/Input/Tiff/BigTiff/readme.md | 3 + 15 files changed, 353 insertions(+), 20 deletions(-) create mode 100644 tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs create mode 100644 tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderBaseTester.cs create mode 100644 tests/Images/Input/Tiff/BigTiff/BigTIFF.tif create mode 100644 tests/Images/Input/Tiff/BigTiff/BigTIFFLong.tif create mode 100644 tests/Images/Input/Tiff/BigTiff/BigTIFFLong8.tif create mode 100644 tests/Images/Input/Tiff/BigTiff/BigTIFFLong8Tiles.tif create mode 100644 tests/Images/Input/Tiff/BigTiff/BigTIFFMotorola.tif create mode 100644 tests/Images/Input/Tiff/BigTiff/BigTIFFMotorolaLongStrips.tif create mode 100644 tests/Images/Input/Tiff/BigTiff/BigTIFFSamples.html create mode 100644 tests/Images/Input/Tiff/BigTiff/BigTIFFSubIFD4.tif create mode 100644 tests/Images/Input/Tiff/BigTiff/BigTIFFSubIFD8.tif create mode 100644 tests/Images/Input/Tiff/BigTiff/Classic.tif create mode 100644 tests/Images/Input/Tiff/BigTiff/readme.md diff --git a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs new file mode 100644 index 000000000..fc507513b --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs @@ -0,0 +1,35 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +// ReSharper disable InconsistentNaming +using System; +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; + +using static SixLabors.ImageSharp.Tests.TestImages.BigTiff; + +namespace SixLabors.ImageSharp.Tests.Formats.Tiff +{ + [Collection("RunSerial")] + [Trait("Format", "Tiff")] + [Trait("Format", "BigTiff")] + public class BigTiffDecoderTests : TiffDecoderBaseTester + { + [Theory] + [WithFile(BigTIFF, PixelTypes.Rgba32)] + [WithFile(BigTIFFLong, PixelTypes.Rgba32)] + [WithFile(BigTIFFLong8, PixelTypes.Rgba32)] + [WithFile(BigTIFFMotorola, PixelTypes.Rgba32)] + [WithFile(BigTIFFMotorolaLongStrips, PixelTypes.Rgba32)] + [WithFile(BigTIFFSubIFD4, PixelTypes.Rgba32)] + [WithFile(BigTIFFSubIFD8, PixelTypes.Rgba32)] + public void TiffDecoder_CanDecode(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); + + [Theory] + [WithFile(BigTIFFLong8Tiles, PixelTypes.Rgba32)] + public void ThrowsNotSupported(TestImageProvider provider) + where TPixel : unmanaged, IPixel => Assert.Throws(() => provider.GetImage(TiffDecoder)); + } +} diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderBaseTester.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderBaseTester.cs new file mode 100644 index 000000000..ff70eaf8a --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderBaseTester.cs @@ -0,0 +1,32 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +// ReSharper disable InconsistentNaming +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; +using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Formats.Tiff +{ + public abstract class TiffDecoderBaseTester + { + protected static TiffDecoder TiffDecoder => new TiffDecoder(); + + protected static MagickReferenceDecoder ReferenceDecoder => new MagickReferenceDecoder(); + + protected static void TestTiffDecoder(TestImageProvider provider, IImageDecoder referenceDecoder = null, bool useExactComparer = true, float compareTolerance = 0.001f) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(TiffDecoder); + image.DebugSave(provider); + image.CompareToOriginal( + provider, + useExactComparer ? ImageComparer.Exact : ImageComparer.Tolerant(compareTolerance), + referenceDecoder ?? ReferenceDecoder); + } + } +} diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index b64d22991..e49f45932 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -4,12 +4,9 @@ // ReSharper disable InconsistentNaming using System; using System.IO; -using SixLabors.ImageSharp.Formats; -using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; -using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; using Xunit; @@ -19,20 +16,16 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { [Collection("RunSerial")] [Trait("Format", "Tiff")] - public class TiffDecoderTests + public class TiffDecoderTests : TiffDecoderBaseTester { public static readonly string[] MultiframeTestImages = Multiframes; - private static TiffDecoder TiffDecoder => new TiffDecoder(); - - private static MagickReferenceDecoder ReferenceDecoder => new MagickReferenceDecoder(); - [Theory] [WithFile(RgbUncompressedTiled, PixelTypes.Rgba32)] [WithFile(MultiframeDifferentSize, PixelTypes.Rgba32)] [WithFile(MultiframeDifferentVariants, PixelTypes.Rgba32)] public void ThrowsNotSupported(TestImageProvider provider) - where TPixel : unmanaged, IPixel => Assert.Throws(() => provider.GetImage(TiffDecoder)); + where TPixel : unmanaged, IPixel => Assert.Throws(() => provider.GetImage(TiffDecoder)); [Theory] [InlineData(RgbUncompressed, 24, 256, 256, 300, 300, PixelResolutionUnit.PixelsPerInch)] @@ -397,16 +390,5 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff image.DebugSaveMultiFrame(provider); image.CompareToOriginalMultiFrame(provider, ImageComparer.Exact, ReferenceDecoder); } - - private static void TestTiffDecoder(TestImageProvider provider, IImageDecoder referenceDecoder = null, bool useExactComparer = true, float compareTolerance = 0.001f) - where TPixel : unmanaged, IPixel - { - using Image image = provider.GetImage(TiffDecoder); - image.DebugSave(provider); - image.CompareToOriginal( - provider, - useExactComparer ? ImageComparer.Exact : ImageComparer.Tolerant(compareTolerance), - referenceDecoder ?? ReferenceDecoder); - } } } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index d1a6624af..9df6472a1 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -672,5 +672,20 @@ namespace SixLabors.ImageSharp.Tests public static readonly string[] Metadata = { SampleMetadata }; } + + public static class BigTiff + { + public const string BasePath = "Tiff/BigTiff/"; + + public const string BigTIFF = BasePath + "BigTIFF.tif"; + public const string BigTIFFLong = BasePath + "BigTIFFLong.tif"; + public const string BigTIFFLong8 = BasePath + "BigTIFFLong8.tif"; + public const string BigTIFFLong8Tiles = BasePath + "BigTIFFLong8Tiles.tif"; + public const string BigTIFFMotorola = BasePath + "BigTIFFMotorola.tif"; + public const string BigTIFFMotorolaLongStrips = BasePath + "BigTIFFMotorolaLongStrips.tif"; + + public const string BigTIFFSubIFD4 = BasePath + "BigTIFFSubIFD4.tif"; + public const string BigTIFFSubIFD8 = BasePath + "BigTIFFSubIFD8.tif"; + } } } diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFF.tif b/tests/Images/Input/Tiff/BigTiff/BigTIFF.tif new file mode 100644 index 000000000..b27834479 --- /dev/null +++ b/tests/Images/Input/Tiff/BigTiff/BigTIFF.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ddb202145a9bce7670cc372ee578de5a53cd52cc8d5ae8a9ebdc9f9c4f4a7e81 +size 12480 diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFFLong.tif b/tests/Images/Input/Tiff/BigTiff/BigTIFFLong.tif new file mode 100644 index 000000000..b390c0a97 --- /dev/null +++ b/tests/Images/Input/Tiff/BigTiff/BigTIFFLong.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:90178643a159ec50335e9314836df924233debeb100763af0f77cd1be3cf58ab +size 12480 diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFFLong8.tif b/tests/Images/Input/Tiff/BigTiff/BigTIFFLong8.tif new file mode 100644 index 000000000..e1815c750 --- /dev/null +++ b/tests/Images/Input/Tiff/BigTiff/BigTIFFLong8.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b38e61ccb01e10e26fb10c335fc6fca9ad087b0fb0df833e54bb02d1246e20d5 +size 12480 diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFFLong8Tiles.tif b/tests/Images/Input/Tiff/BigTiff/BigTIFFLong8Tiles.tif new file mode 100644 index 000000000..c69218948 --- /dev/null +++ b/tests/Images/Input/Tiff/BigTiff/BigTIFFLong8Tiles.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a2d92b0c430cefc390f13961e00950ee7246b013335594dd249ba823eb3c3fdb +size 12564 diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFFMotorola.tif b/tests/Images/Input/Tiff/BigTiff/BigTIFFMotorola.tif new file mode 100644 index 000000000..68c2b3a41 --- /dev/null +++ b/tests/Images/Input/Tiff/BigTiff/BigTIFFMotorola.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ace8a27dbed9f918993615e545a12310b84ad94bc6af8e256258e69731f1c7ce +size 12480 diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFFMotorolaLongStrips.tif b/tests/Images/Input/Tiff/BigTiff/BigTIFFMotorolaLongStrips.tif new file mode 100644 index 000000000..cc62e0362 --- /dev/null +++ b/tests/Images/Input/Tiff/BigTiff/BigTIFFMotorolaLongStrips.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2c5ebdc3774955d3b47644f57bd023e764a93ca2271118152ae920b34c1784bb +size 12480 diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFFSamples.html b/tests/Images/Input/Tiff/BigTiff/BigTIFFSamples.html new file mode 100644 index 000000000..c674e57b5 --- /dev/null +++ b/tests/Images/Input/Tiff/BigTiff/BigTIFFSamples.html @@ -0,0 +1,239 @@ + + + + +

+ These images were created by AWare Systems. +

+

Index

+

+ Classic.tif
+ BigTIFF.tif
+ BigTIFFMotorola.tif
+ BigTIFFLong.tif
+ BigTIFFLong8.tif
+ BigTIFFMotorolaLongStrips.tif
+ BigTIFFLong8Tiles.tif
+ BigTIFFSubIFD4.tif
+ BigTIFFSubIFD8.tif
+

+

Classic.tif

+

+ Classic.tif is a basic Classic TIFF file. All files in this package have the same actual image content, + so this TIFF file serves as a reference. +

+

+ Format: Classic TIFF
+ Byte Order: Intel
+ Ifd Offset: 12302
+     ImageWidth (1 Short): 64
+     ImageLength (1 Short): 64
+     BitsPerSample (3 Short): 8, 8, 8
+     PhotometricInterpretation (1 Short): RGB
+     StripOffsets (1 Short): 8
+     SamplesPerPixel (1 Short): 3
+     RowsPerStrip (1 Short): 64
+     StripByteCounts (1 Short): 12288
+

+

BigTIFF.tif

+

+ BigTIFF.tif ressembles Classic.tif as close as possible. Except that it's a BigTIFF, that is... +

+

+ Format: BigTIFF
+ Byte Order: Intel
+ Ifd Offset: 12304
+     ImageWidth (1 Short): 64
+     ImageLength (1 Short): 64
+     BitsPerSample (3 Short): 8, 8, 8
+     PhotometricInterpretation (1 Short): RGB
+     StripOffsets (1 Short): 16
+     SamplesPerPixel (1 Short): 3
+     RowsPerStrip (1 Short): 64
+     StripByteCounts (1 Short): 12288
+

+

BigTIFFMotorola.tif

+

+ BigTIFFMotorola.tif reverses the byte order. +

+

+ Format: BigTIFF
+ Byte Order: Motorola
+ Ifd Offset: 12304
+     ImageWidth (1 Short): 64
+     ImageLength (1 Short): 64
+     BitsPerSample (3 Short): 8, 8, 8
+     PhotometricInterpretation (1 Short): RGB
+     StripOffsets (1 Short): 16
+     SamplesPerPixel (1 Short): 3
+     RowsPerStrip (1 Short): 64
+     StripByteCounts (1 Short): 12288
+

+

BigTIFFLong.tif

+

+ All previous TIFFs specify DataType Short for StripOffsets and StripByteCounts tags. This BigTIFF instead specifies DataType Long, for these tags. +

+

+ Format: BigTIFF
+ Byte Order: Intel
+ Ifd Offset: 12304
+     ImageWidth (1 Short): 64
+     ImageLength (1 Short): 64
+     BitsPerSample (3 Short): 8, 8, 8
+     PhotometricInterpretation (1 Short): RGB
+     StripOffsets (1 Long): 16
+     SamplesPerPixel (1 Short): 3
+     RowsPerStrip (1 Short): 64
+     StripByteCounts (1 Long): 12288
+

+

BigTIFFLong8.tif

+

+ This next one specifies DataType Long8, for StripOffsets and StripByteCounts tags. +

+

+ Format: BigTIFF
+ Byte Order: Intel
+ Ifd Offset: 12304
+     ImageWidth (1 Short): 64
+     ImageLength (1 Short): 64
+     BitsPerSample (3 Short): 8, 8, 8
+     PhotometricInterpretation (1 Short): RGB
+     StripOffsets (1 Long8): 16
+     SamplesPerPixel (1 Short): 3
+     RowsPerStrip (1 Short): 64
+     StripByteCounts (1 Long8): 12288
+

+

BigTIFFMotorolaLongStrips.tif

+

+ This BigTIFF has Motorola byte order, plus, it's divided over two strips. StripOffsets and StripByteCounts tags have DataType Long, so their actual value fits inside the IFD. +

+

+ Format: BigTIFF
+ Byte Order: Motorola
+ Ifd Offset: 12304
+     ImageWidth (1 Short): 64
+     ImageLength (1 Short): 64
+     BitsPerSample (3 Short): 8, 8, 8
+     PhotometricInterpretation (1 Short): RGB
+     StripOffsets (2 Long): 16, 6160
+     SamplesPerPixel (1 Short): 3
+     RowsPerStrip (1 Short): 32
+     StripByteCounts (2 Long): 6144, 6144
+

+

BigTIFFLong8Tiles.tif

+

+ BigTIFFLong8Tiles.tif is a tiled BigTIFF. TileOffsets and TileByteCounts tags specify DataType Long8. +

+

+ Format: BigTIFF
+ Byte Order: Intel
+ Ifd Offset: 12368
+     ImageWidth (1 Short): 64
+     ImageLength (1 Short): 64
+     BitsPerSample (3 Short): 8, 8, 8
+     PhotometricInterpretation (1 Short): RGB
+     SamplesPerPixel (1 Short): 3
+     TileWidth (1 Short): 32
+     TileLength (1 Short): 32
+     TileOffsets (4 Long8): 16, 3088, 6160, 9232
+     TileByteCounts (4 Long8): 3072, 3072, 3072, 3072
+

+

BigTIFFSubIFD4.tif

+

+ This BigTIFF contains two pages, the second page showing almost the same image content as the first, except that the black square is white, and text color is black. + Both pages point to a downsample SubIFD, using SubIFDs DataType TIFF_IFD. +

+

+ Format: BigTIFF
+ Byte Order: Intel
+ Ifd Offset: 15572
+     ImageWidth (1 Short): 64
+     ImageLength (1 Short): 64
+     BitsPerSample (3 Short): 8, 8, 8
+     PhotometricInterpretation (1 Short): RGB
+     StripOffsets (1 Short): 3284
+     SamplesPerPixel (1 Short): 3
+     RowsPerStrip (1 Short): 64
+     StripByteCounts (1 Short): 12288
+     SubIFDs (1 IFD): 3088
+ SubIfd Offset: 3088
+     NewSubFileType (1 Long): 1
+     ImageWidth (1 Short): 32
+     ImageLength (1 Short): 32
+     BitsPerSample (3 Short): 8, 8, 8
+     PhotometricInterpretation (1 Short): RGB
+     StripOffsets (1 Short): 16
+     SamplesPerPixel (1 Short): 3
+     RowsPerStrip (1 Short): 32
+     StripByteCounts (1 Short): 3072
+ Ifd Offset: 31324
+     ImageWidth (1 Short): 64
+     ImageLength (1 Short): 64
+     BitsPerSample (3 Short): 8, 8, 8
+     PhotometricInterpretation (1 Short): RGB
+     StripOffsets (1 Short): 19036
+     SamplesPerPixel (1 Short): 3
+     RowsPerStrip (1 Short): 64
+     StripByteCounts (1 Short): 12288
+     SubIFDs (1 IFD): 18840
+ SubIfd Offset: 18840
+     NewSubFileType (1 Long): 1
+     ImageWidth (1 Short): 32
+     ImageLength (1 Short): 32
+     BitsPerSample (3 Short): 8, 8, 8
+     PhotometricInterpretation (1 Short): RGB
+     StripOffsets (1 Short): 15768
+     SamplesPerPixel (1 Short): 3
+     RowsPerStrip (1 Short): 32
+     StripByteCounts (1 Short): 3072
+

+

BigTIFFSubIFD8.tif

+

+ BigTIFFSubIFD4.tif is very much the same as BigTIFFSubIFD4.tif, except that the new DataType TIFF_IFD8 is used for the SubIFDs tag. +

+

+ Format: BigTIFF
+ Byte Order: Intel
+ Ifd Offset: 15572
+     ImageWidth (1 Short): 64
+     ImageLength (1 Short): 64
+     BitsPerSample (3 Short): 8, 8, 8
+     PhotometricInterpretation (1 Short): RGB
+     StripOffsets (1 Short): 3284
+     SamplesPerPixel (1 Short): 3
+     RowsPerStrip (1 Short): 64
+     StripByteCounts (1 Short): 12288
+     SubIFDs (1 IFD8): 3088
+ SubIfd Offset: 3088
+     NewSubFileType (1 Long): 1
+     ImageWidth (1 Short): 32
+     ImageLength (1 Short): 32
+     BitsPerSample (3 Short): 8, 8, 8
+     PhotometricInterpretation (1 Short): RGB
+     StripOffsets (1 Short): 16
+     SamplesPerPixel (1 Short): 3
+     RowsPerStrip (1 Short): 32
+     StripByteCounts (1 Short): 3072
+ Ifd Offset: 31324
+     ImageWidth (1 Short): 64
+     ImageLength (1 Short): 64
+     BitsPerSample (3 Short): 8, 8, 8
+     PhotometricInterpretation (1 Short): RGB
+     StripOffsets (1 Short): 19036
+     SamplesPerPixel (1 Short): 3
+     RowsPerStrip (1 Short): 64
+     StripByteCounts (1 Short): 12288
+     SubIFDs (1 IFD8): 18840
+ SubIfd Offset: 18840
+     NewSubFileType (1 Long): 1
+     ImageWidth (1 Short): 32
+     ImageLength (1 Short): 32
+     BitsPerSample (3 Short): 8, 8, 8
+     PhotometricInterpretation (1 Short): RGB
+     StripOffsets (1 Short): 15768
+     SamplesPerPixel (1 Short): 3
+     RowsPerStrip (1 Short): 32
+     StripByteCounts (1 Short): 3072
+

+ + diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFFSubIFD4.tif b/tests/Images/Input/Tiff/BigTiff/BigTIFFSubIFD4.tif new file mode 100644 index 000000000..cd7bdf0b5 --- /dev/null +++ b/tests/Images/Input/Tiff/BigTiff/BigTIFFSubIFD4.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:85c8da46abc2284f0ddac10bd03a7c98ee51024840b7e43f41f1c6a09bc02e7e +size 31520 diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFFSubIFD8.tif b/tests/Images/Input/Tiff/BigTiff/BigTIFFSubIFD8.tif new file mode 100644 index 000000000..bb0b3bf18 --- /dev/null +++ b/tests/Images/Input/Tiff/BigTiff/BigTIFFSubIFD8.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:93e5ac30f507bec7936746ad6e109631c09f9b2332081e986063219ad2452a4a +size 31520 diff --git a/tests/Images/Input/Tiff/BigTiff/Classic.tif b/tests/Images/Input/Tiff/BigTiff/Classic.tif new file mode 100644 index 000000000..1fa6be78b --- /dev/null +++ b/tests/Images/Input/Tiff/BigTiff/Classic.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cfa7fcf6927be5de644beb238067479e8d834d0cbe2257b00302f5dde84a1c1a +size 12404 diff --git a/tests/Images/Input/Tiff/BigTiff/readme.md b/tests/Images/Input/Tiff/BigTiff/readme.md new file mode 100644 index 000000000..be5d28906 --- /dev/null +++ b/tests/Images/Input/Tiff/BigTiff/readme.md @@ -0,0 +1,3 @@ +BigTIFF samples. + +Downloaded from AWARE SYSTEMS: https://www.awaresystems.be/imaging/tiff/bigtiff.html \ No newline at end of file From c5c4f64c89e6a274dbbc00aa24a49d93c21ba3d3 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sat, 11 Sep 2021 22:28:54 +0300 Subject: [PATCH 04/62] format detector for BigTiff --- .../Formats/Tiff/TiffConfigurationModule.cs | 4 +- .../Formats/Tiff/TiffImageFormatDetector.cs | 61 +++++++++++++++++-- 2 files changed, 58 insertions(+), 7 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs b/src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs index e96dba207..5dfda95e8 100644 --- a/src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs +++ b/src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff { configuration.ImageFormatsManager.SetEncoder(TiffFormat.Instance, new TiffEncoder()); configuration.ImageFormatsManager.SetDecoder(TiffFormat.Instance, new TiffDecoder()); - configuration.ImageFormatsManager.AddImageFormatDetector(new TiffImageFormatDetector()); + configuration.ImageFormatsManager.AddImageFormatDetector(new TiffImageFormatDetector(false)); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs b/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs index f7e6f7a99..297da2dd8 100644 --- a/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs +++ b/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs @@ -10,8 +10,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff ///
public sealed class TiffImageFormatDetector : IImageFormatDetector { + private readonly bool isBigTiff; + + /// + /// Initializes a new instance of the class. + /// + /// if set to true [is big tiff]. + public TiffImageFormatDetector(bool isBigTiff) => this.isBigTiff = isBigTiff; + /// - public int HeaderSize => 4; + public int HeaderSize => this.isBigTiff ? 8 : 4; /// public IImageFormat DetectFormat(ReadOnlySpan header) @@ -26,9 +34,52 @@ namespace SixLabors.ImageSharp.Formats.Tiff 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 + if (header.Length >= this.HeaderSize) + { + if (header[0] == 0x49 && header[1] == 0x49) + { + // Little-endian + if (!this.isBigTiff) + { + if (header[2] == 0x2A && header[3] == 0x00) + { + return true; + } + } + else + { + if (header[2] == 0x2B && header[3] == 0x00 && header[4] == 8 && header[5] == 0 && header[6] == 0 && header[7] == 0) + { + return true; + } + } + } + else if (header[0] == 0x4D && header[1] == 0x4D) + { + // Big-endian + if (header[2] == 0x00 && (header[3] == 0x2A || header[3] == 0x2B)) + { + return true; + } + + if (!this.isBigTiff) + { + if (header[2] == 0 && header[3] == 0x2A) + { + return true; + } + } + else + { + if (header[2] == 0 && header[3] == 0x2A && header[4] == 0 && header[5] == 8 && header[6] == 0 && header[7] == 0) + { + return true; + } + } + } + } + + return false; } } -} \ No newline at end of file +} From a6c038b52c0ed3256fc04a9f559c65722a769b98 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sun, 12 Sep 2021 11:07:37 +0300 Subject: [PATCH 05/62] Revert Number changes --- src/ImageSharp/Common/Helpers/Numerics.cs | 32 -------- .../Profiles/Exif/Values/ExifNumber.cs | 17 ----- .../Profiles/Exif/Values/ExifNumberArray.cs | 37 --------- src/ImageSharp/Primitives/Number.cs | 75 +++---------------- 4 files changed, 12 insertions(+), 149 deletions(-) diff --git a/src/ImageSharp/Common/Helpers/Numerics.cs b/src/ImageSharp/Common/Helpers/Numerics.cs index 885443e86..ba5c588ca 100644 --- a/src/ImageSharp/Common/Helpers/Numerics.cs +++ b/src/ImageSharp/Common/Helpers/Numerics.cs @@ -226,38 +226,6 @@ namespace SixLabors.ImageSharp return value; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong Clamp(ulong value, ulong min, ulong max) - { - if (value > max) - { - return max; - } - - if (value < min) - { - return min; - } - - return value; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long Clamp(long value, long min, long max) - { - if (value > max) - { - return max; - } - - if (value < min) - { - return min; - } - - return value; - } - /// /// Returns the value clamped to the inclusive range of min and max. /// diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumber.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumber.cs index d16de4d22..9e206b23d 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumber.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumber.cs @@ -21,11 +21,6 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif { get { - if (this.Value > uint.MaxValue) - { - return ExifDataType.Long8; - } - if (this.Value > ushort.MaxValue) { return ExifDataType.Long; @@ -46,18 +41,6 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif switch (value) { - case long longValue: - if (longValue >= 0) - { - this.Value = (ulong)longValue; - return true; - } - - return false; - case ulong ulongValue: - this.Value = ulongValue; - - return true; case int intValue: if (intValue >= uint.MinValue) { diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs index 15b29dcec..2d3a93aed 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs @@ -26,11 +26,6 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif for (int i = 0; i < this.Value.Length; i++) { - if (this.Value[i] > uint.MaxValue) - { - return ExifDataType.Long8; - } - if (this.Value[i] > ushort.MaxValue) { return ExifDataType.Long; @@ -50,10 +45,6 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif switch (value) { - case long val: - return this.SetSingle(val); - case ulong val: - return this.SetSingle(val); case int val: return this.SetSingle(val); case uint val: @@ -62,10 +53,6 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return this.SetSingle(val); case ushort val: return this.SetSingle(val); - case long[] array: - return this.SetArray(array); - case ulong[] array: - return this.SetArray(array); case int[] array: return this.SetArray(array); case uint[] array: @@ -87,30 +74,6 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return true; } - private bool SetArray(long[] values) - { - var numbers = new Number[values.Length]; - for (int i = 0; i < values.Length; i++) - { - numbers[i] = values[i]; - } - - this.Value = numbers; - return true; - } - - private bool SetArray(ulong[] values) - { - var numbers = new Number[values.Length]; - for (int i = 0; i < values.Length; i++) - { - numbers[i] = values[i]; - } - - this.Value = numbers; - return true; - } - private bool SetArray(int[] values) { var numbers = new Number[values.Length]; diff --git a/src/ImageSharp/Primitives/Number.cs b/src/ImageSharp/Primitives/Number.cs index 690b72d79..e33d2344a 100644 --- a/src/ImageSharp/Primitives/Number.cs +++ b/src/ImageSharp/Primitives/Number.cs @@ -14,19 +14,19 @@ namespace SixLabors.ImageSharp public struct Number : IEquatable, IComparable { [FieldOffset(0)] - private readonly long signedValue; + private readonly int signedValue; [FieldOffset(0)] - private readonly ulong unsignedValue; + private readonly uint unsignedValue; - [FieldOffset(8)] + [FieldOffset(4)] private readonly bool isSigned; /// /// Initializes a new instance of the struct. /// /// The value of the number. - public Number(long value) + public Number(int value) : this() { this.signedValue = value; @@ -37,70 +37,30 @@ namespace SixLabors.ImageSharp /// Initializes a new instance of the struct. ///
/// The value of the number. - public Number(ulong value) + public Number(uint value) : this() { this.unsignedValue = value; this.isSigned = false; } - /// - /// Converts the specified to an instance of this type. - /// - /// The value. - public static implicit operator Number(long value) => new Number(value); - - /// - /// Converts the specified to an instance of this type. - /// - /// The value. - public static implicit operator Number(ulong value) => new Number(value); - /// /// Converts the specified to an instance of this type. /// /// The value. - public static implicit operator Number(int value) => new Number((long)value); + public static implicit operator Number(int value) => new Number(value); /// /// Converts the specified to an instance of this type. /// /// The value. - public static implicit operator Number(uint value) => new Number((ulong)value); - - /// - /// Converts the specified to an instance of this type. - /// - /// The value. - public static implicit operator Number(short value) => new Number((long)value); + public static implicit operator Number(uint value) => new Number(value); /// /// Converts the specified to an instance of this type. /// /// The value. - public static implicit operator Number(ushort value) => new Number((ulong)value); - - /// - /// Converts the specified to an instance of this type. - /// - /// The to convert. - public static explicit operator long(Number number) - { - return number.isSigned - ? number.signedValue - : (long)Numerics.Clamp(number.unsignedValue, 0, long.MaxValue); - } - - /// - /// Converts the specified to an instance of this type. - /// - /// The to convert. - public static explicit operator ulong(Number number) - { - return number.isSigned - ? (ulong)Numerics.Clamp(number.signedValue, 0, long.MaxValue) - : number.unsignedValue; - } + public static implicit operator Number(ushort value) => new Number((uint)value); /// /// Converts the specified to a . @@ -109,8 +69,8 @@ namespace SixLabors.ImageSharp public static explicit operator int(Number number) { return number.isSigned - ? (int)Numerics.Clamp(number.signedValue, int.MinValue, int.MaxValue) - : (int)Numerics.Clamp(number.unsignedValue, 0, (uint)int.MaxValue); + ? number.signedValue + : (int)Numerics.Clamp(number.unsignedValue, 0, int.MaxValue); } /// @@ -120,19 +80,8 @@ namespace SixLabors.ImageSharp public static explicit operator uint(Number number) { return number.isSigned - ? (uint)Numerics.Clamp(number.signedValue, uint.MinValue, uint.MaxValue) - : (uint)Numerics.Clamp(number.unsignedValue, uint.MinValue, uint.MaxValue); - } - - /// - /// Converts the specified to a . - /// - /// The to convert. - public static explicit operator short(Number number) - { - return number.isSigned - ? (short)Numerics.Clamp(number.signedValue, short.MinValue, short.MaxValue) - : (short)Numerics.Clamp(number.unsignedValue, 0, (ushort)short.MaxValue); + ? (uint)Numerics.Clamp(number.signedValue, 0, int.MaxValue) + : number.unsignedValue; } /// From c6383861d22ca81fad55af57c9d58b1aa0af411c Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sun, 12 Sep 2021 11:08:50 +0300 Subject: [PATCH 06/62] Correct read Long8 tags --- .../Formats/Tiff/TiffDecoderCore.cs | 18 +-- .../Metadata/Profiles/Exif/ExifReader.cs | 28 +++- .../Profiles/Exif/Values/ExifLong8Array.cs | 128 +++++++++++++++++- 3 files changed, 163 insertions(+), 11 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 922c1f174..4ab920ff8 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -211,16 +211,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff var frame = new ImageFrame(this.Configuration, width, height, imageFrameMetaData); int rowsPerStrip = tags.GetValue(ExifTag.RowsPerStrip) != null ? (int)tags.GetValue(ExifTag.RowsPerStrip).Value : TiffConstants.RowsPerStripInfinity; - Number[] stripOffsets = tags.GetValue(ExifTag.StripOffsets)?.Value; - Number[] stripByteCounts = tags.GetValue(ExifTag.StripByteCounts)?.Value; + ExifLong8Array stripOffsets = tags.GetValueInternal(ExifTag.StripOffsets) as ExifLong8Array; + ExifLong8Array stripByteCounts = tags.GetValueInternal(ExifTag.StripByteCounts) as ExifLong8Array; if (this.PlanarConfiguration == TiffPlanarConfiguration.Planar) { - this.DecodeStripsPlanar(frame, rowsPerStrip, stripOffsets, stripByteCounts, cancellationToken); + this.DecodeStripsPlanar(frame, rowsPerStrip, stripOffsets.Value, stripByteCounts.Value, cancellationToken); } else { - this.DecodeStripsChunky(frame, rowsPerStrip, stripOffsets, stripByteCounts, cancellationToken); + this.DecodeStripsChunky(frame, rowsPerStrip, stripOffsets.Value, stripByteCounts.Value, cancellationToken); } return frame; @@ -276,7 +276,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// An array of byte offsets to each strip in the image. /// An array of the size of each strip (in bytes). /// The token to monitor cancellation. - private void DecodeStripsPlanar(ImageFrame frame, int rowsPerStrip, Number[] stripOffsets, Number[] stripByteCounts, CancellationToken cancellationToken) + private void DecodeStripsPlanar(ImageFrame frame, int rowsPerStrip, ulong[] stripOffsets, ulong[] stripByteCounts, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { int stripsPerPixel = this.BitsPerSample.Channels; @@ -329,8 +329,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff { decompressor.Decompress( this.inputStream, - (uint)stripOffsets[stripIndex], - (uint)stripByteCounts[stripIndex], + stripOffsets[stripIndex], + stripByteCounts[stripIndex], stripHeight, stripBuffers[planeIndex].GetSpan()); stripIndex += stripsPerPlane; @@ -357,7 +357,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The strip offsets. /// The strip byte counts. /// The token to monitor cancellation. - private void DecodeStripsChunky(ImageFrame frame, int rowsPerStrip, Number[] stripOffsets, Number[] stripByteCounts, CancellationToken cancellationToken) + private void DecodeStripsChunky(ImageFrame frame, int rowsPerStrip, ulong[] stripOffsets, ulong[] stripByteCounts, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { // If the rowsPerStrip has the default value, which is effectively infinity. That is, the entire image is one strip. @@ -413,7 +413,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff break; } - decompressor.Decompress(this.inputStream, (uint)stripOffsets[stripIndex], (uint)stripByteCounts[stripIndex], stripHeight, stripBufferSpan); + decompressor.Decompress(this.inputStream, stripOffsets[stripIndex], stripByteCounts[stripIndex], stripHeight, stripBufferSpan); colorDecoder.Decode(stripBufferSpan, pixels, 0, top, frame.Width, stripHeight); } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs index 5c26fde7b..b9628ad73 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs @@ -420,8 +420,34 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif // The StripOffsets, StripByteCounts, TileOffsets, and TileByteCounts tags are allowed to have the datatype TIFF_LONG8 in BigTIFF. // Old datatypes TIFF_LONG, and TIFF_SHORT where allowed in the TIFF 6.0 specification, are still valid in BigTIFF, too. + // Likewise, tags that point to other IFDs, like e.g. the SubIFDs tag, are now allowed to have the datatype TIFF_IFD8 in BigTIFF. + // Again, the old datatypes TIFF_IFD, and the hardly recommendable TIFF_LONG, are still valid, too. // https://www.awaresystems.be/imaging/tiff/bigtiff.html - ExifValue exifValue = exifValue = ExifValues.Create(tag) ?? ExifValues.Create(tag, dataType, numberOfComponents); + ExifValue exifValue = null; + switch (tag) + { + case ExifTagValue.StripOffsets: + exifValue = new ExifLong8Array(ExifTagValue.StripOffsets); + break; + case ExifTagValue.StripByteCounts: + exifValue = new ExifLong8Array(ExifTagValue.StripByteCounts); + break; + case ExifTagValue.TileOffsets: + exifValue = new ExifLong8Array(ExifTagValue.TileOffsets); + break; + case ExifTagValue.TileByteCounts: + exifValue = new ExifLong8Array(ExifTagValue.TileByteCounts); + break; + //case ExifTagValue.SubIFDOffset: + // exifValue = new ExifLong8Array(ExifTagValue.SubIFDOffset); + // break; + //case ExifTagValue.GPSIFDOffset: + // exifValue = new ExifLong8Array(ExifTagValue.GPSIFDOffset); + // break; + default: + exifValue = exifValue = ExifValues.Create(tag) ?? ExifValues.Create(tag, dataType, numberOfComponents); + break; + } if (exifValue is null) { diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs index 3cf59adac..17fe888f7 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs @@ -20,8 +20,134 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif { } - public override ExifDataType DataType => ExifDataType.Long8; + public override ExifDataType DataType + { + get + { + if (this.Value is null) + { + return ExifDataType.Long; + } + + for (int i = 0; i < this.Value.Length; i++) + { + if (this.Value[i] > uint.MaxValue) + { + return ExifDataType.Long8; + } + } + + return ExifDataType.Long; + } + } + + public override bool TrySetValue(object value) + { + if (base.TrySetValue(value)) + { + return true; + } + + switch (value) + { + case int val: + return this.SetSingle((ulong)val); + case uint val: + return this.SetSingle((ulong)val); + case short val: + return this.SetSingle((ulong)val); + case ushort val: + return this.SetSingle((ulong)val); + case long[] array: + return this.SetArray(array); + case ulong[] array: + return this.SetArray(array); + case int[] array: + return this.SetArray(array); + case uint[] array: + return this.SetArray(array); + case short[] array: + return this.SetArray(array); + case ushort[] array: + return this.SetArray(array); + } + + return false; + } public override IExifValue DeepClone() => new ExifLong8Array(this); + + + private bool SetSingle(ulong value) + { + this.Value = new[] { value }; + return true; + } + + private bool SetArray(long[] values) + { + var numbers = new ulong[values.Length]; + for (int i = 0; i < values.Length; i++) + { + numbers[i] = (ulong)values[i]; + } + + this.Value = numbers; + return true; + } + + private bool SetArray(ulong[] values) + { + this.Value = values; + return true; + } + + private bool SetArray(int[] values) + { + var numbers = new ulong[values.Length]; + for (int i = 0; i < values.Length; i++) + { + numbers[i] = (ulong)values[i]; + } + + this.Value = numbers; + return true; + } + + private bool SetArray(uint[] values) + { + var numbers = new ulong[values.Length]; + for (int i = 0; i < values.Length; i++) + { + numbers[i] = (ulong)values[i]; + } + + this.Value = numbers; + return true; + } + + private bool SetArray(short[] values) + { + var numbers = new ulong[values.Length]; + for (int i = 0; i < values.Length; i++) + { + numbers[i] = (ulong)values[i]; + } + + this.Value = numbers; + return true; + } + + private bool SetArray(ushort[] values) + { + var numbers = new ulong[values.Length]; + for (int i = 0; i < values.Length; i++) + { + numbers[i] = (ulong)values[i]; + } + + this.Value = numbers; + return true; + } } } From 393659f3863ecc8351bf0a6ad2a95a5b29137517 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sun, 12 Sep 2021 11:35:46 +0300 Subject: [PATCH 07/62] Add IsBigTif flag property in metadata --- src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs | 3 +++ src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs | 4 ++-- .../Formats/Tiff/TiffDecoderMetadataCreator.cs | 13 ++++--------- src/ImageSharp/Formats/Tiff/TiffMetadata.cs | 8 ++++++++ 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs index bd673cf3a..e8a087123 100644 --- a/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs +++ b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs @@ -29,6 +29,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// public ByteOrder ByteOrder { get; private set; } + public bool IsBigTiff { get; private set; } + /// /// Reads image file directories. /// @@ -40,6 +42,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff headerReader.ReadFileHeader(); this.nextIfdOffset = headerReader.FirstIfdOffset; + this.IsBigTiff = headerReader.IsBigTiff; return this.ReadIfds(headerReader.IsBigTiff); } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 4ab920ff8..3aae61448 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -154,7 +154,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff frames.Add(frame); } - ImageMetadata metadata = TiffDecoderMetadataCreator.Create(frames, this.ignoreMetadata, reader.ByteOrder); + ImageMetadata metadata = TiffDecoderMetadataCreator.Create(frames, this.ignoreMetadata, reader.ByteOrder, reader.IsBigTiff); // TODO: Tiff frames can have different sizes ImageFrame root = frames[0]; @@ -180,7 +180,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff ExifProfile rootFrameExifProfile = directories.First(); var rootMetadata = TiffFrameMetadata.Parse(rootFrameExifProfile); - ImageMetadata metadata = TiffDecoderMetadataCreator.Create(reader.ByteOrder, rootFrameExifProfile); + ImageMetadata metadata = TiffDecoderMetadataCreator.Create(reader.ByteOrder, reader.IsBigTiff, rootFrameExifProfile); int width = GetImageWidth(rootFrameExifProfile); int height = GetImageHeight(rootFrameExifProfile); diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs index 6f8a81a82..ddaf6fa96 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs @@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// internal static class TiffDecoderMetadataCreator { - public static ImageMetadata Create(List> frames, bool ignoreMetadata, ByteOrder byteOrder) + public static ImageMetadata Create(List> frames, bool ignoreMetadata, ByteOrder byteOrder, bool isBigTiff) where TPixel : unmanaged, IPixel { if (frames.Count < 1) @@ -26,13 +26,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff TiffThrowHelper.ThrowImageFormatException("Expected at least one frame."); } - var imageMetaData = new ImageMetadata(); - ExifProfile exifProfileRootFrame = frames[0].Metadata.ExifProfile; - - SetResolution(imageMetaData, exifProfileRootFrame); - - TiffMetadata tiffMetadata = imageMetaData.GetTiffMetadata(); - tiffMetadata.ByteOrder = byteOrder; + ImageMetadata imageMetaData = Create(byteOrder, isBigTiff, frames[0].Metadata.ExifProfile); if (!ignoreMetadata) { @@ -56,13 +50,14 @@ namespace SixLabors.ImageSharp.Formats.Tiff return imageMetaData; } - public static ImageMetadata Create(ByteOrder byteOrder, ExifProfile exifProfile) + public static ImageMetadata Create(ByteOrder byteOrder, bool isBigTiff, ExifProfile exifProfile) { var imageMetaData = new ImageMetadata(); SetResolution(imageMetaData, exifProfile); TiffMetadata tiffMetadata = imageMetaData.GetTiffMetadata(); tiffMetadata.ByteOrder = byteOrder; + tiffMetadata.IsBigTiff = isBigTiff; return imageMetaData; } diff --git a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs index cf1ab3754..d537981db 100644 --- a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs @@ -26,6 +26,14 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// public ByteOrder ByteOrder { get; set; } + /// + /// Gets or sets a value indicating whether this instance is BigTiff format. + /// + /// + /// true if this instance BigTiff format; otherwise, false. + /// + public bool IsBigTiff { get; set; } + /// public IDeepCloneable DeepClone() => new TiffMetadata(this); } From 6755286a8ee98363262d4a6edcae87f47af06a44 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sun, 12 Sep 2021 11:09:40 +0300 Subject: [PATCH 08/62] Add BigTiff into ConfigurationModule, add identify tests --- .../Formats/Tiff/TiffConfigurationModule.cs | 2 + .../Formats/Tiff/BigTiffDecoderTests.cs | 51 +++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs b/src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs index 5dfda95e8..656483435 100644 --- a/src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs +++ b/src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs @@ -14,6 +14,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff configuration.ImageFormatsManager.SetEncoder(TiffFormat.Instance, new TiffEncoder()); configuration.ImageFormatsManager.SetDecoder(TiffFormat.Instance, new TiffDecoder()); configuration.ImageFormatsManager.AddImageFormatDetector(new TiffImageFormatDetector(false)); + + configuration.ImageFormatsManager.AddImageFormatDetector(new TiffImageFormatDetector(true)); } } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs index fc507513b..7843ba4b9 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs @@ -3,6 +3,8 @@ // ReSharper disable InconsistentNaming using System; +using System.IO; +using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using Xunit; @@ -31,5 +33,54 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [WithFile(BigTIFFLong8Tiles, PixelTypes.Rgba32)] public void ThrowsNotSupported(TestImageProvider provider) where TPixel : unmanaged, IPixel => Assert.Throws(() => provider.GetImage(TiffDecoder)); + + [Theory] + [InlineData(BigTIFF, 24, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] + [InlineData(BigTIFFLong, 24, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] + [InlineData(BigTIFFLong8, 24, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] + [InlineData(BigTIFFMotorola, 24, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] + [InlineData(BigTIFFMotorolaLongStrips, 24, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] + [InlineData(BigTIFFSubIFD4, 24, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] + [InlineData(BigTIFFSubIFD8, 24, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] + public void Identify(string imagePath, int expectedPixelSize, int expectedWidth, int expectedHeight, double expectedHResolution, double expectedVResolution, PixelResolutionUnit expectedResolutionUnit) + { + var testFile = TestFile.Create(imagePath); + using (var stream = new MemoryStream(testFile.Bytes, false)) + { + IImageInfo info = Image.Identify(stream); + + Assert.Equal(expectedPixelSize, info.PixelType?.BitsPerPixel); + Assert.Equal(expectedWidth, info.Width); + Assert.Equal(expectedHeight, info.Height); + Assert.NotNull(info.Metadata); + Assert.Equal(expectedHResolution, info.Metadata.HorizontalResolution); + Assert.Equal(expectedVResolution, info.Metadata.VerticalResolution); + Assert.Equal(expectedResolutionUnit, info.Metadata.ResolutionUnits); + + ImageSharp.Formats.Tiff.TiffMetadata tiffmeta = info.Metadata.GetTiffMetadata(); + Assert.NotNull(tiffmeta); + Assert.True(tiffmeta.IsBigTiff); + } + } + + [Theory] + [InlineData(BigTIFFLong, ImageSharp.ByteOrder.LittleEndian)] + [InlineData(BigTIFFMotorola, ImageSharp.ByteOrder.BigEndian)] + public void ByteOrder(string imagePath, ByteOrder expectedByteOrder) + { + var testFile = TestFile.Create(imagePath); + using (var stream = new MemoryStream(testFile.Bytes, false)) + { + IImageInfo info = Image.Identify(stream); + + Assert.NotNull(info.Metadata); + Assert.Equal(expectedByteOrder, info.Metadata.GetTiffMetadata().ByteOrder); + + stream.Seek(0, SeekOrigin.Begin); + + using var img = Image.Load(stream); + Assert.Equal(expectedByteOrder, img.Metadata.GetTiffMetadata().ByteOrder); + } + } } } From a4a0d1c2e73ec6c60478e5b4c290995546b4383b Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sun, 12 Sep 2021 12:05:31 +0300 Subject: [PATCH 09/62] Correct parse strip tags --- .../Formats/Tiff/TiffDecoderCore.cs | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 3aae61448..9822cf63b 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -41,6 +41,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// private ByteOrder byteOrder; + /// + /// Indicating whether is BigTiff format. + /// + private bool isBigTiff; + /// /// Initializes a new instance of the class. /// @@ -145,6 +150,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff IEnumerable directories = reader.Read(); this.byteOrder = reader.ByteOrder; + this.isBigTiff = reader.IsBigTiff; var frames = new List>(); foreach (ExifProfile ifd in directories) @@ -211,16 +217,27 @@ namespace SixLabors.ImageSharp.Formats.Tiff var frame = new ImageFrame(this.Configuration, width, height, imageFrameMetaData); int rowsPerStrip = tags.GetValue(ExifTag.RowsPerStrip) != null ? (int)tags.GetValue(ExifTag.RowsPerStrip).Value : TiffConstants.RowsPerStripInfinity; - ExifLong8Array stripOffsets = tags.GetValueInternal(ExifTag.StripOffsets) as ExifLong8Array; - ExifLong8Array stripByteCounts = tags.GetValueInternal(ExifTag.StripByteCounts) as ExifLong8Array; + + ulong[] stripOffsets; + ulong[] stripByteCounts; + if (this.isBigTiff) + { + stripOffsets = tags.GetValueInternal(ExifTag.StripOffsets)?.GetValue() as ulong[]; + stripByteCounts = tags.GetValueInternal(ExifTag.StripByteCounts)?.GetValue() as ulong[]; + } + else + { + stripOffsets = tags.GetValue(ExifTag.StripOffsets)?.Value.Select(s => (ulong)(uint)s).ToArray(); + stripByteCounts = tags.GetValue(ExifTag.StripByteCounts)?.Value.Select(s => (ulong)(uint)s).ToArray(); + } if (this.PlanarConfiguration == TiffPlanarConfiguration.Planar) { - this.DecodeStripsPlanar(frame, rowsPerStrip, stripOffsets.Value, stripByteCounts.Value, cancellationToken); + this.DecodeStripsPlanar(frame, rowsPerStrip, stripOffsets, stripByteCounts, cancellationToken); } else { - this.DecodeStripsChunky(frame, rowsPerStrip, stripOffsets.Value, stripByteCounts.Value, cancellationToken); + this.DecodeStripsChunky(frame, rowsPerStrip, stripOffsets, stripByteCounts, cancellationToken); } return frame; From d1c78f8d226c0359ed0dcfe8219d8e53364f4bd3 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sun, 12 Sep 2021 12:06:10 +0300 Subject: [PATCH 10/62] revert missed value --- src/ImageSharp/Metadata/Profiles/Exif/ExifDataTypes.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifDataTypes.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifDataTypes.cs index ee30c6b08..729efb390 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifDataTypes.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifDataTypes.cs @@ -36,6 +36,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return 4; case ExifDataType.DoubleFloat: case ExifDataType.Rational: + case ExifDataType.SignedRational: case ExifDataType.Long8: case ExifDataType.SignedLong8: case ExifDataType.Ifd8: From 1ed2a9becf73022f96f9434800852cf7462de4d0 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sun, 12 Sep 2021 12:06:37 +0300 Subject: [PATCH 11/62] build fixes --- src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs | 12 ++++++------ .../Metadata/Profiles/Exif/Values/ExifLong8Array.cs | 1 - 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs index b9628ad73..2eaae2664 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs @@ -438,12 +438,12 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif case ExifTagValue.TileByteCounts: exifValue = new ExifLong8Array(ExifTagValue.TileByteCounts); break; - //case ExifTagValue.SubIFDOffset: - // exifValue = new ExifLong8Array(ExifTagValue.SubIFDOffset); - // break; - //case ExifTagValue.GPSIFDOffset: - // exifValue = new ExifLong8Array(ExifTagValue.GPSIFDOffset); - // break; + ////case ExifTagValue.SubIFDOffset: + //// exifValue = new ExifLong8Array(ExifTagValue.SubIFDOffset); + //// break; + ////case ExifTagValue.GPSIFDOffset: + //// exifValue = new ExifLong8Array(ExifTagValue.GPSIFDOffset); + //// break; default: exifValue = exifValue = ExifValues.Create(tag) ?? ExifValues.Create(tag, dataType, numberOfComponents); break; diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs index 17fe888f7..055a50104 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs @@ -77,7 +77,6 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif public override IExifValue DeepClone() => new ExifLong8Array(this); - private bool SetSingle(ulong value) { this.Value = new[] { value }; From 9b042a753634dcad4b5dc92cba49583ef54806fa Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sun, 12 Sep 2021 20:25:01 +0300 Subject: [PATCH 12/62] FormatDetector fixes --- .../Formats/Tiff/TiffImageFormatDetector.cs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs b/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs index 297da2dd8..5755b306a 100644 --- a/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs +++ b/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Initializes a new instance of the class. /// - /// if set to true [is big tiff]. + /// if set to true is BigTiff. public TiffImageFormatDetector(bool isBigTiff) => this.isBigTiff = isBigTiff; /// @@ -39,7 +39,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff if (header[0] == 0x49 && header[1] == 0x49) { // Little-endian - if (!this.isBigTiff) + if (this.isBigTiff is false) { if (header[2] == 0x2A && header[3] == 0x00) { @@ -48,7 +48,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff } else { - if (header[2] == 0x2B && header[3] == 0x00 && header[4] == 8 && header[5] == 0 && header[6] == 0 && header[7] == 0) + if (header[2] == 0x2B && header[3] == 0x00 + && header[4] == 8 && header[5] == 0 && header[6] == 0 && header[7] == 0) { return true; } @@ -57,12 +58,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff else if (header[0] == 0x4D && header[1] == 0x4D) { // Big-endian - if (header[2] == 0x00 && (header[3] == 0x2A || header[3] == 0x2B)) - { - return true; - } - - if (!this.isBigTiff) + if (this.isBigTiff is false) { if (header[2] == 0 && header[3] == 0x2A) { @@ -71,7 +67,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff } else { - if (header[2] == 0 && header[3] == 0x2A && header[4] == 0 && header[5] == 8 && header[6] == 0 && header[7] == 0) + if (header[2] == 0 && header[3] == 0x2B + && header[4] == 0 && header[5] == 8 && header[6] == 0 && header[7] == 0) { return true; } From f370df714101c18cddd67ee6e05304635cf6bd4a Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Fri, 17 Sep 2021 07:54:05 +0300 Subject: [PATCH 13/62] Update src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Günther Foidl --- src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs index 2eaae2664..9f53bf64c 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs @@ -459,7 +459,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif if (size > 8) { ulong newOffset = this.ConvertToUInt64(this.offsetBuffer8); - if (newOffset > ulong.MaxValue || (newOffset + size) > (ulong)this.data.Length) + if (newOffset > ulong.MaxValue || newOffset > (ulong)(this.data.Length - size)) { this.AddInvalidTag(new UnkownExifTag(tag)); return; From 4bb6c7715dddf6ca1ac272890b859ceb0ed42e4e Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Fri, 17 Sep 2021 07:54:29 +0300 Subject: [PATCH 14/62] Update src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Günther Foidl --- src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs index e8a087123..f6bc33079 100644 --- a/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs +++ b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs @@ -49,8 +49,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff private static ByteOrder ReadByteOrder(Stream stream) { - byte[] headerBytes = new byte[2]; - stream.Read(headerBytes, 0, 2); + Span headerBytes = stackalloc byte[2]; + stream.Read(headerBytes); if (headerBytes[0] == TiffConstants.ByteOrderLittleEndian && headerBytes[1] == TiffConstants.ByteOrderLittleEndian) { return ByteOrder.LittleEndian; From 789dc78864c27ec6b79b9143cd9765a0bc04ec7b Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Fri, 17 Sep 2021 08:03:18 +0300 Subject: [PATCH 15/62] build fix --- src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs index 9f53bf64c..a63a40d66 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs @@ -459,7 +459,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif if (size > 8) { ulong newOffset = this.ConvertToUInt64(this.offsetBuffer8); - if (newOffset > ulong.MaxValue || newOffset > (ulong)(this.data.Length - size)) + if (newOffset > ulong.MaxValue || newOffset > ((ulong)this.data.Length - size)) { this.AddInvalidTag(new UnkownExifTag(tag)); return; From ebb277a7942e0c18069bb103294661ec6c774d4d Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Fri, 17 Sep 2021 10:34:29 +0300 Subject: [PATCH 16/62] try improve reading ext values --- .../Formats/Tiff/Ifd/DirectoryReader.cs | 25 ++++---- .../Formats/Tiff/Ifd/EntryReader.cs | 10 +--- .../Metadata/Profiles/Exif/ExifReader.cs | 57 +++++++++---------- 3 files changed, 41 insertions(+), 51 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs index f6bc33079..3c70ee591 100644 --- a/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs +++ b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs @@ -18,10 +18,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff private ulong nextIfdOffset; - // used for sequential read big values (actual for multiframe big files) - // todo: different tags can link to the same data (stream offset) - investigate - private readonly SortedList lazyLoaders = new SortedList(new DuplicateKeyComparer()); - public DirectoryReader(Stream stream) => this.stream = stream; /// @@ -68,23 +64,28 @@ namespace SixLabors.ImageSharp.Formats.Tiff var readers = new List(); while (this.nextIfdOffset != 0 && this.nextIfdOffset < (ulong)this.stream.Length) { - var reader = new EntryReader(this.stream, this.ByteOrder, this.lazyLoaders); + var reader = new EntryReader(this.stream, this.ByteOrder); reader.ReadTags(isBigTiff, this.nextIfdOffset); - this.nextIfdOffset = reader.NextIfdOffset; + if (reader.ExtTags.Count > 0) + { + reader.ExtTags.Sort((t1, t2) => t1.offset.CompareTo(t2.offset)); - readers.Add(reader); - } + if (reader.ExtTags[0].offset < reader.NextIfdOffset) + { + // this means that most likely all elements are placed before next IFD + reader.ReadExtValues(); + } + } - // Sequential reading big values. - foreach (Action loader in this.lazyLoaders.Values) - { - loader(); + this.nextIfdOffset = reader.NextIfdOffset; + readers.Add(reader); } var list = new List(readers.Count); foreach (EntryReader reader in readers) { + reader.ReadExtValues(); var profile = new ExifProfile(reader.Values, reader.InvalidTags); list.Add(profile); } diff --git a/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs b/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs index 7884242ef..5a83febf8 100644 --- a/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs +++ b/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs @@ -12,13 +12,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff { internal class EntryReader : BaseExifReader { - private readonly SortedList lazyLoaders; - - public EntryReader(Stream stream, ByteOrder byteOrder, SortedList lazyLoaders) + public EntryReader(Stream stream, ByteOrder byteOrder) : base(stream) { this.IsBigEndian = byteOrder == ByteOrder.BigEndian; - this.lazyLoaders = lazyLoaders; } public List Values { get; } = new List(); @@ -43,8 +40,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff } } - protected override void RegisterExtLoader(ulong offset, Action reader) => - this.lazyLoaders.Add(offset, reader); + public void ReadExtValues() => this.ReadExtValues(this.Values); } internal class HeaderReader : BaseExifReader @@ -81,7 +77,5 @@ namespace SixLabors.ImageSharp.Formats.Tiff TiffThrowHelper.ThrowInvalidHeader(); } - - protected override void RegisterExtLoader(ulong offset, Action reader) => throw new NotSupportedException(); } } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs index a63a40d66..0cac35197 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs @@ -14,8 +14,6 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif { internal class ExifReader : BaseExifReader { - private readonly List loaders = new List(); - public ExifReader(byte[] exifData) : base(new MemoryStream(exifData ?? throw new ArgumentNullException(nameof(exifData)))) { @@ -41,22 +39,16 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif uint ifdOffset = this.ReadUInt32(); this.ReadValues(values, ifdOffset); + this.ReadExtValues(values); uint thumbnailOffset = this.ReadUInt32(); this.GetThumbnail(thumbnailOffset); this.ReadSubIfd(values); - foreach (Action loader in this.loaders) - { - loader(); - } - return values; } - protected override void RegisterExtLoader(ulong offset, Action loader) => this.loaders.Add(loader); - private void GetThumbnail(uint offset) { if (offset == 0) @@ -121,7 +113,17 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif public bool IsBigEndian { get; protected set; } - protected abstract void RegisterExtLoader(ulong offset, Action loader); + public List<(ulong offset, ExifDataType dataType, ulong numberOfComponents, ExifValue exif)> ExtTags { get; } = new (); + + protected void ReadExtValues(List values) + { + foreach ((ulong offset, ExifDataType dataType, ulong numberOfComponents, ExifValue exif) tag in this.ExtTags) + { + this.ReadExtValue(values, tag); + } + + this.ExtTags.Clear(); + } /// /// Reads the values to the values collection. @@ -173,6 +175,18 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif } } + protected void ReadExtValue(IList values, (ulong offset, ExifDataType dataType, ulong numberOfComponents, ExifValue exif) tag) + { + ulong size = tag.numberOfComponents * ExifDataTypes.GetSize(tag.dataType); + byte[] dataBuffer = new byte[size]; + this.Seek(tag.offset); + if (this.TryReadSpan(dataBuffer)) + { + object value = this.ConvertValue(tag.dataType, dataBuffer, tag.numberOfComponents); + this.Add(values, tag.exif, value); + } + } + protected void ReadSubIfd64(List values) { if (this.exifOffset != 0) @@ -375,21 +389,11 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return; } - this.RegisterExtLoader(newOffset, () => - { - byte[] dataBuffer = new byte[size]; - this.Seek(newOffset); - if (this.TryReadSpan(dataBuffer)) - { - object value = this.ConvertValue(dataType, dataBuffer, numberOfComponents); - this.Add(values, exifValue, value); - } - }); + this.ExtTags.Add((newOffset, dataType, numberOfComponents, exifValue)); } else { object value = this.ConvertValue(dataType, this.offsetBuffer, numberOfComponents); - this.Add(values, exifValue, value); } } @@ -465,16 +469,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return; } - this.RegisterExtLoader(newOffset, () => - { - byte[] dataBuffer = new byte[size]; - this.Seek(newOffset); - if (this.TryReadSpan(dataBuffer)) - { - object value = this.ConvertValue(dataType, dataBuffer, numberOfComponents); - this.Add(values, exifValue, value); - } - }); + this.ExtTags.Add((newOffset, dataType, numberOfComponents, exifValue)); } else { From 7fe80cfd9c229f743649523fa2a297009c28d1c5 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sat, 18 Sep 2021 18:41:32 +0300 Subject: [PATCH 17/62] Implement reading SubIfds (Code 330 (hex 0x014A)), ExifReader improvements --- .../Formats/Tiff/Ifd/DirectoryReader.cs | 2 +- .../Metadata/Profiles/Exif/ExifReader.cs | 110 ++++++++++-------- .../Profiles/Exif/Tags/ExifTag.LongArray.cs | 5 + .../Profiles/Exif/Values/ExifNumberArray.cs | 10 +- .../Profiles/Exif/Values/ExifValues.cs | 1 + .../Formats/Tiff/BigTiffDecoderTests.cs | 28 +++++ .../Profiles/Exif/ExifProfileTests.cs | 6 +- 7 files changed, 105 insertions(+), 57 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs index 3c70ee591..1e298028d 100644 --- a/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs +++ b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs @@ -71,9 +71,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff { reader.ExtTags.Sort((t1, t2) => t1.offset.CompareTo(t2.offset)); + // this means that most likely all elements are placed before next IFD if (reader.ExtTags[0].offset < reader.NextIfdOffset) { - // this means that most likely all elements are placed before next IFD reader.ReadExtValues(); } } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs index 0cac35197..eea42af28 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs @@ -39,13 +39,14 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif uint ifdOffset = this.ReadUInt32(); this.ReadValues(values, ifdOffset); - this.ReadExtValues(values); uint thumbnailOffset = this.ReadUInt32(); this.GetThumbnail(thumbnailOffset); this.ReadSubIfd(values); + this.ReadExtValues(values); + return values; } @@ -78,8 +79,6 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// internal abstract class BaseExifReader { - private readonly byte[] offsetBuffer = new byte[4]; - private readonly byte[] offsetBuffer8 = new byte[8]; private readonly byte[] buf8 = new byte[8]; private readonly byte[] buf4 = new byte[4]; private readonly byte[] buf2 = new byte[2]; @@ -87,9 +86,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif private readonly Stream data; private List invalidTags; - private uint exifOffset; - - private uint gpsOffset; + private List subIfds; protected BaseExifReader(Stream stream) => this.data = stream ?? throw new ArgumentNullException(nameof(stream)); @@ -140,22 +137,23 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif this.Seek(offset); int count = this.ReadUInt16(); + Span offsetBuffer = new byte[4]; for (int i = 0; i < count; i++) { - this.ReadValue(values); + this.ReadValue(values, offsetBuffer); } } protected void ReadSubIfd(List values) { - if (this.exifOffset != 0) + if (this.subIfds is null) { - this.ReadValues(values, this.exifOffset); + return; } - if (this.gpsOffset != 0) + foreach (ulong subIfdOffset in this.subIfds) { - this.ReadValues(values, this.gpsOffset); + this.ReadValues(values, (uint)subIfdOffset); } } @@ -169,9 +167,10 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif this.Seek(offset); ulong count = this.ReadUInt64(); + Span offsetBuffer = new byte[8]; for (ulong i = 0; i < count; i++) { - this.ReadValue64(values); + this.ReadValue64(values, offsetBuffer); } } @@ -182,21 +181,21 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif this.Seek(tag.offset); if (this.TryReadSpan(dataBuffer)) { - object value = this.ConvertValue(tag.dataType, dataBuffer, tag.numberOfComponents); + object value = this.ConvertValue(tag.dataType, dataBuffer, tag.numberOfComponents > 1 || tag.exif.IsArray); this.Add(values, tag.exif, value); } } protected void ReadSubIfd64(List values) { - if (this.exifOffset != 0) + if (this.subIfds is null) { - this.ReadValues64(values, this.exifOffset); + return; } - if (this.gpsOffset != 0) + foreach (ulong subIfdOffset in this.subIfds) { - this.ReadValues64(values, this.gpsOffset); + this.ReadValues64(values, subIfdOffset); } } @@ -231,7 +230,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return Encoding.UTF8.GetString(buffer); } - private object ConvertValue(ExifDataType dataType, ReadOnlySpan buffer, ulong numberOfComponents) + private object ConvertValue(ExifDataType dataType, ReadOnlySpan buffer, bool isArray) { if (buffer.Length == 0) { @@ -245,102 +244,104 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif case ExifDataType.Ascii: return this.ConvertToString(buffer); case ExifDataType.Byte: - if (numberOfComponents == 1) + if (!isArray) { return this.ConvertToByte(buffer); } return buffer.ToArray(); case ExifDataType.DoubleFloat: - if (numberOfComponents == 1) + if (!isArray) { return this.ConvertToDouble(buffer); } return ToArray(dataType, buffer, this.ConvertToDouble); case ExifDataType.Long: - if (numberOfComponents == 1) + case ExifDataType.Ifd: + if (!isArray) { return this.ConvertToUInt32(buffer); } return ToArray(dataType, buffer, this.ConvertToUInt32); case ExifDataType.Rational: - if (numberOfComponents == 1) + if (!isArray) { return this.ToRational(buffer); } return ToArray(dataType, buffer, this.ToRational); case ExifDataType.Short: - if (numberOfComponents == 1) + if (!isArray) { return this.ConvertToShort(buffer); } return ToArray(dataType, buffer, this.ConvertToShort); case ExifDataType.SignedByte: - if (numberOfComponents == 1) + if (!isArray) { return this.ConvertToSignedByte(buffer); } return ToArray(dataType, buffer, this.ConvertToSignedByte); case ExifDataType.SignedLong: - if (numberOfComponents == 1) + if (!isArray) { return this.ConvertToInt32(buffer); } return ToArray(dataType, buffer, this.ConvertToInt32); case ExifDataType.SignedRational: - if (numberOfComponents == 1) + if (!isArray) { return this.ToSignedRational(buffer); } return ToArray(dataType, buffer, this.ToSignedRational); case ExifDataType.SignedShort: - if (numberOfComponents == 1) + if (!isArray) { return this.ConvertToSignedShort(buffer); } return ToArray(dataType, buffer, this.ConvertToSignedShort); case ExifDataType.SingleFloat: - if (numberOfComponents == 1) + if (!isArray) { return this.ConvertToSingle(buffer); } return ToArray(dataType, buffer, this.ConvertToSingle); case ExifDataType.Long8: - if (numberOfComponents == 1) + case ExifDataType.Ifd8: + if (!isArray) { return this.ConvertToUInt64(buffer); } return ToArray(dataType, buffer, this.ConvertToUInt64); case ExifDataType.SignedLong8: - if (numberOfComponents == 1) + if (!isArray) { return this.ConvertToInt64(buffer); } return ToArray(dataType, buffer, this.ConvertToUInt64); case ExifDataType.Undefined: - if (numberOfComponents == 1) + if (!isArray) { return this.ConvertToByte(buffer); } return buffer.ToArray(); default: - throw new NotSupportedException(); + throw new NotSupportedException($"Data type {dataType} is not supported."); } } - private void ReadValue(List values) + private void ReadValue(List values, Span offsetBuffer) { // 2 | 2 | 4 | 4 // tag | type | count | value offset @@ -354,7 +355,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif uint numberOfComponents = this.ReadUInt32(); - this.TryReadSpan(this.offsetBuffer); + this.TryReadSpan(offsetBuffer); // Ensure that the data type is valid if (dataType == ExifDataType.Unknown) @@ -380,7 +381,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif uint size = numberOfComponents * ExifDataTypes.GetSize(dataType); if (size > 4) { - uint newOffset = this.ConvertToUInt32(this.offsetBuffer); + uint newOffset = this.ConvertToUInt32(offsetBuffer); // Ensure that the new index does not overrun the data. if (newOffset > int.MaxValue || (newOffset + size) > this.data.Length) @@ -393,12 +394,12 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif } else { - object value = this.ConvertValue(dataType, this.offsetBuffer, numberOfComponents); + object value = this.ConvertValue(dataType, offsetBuffer.Slice(0, (int)size), numberOfComponents > 1 || exifValue.IsArray); this.Add(values, exifValue, value); } } - private void ReadValue64(List values) + private void ReadValue64(List values, Span offsetBuffer) { if ((this.data.Length - this.data.Position) < 20) { @@ -410,7 +411,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif ulong numberOfComponents = this.ReadUInt64(); - this.TryReadSpan(this.offsetBuffer8); + this.TryReadSpan(offsetBuffer); if (dataType == ExifDataType.Unknown) { @@ -442,12 +443,15 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif case ExifTagValue.TileByteCounts: exifValue = new ExifLong8Array(ExifTagValue.TileByteCounts); break; - ////case ExifTagValue.SubIFDOffset: - //// exifValue = new ExifLong8Array(ExifTagValue.SubIFDOffset); - //// break; - ////case ExifTagValue.GPSIFDOffset: - //// exifValue = new ExifLong8Array(ExifTagValue.GPSIFDOffset); - //// break; + case ExifTagValue.SubIFDOffset: + exifValue = new ExifLong8(ExifTagValue.SubIFDOffset); + break; + case ExifTagValue.GPSIFDOffset: + exifValue = new ExifLong8(ExifTagValue.GPSIFDOffset); + break; + case ExifTagValue.SubIFDs: + exifValue = new ExifLong8Array(ExifTagValue.SubIFDs); + break; default: exifValue = exifValue = ExifValues.Create(tag) ?? ExifValues.Create(tag, dataType, numberOfComponents); break; @@ -462,7 +466,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif ulong size = numberOfComponents * ExifDataTypes.GetSize(dataType); if (size > 8) { - ulong newOffset = this.ConvertToUInt64(this.offsetBuffer8); + ulong newOffset = this.ConvertToUInt64(offsetBuffer); if (newOffset > ulong.MaxValue || newOffset > ((ulong)this.data.Length - size)) { this.AddInvalidTag(new UnkownExifTag(tag)); @@ -473,7 +477,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif } else { - object value = this.ConvertValue(dataType, ((Span)this.offsetBuffer8).Slice(0, (int)size), numberOfComponents); + object value = this.ConvertValue(dataType, offsetBuffer.Slice(0, (int)size), numberOfComponents > 1 || exifValue.IsArray); this.Add(values, exifValue, value); } } @@ -497,11 +501,18 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif if (exif.Tag == ExifTag.SubIFDOffset) { - this.exifOffset = (uint)value; + this.AddSubIfd(value); } else if (exif.Tag == ExifTag.GPSIFDOffset) { - this.gpsOffset = (uint)value; + this.AddSubIfd(value); + } + else if (exif.Tag == ExifTag.SubIFDs) + { + foreach (object val in (Array)value) + { + this.AddSubIfd(val); + } } else { @@ -512,6 +523,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif private void AddInvalidTag(ExifTag tag) => (this.invalidTags ??= new List()).Add(tag); + private void AddSubIfd(object val) + => (this.subIfds ??= new List()).Add(Convert.ToUInt64(val)); + private void Seek(ulong pos) => this.data.Seek((long)pos, SeekOrigin.Begin); diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.LongArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.LongArray.cs index ac4b0a1bf..390599b73 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.LongArray.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.LongArray.cs @@ -65,5 +65,10 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// Gets the TimeZoneOffset exif tag. /// public static ExifTag TimeZoneOffset { get; } = new ExifTag(ExifTagValue.TimeZoneOffset); + + /// + /// Gets the offset to child IFDs exif tag. + /// + public static ExifTag SubIFDs { get; } = new ExifTag(ExifTagValue.SubIFDs); } } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs index 2d3a93aed..9e9e0e737 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs @@ -49,18 +49,18 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return this.SetSingle(val); case uint val: return this.SetSingle(val); - case short val: - return this.SetSingle(val); case ushort val: return this.SetSingle(val); - case int[] array: - return this.SetArray(array); + case short val: + return this.SetSingle(val); case uint[] array: return this.SetArray(array); - case short[] array: + case int[] array: return this.SetArray(array); case ushort[] array: return this.SetArray(array); + case short[] array: + return this.SetArray(array); } return false; diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs index 7e5b35f49..33fb90cc0 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs @@ -92,6 +92,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif case ExifTagValue.StripRowCounts: return new ExifLongArray(ExifTag.StripRowCounts); case ExifTagValue.IntergraphRegisters: return new ExifLongArray(ExifTag.IntergraphRegisters); case ExifTagValue.TimeZoneOffset: return new ExifLongArray(ExifTag.TimeZoneOffset); + case ExifTagValue.SubIFDs: return new ExifLongArray(ExifTag.SubIFDs); case ExifTagValue.ImageWidth: return new ExifNumber(ExifTag.ImageWidth); case ExifTagValue.ImageLength: return new ExifNumber(ExifTag.ImageLength); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs index 7843ba4b9..d59e276cf 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs @@ -3,8 +3,10 @@ // ReSharper disable InconsistentNaming using System; +using System.Linq; using System.IO; using SixLabors.ImageSharp.Metadata; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; using Xunit; @@ -26,6 +28,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [WithFile(BigTIFFMotorolaLongStrips, PixelTypes.Rgba32)] [WithFile(BigTIFFSubIFD4, PixelTypes.Rgba32)] [WithFile(BigTIFFSubIFD8, PixelTypes.Rgba32)] + [WithFile(Indexed4_Deflate, PixelTypes.Rgba32)] + [WithFile(Indexed8_LZW, PixelTypes.Rgba32)] + [WithFile(RLE, PixelTypes.Rgba32)] public void TiffDecoder_CanDecode(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); @@ -42,6 +47,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [InlineData(BigTIFFMotorolaLongStrips, 24, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] [InlineData(BigTIFFSubIFD4, 24, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] [InlineData(BigTIFFSubIFD8, 24, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] + [InlineData(Indexed4_Deflate, 24, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] + [InlineData(Indexed8_LZW, 8, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] + [InlineData(RLE, 1, 32, 32, 96, 96, PixelResolutionUnit.PixelsPerInch)] public void Identify(string imagePath, int expectedPixelSize, int expectedWidth, int expectedHeight, double expectedHResolution, double expectedVResolution, PixelResolutionUnit expectedResolutionUnit) { var testFile = TestFile.Create(imagePath); @@ -82,5 +90,25 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal(expectedByteOrder, img.Metadata.GetTiffMetadata().ByteOrder); } } + + [Theory] + [WithFile(BigTIFFSubIFD8, PixelTypes.Rgba32)] + public void TiffDecoder_SubIfd8(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(TiffDecoder); + + ExifProfile meta = image.Frames.RootFrame.Metadata.ExifProfile; + + Assert.Equal(0, meta.InvalidTags.Count); + Assert.Equal(15, meta.Values.Count); + Assert.Equal(64, (int)meta.GetValue(ExifTag.ImageWidth).Value); + Assert.Equal(64, (int)meta.GetValue(ExifTag.ImageLength).Value); + Assert.Equal(64, (int)meta.GetValue(ExifTag.RowsPerStrip).Value); + + Assert.Equal(2, meta.Values.Count(v => (ushort)v.Tag == (ushort)ExifTagValue.ImageWidth)); + Assert.Equal(2, meta.Values.Count(v => (ushort)v.Tag == (ushort)ExifTagValue.StripOffsets)); + Assert.Equal(2, meta.Values.Count(v => (ushort)v.Tag == (ushort)ExifTagValue.StripByteCounts)); + } } } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs index 1f23838ab..4b8551042 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs @@ -168,8 +168,8 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif 2 x due to use of non-standard padding tag 0xEA1C listed in EXIF Tool. We can read those values but adhere strictly to the 2.3.1 specification when writing. (TODO: Support 2.3.2) https://exiftool.org/TagNames/EXIF.html */ - [InlineData(TestImageWriteFormat.Jpeg, 16)] - [InlineData(TestImageWriteFormat.Png, 16)] + [InlineData(TestImageWriteFormat.Jpeg, 18)] + [InlineData(TestImageWriteFormat.Png, 18)] public void SetValue(TestImageWriteFormat imageFormat, int expectedProfileValueCount) { Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateRgba32Image(); @@ -519,7 +519,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif // todo: duplicate tags Assert.Equal(2, profile.Values.Count(v => (ushort)v.Tag == 59932)); - Assert.Equal(16, profile.Values.Count); + Assert.Equal(18, profile.Values.Count); foreach (IExifValue value in profile.Values) { From 42eab9500e03a4aceb49ce5a72070b4eabe28ace Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sat, 18 Sep 2021 18:42:14 +0300 Subject: [PATCH 18/62] Add test files --- tests/ImageSharp.Tests/TestImages.cs | 22 +++++++++++-------- .../Tiff/BigTiff/BigTIFF_Indexed4_Deflate.tif | 3 +++ .../Tiff/BigTiff/BigTIFF_Indexed8_LZW.tif | 3 +++ .../Images/Input/Tiff/BigTiff/BigTIFF_RLE.tif | 3 +++ 4 files changed, 22 insertions(+), 9 deletions(-) create mode 100644 tests/Images/Input/Tiff/BigTiff/BigTIFF_Indexed4_Deflate.tif create mode 100644 tests/Images/Input/Tiff/BigTiff/BigTIFF_Indexed8_LZW.tif create mode 100644 tests/Images/Input/Tiff/BigTiff/BigTIFF_RLE.tif diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 9df6472a1..eccc3b089 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -675,17 +675,21 @@ namespace SixLabors.ImageSharp.Tests public static class BigTiff { - public const string BasePath = "Tiff/BigTiff/"; + public const string Base = "Tiff/BigTiff/"; - public const string BigTIFF = BasePath + "BigTIFF.tif"; - public const string BigTIFFLong = BasePath + "BigTIFFLong.tif"; - public const string BigTIFFLong8 = BasePath + "BigTIFFLong8.tif"; - public const string BigTIFFLong8Tiles = BasePath + "BigTIFFLong8Tiles.tif"; - public const string BigTIFFMotorola = BasePath + "BigTIFFMotorola.tif"; - public const string BigTIFFMotorolaLongStrips = BasePath + "BigTIFFMotorolaLongStrips.tif"; + public const string BigTIFF = Base + "BigTIFF.tif"; + public const string BigTIFFLong = Base + "BigTIFFLong.tif"; + public const string BigTIFFLong8 = Base + "BigTIFFLong8.tif"; + public const string BigTIFFLong8Tiles = Base + "BigTIFFLong8Tiles.tif"; + public const string BigTIFFMotorola = Base + "BigTIFFMotorola.tif"; + public const string BigTIFFMotorolaLongStrips = Base + "BigTIFFMotorolaLongStrips.tif"; - public const string BigTIFFSubIFD4 = BasePath + "BigTIFFSubIFD4.tif"; - public const string BigTIFFSubIFD8 = BasePath + "BigTIFFSubIFD8.tif"; + public const string BigTIFFSubIFD4 = Base + "BigTIFFSubIFD4.tif"; + public const string BigTIFFSubIFD8 = Base + "BigTIFFSubIFD8.tif"; + + public const string Indexed4_Deflate = Base + "BigTIFF_Indexed4_Deflate.tif"; + public const string Indexed8_LZW = Base + "BigTIFF_Indexed8_LZW.tif"; + public const string RLE = Base + "BigTIFF_RLE.tif"; } } } diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFF_Indexed4_Deflate.tif b/tests/Images/Input/Tiff/BigTiff/BigTIFF_Indexed4_Deflate.tif new file mode 100644 index 000000000..bc736790a --- /dev/null +++ b/tests/Images/Input/Tiff/BigTiff/BigTIFF_Indexed4_Deflate.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:38ba3717b284d7914243609576d0f9b75d732692bf05e2e1ec8b119feb1409fd +size 687 diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFF_Indexed8_LZW.tif b/tests/Images/Input/Tiff/BigTiff/BigTIFF_Indexed8_LZW.tif new file mode 100644 index 000000000..edff7c40a --- /dev/null +++ b/tests/Images/Input/Tiff/BigTiff/BigTIFF_Indexed8_LZW.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ca404b3ec5560b82169855f0ae69e64c6bc7286117b95fc0e0d505e5e356fa0e +size 2548 diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFF_RLE.tif b/tests/Images/Input/Tiff/BigTiff/BigTIFF_RLE.tif new file mode 100644 index 000000000..d15188c17 --- /dev/null +++ b/tests/Images/Input/Tiff/BigTiff/BigTIFF_RLE.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:038a298bcace02810054af650f490b6858863c8755e41b786605aa807b43350a +size 509 From 7918ecc5af551cf98d8bc3635125af02b867ee11 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sat, 18 Sep 2021 19:28:40 +0300 Subject: [PATCH 19/62] temporary disable SubIFDs tag reading --- src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs | 8 ++++---- .../Formats/Tiff/BigTiffDecoderTests.cs | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs index eea42af28..eb15f9a49 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs @@ -509,10 +509,10 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif } else if (exif.Tag == ExifTag.SubIFDs) { - foreach (object val in (Array)value) - { - this.AddSubIfd(val); - } + ////foreach (object val in (Array)value) + ////{ + //// this.AddSubIfd(val); + ////} } else { diff --git a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs index d59e276cf..ae15eac5c 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs @@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [InlineData(BigTIFFMotorolaLongStrips, 24, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] [InlineData(BigTIFFSubIFD4, 24, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] [InlineData(BigTIFFSubIFD8, 24, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] - [InlineData(Indexed4_Deflate, 24, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] + [InlineData(Indexed4_Deflate, 4, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] [InlineData(Indexed8_LZW, 8, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] [InlineData(RLE, 1, 32, 32, 96, 96, PixelResolutionUnit.PixelsPerInch)] public void Identify(string imagePath, int expectedPixelSize, int expectedWidth, int expectedHeight, double expectedHResolution, double expectedVResolution, PixelResolutionUnit expectedResolutionUnit) @@ -101,14 +101,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff ExifProfile meta = image.Frames.RootFrame.Metadata.ExifProfile; Assert.Equal(0, meta.InvalidTags.Count); - Assert.Equal(15, meta.Values.Count); + Assert.Equal(6, meta.Values.Count); Assert.Equal(64, (int)meta.GetValue(ExifTag.ImageWidth).Value); Assert.Equal(64, (int)meta.GetValue(ExifTag.ImageLength).Value); Assert.Equal(64, (int)meta.GetValue(ExifTag.RowsPerStrip).Value); - Assert.Equal(2, meta.Values.Count(v => (ushort)v.Tag == (ushort)ExifTagValue.ImageWidth)); - Assert.Equal(2, meta.Values.Count(v => (ushort)v.Tag == (ushort)ExifTagValue.StripOffsets)); - Assert.Equal(2, meta.Values.Count(v => (ushort)v.Tag == (ushort)ExifTagValue.StripByteCounts)); + Assert.Equal(1, meta.Values.Count(v => (ushort)v.Tag == (ushort)ExifTagValue.ImageWidth)); + Assert.Equal(1, meta.Values.Count(v => (ushort)v.Tag == (ushort)ExifTagValue.StripOffsets)); + Assert.Equal(1, meta.Values.Count(v => (ushort)v.Tag == (ushort)ExifTagValue.StripByteCounts)); } } } From e55a63ac64cc3d7df14768f80c67c64c2a925549 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sat, 18 Sep 2021 20:28:29 +0300 Subject: [PATCH 20/62] Revert some changes --- src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs | 4 ++-- .../Metadata/Profiles/Exif/Values/ExifNumberArray.cs | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs index eb15f9a49..2aa2fa27d 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs @@ -394,7 +394,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif } else { - object value = this.ConvertValue(dataType, offsetBuffer.Slice(0, (int)size), numberOfComponents > 1 || exifValue.IsArray); + object value = this.ConvertValue(dataType, offsetBuffer.Slice(0, (int)size), numberOfComponents > 1); this.Add(values, exifValue, value); } } @@ -477,7 +477,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif } else { - object value = this.ConvertValue(dataType, offsetBuffer.Slice(0, (int)size), numberOfComponents > 1 || exifValue.IsArray); + object value = this.ConvertValue(dataType, offsetBuffer.Slice(0, (int)size), numberOfComponents > 1); this.Add(values, exifValue, value); } } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs index 9e9e0e737..2d3a93aed 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs @@ -49,18 +49,18 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return this.SetSingle(val); case uint val: return this.SetSingle(val); - case ushort val: - return this.SetSingle(val); case short val: return this.SetSingle(val); - case uint[] array: - return this.SetArray(array); + case ushort val: + return this.SetSingle(val); case int[] array: return this.SetArray(array); - case ushort[] array: + case uint[] array: return this.SetArray(array); case short[] array: return this.SetArray(array); + case ushort[] array: + return this.SetArray(array); } return false; From f170a73d3cb5ae444697031c6cba5f6662223c0b Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sat, 18 Sep 2021 21:54:16 +0300 Subject: [PATCH 21/62] correct reading --- src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs index 2aa2fa27d..7a102b3b2 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs @@ -365,9 +365,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif // Issue #132: ExifDataType == Undefined is treated like a byte array. // If numberOfComponents == 0 this value can only be handled as an inline value and must fallback to 4 (bytes) - if (dataType == ExifDataType.Undefined && numberOfComponents == 0) + if (numberOfComponents == 0) { - numberOfComponents = 4; + numberOfComponents = 4 / ExifDataTypes.GetSize(dataType); } ExifValue exifValue = ExifValues.Create(tag) ?? ExifValues.Create(tag, dataType, numberOfComponents); @@ -418,9 +418,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return; } - if (dataType == ExifDataType.Undefined && numberOfComponents == 0) + if (numberOfComponents == 0) { - numberOfComponents = 8; + numberOfComponents = 8 / ExifDataTypes.GetSize(dataType); } // The StripOffsets, StripByteCounts, TileOffsets, and TileByteCounts tags are allowed to have the datatype TIFF_LONG8 in BigTIFF. From 82d9b974cad9d0e627564a9420ece815a5f76bfc Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sat, 18 Sep 2021 22:13:49 +0300 Subject: [PATCH 22/62] memory reduce big values reading --- .../Metadata/Profiles/Exif/ExifReader.cs | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs index 7a102b3b2..bae2d6c81 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs @@ -114,9 +114,22 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif protected void ReadExtValues(List values) { + ulong maxSize = 0; foreach ((ulong offset, ExifDataType dataType, ulong numberOfComponents, ExifValue exif) tag in this.ExtTags) { - this.ReadExtValue(values, tag); + ulong size = tag.numberOfComponents * ExifDataTypes.GetSize(tag.dataType); + if (size > maxSize) + { + maxSize = size; + } + } + + Span buf = new byte[maxSize]; + foreach ((ulong offset, ExifDataType dataType, ulong numberOfComponents, ExifValue exif) tag in this.ExtTags) + { + ulong size = tag.numberOfComponents * ExifDataTypes.GetSize(tag.dataType); + + this.ReadExtValue(values, tag, buf.Slice(0, (int)size)); } this.ExtTags.Clear(); @@ -174,14 +187,12 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif } } - protected void ReadExtValue(IList values, (ulong offset, ExifDataType dataType, ulong numberOfComponents, ExifValue exif) tag) + protected void ReadExtValue(IList values, (ulong offset, ExifDataType dataType, ulong numberOfComponents, ExifValue exif) tag, Span buffer) { - ulong size = tag.numberOfComponents * ExifDataTypes.GetSize(tag.dataType); - byte[] dataBuffer = new byte[size]; this.Seek(tag.offset); - if (this.TryReadSpan(dataBuffer)) + if (this.TryReadSpan(buffer)) { - object value = this.ConvertValue(tag.dataType, dataBuffer, tag.numberOfComponents > 1 || tag.exif.IsArray); + object value = this.ConvertValue(tag.dataType, buffer, tag.numberOfComponents > 1); this.Add(values, tag.exif, value); } } @@ -453,7 +464,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif exifValue = new ExifLong8Array(ExifTagValue.SubIFDs); break; default: - exifValue = exifValue = ExifValues.Create(tag) ?? ExifValues.Create(tag, dataType, numberOfComponents); + exifValue = ExifValues.Create(tag) ?? ExifValues.Create(tag, dataType, numberOfComponents); break; } From 8b7aaba6151bc3e794bade8f66146098285e3428 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sat, 18 Sep 2021 22:41:34 +0300 Subject: [PATCH 23/62] report problem files --- .../Formats/Tiff/BigTiffDecoderTests.cs | 11 +++++++++-- tests/ImageSharp.Tests/TestImages.cs | 3 ++- .../{BigTIFF_RLE.tif => BigTIFF_MinInBlack_RLE.tif} | 0 .../Input/Tiff/BigTiff/BigTIFF_MinInWhite_RLE.tif | 3 +++ 4 files changed, 14 insertions(+), 3 deletions(-) rename tests/Images/Input/Tiff/BigTiff/{BigTIFF_RLE.tif => BigTIFF_MinInBlack_RLE.tif} (100%) create mode 100644 tests/Images/Input/Tiff/BigTiff/BigTIFF_MinInWhite_RLE.tif diff --git a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs index ae15eac5c..e95844707 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs @@ -12,6 +12,7 @@ using SixLabors.ImageSharp.PixelFormats; using Xunit; using static SixLabors.ImageSharp.Tests.TestImages.BigTiff; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; namespace SixLabors.ImageSharp.Tests.Formats.Tiff { @@ -30,7 +31,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [WithFile(BigTIFFSubIFD8, PixelTypes.Rgba32)] [WithFile(Indexed4_Deflate, PixelTypes.Rgba32)] [WithFile(Indexed8_LZW, PixelTypes.Rgba32)] - [WithFile(RLE, PixelTypes.Rgba32)] public void TiffDecoder_CanDecode(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); @@ -39,6 +39,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public void ThrowsNotSupported(TestImageProvider provider) where TPixel : unmanaged, IPixel => Assert.Throws(() => provider.GetImage(TiffDecoder)); + [Theory] + [WithFile(MinInWhite_RLE, PixelTypes.Rgba32)] + [WithFile(MinInBlack_RLE, PixelTypes.Rgba32)] + public void ProblemFiles(TestImageProvider provider) + where TPixel : unmanaged, IPixel => Assert.Throws(() => TestTiffDecoder(provider)); + [Theory] [InlineData(BigTIFF, 24, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] [InlineData(BigTIFFLong, 24, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] @@ -49,7 +55,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [InlineData(BigTIFFSubIFD8, 24, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] [InlineData(Indexed4_Deflate, 4, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] [InlineData(Indexed8_LZW, 8, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] - [InlineData(RLE, 1, 32, 32, 96, 96, PixelResolutionUnit.PixelsPerInch)] + [InlineData(MinInWhite_RLE, 1, 32, 32, 96, 96, PixelResolutionUnit.PixelsPerInch)] + [InlineData(MinInBlack_RLE, 1, 32, 32, 96, 96, PixelResolutionUnit.PixelsPerInch)] public void Identify(string imagePath, int expectedPixelSize, int expectedWidth, int expectedHeight, double expectedHResolution, double expectedVResolution, PixelResolutionUnit expectedResolutionUnit) { var testFile = TestFile.Create(imagePath); diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index eccc3b089..e6342e660 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -689,7 +689,8 @@ namespace SixLabors.ImageSharp.Tests public const string Indexed4_Deflate = Base + "BigTIFF_Indexed4_Deflate.tif"; public const string Indexed8_LZW = Base + "BigTIFF_Indexed8_LZW.tif"; - public const string RLE = Base + "BigTIFF_RLE.tif"; + public const string MinInWhite_RLE = Base + "BigTIFF_MinInWhite_RLE.tif"; + public const string MinInBlack_RLE = Base + "BigTIFF_MinInBlack_RLE.tif"; } } } diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFF_RLE.tif b/tests/Images/Input/Tiff/BigTiff/BigTIFF_MinInBlack_RLE.tif similarity index 100% rename from tests/Images/Input/Tiff/BigTiff/BigTIFF_RLE.tif rename to tests/Images/Input/Tiff/BigTiff/BigTIFF_MinInBlack_RLE.tif diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFF_MinInWhite_RLE.tif b/tests/Images/Input/Tiff/BigTiff/BigTIFF_MinInWhite_RLE.tif new file mode 100644 index 000000000..d15188c17 --- /dev/null +++ b/tests/Images/Input/Tiff/BigTiff/BigTIFF_MinInWhite_RLE.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:038a298bcace02810054af650f490b6858863c8755e41b786605aa807b43350a +size 509 From d1e778f097175d96716d3021579d41a0325489fa Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sun, 19 Sep 2021 10:43:47 +0300 Subject: [PATCH 24/62] renaming, add fax3 test file --- .../Formats/Tiff/BigTiffDecoderTests.cs | 10 ++++++---- tests/ImageSharp.Tests/TestImages.cs | 5 +++-- .../Input/Tiff/BigTiff/BigTIFF_MinIsBlack_Fax3.tiff | 3 +++ ...F_MinInBlack_RLE.tif => BigTIFF_MinIsBlack_RLE.tif} | 0 ...F_MinInWhite_RLE.tif => BigTIFF_MinIsWhite_RLE.tif} | 0 5 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 tests/Images/Input/Tiff/BigTiff/BigTIFF_MinIsBlack_Fax3.tiff rename tests/Images/Input/Tiff/BigTiff/{BigTIFF_MinInBlack_RLE.tif => BigTIFF_MinIsBlack_RLE.tif} (100%) rename tests/Images/Input/Tiff/BigTiff/{BigTIFF_MinInWhite_RLE.tif => BigTIFF_MinIsWhite_RLE.tif} (100%) diff --git a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs index e95844707..a6f08cbc9 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs @@ -31,6 +31,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [WithFile(BigTIFFSubIFD8, PixelTypes.Rgba32)] [WithFile(Indexed4_Deflate, PixelTypes.Rgba32)] [WithFile(Indexed8_LZW, PixelTypes.Rgba32)] + [WithFile(MinIsBlack_Fax3, PixelTypes.Rgba32)] public void TiffDecoder_CanDecode(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); @@ -40,8 +41,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff where TPixel : unmanaged, IPixel => Assert.Throws(() => provider.GetImage(TiffDecoder)); [Theory] - [WithFile(MinInWhite_RLE, PixelTypes.Rgba32)] - [WithFile(MinInBlack_RLE, PixelTypes.Rgba32)] + [WithFile(MinIsWhite_RLE, PixelTypes.Rgba32)] + [WithFile(MinIsBlack_RLE, PixelTypes.Rgba32)] public void ProblemFiles(TestImageProvider provider) where TPixel : unmanaged, IPixel => Assert.Throws(() => TestTiffDecoder(provider)); @@ -55,8 +56,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [InlineData(BigTIFFSubIFD8, 24, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] [InlineData(Indexed4_Deflate, 4, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] [InlineData(Indexed8_LZW, 8, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] - [InlineData(MinInWhite_RLE, 1, 32, 32, 96, 96, PixelResolutionUnit.PixelsPerInch)] - [InlineData(MinInBlack_RLE, 1, 32, 32, 96, 96, PixelResolutionUnit.PixelsPerInch)] + [InlineData(MinIsWhite_RLE, 1, 32, 32, 96, 96, PixelResolutionUnit.PixelsPerInch)] + [InlineData(MinIsBlack_RLE, 1, 32, 32, 96, 96, PixelResolutionUnit.PixelsPerInch)] + [InlineData(MinIsBlack_Fax3, 1, 32, 32, 96, 96, PixelResolutionUnit.PixelsPerInch)] public void Identify(string imagePath, int expectedPixelSize, int expectedWidth, int expectedHeight, double expectedHResolution, double expectedVResolution, PixelResolutionUnit expectedResolutionUnit) { var testFile = TestFile.Create(imagePath); diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index e6342e660..7292d232d 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -689,8 +689,9 @@ namespace SixLabors.ImageSharp.Tests public const string Indexed4_Deflate = Base + "BigTIFF_Indexed4_Deflate.tif"; public const string Indexed8_LZW = Base + "BigTIFF_Indexed8_LZW.tif"; - public const string MinInWhite_RLE = Base + "BigTIFF_MinInWhite_RLE.tif"; - public const string MinInBlack_RLE = Base + "BigTIFF_MinInBlack_RLE.tif"; + public const string MinIsWhite_RLE = Base + "BigTIFF_MinIsWhite_RLE.tif"; + public const string MinIsBlack_RLE = Base + "BigTIFF_MinIsBlack_RLE.tif"; + public const string MinIsBlack_Fax3 = Base + "BigTIFF_MinIsBlack_Fax3.tif"; } } } diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFF_MinIsBlack_Fax3.tiff b/tests/Images/Input/Tiff/BigTiff/BigTIFF_MinIsBlack_Fax3.tiff new file mode 100644 index 000000000..952f4fab9 --- /dev/null +++ b/tests/Images/Input/Tiff/BigTiff/BigTIFF_MinIsBlack_Fax3.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3853a4850a023bce431bc224145f47e095e9910cddce9ba5fcaf496c9d385e04 +size 564 diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFF_MinInBlack_RLE.tif b/tests/Images/Input/Tiff/BigTiff/BigTIFF_MinIsBlack_RLE.tif similarity index 100% rename from tests/Images/Input/Tiff/BigTiff/BigTIFF_MinInBlack_RLE.tif rename to tests/Images/Input/Tiff/BigTiff/BigTIFF_MinIsBlack_RLE.tif diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFF_MinInWhite_RLE.tif b/tests/Images/Input/Tiff/BigTiff/BigTIFF_MinIsWhite_RLE.tif similarity index 100% rename from tests/Images/Input/Tiff/BigTiff/BigTIFF_MinInWhite_RLE.tif rename to tests/Images/Input/Tiff/BigTiff/BigTIFF_MinIsWhite_RLE.tif From e13122cab1997f4aa91cc30844d81694eaf352a6 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sun, 19 Sep 2021 10:54:18 +0300 Subject: [PATCH 25/62] workaround for inconsistent covariance of value-typed arrays --- .../Profiles/Exif/Values/ExifLong8Array.cs | 37 +++++++++++++++---- .../Profiles/Exif/Values/ExifNumberArray.cs | 20 ++++++++-- 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs index 055a50104..d012f646b 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs @@ -51,25 +51,46 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif switch (value) { case int val: - return this.SetSingle((ulong)val); + return this.SetSingle((ulong)Numerics.Clamp(val, 0, int.MaxValue)); + case uint val: return this.SetSingle((ulong)val); + case short val: - return this.SetSingle((ulong)val); + return this.SetSingle((ulong)Numerics.Clamp(val, 0, short.MaxValue)); + case ushort val: return this.SetSingle((ulong)val); + case long[] array: + { + if (value.GetType().Equals(typeof(ulong[]))) + { + return this.SetArray((ulong[])value); + } + return this.SetArray(array); - case ulong[] array: - return this.SetArray(array); + } + case int[] array: + { + if (value.GetType().Equals(typeof(uint[]))) + { + return this.SetArray((uint[])value); + } + return this.SetArray(array); - case uint[] array: - return this.SetArray(array); + } + case short[] array: + { + if (value.GetType().Equals(typeof(ushort[]))) + { + return this.SetArray((ushort[])value); + } + return this.SetArray(array); - case ushort[] array: - return this.SetArray(array); + } } return false; diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs index 2d3a93aed..4b7433d7d 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs @@ -54,13 +54,25 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif case ushort val: return this.SetSingle(val); case int[] array: + { + // workaround for inconsistent covariance of value-typed arrays + if (value.GetType().Equals(typeof(uint[]))) + { + return this.SetArray((uint[])value); + } + return this.SetArray(array); - case uint[] array: - return this.SetArray(array); + } + case short[] array: + { + if (value.GetType().Equals(typeof(ushort[]))) + { + return this.SetArray((ushort[])value); + } + return this.SetArray(array); - case ushort[] array: - return this.SetArray(array); + } } return false; From 1ff115638229073531e72e95e95f3c28088bd44a Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sun, 19 Sep 2021 11:06:46 +0300 Subject: [PATCH 26/62] renaming --- .../Formats/Tiff/Ifd/DirectoryReader.cs | 10 +++---- .../Formats/Tiff/Ifd/EntryReader.cs | 3 +- .../Metadata/Profiles/Exif/ExifReader.cs | 28 +++++++++---------- 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs index 1e298028d..3aed89bc5 100644 --- a/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs +++ b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs @@ -67,14 +67,14 @@ namespace SixLabors.ImageSharp.Formats.Tiff var reader = new EntryReader(this.stream, this.ByteOrder); reader.ReadTags(isBigTiff, this.nextIfdOffset); - if (reader.ExtTags.Count > 0) + if (reader.BigValues.Count > 0) { - reader.ExtTags.Sort((t1, t2) => t1.offset.CompareTo(t2.offset)); + reader.BigValues.Sort((t1, t2) => t1.offset.CompareTo(t2.offset)); // this means that most likely all elements are placed before next IFD - if (reader.ExtTags[0].offset < reader.NextIfdOffset) + if (reader.BigValues[0].offset < reader.NextIfdOffset) { - reader.ReadExtValues(); + reader.ReadBigValues(); } } @@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff var list = new List(readers.Count); foreach (EntryReader reader in readers) { - reader.ReadExtValues(); + reader.ReadBigValues(); var profile = new ExifProfile(reader.Values, reader.InvalidTags); list.Add(profile); } diff --git a/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs b/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs index 5a83febf8..98cc1deb3 100644 --- a/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs +++ b/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using System; using System.Collections.Generic; using System.IO; @@ -40,7 +39,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff } } - public void ReadExtValues() => this.ReadExtValues(this.Values); + public void ReadBigValues() => this.ReadBigValues(this.Values); } internal class HeaderReader : BaseExifReader diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs index bae2d6c81..feca52871 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs @@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif this.ReadSubIfd(values); - this.ReadExtValues(values); + this.ReadBigValues(values); return values; } @@ -110,14 +110,14 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif public bool IsBigEndian { get; protected set; } - public List<(ulong offset, ExifDataType dataType, ulong numberOfComponents, ExifValue exif)> ExtTags { get; } = new (); + public List<(ulong offset, ExifDataType dataType, ulong numberOfComponents, ExifValue exif)> BigValues { get; } = new (); - protected void ReadExtValues(List values) + protected void ReadBigValues(List values) { ulong maxSize = 0; - foreach ((ulong offset, ExifDataType dataType, ulong numberOfComponents, ExifValue exif) tag in this.ExtTags) + foreach ((ulong offset, ExifDataType dataType, ulong numberOfComponents, ExifValue exif) in this.BigValues) { - ulong size = tag.numberOfComponents * ExifDataTypes.GetSize(tag.dataType); + ulong size = numberOfComponents * ExifDataTypes.GetSize(dataType); if (size > maxSize) { maxSize = size; @@ -125,14 +125,14 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif } Span buf = new byte[maxSize]; - foreach ((ulong offset, ExifDataType dataType, ulong numberOfComponents, ExifValue exif) tag in this.ExtTags) + foreach ((ulong offset, ExifDataType dataType, ulong numberOfComponents, ExifValue exif) tag in this.BigValues) { ulong size = tag.numberOfComponents * ExifDataTypes.GetSize(tag.dataType); - this.ReadExtValue(values, tag, buf.Slice(0, (int)size)); + this.ReadBigValue(values, tag, buf.Slice(0, (int)size)); } - this.ExtTags.Clear(); + this.BigValues.Clear(); } /// @@ -187,12 +187,12 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif } } - protected void ReadExtValue(IList values, (ulong offset, ExifDataType dataType, ulong numberOfComponents, ExifValue exif) tag, Span buffer) + protected void ReadBigValue(IList values, (ulong offset, ExifDataType dataType, ulong numberOfComponents, ExifValue exif) tag, Span buffer) { this.Seek(tag.offset); if (this.TryReadSpan(buffer)) { - object value = this.ConvertValue(tag.dataType, buffer, tag.numberOfComponents > 1); + object value = this.ConvertValue(tag.dataType, buffer, tag.numberOfComponents > 1 || tag.exif.IsArray); this.Add(values, tag.exif, value); } } @@ -401,11 +401,11 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return; } - this.ExtTags.Add((newOffset, dataType, numberOfComponents, exifValue)); + this.BigValues.Add((newOffset, dataType, numberOfComponents, exifValue)); } else { - object value = this.ConvertValue(dataType, offsetBuffer.Slice(0, (int)size), numberOfComponents > 1); + object value = this.ConvertValue(dataType, offsetBuffer.Slice(0, (int)size), numberOfComponents > 1 || exifValue.IsArray); this.Add(values, exifValue, value); } } @@ -484,11 +484,11 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return; } - this.ExtTags.Add((newOffset, dataType, numberOfComponents, exifValue)); + this.BigValues.Add((newOffset, dataType, numberOfComponents, exifValue)); } else { - object value = this.ConvertValue(dataType, offsetBuffer.Slice(0, (int)size), numberOfComponents > 1); + object value = this.ConvertValue(dataType, offsetBuffer.Slice(0, (int)size), numberOfComponents > 1 || exifValue.IsArray); this.Add(values, exifValue, value); } } From 46b6214c8cbe81bc9195762eab81145edd240ed1 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sun, 19 Sep 2021 11:15:03 +0300 Subject: [PATCH 27/62] rename --- .../{BigTIFF_MinIsBlack_Fax3.tiff => BigTIFF_MinIsBlack_Fax3.tif} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/Images/Input/Tiff/BigTiff/{BigTIFF_MinIsBlack_Fax3.tiff => BigTIFF_MinIsBlack_Fax3.tif} (100%) diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFF_MinIsBlack_Fax3.tiff b/tests/Images/Input/Tiff/BigTiff/BigTIFF_MinIsBlack_Fax3.tif similarity index 100% rename from tests/Images/Input/Tiff/BigTiff/BigTIFF_MinIsBlack_Fax3.tiff rename to tests/Images/Input/Tiff/BigTiff/BigTIFF_MinIsBlack_Fax3.tif From 75db00274fe4aba7d91c0fcd62f4321c99672759 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sun, 19 Sep 2021 11:29:53 +0300 Subject: [PATCH 28/62] remove obsolete code --- .../Formats/Tiff/Ifd/DirectoryReader.cs | 16 ---------------- .../Metadata/Profiles/Exif/ExifReader.cs | 5 +---- 2 files changed, 1 insertion(+), 20 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs index 3aed89bc5..6421db8d8 100644 --- a/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs +++ b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs @@ -92,21 +92,5 @@ namespace SixLabors.ImageSharp.Formats.Tiff return list; } - - /// - /// used for possibility add a duplicate offsets (but tags don't duplicate). - /// - /// The type of the key. - private class DuplicateKeyComparer : IComparer - where TKey : IComparable - { - public int Compare(TKey x, TKey y) - { - int result = x.CompareTo(y); - - // Handle equality as being greater. - return (result == 0) ? 1 : result; - } - } } } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs index feca52871..9abaf1432 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs @@ -172,10 +172,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif protected void ReadValues64(List values, ulong offset) { - if (offset > (ulong)this.data.Length) - { - return; - } + DebugGuard.MustBeLessThanOrEqualTo(offset, (ulong)this.data.Length, "By spec UInt64.MaxValue is supported, but .Net Stream.Length can Int64.MaxValue."); this.Seek(offset); ulong count = this.ReadUInt64(); From df0bc4c2227ec019859b42bd655ba7fc4c105693 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sun, 19 Sep 2021 11:30:09 +0300 Subject: [PATCH 29/62] problem test file --- tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs index a6f08cbc9..c00336b25 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs @@ -31,7 +31,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [WithFile(BigTIFFSubIFD8, PixelTypes.Rgba32)] [WithFile(Indexed4_Deflate, PixelTypes.Rgba32)] [WithFile(Indexed8_LZW, PixelTypes.Rgba32)] - [WithFile(MinIsBlack_Fax3, PixelTypes.Rgba32)] public void TiffDecoder_CanDecode(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); @@ -43,6 +42,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Theory] [WithFile(MinIsWhite_RLE, PixelTypes.Rgba32)] [WithFile(MinIsBlack_RLE, PixelTypes.Rgba32)] + [WithFile(MinIsBlack_Fax3, PixelTypes.Rgba32)] public void ProblemFiles(TestImageProvider provider) where TPixel : unmanaged, IPixel => Assert.Throws(() => TestTiffDecoder(provider)); From 82f64912ea5ffb6e66c075fd8ed67abea7e7a613 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sun, 19 Sep 2021 15:05:49 +0300 Subject: [PATCH 30/62] improve strip offset and strip byte counts parsing --- .../Formats/Tiff/TiffDecoderCore.cs | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 9822cf63b..6692b7991 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; using System.Buffers; using System.Collections.Generic; using System.Linq; @@ -218,18 +219,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff int rowsPerStrip = tags.GetValue(ExifTag.RowsPerStrip) != null ? (int)tags.GetValue(ExifTag.RowsPerStrip).Value : TiffConstants.RowsPerStripInfinity; - ulong[] stripOffsets; - ulong[] stripByteCounts; - if (this.isBigTiff) - { - stripOffsets = tags.GetValueInternal(ExifTag.StripOffsets)?.GetValue() as ulong[]; - stripByteCounts = tags.GetValueInternal(ExifTag.StripByteCounts)?.GetValue() as ulong[]; - } - else - { - stripOffsets = tags.GetValue(ExifTag.StripOffsets)?.Value.Select(s => (ulong)(uint)s).ToArray(); - stripByteCounts = tags.GetValue(ExifTag.StripByteCounts)?.Value.Select(s => (ulong)(uint)s).ToArray(); - } + var stripOffsets = (Array)tags.GetValueInternal(ExifTag.StripOffsets)?.GetValue(); + var stripByteCounts = (Array)tags.GetValueInternal(ExifTag.StripByteCounts)?.GetValue(); if (this.PlanarConfiguration == TiffPlanarConfiguration.Planar) { @@ -293,7 +284,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// An array of byte offsets to each strip in the image. /// An array of the size of each strip (in bytes). /// The token to monitor cancellation. - private void DecodeStripsPlanar(ImageFrame frame, int rowsPerStrip, ulong[] stripOffsets, ulong[] stripByteCounts, CancellationToken cancellationToken) + private void DecodeStripsPlanar(ImageFrame frame, int rowsPerStrip, Array stripOffsets, Array stripByteCounts, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { int stripsPerPixel = this.BitsPerSample.Channels; @@ -344,10 +335,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff int stripIndex = i; for (int planeIndex = 0; planeIndex < stripsPerPixel; planeIndex++) { + ulong offset = stripOffsets.GetValue(stripIndex) switch { ulong val => val, Number val => (uint)val }; + ulong count = stripByteCounts.GetValue(stripIndex) switch { ulong val => val, Number val => (uint)val }; decompressor.Decompress( this.inputStream, - stripOffsets[stripIndex], - stripByteCounts[stripIndex], + offset, + count, stripHeight, stripBuffers[planeIndex].GetSpan()); stripIndex += stripsPerPlane; @@ -374,7 +367,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The strip offsets. /// The strip byte counts. /// The token to monitor cancellation. - private void DecodeStripsChunky(ImageFrame frame, int rowsPerStrip, ulong[] stripOffsets, ulong[] stripByteCounts, CancellationToken cancellationToken) + private void DecodeStripsChunky(ImageFrame frame, int rowsPerStrip, Array stripOffsets, Array stripByteCounts, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { // If the rowsPerStrip has the default value, which is effectively infinity. That is, the entire image is one strip. @@ -387,7 +380,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff int bitsPerPixel = this.BitsPerPixel; using IMemoryOwner stripBuffer = this.memoryAllocator.Allocate(uncompressedStripSize, AllocationOptions.Clean); - System.Span stripBufferSpan = stripBuffer.GetSpan(); + Span stripBufferSpan = stripBuffer.GetSpan(); Buffer2D pixels = frame.PixelBuffer; using TiffBaseDecompressor decompressor = TiffDecompressorsFactory.Create( @@ -430,7 +423,14 @@ namespace SixLabors.ImageSharp.Formats.Tiff break; } - decompressor.Decompress(this.inputStream, stripOffsets[stripIndex], stripByteCounts[stripIndex], stripHeight, stripBufferSpan); + ulong offset = stripOffsets.GetValue(stripIndex) switch { ulong val => val, Number val => (uint)val }; + ulong count = stripByteCounts.GetValue(stripIndex) switch { ulong val => val, Number val => (uint)val }; + decompressor.Decompress( + this.inputStream, + offset, + count, + stripHeight, + stripBufferSpan); colorDecoder.Decode(stripBufferSpan, pixels, 0, top, frame.Width, stripHeight); } From 04884d56a99da4bf4b2be84d4ada0e85a01f4915 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sun, 19 Sep 2021 22:13:23 +0300 Subject: [PATCH 31/62] add 1-bit test files, renaming --- .../Formats/Tiff/BigTiffDecoderTests.cs | 25 +++++++++++++------ tests/ImageSharp.Tests/TestImages.cs | 8 +++--- .../Input/Tiff/BigTiff/BigTIFF_MinIsBlack.tif | 3 +++ .../Tiff/BigTiff/BigTIFF_MinIsBlack_Fax3.tif | 3 --- .../Input/Tiff/BigTiff/BigTIFF_MinIsWhite.tif | 3 +++ 5 files changed, 28 insertions(+), 14 deletions(-) create mode 100644 tests/Images/Input/Tiff/BigTiff/BigTIFF_MinIsBlack.tif delete mode 100644 tests/Images/Input/Tiff/BigTiff/BigTIFF_MinIsBlack_Fax3.tif create mode 100644 tests/Images/Input/Tiff/BigTiff/BigTIFF_MinIsWhite.tif diff --git a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs index c00336b25..131310086 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs @@ -31,6 +31,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [WithFile(BigTIFFSubIFD8, PixelTypes.Rgba32)] [WithFile(Indexed4_Deflate, PixelTypes.Rgba32)] [WithFile(Indexed8_LZW, PixelTypes.Rgba32)] + [WithFile(MinIsBlack, PixelTypes.Rgba32)] + [WithFile(MinIsWhite, PixelTypes.Rgba32)] public void TiffDecoder_CanDecode(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); @@ -40,11 +42,19 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff where TPixel : unmanaged, IPixel => Assert.Throws(() => provider.GetImage(TiffDecoder)); [Theory] - [WithFile(MinIsWhite_RLE, PixelTypes.Rgba32)] - [WithFile(MinIsBlack_RLE, PixelTypes.Rgba32)] - [WithFile(MinIsBlack_Fax3, PixelTypes.Rgba32)] - public void ProblemFiles(TestImageProvider provider) - where TPixel : unmanaged, IPixel => Assert.Throws(() => TestTiffDecoder(provider)); + [WithFile(Damaged_MinIsWhite_RLE, PixelTypes.Rgba32)] + [WithFile(Damaged_MinIsBlack_RLE, PixelTypes.Rgba32)] + public void DamagedFiles(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + Assert.Throws(() => TestTiffDecoder(provider)); + + using Image image = provider.GetImage(TiffDecoder); + ExifProfile exif = image.Frames.RootFrame.Metadata.ExifProfile; + + // PhotometricInterpretation is required tag: https://www.awaresystems.be/imaging/tiff/tifftags/photometricinterpretation.html + Assert.Null(exif.GetValueInternal(ExifTag.PhotometricInterpretation)); + } [Theory] [InlineData(BigTIFF, 24, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] @@ -56,9 +66,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [InlineData(BigTIFFSubIFD8, 24, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] [InlineData(Indexed4_Deflate, 4, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] [InlineData(Indexed8_LZW, 8, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] - [InlineData(MinIsWhite_RLE, 1, 32, 32, 96, 96, PixelResolutionUnit.PixelsPerInch)] - [InlineData(MinIsBlack_RLE, 1, 32, 32, 96, 96, PixelResolutionUnit.PixelsPerInch)] - [InlineData(MinIsBlack_Fax3, 1, 32, 32, 96, 96, PixelResolutionUnit.PixelsPerInch)] + [InlineData(MinIsWhite, 1, 32, 32, 96, 96, PixelResolutionUnit.PixelsPerInch)] + [InlineData(MinIsBlack, 1, 32, 32, 96, 96, PixelResolutionUnit.PixelsPerInch)] public void Identify(string imagePath, int expectedPixelSize, int expectedWidth, int expectedHeight, double expectedHResolution, double expectedVResolution, PixelResolutionUnit expectedResolutionUnit) { var testFile = TestFile.Create(imagePath); diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 7292d232d..b0e67d669 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -689,9 +689,11 @@ namespace SixLabors.ImageSharp.Tests public const string Indexed4_Deflate = Base + "BigTIFF_Indexed4_Deflate.tif"; public const string Indexed8_LZW = Base + "BigTIFF_Indexed8_LZW.tif"; - public const string MinIsWhite_RLE = Base + "BigTIFF_MinIsWhite_RLE.tif"; - public const string MinIsBlack_RLE = Base + "BigTIFF_MinIsBlack_RLE.tif"; - public const string MinIsBlack_Fax3 = Base + "BigTIFF_MinIsBlack_Fax3.tif"; + public const string MinIsBlack = Base + "BigTIFF_MinIsBlack.tif"; + public const string MinIsWhite = Base + "BigTIFF_MinIsWhite.tif"; + + public const string Damaged_MinIsWhite_RLE = Base + "BigTIFF_MinIsWhite_RLE.tif"; + public const string Damaged_MinIsBlack_RLE = Base + "BigTIFF_MinIsBlack_RLE.tif"; } } } diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFF_MinIsBlack.tif b/tests/Images/Input/Tiff/BigTiff/BigTIFF_MinIsBlack.tif new file mode 100644 index 000000000..491b4092e --- /dev/null +++ b/tests/Images/Input/Tiff/BigTiff/BigTIFF_MinIsBlack.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ac0471c1600f6e5fb47037dab07172aff524abc866a40c9ec54279bd49cbef77 +size 517 diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFF_MinIsBlack_Fax3.tif b/tests/Images/Input/Tiff/BigTiff/BigTIFF_MinIsBlack_Fax3.tif deleted file mode 100644 index 952f4fab9..000000000 --- a/tests/Images/Input/Tiff/BigTiff/BigTIFF_MinIsBlack_Fax3.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3853a4850a023bce431bc224145f47e095e9910cddce9ba5fcaf496c9d385e04 -size 564 diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFF_MinIsWhite.tif b/tests/Images/Input/Tiff/BigTiff/BigTIFF_MinIsWhite.tif new file mode 100644 index 000000000..fb26e58a4 --- /dev/null +++ b/tests/Images/Input/Tiff/BigTiff/BigTIFF_MinIsWhite.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a95b0b46bf4f75babb86d9ec74694e6d684087504be214df48a6c8a54338834c +size 517 From 7f1376ffff31c6c0d6d4e8add19e2cfd2fb731e5 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sat, 2 Oct 2021 10:29:05 +0300 Subject: [PATCH 32/62] Update src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Günther Foidl --- .../Metadata/Profiles/Exif/Values/ExifLong8Array.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs index d012f646b..d76deba07 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs @@ -107,10 +107,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif private bool SetArray(long[] values) { var numbers = new ulong[values.Length]; - for (int i = 0; i < values.Length; i++) - { - numbers[i] = (ulong)values[i]; - } + this.Value = Unsafe.As(ref values); this.Value = numbers; return true; From f95102e340e34cfb5f22fec4c436db8b1321fc4f Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sat, 2 Oct 2021 10:30:38 +0300 Subject: [PATCH 33/62] Update src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Günther Foidl --- src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs index 4b7433d7d..39d1ff049 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs @@ -56,7 +56,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif case int[] array: { // workaround for inconsistent covariance of value-typed arrays - if (value.GetType().Equals(typeof(uint[]))) + if (value.GetType() == typeof(uint[])) { return this.SetArray((uint[])value); } From 22d67f9210e74e9a7d762b49c21d3692ce99e427 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sat, 2 Oct 2021 10:30:49 +0300 Subject: [PATCH 34/62] Update src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Günther Foidl --- src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs index 39d1ff049..c65e0b460 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs @@ -66,7 +66,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif case short[] array: { - if (value.GetType().Equals(typeof(ushort[]))) + if (value.GetType() == typeof(ushort[])) { return this.SetArray((ushort[])value); } From f0ef4f9d510c516688a982e94453d87ece5b0f19 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sat, 2 Oct 2021 10:31:46 +0300 Subject: [PATCH 35/62] Update src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Günther Foidl --- src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs index 9abaf1432..f36f2cbae 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs @@ -150,7 +150,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif this.Seek(offset); int count = this.ReadUInt16(); - Span offsetBuffer = new byte[4]; + Span offsetBuffer = stackalloc byte[4]; for (int i = 0; i < count; i++) { this.ReadValue(values, offsetBuffer); From 02c891134c946ee2326da3c57ffff221c7b19b11 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sat, 2 Oct 2021 10:31:54 +0300 Subject: [PATCH 36/62] Update src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Günther Foidl --- src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs index f36f2cbae..d9fe29e82 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs @@ -177,7 +177,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif this.Seek(offset); ulong count = this.ReadUInt64(); - Span offsetBuffer = new byte[8]; + Span offsetBuffer = stackalloc byte[8]; for (ulong i = 0; i < count; i++) { this.ReadValue64(values, offsetBuffer); From d670edcccf25f1778ed61730d2d888153b45c714 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sat, 2 Oct 2021 10:59:46 +0300 Subject: [PATCH 37/62] build fixes, minor improves --- .../Profiles/Exif/Values/ExifLong8Array.cs | 15 +++++++-------- .../Profiles/Exif/Values/ExifNumberArray.cs | 4 ++-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs index d76deba07..618135e92 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System.Runtime.CompilerServices; + namespace SixLabors.ImageSharp.Metadata.Profiles.Exif { internal sealed class ExifLong8Array : ExifArrayValue @@ -29,9 +31,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return ExifDataType.Long; } - for (int i = 0; i < this.Value.Length; i++) + foreach (ulong value in this.Value) { - if (this.Value[i] > uint.MaxValue) + if (value > uint.MaxValue) { return ExifDataType.Long8; } @@ -64,7 +66,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif case long[] array: { - if (value.GetType().Equals(typeof(ulong[]))) + if (value.GetType() == typeof(ulong[])) { return this.SetArray((ulong[])value); } @@ -74,7 +76,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif case int[] array: { - if (value.GetType().Equals(typeof(uint[]))) + if (value.GetType() == typeof(uint[])) { return this.SetArray((uint[])value); } @@ -84,7 +86,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif case short[] array: { - if (value.GetType().Equals(typeof(ushort[]))) + if (value.GetType() == typeof(ushort[])) { return this.SetArray((ushort[])value); } @@ -106,10 +108,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif private bool SetArray(long[] values) { - var numbers = new ulong[values.Length]; this.Value = Unsafe.As(ref values); - - this.Value = numbers; return true; } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs index c65e0b460..2d006c538 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs @@ -24,9 +24,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return ExifDataType.Short; } - for (int i = 0; i < this.Value.Length; i++) + foreach (Number value in this.Value) { - if (this.Value[i] > ushort.MaxValue) + if (value > ushort.MaxValue) { return ExifDataType.Long; } From eb8b6f25862da4f405f9f0081d07e0ac2cd6f595 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sat, 2 Oct 2021 11:22:57 +0300 Subject: [PATCH 38/62] build fixes --- .../Formats/Tiff/TiffDecoderCore.cs | 32 ++++++++++++++++--- .../Formats/Tiff/TiffThrowHelper.cs | 2 +- .../Profiles/Exif/Values/ExifLong8Array.cs | 2 +- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 6692b7991..9f0a4b514 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -335,8 +335,20 @@ namespace SixLabors.ImageSharp.Formats.Tiff int stripIndex = i; for (int planeIndex = 0; planeIndex < stripsPerPixel; planeIndex++) { - ulong offset = stripOffsets.GetValue(stripIndex) switch { ulong val => val, Number val => (uint)val }; - ulong count = stripByteCounts.GetValue(stripIndex) switch { ulong val => val, Number val => (uint)val }; + ulong offset = stripOffsets.GetValue(stripIndex) switch + { + ulong val => val, + Number val => (uint)val, + _ => throw TiffThrowHelper.ThrowImageFormatException("Expected Number or Long8 array") + }; + + ulong count = stripByteCounts.GetValue(stripIndex) switch + { + ulong val => val, + Number val => (uint)val, + _ => throw TiffThrowHelper.ThrowImageFormatException("Expected Number or Long8 array") + }; + decompressor.Decompress( this.inputStream, offset, @@ -423,8 +435,20 @@ namespace SixLabors.ImageSharp.Formats.Tiff break; } - ulong offset = stripOffsets.GetValue(stripIndex) switch { ulong val => val, Number val => (uint)val }; - ulong count = stripByteCounts.GetValue(stripIndex) switch { ulong val => val, Number val => (uint)val }; + ulong offset = stripOffsets.GetValue(stripIndex) switch + { + ulong val => val, + Number val => (uint)val, + _ => throw TiffThrowHelper.ThrowImageFormatException("Expected Number or Long8 array") + }; + + ulong count = stripByteCounts.GetValue(stripIndex) switch + { + ulong val => val, + Number val => (uint)val, + _ => throw TiffThrowHelper.ThrowImageFormatException("Expected Number or Long8 array") + }; + decompressor.Decompress( this.inputStream, offset, diff --git a/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs b/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs index 3c541a786..8223c445a 100644 --- a/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs +++ b/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs @@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// The error message for the exception. [MethodImpl(MethodImplOptions.NoInlining)] - public static void ThrowImageFormatException(string errorMessage) => throw new ImageFormatException(errorMessage); + public static Exception ThrowImageFormatException(string errorMessage) => throw new ImageFormatException(errorMessage); [MethodImpl(InliningOptions.ColdPath)] public static Exception NotSupportedDecompressor(string compressionType) => throw new NotSupportedException($"Not supported decoder compression method: {compressionType}"); diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs index 618135e92..d75e68565 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs @@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return ExifDataType.Long; } - foreach (ulong value in this.Value) + foreach (ulong value in this.Value) { if (value > uint.MaxValue) { From dfcb74305b7a04e328ca7241d57105d4ba9f91a0 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sat, 2 Oct 2021 19:51:06 +0300 Subject: [PATCH 39/62] Add long8 tests --- .../Profiles/Exif/Values/ExifLong8.cs | 13 +++ .../Profiles/Exif/Values/ExifLong8Array.cs | 3 + .../Formats/Tiff/BigTiffDecoderTests.cs | 7 +- .../Formats/Tiff/BigTiffMetadataTests.cs | 90 +++++++++++++++++++ 4 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8.cs index 47da962f3..a4a1dd3df 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8.cs @@ -35,6 +35,18 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif switch (value) { + case int intValue: + if (intValue >= uint.MinValue) + { + this.Value = (uint)intValue; + return true; + } + + return false; + case uint uintValue: + this.Value = uintValue; + + return true; case long intValue: if (intValue >= 0) { @@ -44,6 +56,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return false; default: + return false; } } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs index d75e68565..3d14709a4 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs @@ -64,6 +64,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif case ushort val: return this.SetSingle((ulong)val); + case long val: + return this.SetSingle((ulong)Numerics.Clamp(val, 0, long.MaxValue)); + case long[] array: { if (value.GetType() == typeof(ulong[])) diff --git a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs index 131310086..24fce1d0d 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs @@ -3,22 +3,19 @@ // ReSharper disable InconsistentNaming using System; -using System.Linq; using System.IO; +using System.Linq; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; - +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using Xunit; - using static SixLabors.ImageSharp.Tests.TestImages.BigTiff; -using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; namespace SixLabors.ImageSharp.Tests.Formats.Tiff { [Collection("RunSerial")] [Trait("Format", "Tiff")] - [Trait("Format", "BigTiff")] public class BigTiffDecoderTests : TiffDecoderBaseTester { [Theory] diff --git a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs new file mode 100644 index 000000000..91cfa1efa --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs @@ -0,0 +1,90 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Formats.Tiff +{ + [Trait("Format", "Tiff")] + public class BigTiffMetadataTests + { + private static TiffDecoder TiffDecoder => new TiffDecoder(); + + [Fact] + public void ExifLong8() + { + var long8 = new ExifLong8(ExifTagValue.StripByteCounts); + + Assert.True(long8.TrySetValue(0)); + Assert.Equal(0UL, long8.GetValue()); + + Assert.True(long8.TrySetValue(100u)); + Assert.Equal(100UL, long8.GetValue()); + + Assert.True(long8.TrySetValue(ulong.MaxValue)); + Assert.Equal(ulong.MaxValue, long8.GetValue()); + + Assert.False(long8.TrySetValue(-65)); + Assert.Equal(ulong.MaxValue, long8.GetValue()); + } + + [Fact] + public void ExifSignedLong8() + { + var long8 = new ExifSignedLong8(ExifTagValue.ImageID); + + Assert.False(long8.TrySetValue(0)); + + Assert.True(long8.TrySetValue(0L)); + Assert.Equal(0L, long8.GetValue()); + + Assert.True(long8.TrySetValue(-100L)); + Assert.Equal(-100L, long8.GetValue()); + + Assert.True(long8.TrySetValue(100L)); + Assert.Equal(100L, long8.GetValue()); + } + + [Fact] + public void ExifLong8Array() + { + var long8 = new ExifLong8Array(ExifTagValue.StripOffsets); + + Assert.True(long8.TrySetValue((short)-123)); + Assert.Equal(new[] { 0UL }, long8.GetValue()); + + Assert.True(long8.TrySetValue((ushort)123)); + Assert.Equal(new[] { 123UL }, long8.GetValue()); + + Assert.True(long8.TrySetValue((short)123)); + Assert.Equal(new[] { 123UL }, long8.GetValue()); + + Assert.True(long8.TrySetValue(123)); + Assert.Equal(new[] { 123UL }, long8.GetValue()); + + Assert.True(long8.TrySetValue(123L)); + Assert.Equal(new[] { 123UL }, long8.GetValue()); + + Assert.True(long8.TrySetValue(123UL)); + Assert.Equal(new[] { 123UL }, long8.GetValue()); + + Assert.True(long8.TrySetValue(new[] { 1, 2, 3, 4 })); + Assert.Equal(new[] { 1UL, 2UL, 3UL, 4UL }, long8.GetValue()); + } + + [Fact] + public void ExifSignedLong8Array() + { + var long8 = new ExifSignedLong8Array(ExifTagValue.StripOffsets); + + Assert.True(long8.TrySetValue(new[] { 0L })); + Assert.Equal(new[] { 0L }, long8.GetValue()); + + Assert.True(long8.TrySetValue(new[] { -1L, 2L, -3L, 4L })); + Assert.Equal(new[] { -1L, 2L, -3L, 4L }, long8.GetValue()); + } + } +} From 7185a24baf78ca39ab38be437adfaaf19701bda5 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sat, 2 Oct 2021 20:15:06 +0300 Subject: [PATCH 40/62] minor --- .../Profiles/Exif/Values/ExifLong8Array.cs | 14 ++++++-------- .../Profiles/Exif/Values/ExifNumberArray.cs | 14 ++++++-------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs index 3d14709a4..366eb8d12 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs @@ -26,16 +26,14 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif { get { - if (this.Value is null) + if (this.Value is not null) { - return ExifDataType.Long; - } - - foreach (ulong value in this.Value) - { - if (value > uint.MaxValue) + foreach (ulong value in this.Value) { - return ExifDataType.Long8; + if (value > uint.MaxValue) + { + return ExifDataType.Long8; + } } } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs index 2d006c538..bf9c2cf9a 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs @@ -19,16 +19,14 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif { get { - if (this.Value is null) + if (this.Value is not null) { - return ExifDataType.Short; - } - - foreach (Number value in this.Value) - { - if (value > ushort.MaxValue) + foreach (Number value in this.Value) { - return ExifDataType.Long; + if (value > ushort.MaxValue) + { + return ExifDataType.Long; + } } } From 775f43e754f88c337ffbfe690c229d09d83b98a8 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sat, 2 Oct 2021 20:33:14 +0300 Subject: [PATCH 41/62] Add Number tests --- .../Profiles/Exif/Values/ExifValuesTests.cs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/Values/ExifValuesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/Values/ExifValuesTests.cs index 3358b1f97..3871ac61c 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/Values/ExifValuesTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/Values/ExifValuesTests.cs @@ -394,6 +394,32 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif.Values Assert.Equal(expected, typed.Value); } + [Fact] + public void NumberTests() + { + Number value1 = ushort.MaxValue; + Number value2 = ushort.MaxValue; + Assert.True(value1 == value2); + + value2 = short.MaxValue; + Assert.True(value1 != value2); + + value2 = -2; + Assert.True(value1 > value2); + + value1 = -6; + Assert.True(value1 <= value2); + + value2 = -2; + Assert.True(value1 >= value2); + + Assert.True(value1.Equals(value2)); + Assert.True(value1.GetHashCode() == value2.GetHashCode()); + + value1 = 1; + Assert.False(value1.Equals(value2)); + } + [Theory] [MemberData(nameof(NumberTags))] public void ExifNumberTests(ExifTag tag) @@ -422,6 +448,15 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif.Values var typed = (ExifNumberArray)value; Assert.Equal(expected, typed.Value); + + Assert.True(value.TrySetValue(int.MaxValue)); + Assert.Equal(new[] { int.MaxValue }, value.GetValue()); + + Assert.True(value.TrySetValue(new[] { 1u, 2u, 5u })); + Assert.Equal(new[] { 1u, 2u, 5u }, value.GetValue()); + + Assert.True(value.TrySetValue(new[] { (short)1, (short)2, (short)5 })); + Assert.Equal(new[] { (short)1, (short)2, (short)5 }, value.GetValue()); } [Theory] From 02d83c9bd240062d67e94ee3b4d99971d22c2274 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sat, 2 Oct 2021 20:38:53 +0300 Subject: [PATCH 42/62] minor --- .../Metadata/Profiles/Exif/ExifReader.cs | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs index d9fe29e82..6107bd766 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs @@ -114,6 +114,11 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif protected void ReadBigValues(List values) { + if (this.BigValues.Count == 0) + { + return; + } + ulong maxSize = 0; foreach ((ulong offset, ExifDataType dataType, ulong numberOfComponents, ExifValue exif) in this.BigValues) { @@ -159,14 +164,12 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif protected void ReadSubIfd(List values) { - if (this.subIfds is null) - { - return; - } - - foreach (ulong subIfdOffset in this.subIfds) + if (this.subIfds is not null) { - this.ReadValues(values, (uint)subIfdOffset); + foreach (ulong subIfdOffset in this.subIfds) + { + this.ReadValues(values, (uint)subIfdOffset); + } } } @@ -196,14 +199,12 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif protected void ReadSubIfd64(List values) { - if (this.subIfds is null) - { - return; - } - - foreach (ulong subIfdOffset in this.subIfds) + if (this.subIfds is not null) { - this.ReadValues64(values, subIfdOffset); + foreach (ulong subIfdOffset in this.subIfds) + { + this.ReadValues64(values, subIfdOffset); + } } } @@ -517,6 +518,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif } else if (exif.Tag == ExifTag.SubIFDs) { + //// didn't find any useful data in SubIFDs ////foreach (object val in (Array)value) ////{ //// this.AddSubIfd(val); From 2c06edc07561ebee678747f6546571624a6f9f99 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sat, 2 Oct 2021 20:51:54 +0300 Subject: [PATCH 43/62] test fixes --- .../Metadata/Profiles/Exif/Values/ExifValuesTests.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/Values/ExifValuesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/Values/ExifValuesTests.cs index 3871ac61c..341beca5c 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/Values/ExifValuesTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/Values/ExifValuesTests.cs @@ -404,13 +404,15 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif.Values value2 = short.MaxValue; Assert.True(value1 != value2); + value1 = -1; value2 = -2; Assert.True(value1 > value2); value1 = -6; Assert.True(value1 <= value2); - value2 = -2; + value1 = 10; + value2 = 10; Assert.True(value1 >= value2); Assert.True(value1.Equals(value2)); @@ -450,13 +452,13 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif.Values Assert.Equal(expected, typed.Value); Assert.True(value.TrySetValue(int.MaxValue)); - Assert.Equal(new[] { int.MaxValue }, value.GetValue()); + Assert.Equal(new[] { (Number)int.MaxValue }, value.GetValue()); Assert.True(value.TrySetValue(new[] { 1u, 2u, 5u })); - Assert.Equal(new[] { 1u, 2u, 5u }, value.GetValue()); + Assert.Equal(new[] { (Number)1u, (Number)2u, (Number)5u }, value.GetValue()); Assert.True(value.TrySetValue(new[] { (short)1, (short)2, (short)5 })); - Assert.Equal(new[] { (short)1, (short)2, (short)5 }, value.GetValue()); + Assert.Equal(new[] { (Number)(short)1, (Number)(short)2, (Number)(short)5 }, value.GetValue()); } [Theory] From e277a0dce68bb0f8212a134e012ac16be286e855 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Fri, 15 Oct 2021 10:46:25 +0300 Subject: [PATCH 44/62] Use MemoryAllocator --- .../Formats/Tiff/Ifd/DirectoryReader.cs | 11 +++- .../Formats/Tiff/Ifd/EntryReader.cs | 9 ++-- .../Formats/Tiff/TiffDecoderCore.cs | 4 +- .../Metadata/Profiles/Exif/ExifReader.cs | 52 +++++++++++++++---- 4 files changed, 56 insertions(+), 20 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs index 6421db8d8..121341b46 100644 --- a/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs +++ b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.IO; using SixLabors.ImageSharp.Formats.Tiff.Constants; +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata.Profiles.Exif; namespace SixLabors.ImageSharp.Formats.Tiff @@ -16,9 +17,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff { private readonly Stream stream; + private readonly MemoryAllocator allocator; + private ulong nextIfdOffset; - public DirectoryReader(Stream stream) => this.stream = stream; + public DirectoryReader(Stream stream, MemoryAllocator allocator) + { + this.stream = stream; + this.allocator = allocator; + } /// /// Gets the byte order. @@ -64,7 +71,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff var readers = new List(); while (this.nextIfdOffset != 0 && this.nextIfdOffset < (ulong)this.stream.Length) { - var reader = new EntryReader(this.stream, this.ByteOrder); + var reader = new EntryReader(this.stream, this.ByteOrder, this.allocator); reader.ReadTags(isBigTiff, this.nextIfdOffset); if (reader.BigValues.Count > 0) diff --git a/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs b/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs index 98cc1deb3..ef614b0e2 100644 --- a/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs +++ b/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs @@ -5,17 +5,16 @@ using System.Collections.Generic; using System.IO; using SixLabors.ImageSharp.Formats.Tiff.Constants; +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata.Profiles.Exif; namespace SixLabors.ImageSharp.Formats.Tiff { internal class EntryReader : BaseExifReader { - public EntryReader(Stream stream, ByteOrder byteOrder) - : base(stream) - { + public EntryReader(Stream stream, ByteOrder byteOrder, MemoryAllocator allocator) + : base(stream, allocator) => this.IsBigEndian = byteOrder == ByteOrder.BigEndian; - } public List Values { get; } = new List(); @@ -45,7 +44,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff internal class HeaderReader : BaseExifReader { public HeaderReader(Stream stream, ByteOrder byteOrder) - : base(stream) => + : base(stream, null) => this.IsBigEndian = byteOrder == ByteOrder.BigEndian; public bool IsBigTiff { get; private set; } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 9f0a4b514..98adc5183 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -147,7 +147,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff where TPixel : unmanaged, IPixel { this.inputStream = stream; - var reader = new DirectoryReader(stream); + var reader = new DirectoryReader(stream, this.Configuration.MemoryAllocator); IEnumerable directories = reader.Read(); this.byteOrder = reader.ByteOrder; @@ -181,7 +181,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { this.inputStream = stream; - var reader = new DirectoryReader(stream); + var reader = new DirectoryReader(stream, this.Configuration.MemoryAllocator); IEnumerable directories = reader.Read(); ExifProfile rootFrameExifProfile = directories.First(); diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs index 6107bd766..0633d3715 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Buffers.Binary; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -9,13 +10,19 @@ using System.Diagnostics; using System.IO; using System.Runtime.CompilerServices; using System.Text; +using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Metadata.Profiles.Exif { internal class ExifReader : BaseExifReader { public ExifReader(byte[] exifData) - : base(new MemoryStream(exifData ?? throw new ArgumentNullException(nameof(exifData)))) + : this(exifData, null) + { + } + + public ExifReader(byte[] exifData, MemoryAllocator allocator) + : base(new MemoryStream(exifData ?? throw new ArgumentNullException(nameof(exifData))), allocator) { } @@ -83,13 +90,17 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif private readonly byte[] buf4 = new byte[4]; private readonly byte[] buf2 = new byte[2]; + private readonly MemoryAllocator allocator; private readonly Stream data; private List invalidTags; private List subIfds; - protected BaseExifReader(Stream stream) => + protected BaseExifReader(Stream stream, MemoryAllocator allocator) + { this.data = stream ?? throw new ArgumentNullException(nameof(stream)); + this.allocator = allocator; + } private delegate TDataType ConverterMethod(ReadOnlySpan data); @@ -110,7 +121,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif public bool IsBigEndian { get; protected set; } - public List<(ulong offset, ExifDataType dataType, ulong numberOfComponents, ExifValue exif)> BigValues { get; } = new (); + public List<(ulong offset, ExifDataType dataType, ulong numberOfComponents, ExifValue exif)> BigValues { get; } = new(); protected void ReadBigValues(List values) { @@ -119,22 +130,41 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return; } - ulong maxSize = 0; + int maxSize = 0; foreach ((ulong offset, ExifDataType dataType, ulong numberOfComponents, ExifValue exif) in this.BigValues) { ulong size = numberOfComponents * ExifDataTypes.GetSize(dataType); - if (size > maxSize) + if (size > int.MaxValue) + { + ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThanOrEqualTo(size, int.MaxValue, nameof(size)); + } + + if ((int)size > maxSize) { - maxSize = size; + maxSize = (int)size; } } - Span buf = new byte[maxSize]; - foreach ((ulong offset, ExifDataType dataType, ulong numberOfComponents, ExifValue exif) tag in this.BigValues) + if (this.allocator != null) { - ulong size = tag.numberOfComponents * ExifDataTypes.GetSize(tag.dataType); - - this.ReadBigValue(values, tag, buf.Slice(0, (int)size)); + // tiff, bigTiff + using IMemoryOwner memory = this.allocator.Allocate(maxSize); + Span buf = memory.GetSpan(); + foreach ((ulong offset, ExifDataType dataType, ulong numberOfComponents, ExifValue exif) tag in this.BigValues) + { + ulong size = tag.numberOfComponents * ExifDataTypes.GetSize(tag.dataType); + this.ReadBigValue(values, tag, buf.Slice(0, (int)size)); + } + } + else + { + // embedded exif + Span buf = maxSize <= 256 ? stackalloc byte[maxSize] : new byte[maxSize]; + foreach ((ulong offset, ExifDataType dataType, ulong numberOfComponents, ExifValue exif) tag in this.BigValues) + { + ulong size = tag.numberOfComponents * ExifDataTypes.GetSize(tag.dataType); + this.ReadBigValue(values, tag, buf.Slice(0, (int)size)); + } } this.BigValues.Clear(); From d4784cb683be9bfa072f89b38cd4cfd58ed1d473 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Fri, 15 Oct 2021 11:22:13 +0300 Subject: [PATCH 45/62] correct casings --- .../Metadata/Profiles/Exif/ExifReader.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs index 0633d3715..fbc3b5c8b 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs @@ -121,7 +121,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif public bool IsBigEndian { get; protected set; } - public List<(ulong offset, ExifDataType dataType, ulong numberOfComponents, ExifValue exif)> BigValues { get; } = new(); + public List<(ulong Offset, ExifDataType DataType, ulong NumberOfComponents, ExifValue Exif)> BigValues { get; } = new(); protected void ReadBigValues(List values) { @@ -150,9 +150,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif // tiff, bigTiff using IMemoryOwner memory = this.allocator.Allocate(maxSize); Span buf = memory.GetSpan(); - foreach ((ulong offset, ExifDataType dataType, ulong numberOfComponents, ExifValue exif) tag in this.BigValues) + foreach ((ulong Offset, ExifDataType DataType, ulong NumberOfComponents, ExifValue Exif) tag in this.BigValues) { - ulong size = tag.numberOfComponents * ExifDataTypes.GetSize(tag.dataType); + ulong size = tag.NumberOfComponents * ExifDataTypes.GetSize(tag.DataType); this.ReadBigValue(values, tag, buf.Slice(0, (int)size)); } } @@ -160,9 +160,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif { // embedded exif Span buf = maxSize <= 256 ? stackalloc byte[maxSize] : new byte[maxSize]; - foreach ((ulong offset, ExifDataType dataType, ulong numberOfComponents, ExifValue exif) tag in this.BigValues) + foreach ((ulong Offset, ExifDataType DataType, ulong NumberOfComponents, ExifValue Exif) tag in this.BigValues) { - ulong size = tag.numberOfComponents * ExifDataTypes.GetSize(tag.dataType); + ulong size = tag.NumberOfComponents * ExifDataTypes.GetSize(tag.DataType); this.ReadBigValue(values, tag, buf.Slice(0, (int)size)); } } @@ -217,13 +217,13 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif } } - protected void ReadBigValue(IList values, (ulong offset, ExifDataType dataType, ulong numberOfComponents, ExifValue exif) tag, Span buffer) + protected void ReadBigValue(IList values, (ulong Offset, ExifDataType DataType, ulong NumberOfComponents, ExifValue Exif) tag, Span buffer) { - this.Seek(tag.offset); + this.Seek(tag.Offset); if (this.TryReadSpan(buffer)) { - object value = this.ConvertValue(tag.dataType, buffer, tag.numberOfComponents > 1 || tag.exif.IsArray); - this.Add(values, tag.exif, value); + object value = this.ConvertValue(tag.DataType, buffer, tag.NumberOfComponents > 1 || tag.Exif.IsArray); + this.Add(values, tag.Exif, value); } } From c6478426a3520fd894bd416f7637e4b51137fe35 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Fri, 15 Oct 2021 14:19:52 +0300 Subject: [PATCH 46/62] Update src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs --- src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs index fbc3b5c8b..1a39d723a 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs @@ -159,7 +159,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif else { // embedded exif - Span buf = maxSize <= 256 ? stackalloc byte[maxSize] : new byte[maxSize]; + Span buf = maxSize <= 256 ? stackalloc byte[256] : new byte[maxSize]; foreach ((ulong Offset, ExifDataType DataType, ulong NumberOfComponents, ExifValue Exif) tag in this.BigValues) { ulong size = tag.NumberOfComponents * ExifDataTypes.GetSize(tag.DataType); From c8974ab4f45096e4ed8c0a63edaa9e1b2bf480bd Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Fri, 15 Oct 2021 14:34:39 +0300 Subject: [PATCH 47/62] build fixes --- src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs index 121341b46..02035265d 100644 --- a/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs +++ b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs @@ -76,10 +76,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff if (reader.BigValues.Count > 0) { - reader.BigValues.Sort((t1, t2) => t1.offset.CompareTo(t2.offset)); + reader.BigValues.Sort((t1, t2) => t1.Offset.CompareTo(t2.Offset)); // this means that most likely all elements are placed before next IFD - if (reader.BigValues[0].offset < reader.NextIfdOffset) + if (reader.BigValues[0].Offset < reader.NextIfdOffset) { reader.ReadBigValues(); } From 4f10036f144b22ca4015c88f7ca028e55726bc98 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Wed, 27 Oct 2021 08:55:49 +0300 Subject: [PATCH 48/62] skipping SubIfd64 (disable SubIfd64 related code) --- .../Formats/Tiff/Ifd/EntryReader.cs | 2 +- .../Metadata/Profiles/Exif/ExifReader.cs | 38 +++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs b/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs index ee7315a7b..58b042e84 100644 --- a/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs +++ b/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs @@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff this.ReadValues64(this.Values, ifdOffset); this.NextIfdOffset = this.ReadUInt64(); - this.ReadSubIfd64(this.Values); + //// this.ReadSubIfd64(this.Values); } } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs index 1a39d723a..bb8e6b9f3 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs @@ -227,16 +227,16 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif } } - protected void ReadSubIfd64(List values) - { - if (this.subIfds is not null) - { - foreach (ulong subIfdOffset in this.subIfds) - { - this.ReadValues64(values, subIfdOffset); - } - } - } + ////protected void ReadSubIfd64(List values) + ////{ + //// if (this.subIfds is not null) + //// { + //// foreach (ulong subIfdOffset in this.subIfds) + //// { + //// this.ReadValues64(values, subIfdOffset); + //// } + //// } + ////} private static TDataType[] ToArray(ExifDataType dataType, ReadOnlySpan data, ConverterMethod converter) { @@ -482,15 +482,15 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif case ExifTagValue.TileByteCounts: exifValue = new ExifLong8Array(ExifTagValue.TileByteCounts); break; - case ExifTagValue.SubIFDOffset: - exifValue = new ExifLong8(ExifTagValue.SubIFDOffset); - break; - case ExifTagValue.GPSIFDOffset: - exifValue = new ExifLong8(ExifTagValue.GPSIFDOffset); - break; - case ExifTagValue.SubIFDs: - exifValue = new ExifLong8Array(ExifTagValue.SubIFDs); - break; + ////case ExifTagValue.SubIFDOffset: + //// exifValue = new ExifLong8(ExifTagValue.SubIFDOffset); + //// break; + ////case ExifTagValue.GPSIFDOffset: + //// exifValue = new ExifLong8(ExifTagValue.GPSIFDOffset); + //// break; + ////case ExifTagValue.SubIFDs: + //// exifValue = new ExifLong8Array(ExifTagValue.SubIFDs); + //// break; default: exifValue = ExifValues.Create(tag) ?? ExifValues.Create(tag, dataType, numberOfComponents); break; From e66145e2d6ea89acd5eb4b6a9431ae0e65789fab Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Wed, 27 Oct 2021 09:59:40 +0300 Subject: [PATCH 49/62] webp test fix --- .../ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs index e20ad5136..3ee20cbd1 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs @@ -187,7 +187,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif https://exiftool.org/TagNames/EXIF.html */ [InlineData(TestImageWriteFormat.Jpeg, 18)] [InlineData(TestImageWriteFormat.Png, 18)] - [InlineData(TestImageWriteFormat.WebpLossless, 16)] + [InlineData(TestImageWriteFormat.WebpLossless, 18)] public void SetValue(TestImageWriteFormat imageFormat, int expectedProfileValueCount) { Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateRgba32Image(); From 831501bc3254ed9f4e7018c8aecc41cbcbb187be Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Wed, 27 Oct 2021 10:31:54 +0300 Subject: [PATCH 50/62] comment unused code --- .../Metadata/Profiles/Exif/ExifReader.cs | 37 +++++++++---------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs index bb8e6b9f3..651ebe422 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs @@ -134,10 +134,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif foreach ((ulong offset, ExifDataType dataType, ulong numberOfComponents, ExifValue exif) in this.BigValues) { ulong size = numberOfComponents * ExifDataTypes.GetSize(dataType); - if (size > int.MaxValue) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThanOrEqualTo(size, int.MaxValue, nameof(size)); - } + DebugGuard.MustBeLessThanOrEqualTo(size, int.MaxValue, nameof(size)); if ((int)size > maxSize) { @@ -361,13 +358,13 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif } return ToArray(dataType, buffer, this.ConvertToUInt64); - case ExifDataType.SignedLong8: - if (!isArray) - { - return this.ConvertToInt64(buffer); - } + ////case ExifDataType.SignedLong8: + //// if (!isArray) + //// { + //// return this.ConvertToInt64(buffer); + //// } - return ToArray(dataType, buffer, this.ConvertToUInt64); + //// return ToArray(dataType, buffer, this.ConvertToUInt64); case ExifDataType.Undefined: if (!isArray) { @@ -596,17 +593,17 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif ? this.ConvertToShort(this.buf2) : default; - private long ConvertToInt64(ReadOnlySpan buffer) - { - if (buffer.Length < 8) - { - return default; - } + ////private long ConvertToInt64(ReadOnlySpan buffer) + ////{ + //// if (buffer.Length < 8) + //// { + //// return default; + //// } - return this.IsBigEndian - ? BinaryPrimitives.ReadInt64BigEndian(buffer) - : BinaryPrimitives.ReadInt64LittleEndian(buffer); - } + //// return this.IsBigEndian + //// ? BinaryPrimitives.ReadInt64BigEndian(buffer) + //// : BinaryPrimitives.ReadInt64LittleEndian(buffer); + ////} private ulong ConvertToUInt64(ReadOnlySpan buffer) { From 6e25e2f87e41551e0bb8934fc65cfcfca3e2976e Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Wed, 27 Oct 2021 11:26:56 +0300 Subject: [PATCH 51/62] improve coverage --- .../Tiff/Compression/TiffBaseDecompressor.cs | 6 ++--- .../Formats/Tiff/TiffDecoderCore.cs | 5 +--- .../Profiles/Exif/Values/ExifLong8Array.cs | 17 +++++++------- .../Formats/Tiff/BigTiffMetadataTests.cs | 23 +++++++++++++++---- 4 files changed, 31 insertions(+), 20 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseDecompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseDecompressor.cs index d5de4937f..986d585d3 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseDecompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseDecompressor.cs @@ -36,10 +36,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression /// The output buffer for uncompressed data. public void Decompress(BufferedReadStream stream, ulong stripOffset, ulong stripByteCount, int stripHeight, Span buffer) { - if (stripOffset > long.MaxValue || stripByteCount > long.MaxValue) - { - TiffThrowHelper.ThrowImageFormatException("The StripOffset or StripByteCount value is too big."); - } + DebugGuard.MustBeLessThanOrEqualTo(stripOffset, (ulong)long.MaxValue, nameof(stripOffset)); + DebugGuard.MustBeLessThanOrEqualTo(stripByteCount, (ulong)long.MaxValue, nameof(stripByteCount)); stream.Seek((long)stripOffset, SeekOrigin.Begin); this.Decompress(stream, (int)stripByteCount, stripHeight, buffer); diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 98adc5183..419fbc31c 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -473,10 +473,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff TiffThrowHelper.ThrowImageFormatException("The TIFF image frame is missing the ImageWidth"); } - if (((ulong)width.Value) > int.MaxValue) - { - TiffThrowHelper.ThrowImageFormatException("Too big ImageWidth value"); - } + DebugGuard.MustBeLessThanOrEqualTo((ulong)width.Value, (ulong)int.MaxValue, nameof(ExifTag.ImageWidth)); return (int)width.Value; } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs index 366eb8d12..eced4e3de 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs @@ -7,11 +7,6 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif { internal sealed class ExifLong8Array : ExifArrayValue { - public ExifLong8Array(ExifTag tag) - : base(tag) - { - } - public ExifLong8Array(ExifTagValue tag) : base(tag) { @@ -109,7 +104,13 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif private bool SetArray(long[] values) { - this.Value = Unsafe.As(ref values); + var numbers = new ulong[values.Length]; + for (int i = 0; i < values.Length; i++) + { + numbers[i] = (ulong)(values[i] < 0 ? 0 : values[i]); + } + + this.Value = numbers; return true; } @@ -124,7 +125,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif var numbers = new ulong[values.Length]; for (int i = 0; i < values.Length; i++) { - numbers[i] = (ulong)values[i]; + numbers[i] = (ulong)Numerics.Clamp(values[i], 0, int.MaxValue); } this.Value = numbers; @@ -148,7 +149,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif var numbers = new ulong[values.Length]; for (int i = 0; i < values.Length; i++) { - numbers[i] = (ulong)values[i]; + numbers[i] = (ulong)Numerics.Clamp(values[i], 0, short.MaxValue); } this.Value = numbers; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs index 91cfa1efa..8e90aeea1 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs @@ -43,9 +43,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.True(long8.TrySetValue(-100L)); Assert.Equal(-100L, long8.GetValue()); + Assert.Equal(ExifDataType.SignedLong8, long8.DataType); - Assert.True(long8.TrySetValue(100L)); - Assert.Equal(100L, long8.GetValue()); + Assert.True(long8.TrySetValue(long.MaxValue)); + Assert.Equal(long.MaxValue, long8.GetValue()); + Assert.Equal(ExifDataType.SignedLong8, long8.DataType); } [Fact] @@ -65,14 +67,25 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.True(long8.TrySetValue(123)); Assert.Equal(new[] { 123UL }, long8.GetValue()); + Assert.True(long8.TrySetValue(123u)); + Assert.Equal(new[] { 123UL }, long8.GetValue()); + Assert.True(long8.TrySetValue(123L)); Assert.Equal(new[] { 123UL }, long8.GetValue()); Assert.True(long8.TrySetValue(123UL)); Assert.Equal(new[] { 123UL }, long8.GetValue()); + Assert.True(long8.TrySetValue(new short[] { -1, 2, -3, 4 })); + Assert.Equal(new ulong[] { 0, 2UL, 0, 4UL }, long8.GetValue()); + Assert.True(long8.TrySetValue(new[] { 1, 2, 3, 4 })); Assert.Equal(new[] { 1UL, 2UL, 3UL, 4UL }, long8.GetValue()); + Assert.Equal(ExifDataType.Long, long8.DataType); + + Assert.True(long8.TrySetValue(new[] { 1, 2, 3, 4, long.MaxValue })); + Assert.Equal(new[] { 1UL, 2UL, 3UL, 4UL, (ulong)long.MaxValue }, long8.GetValue()); + Assert.Equal(ExifDataType.Long8, long8.DataType); } [Fact] @@ -82,9 +95,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.True(long8.TrySetValue(new[] { 0L })); Assert.Equal(new[] { 0L }, long8.GetValue()); + Assert.Equal(ExifDataType.SignedLong8, long8.DataType); - Assert.True(long8.TrySetValue(new[] { -1L, 2L, -3L, 4L })); - Assert.Equal(new[] { -1L, 2L, -3L, 4L }, long8.GetValue()); + Assert.True(long8.TrySetValue(new[] { -1L, 2L, long.MinValue, 4L })); + Assert.Equal(new[] { -1L, 2L, long.MinValue, 4L }, long8.GetValue()); + Assert.Equal(ExifDataType.SignedLong8, long8.DataType); } } } From 0e3fada06abef8ed1314ba61540d2e4f0211c99c Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Wed, 27 Oct 2021 15:55:51 +0300 Subject: [PATCH 52/62] rework number/ulong arrays --- .../Formats/Tiff/TiffDecoderCore.cs | 85 ++++++++++--------- 1 file changed, 47 insertions(+), 38 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 419fbc31c..177a93d24 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -219,21 +219,57 @@ namespace SixLabors.ImageSharp.Formats.Tiff int rowsPerStrip = tags.GetValue(ExifTag.RowsPerStrip) != null ? (int)tags.GetValue(ExifTag.RowsPerStrip).Value : TiffConstants.RowsPerStripInfinity; - var stripOffsets = (Array)tags.GetValueInternal(ExifTag.StripOffsets)?.GetValue(); - var stripByteCounts = (Array)tags.GetValueInternal(ExifTag.StripByteCounts)?.GetValue(); + var stripOffsetsArray = (Array)tags.GetValueInternal(ExifTag.StripOffsets).GetValue(); + var stripByteCountsArray = (Array)tags.GetValueInternal(ExifTag.StripByteCounts).GetValue(); + + IMemoryOwner stripOffsetsMemory = this.ConvertNumbers(stripOffsetsArray, out Span stripOffsets); + IMemoryOwner stripByteCountsMemory = this.ConvertNumbers(stripByteCountsArray, out Span stripByteCounts); if (this.PlanarConfiguration == TiffPlanarConfiguration.Planar) { - this.DecodeStripsPlanar(frame, rowsPerStrip, stripOffsets, stripByteCounts, cancellationToken); + this.DecodeStripsPlanar( + frame, + rowsPerStrip, + stripOffsets, + stripByteCounts, + cancellationToken); } else { - this.DecodeStripsChunky(frame, rowsPerStrip, stripOffsets, stripByteCounts, cancellationToken); + this.DecodeStripsChunky( + frame, + rowsPerStrip, + stripOffsets, + stripByteCounts, + cancellationToken); } + stripOffsetsMemory?.Dispose(); + stripByteCountsMemory?.Dispose(); return frame; } + private IMemoryOwner ConvertNumbers(Array array, out Span span) + { + if (array is Number[] numbers) + { + IMemoryOwner memory = this.memoryAllocator.Allocate(numbers.Length); + span = memory.GetSpan(); + for (int i = 0; i < numbers.Length; i++) + { + span[i] = (uint)numbers[i]; + } + + return memory; + } + else + { + DebugGuard.IsTrue(array is ulong[], $"Expected {nameof(UInt64)} array."); + span = (ulong[])array; + return null; + } + } + /// /// Calculates the size (in bytes) for a pixel buffer using the determined color format. /// @@ -284,7 +320,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// An array of byte offsets to each strip in the image. /// An array of the size of each strip (in bytes). /// The token to monitor cancellation. - private void DecodeStripsPlanar(ImageFrame frame, int rowsPerStrip, Array stripOffsets, Array stripByteCounts, CancellationToken cancellationToken) + private void DecodeStripsPlanar(ImageFrame frame, int rowsPerStrip, Span stripOffsets, Span stripByteCounts, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { int stripsPerPixel = this.BitsPerSample.Channels; @@ -335,26 +371,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff int stripIndex = i; for (int planeIndex = 0; planeIndex < stripsPerPixel; planeIndex++) { - ulong offset = stripOffsets.GetValue(stripIndex) switch - { - ulong val => val, - Number val => (uint)val, - _ => throw TiffThrowHelper.ThrowImageFormatException("Expected Number or Long8 array") - }; - - ulong count = stripByteCounts.GetValue(stripIndex) switch - { - ulong val => val, - Number val => (uint)val, - _ => throw TiffThrowHelper.ThrowImageFormatException("Expected Number or Long8 array") - }; - decompressor.Decompress( this.inputStream, - offset, - count, + stripOffsets[stripIndex], + stripByteCounts[stripIndex], stripHeight, stripBuffers[planeIndex].GetSpan()); + stripIndex += stripsPerPlane; } @@ -379,7 +402,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The strip offsets. /// The strip byte counts. /// The token to monitor cancellation. - private void DecodeStripsChunky(ImageFrame frame, int rowsPerStrip, Array stripOffsets, Array stripByteCounts, CancellationToken cancellationToken) + private void DecodeStripsChunky(ImageFrame frame, int rowsPerStrip, Span stripOffsets, Span stripByteCounts, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { // If the rowsPerStrip has the default value, which is effectively infinity. That is, the entire image is one strip. @@ -435,24 +458,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff break; } - ulong offset = stripOffsets.GetValue(stripIndex) switch - { - ulong val => val, - Number val => (uint)val, - _ => throw TiffThrowHelper.ThrowImageFormatException("Expected Number or Long8 array") - }; - - ulong count = stripByteCounts.GetValue(stripIndex) switch - { - ulong val => val, - Number val => (uint)val, - _ => throw TiffThrowHelper.ThrowImageFormatException("Expected Number or Long8 array") - }; - decompressor.Decompress( this.inputStream, - offset, - count, + stripOffsets[stripIndex], + stripByteCounts[stripIndex], stripHeight, stripBufferSpan); From 0f4bc33c60958f22700491de1d94977cf4b3d75c Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sat, 27 Nov 2021 12:05:32 +0300 Subject: [PATCH 53/62] Add exif tag tests --- .../Formats/Tiff/BigTiffMetadataTests.cs | 66 ++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs index 8e90aeea1..b81ddce4d 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs @@ -1,10 +1,15 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; +using System.Collections.Generic; +using System.IO; using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; - +using SixLabors.ImageSharp.PixelFormats; using Xunit; +using static SixLabors.ImageSharp.Tests.TestImages.Tiff; namespace SixLabors.ImageSharp.Tests.Formats.Tiff { @@ -101,5 +106,64 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal(new[] { -1L, 2L, long.MinValue, 4L }, long8.GetValue()); Assert.Equal(ExifDataType.SignedLong8, long8.DataType); } + + [Theory] + [WithFile(RgbUncompressed, PixelTypes.Rgb24)] + public void ExifTags(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image input = provider.GetImage(); + + var testTags = new Dictionary + { + { new ExifTag((ExifTagValue)0xdd01), (ExifDataType.SingleFloat, new float[] { 1.2f, 2.3f, 4.5f }) }, + { new ExifTag((ExifTagValue)0xdd02), (ExifDataType.DoubleFloat, new double[] { 4.5, 6.7 }) }, + { new ExifTag((ExifTagValue)0xdd03), (ExifDataType.DoubleFloat, 8.903) }, + { new ExifTag((ExifTagValue)0xdd04), (ExifDataType.SignedByte, (sbyte)-3) }, + { new ExifTag((ExifTagValue)0xdd05), (ExifDataType.SignedByte, new sbyte[] { -3, 0, 5 }) }, + { new ExifTag((ExifTagValue)0xdd06), (ExifDataType.SignedLong, new int[] { int.MinValue, 1, int.MaxValue }) }, + { new ExifTag((ExifTagValue)0xdd07), (ExifDataType.Long, new uint[] { 0, 1, uint.MaxValue }) }, + { new ExifTag((ExifTagValue)0xdd08), (ExifDataType.Short, (ushort)1234) }, + //{ new ExifTag((ExifTagValue)0xdd09), (ExifDataType.Long8, ulong.MaxValue) }, + }; + + // arrange + var values = new List(); + foreach (KeyValuePair tag in testTags) + { + ExifValue newExifValue = ExifValues.Create((ExifTagValue)(ushort)tag.Key, tag.Value.DataType, tag.Value.Value is Array); + + Assert.True(newExifValue.TrySetValue(tag.Value.Value)); + values.Add(newExifValue); + } + + input.Frames.RootFrame.Metadata.ExifProfile = new ExifProfile(values, Array.Empty()); + + // act + var encoder = new TiffEncoder(); + using var memStream = new MemoryStream(); + input.Save(memStream, encoder); + + // assert + memStream.Position = 0; + using var output = Image.Load(memStream); + ImageFrameMetadata loadedFrameMetadata = output.Frames.RootFrame.Metadata; + foreach (KeyValuePair tag in testTags) + { + IExifValue exifValue = loadedFrameMetadata.ExifProfile.GetValueInternal(tag.Key); + Assert.NotNull(exifValue); + object value = exifValue.GetValue(); + + Assert.Equal(tag.Value.DataType, exifValue.DataType); + if (value is Array array) + { + Assert.Equal(array, tag.Value.Value as Array); + } + else + { + Assert.Equal(value, tag.Value.Value); + } + } + } } } From 494ca166cb1833ee139f2bc7968d8f87b0b4ee26 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sat, 27 Nov 2021 12:33:43 +0300 Subject: [PATCH 54/62] cleanup --- tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderBaseTester.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderBaseTester.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderBaseTester.cs index ff70eaf8a..aeadae255 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderBaseTester.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderBaseTester.cs @@ -8,8 +8,6 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; -using Xunit; - namespace SixLabors.ImageSharp.Tests.Formats.Tiff { public abstract class TiffDecoderBaseTester From 6aee72308a1cbb88c3f5e9f112996db399cf5f9c Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sat, 27 Nov 2021 14:21:52 +0300 Subject: [PATCH 55/62] Enable long8 read/write methods, add tests --- .../Metadata/Profiles/Exif/ExifReader.cs | 32 ++--- .../Metadata/Profiles/Exif/ExifWriter.cs | 18 +++ .../Formats/Tiff/BigTiffMetadataTests.cs | 132 +++++++++++++++--- .../Profiles/Exif/Values/ExifValuesTests.cs | 3 + 4 files changed, 150 insertions(+), 35 deletions(-) diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs index 651ebe422..4fcac5299 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs @@ -358,13 +358,13 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif } return ToArray(dataType, buffer, this.ConvertToUInt64); - ////case ExifDataType.SignedLong8: - //// if (!isArray) - //// { - //// return this.ConvertToInt64(buffer); - //// } + case ExifDataType.SignedLong8: + if (!isArray) + { + return this.ConvertToInt64(buffer); + } - //// return ToArray(dataType, buffer, this.ConvertToUInt64); + return ToArray(dataType, buffer, this.ConvertToUInt64); case ExifDataType.Undefined: if (!isArray) { @@ -593,17 +593,17 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif ? this.ConvertToShort(this.buf2) : default; - ////private long ConvertToInt64(ReadOnlySpan buffer) - ////{ - //// if (buffer.Length < 8) - //// { - //// return default; - //// } + private long ConvertToInt64(ReadOnlySpan buffer) + { + if (buffer.Length < 8) + { + return default; + } - //// return this.IsBigEndian - //// ? BinaryPrimitives.ReadInt64BigEndian(buffer) - //// : BinaryPrimitives.ReadInt64LittleEndian(buffer); - ////} + return this.IsBigEndian + ? BinaryPrimitives.ReadInt64BigEndian(buffer) + : BinaryPrimitives.ReadInt64LittleEndian(buffer); + } private ulong ConvertToUInt64(ReadOnlySpan buffer) { diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs index e7a01b070..498eec952 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs @@ -150,6 +150,20 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return offset + 4; } + private static int WriteInt64(long value, Span destination, int offset) + { + BinaryPrimitives.WriteInt64LittleEndian(destination.Slice(offset, 8), value); + + return offset + 8; + } + + private static int WriteUInt64(ulong value, Span destination, int offset) + { + BinaryPrimitives.WriteUInt64LittleEndian(destination.Slice(offset, 8), value); + + return offset + 8; + } + private static int WriteInt32(int value, Span destination, int offset) { BinaryPrimitives.WriteInt32LittleEndian(destination.Slice(offset, 4), value); @@ -390,6 +404,10 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif } return WriteUInt32((uint)value, destination, offset); + case ExifDataType.Long8: + return WriteUInt64((ulong)value, destination, offset); + case ExifDataType.SignedLong8: + return WriteInt64((long)value, destination, offset); case ExifDataType.Rational: WriteRational(destination.Slice(offset, 8), (Rational)value); return offset + 8; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs index b81ddce4d..cdeb789de 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs @@ -2,22 +2,21 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers.Binary; using System.Collections.Generic; using System.IO; using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Tiff.Writers; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; using Xunit; -using static SixLabors.ImageSharp.Tests.TestImages.Tiff; namespace SixLabors.ImageSharp.Tests.Formats.Tiff { [Trait("Format", "Tiff")] public class BigTiffMetadataTests { - private static TiffDecoder TiffDecoder => new TiffDecoder(); - [Fact] public void ExifLong8() { @@ -107,24 +106,27 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal(ExifDataType.SignedLong8, long8.DataType); } - [Theory] - [WithFile(RgbUncompressed, PixelTypes.Rgb24)] - public void ExifTags(TestImageProvider provider) - where TPixel : unmanaged, IPixel + [Fact] + public void NotCoveredTags() { - using Image input = provider.GetImage(); + using var input = new Image(10, 10); var testTags = new Dictionary { { new ExifTag((ExifTagValue)0xdd01), (ExifDataType.SingleFloat, new float[] { 1.2f, 2.3f, 4.5f }) }, - { new ExifTag((ExifTagValue)0xdd02), (ExifDataType.DoubleFloat, new double[] { 4.5, 6.7 }) }, - { new ExifTag((ExifTagValue)0xdd03), (ExifDataType.DoubleFloat, 8.903) }, - { new ExifTag((ExifTagValue)0xdd04), (ExifDataType.SignedByte, (sbyte)-3) }, - { new ExifTag((ExifTagValue)0xdd05), (ExifDataType.SignedByte, new sbyte[] { -3, 0, 5 }) }, - { new ExifTag((ExifTagValue)0xdd06), (ExifDataType.SignedLong, new int[] { int.MinValue, 1, int.MaxValue }) }, - { new ExifTag((ExifTagValue)0xdd07), (ExifDataType.Long, new uint[] { 0, 1, uint.MaxValue }) }, - { new ExifTag((ExifTagValue)0xdd08), (ExifDataType.Short, (ushort)1234) }, - //{ new ExifTag((ExifTagValue)0xdd09), (ExifDataType.Long8, ulong.MaxValue) }, + { new ExifTag((ExifTagValue)0xdd02), (ExifDataType.SingleFloat, 2.345f) }, + { new ExifTag((ExifTagValue)0xdd03), (ExifDataType.DoubleFloat, new double[] { 4.5, 6.7 }) }, + { new ExifTag((ExifTagValue)0xdd04), (ExifDataType.DoubleFloat, 8.903) }, + { new ExifTag((ExifTagValue)0xdd05), (ExifDataType.SignedByte, (sbyte)-3) }, + { new ExifTag((ExifTagValue)0xdd06), (ExifDataType.SignedByte, new sbyte[] { -3, 0, 5 }) }, + { new ExifTag((ExifTagValue)0xdd07), (ExifDataType.SignedLong, new int[] { int.MinValue, 1, int.MaxValue }) }, + { new ExifTag((ExifTagValue)0xdd08), (ExifDataType.Long, new uint[] { 0, 1, uint.MaxValue }) }, + { new ExifTag((ExifTagValue)0xdd09), (ExifDataType.SignedShort, (short)-1234) }, + { new ExifTag((ExifTagValue)0xdd10), (ExifDataType.Short, (ushort)1234) }, + ////{ new ExifTag((ExifTagValue)0xdd11), (ExifDataType.Long8, ulong.MaxValue) }, + ////{ new ExifTag((ExifTagValue)0xdd12), (ExifDataType.SignedLong8, long.MaxValue) }, + ////{ new ExifTag((ExifTagValue)0xdd13), (ExifDataType.Long8, new ulong[] { 0, 1234, 56789UL, ulong.MaxValue }) }, + ////{ new ExifTag((ExifTagValue)0xdd14), (ExifDataType.SignedLong8, new long[] { -1234, 56789L, long.MaxValue }) }, }; // arrange @@ -155,15 +157,107 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff object value = exifValue.GetValue(); Assert.Equal(tag.Value.DataType, exifValue.DataType); - if (value is Array array) { - Assert.Equal(array, tag.Value.Value as Array); + Assert.Equal(value, tag.Value.Value); } - else + } + } + + [Fact] + public void NotCoveredTags64() + { + var testTags = new Dictionary + { + { new ExifTag((ExifTagValue)0xdd11), (ExifDataType.Long8, ulong.MaxValue) }, + { new ExifTag((ExifTagValue)0xdd12), (ExifDataType.SignedLong8, long.MaxValue) }, + ////{ new ExifTag((ExifTagValue)0xdd13), (ExifDataType.Long8, new ulong[] { 0, 1234, 56789UL, ulong.MaxValue }) }, + ////{ new ExifTag((ExifTagValue)0xdd14), (ExifDataType.SignedLong8, new long[] { -1234, 56789L, long.MaxValue }) }, + }; + + var values = new List(); + foreach (KeyValuePair tag in testTags) + { + ExifValue newExifValue = ExifValues.Create((ExifTagValue)(ushort)tag.Key, tag.Value.DataType, tag.Value.Value is Array); + + Assert.True(newExifValue.TrySetValue(tag.Value.Value)); + values.Add(newExifValue); + } + + // act + byte[] inputBytes = WriteIfd64(values); + Configuration config = Configuration.Default; + var reader = new EntryReader( + new MemoryStream(inputBytes), + BitConverter.IsLittleEndian ? ByteOrder.LittleEndian : ByteOrder.BigEndian, + config.MemoryAllocator); + + reader.ReadTags(true, 0); + + List outputTags = reader.Values; + + // assert + foreach (KeyValuePair tag in testTags) + { + IExifValue exifValue = outputTags.Find(t => t.Tag == tag.Key); + Assert.NotNull(exifValue); + object value = exifValue.GetValue(); + + Assert.Equal(tag.Value.DataType, exifValue.DataType); { Assert.Equal(value, tag.Value.Value); } } } + + private static byte[] WriteIfd64(List values) + { + byte[] buffer = new byte[8]; + var ms = new MemoryStream(); + var writer = new TiffStreamWriter(ms); + WriteLong8(writer, buffer, (ulong)values.Count); + + foreach (IExifValue entry in values) + { + writer.Write((ushort)entry.Tag); + writer.Write((ushort)entry.DataType); + WriteLong8(writer, buffer, ExifWriter.GetNumberOfComponents(entry)); + + uint length = ExifWriter.GetLength(entry); + + Assert.True(length <= 8); + + if (length <= 8) + { + int sz = ExifWriter.WriteValue(entry, buffer, 0); + DebugGuard.IsTrue(sz == length, "Incorrect number of bytes written"); + + // write padded + writer.BaseStream.Write(buffer.AsSpan(0, sz)); + int d = sz % 8; + if (d != 0) + { + writer.BaseStream.Write(new byte[d]); + } + } + } + + WriteLong8(writer, buffer, 0); + + return ms.ToArray(); + } + + private static void WriteLong8(TiffStreamWriter writer, byte[] buffer, ulong value) + { + if (writer.IsLittleEndian) + { + BinaryPrimitives.WriteUInt64LittleEndian(buffer, value); + } + else + { + BinaryPrimitives.WriteUInt64BigEndian(buffer, value); + } + + writer.BaseStream.Write(buffer); + } } } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/Values/ExifValuesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/Values/ExifValuesTests.cs index 341beca5c..a5ca115d4 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/Values/ExifValuesTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/Values/ExifValuesTests.cs @@ -436,6 +436,9 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif.Values var typed = (ExifNumber)value; Assert.Equal(expected, typed.Value); + + typed.Value = ushort.MaxValue + 1; + Assert.True(expected < typed.Value); } [Theory] From 8c61c8064c7219aa24b48d3397d87d3077259b2b Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sat, 27 Nov 2021 14:55:19 +0300 Subject: [PATCH 56/62] cleanup --- .../Formats/Tiff/BigTiffMetadataTests.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs index cdeb789de..1493060d5 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs @@ -123,10 +123,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { new ExifTag((ExifTagValue)0xdd08), (ExifDataType.Long, new uint[] { 0, 1, uint.MaxValue }) }, { new ExifTag((ExifTagValue)0xdd09), (ExifDataType.SignedShort, (short)-1234) }, { new ExifTag((ExifTagValue)0xdd10), (ExifDataType.Short, (ushort)1234) }, - ////{ new ExifTag((ExifTagValue)0xdd11), (ExifDataType.Long8, ulong.MaxValue) }, - ////{ new ExifTag((ExifTagValue)0xdd12), (ExifDataType.SignedLong8, long.MaxValue) }, - ////{ new ExifTag((ExifTagValue)0xdd13), (ExifDataType.Long8, new ulong[] { 0, 1234, 56789UL, ulong.MaxValue }) }, - ////{ new ExifTag((ExifTagValue)0xdd14), (ExifDataType.SignedLong8, new long[] { -1234, 56789L, long.MaxValue }) }, }; // arrange @@ -164,12 +160,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff } [Fact] - public void NotCoveredTags64() + public void NotCoveredTags64bit() { var testTags = new Dictionary { { new ExifTag((ExifTagValue)0xdd11), (ExifDataType.Long8, ulong.MaxValue) }, { new ExifTag((ExifTagValue)0xdd12), (ExifDataType.SignedLong8, long.MaxValue) }, + //// WriteIfdTags64Bit: arrays aren't support ////{ new ExifTag((ExifTagValue)0xdd13), (ExifDataType.Long8, new ulong[] { 0, 1234, 56789UL, ulong.MaxValue }) }, ////{ new ExifTag((ExifTagValue)0xdd14), (ExifDataType.SignedLong8, new long[] { -1234, 56789L, long.MaxValue }) }, }; @@ -184,7 +181,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff } // act - byte[] inputBytes = WriteIfd64(values); + byte[] inputBytes = WriteIfdTags64Bit(values); Configuration config = Configuration.Default; var reader = new EntryReader( new MemoryStream(inputBytes), @@ -209,7 +206,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff } } - private static byte[] WriteIfd64(List values) + private static byte[] WriteIfdTags64Bit(List values) { byte[] buffer = new byte[8]; var ms = new MemoryStream(); From d2500953f766281f7df571215f3a49923d49225c Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sat, 18 Dec 2021 16:56:41 +0300 Subject: [PATCH 57/62] universal format detector --- .../Formats/Tiff/TiffConfigurationModule.cs | 4 +- .../Formats/Tiff/TiffImageFormatDetector.cs | 45 +++++++------------ 2 files changed, 16 insertions(+), 33 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs b/src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs index 656483435..cc97da5bb 100644 --- a/src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs +++ b/src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs @@ -13,9 +13,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff { configuration.ImageFormatsManager.SetEncoder(TiffFormat.Instance, new TiffEncoder()); configuration.ImageFormatsManager.SetDecoder(TiffFormat.Instance, new TiffDecoder()); - configuration.ImageFormatsManager.AddImageFormatDetector(new TiffImageFormatDetector(false)); - - configuration.ImageFormatsManager.AddImageFormatDetector(new TiffImageFormatDetector(true)); + configuration.ImageFormatsManager.AddImageFormatDetector(new TiffImageFormatDetector()); } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs b/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs index 5755b306a..51280b4bf 100644 --- a/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs +++ b/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs @@ -10,16 +10,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// public sealed class TiffImageFormatDetector : IImageFormatDetector { - private readonly bool isBigTiff; - - /// - /// Initializes a new instance of the class. - /// - /// if set to true is BigTiff. - public TiffImageFormatDetector(bool isBigTiff) => this.isBigTiff = isBigTiff; - /// - public int HeaderSize => this.isBigTiff ? 8 : 4; + public int HeaderSize => 8; /// public IImageFormat DetectFormat(ReadOnlySpan header) @@ -39,39 +31,32 @@ namespace SixLabors.ImageSharp.Formats.Tiff if (header[0] == 0x49 && header[1] == 0x49) { // Little-endian - if (this.isBigTiff is false) + if (header[2] == 0x2A && header[3] == 0x00) { - if (header[2] == 0x2A && header[3] == 0x00) - { - return true; - } + // tiff + return true; } - else + else if (header[2] == 0x2B && header[3] == 0x00 + && header[4] == 8 && header[5] == 0 && header[6] == 0 && header[7] == 0) { - if (header[2] == 0x2B && header[3] == 0x00 - && header[4] == 8 && header[5] == 0 && header[6] == 0 && header[7] == 0) - { - return true; - } + // big tiff + return true; } } else if (header[0] == 0x4D && header[1] == 0x4D) { // Big-endian - if (this.isBigTiff is false) + if (header[2] == 0 && header[3] == 0x2A) { - if (header[2] == 0 && header[3] == 0x2A) - { - return true; - } + // tiff + return true; } else + if (header[2] == 0 && header[3] == 0x2B + && header[4] == 0 && header[5] == 8 && header[6] == 0 && header[7] == 0) { - if (header[2] == 0 && header[3] == 0x2B - && header[4] == 0 && header[5] == 8 && header[6] == 0 && header[7] == 0) - { - return true; - } + // big tiff + return true; } } } From 7df7d0dbec9cbfb598d9814a107b865789d1e27c Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sat, 18 Dec 2021 17:05:43 +0300 Subject: [PATCH 58/62] add TiffFormatType enum --- .../Tiff/TiffDecoderMetadataCreator.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffFormatType.cs | 21 +++++++++++++++++++ src/ImageSharp/Formats/Tiff/TiffMetadata.cs | 7 ++----- .../Formats/Tiff/BigTiffDecoderTests.cs | 5 +++-- 4 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 src/ImageSharp/Formats/Tiff/TiffFormatType.cs diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs index ddaf6fa96..4c4023ace 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs @@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff TiffMetadata tiffMetadata = imageMetaData.GetTiffMetadata(); tiffMetadata.ByteOrder = byteOrder; - tiffMetadata.IsBigTiff = isBigTiff; + tiffMetadata.FormatType = isBigTiff ? TiffFormatType.BigTIFF : TiffFormatType.Default; return imageMetaData; } diff --git a/src/ImageSharp/Formats/Tiff/TiffFormatType.cs b/src/ImageSharp/Formats/Tiff/TiffFormatType.cs new file mode 100644 index 000000000..320386e4c --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/TiffFormatType.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Tiff +{ + /// + /// The TIFF format type enum. + /// + public enum TiffFormatType + { + /// + /// The TIFF file format type. + /// + Default, + + /// + /// The BigTIFF format type. + /// + BigTIFF + } +} diff --git a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs index d537981db..f5124a78a 100644 --- a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs @@ -27,12 +27,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff public ByteOrder ByteOrder { get; set; } /// - /// Gets or sets a value indicating whether this instance is BigTiff format. + /// Gets or sets the format type. /// - /// - /// true if this instance BigTiff format; otherwise, false. - /// - public bool IsBigTiff { get; set; } + public TiffFormatType FormatType { get; set; } /// public IDeepCloneable DeepClone() => new TiffMetadata(this); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs index 24fce1d0d..7a02c91b8 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs @@ -5,6 +5,7 @@ using System; using System.IO; using System.Linq; +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; @@ -80,9 +81,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal(expectedVResolution, info.Metadata.VerticalResolution); Assert.Equal(expectedResolutionUnit, info.Metadata.ResolutionUnits); - ImageSharp.Formats.Tiff.TiffMetadata tiffmeta = info.Metadata.GetTiffMetadata(); + TiffMetadata tiffmeta = info.Metadata.GetTiffMetadata(); Assert.NotNull(tiffmeta); - Assert.True(tiffmeta.IsBigTiff); + Assert.Equal(TiffFormatType.BigTIFF, tiffmeta.FormatType); } } From 7d3921aa74ce04ca4092d4fb1db0925377900cd0 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Tue, 4 Jan 2022 15:08:49 +0300 Subject: [PATCH 59/62] cleanup --- .../Metadata/Profiles/Exif/ExifReader.cs | 28 ------------------- 1 file changed, 28 deletions(-) diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs index 4fcac5299..2fcd1cc07 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs @@ -224,17 +224,6 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif } } - ////protected void ReadSubIfd64(List values) - ////{ - //// if (this.subIfds is not null) - //// { - //// foreach (ulong subIfdOffset in this.subIfds) - //// { - //// this.ReadValues64(values, subIfdOffset); - //// } - //// } - ////} - private static TDataType[] ToArray(ExifDataType dataType, ReadOnlySpan data, ConverterMethod converter) { int dataTypeSize = (int)ExifDataTypes.GetSize(dataType); @@ -479,15 +468,6 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif case ExifTagValue.TileByteCounts: exifValue = new ExifLong8Array(ExifTagValue.TileByteCounts); break; - ////case ExifTagValue.SubIFDOffset: - //// exifValue = new ExifLong8(ExifTagValue.SubIFDOffset); - //// break; - ////case ExifTagValue.GPSIFDOffset: - //// exifValue = new ExifLong8(ExifTagValue.GPSIFDOffset); - //// break; - ////case ExifTagValue.SubIFDs: - //// exifValue = new ExifLong8Array(ExifTagValue.SubIFDs); - //// break; default: exifValue = ExifValues.Create(tag) ?? ExifValues.Create(tag, dataType, numberOfComponents); break; @@ -543,14 +523,6 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif { this.AddSubIfd(value); } - else if (exif.Tag == ExifTag.SubIFDs) - { - //// didn't find any useful data in SubIFDs - ////foreach (object val in (Array)value) - ////{ - //// this.AddSubIfd(val); - ////} - } else { values.Add(exif); From 46f976a9b6c84b468fe41bc43a42a816dc99bd35 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Tue, 4 Jan 2022 15:09:11 +0300 Subject: [PATCH 60/62] comment --- tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs index 1493060d5..9f5b78cc3 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs @@ -166,7 +166,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { { new ExifTag((ExifTagValue)0xdd11), (ExifDataType.Long8, ulong.MaxValue) }, { new ExifTag((ExifTagValue)0xdd12), (ExifDataType.SignedLong8, long.MaxValue) }, - //// WriteIfdTags64Bit: arrays aren't support + //// WriteIfdTags64Bit: arrays aren't support (by our code) ////{ new ExifTag((ExifTagValue)0xdd13), (ExifDataType.Long8, new ulong[] { 0, 1234, 56789UL, ulong.MaxValue }) }, ////{ new ExifTag((ExifTagValue)0xdd14), (ExifDataType.SignedLong8, new long[] { -1234, 56789L, long.MaxValue }) }, }; From 62fc16224e72f558243e6207b22381afe9262ad2 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Tue, 4 Jan 2022 15:10:03 +0300 Subject: [PATCH 61/62] HTML -> MD --- .../Input/Tiff/BigTiff/BigTIFFSamples.html | 239 ------------------ .../Input/Tiff/BigTiff/BigTIFFSamples.md | 220 ++++++++++++++++ tests/Images/Input/Tiff/BigTiff/readme.md | 6 +- 3 files changed, 224 insertions(+), 241 deletions(-) delete mode 100644 tests/Images/Input/Tiff/BigTiff/BigTIFFSamples.html create mode 100644 tests/Images/Input/Tiff/BigTiff/BigTIFFSamples.md diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFFSamples.html b/tests/Images/Input/Tiff/BigTiff/BigTIFFSamples.html deleted file mode 100644 index c674e57b5..000000000 --- a/tests/Images/Input/Tiff/BigTiff/BigTIFFSamples.html +++ /dev/null @@ -1,239 +0,0 @@ - - - - -

- These images were created by AWare Systems. -

-

Index

-

- Classic.tif
- BigTIFF.tif
- BigTIFFMotorola.tif
- BigTIFFLong.tif
- BigTIFFLong8.tif
- BigTIFFMotorolaLongStrips.tif
- BigTIFFLong8Tiles.tif
- BigTIFFSubIFD4.tif
- BigTIFFSubIFD8.tif
-

-

Classic.tif

-

- Classic.tif is a basic Classic TIFF file. All files in this package have the same actual image content, - so this TIFF file serves as a reference. -

-

- Format: Classic TIFF
- Byte Order: Intel
- Ifd Offset: 12302
-     ImageWidth (1 Short): 64
-     ImageLength (1 Short): 64
-     BitsPerSample (3 Short): 8, 8, 8
-     PhotometricInterpretation (1 Short): RGB
-     StripOffsets (1 Short): 8
-     SamplesPerPixel (1 Short): 3
-     RowsPerStrip (1 Short): 64
-     StripByteCounts (1 Short): 12288
-

-

BigTIFF.tif

-

- BigTIFF.tif ressembles Classic.tif as close as possible. Except that it's a BigTIFF, that is... -

-

- Format: BigTIFF
- Byte Order: Intel
- Ifd Offset: 12304
-     ImageWidth (1 Short): 64
-     ImageLength (1 Short): 64
-     BitsPerSample (3 Short): 8, 8, 8
-     PhotometricInterpretation (1 Short): RGB
-     StripOffsets (1 Short): 16
-     SamplesPerPixel (1 Short): 3
-     RowsPerStrip (1 Short): 64
-     StripByteCounts (1 Short): 12288
-

-

BigTIFFMotorola.tif

-

- BigTIFFMotorola.tif reverses the byte order. -

-

- Format: BigTIFF
- Byte Order: Motorola
- Ifd Offset: 12304
-     ImageWidth (1 Short): 64
-     ImageLength (1 Short): 64
-     BitsPerSample (3 Short): 8, 8, 8
-     PhotometricInterpretation (1 Short): RGB
-     StripOffsets (1 Short): 16
-     SamplesPerPixel (1 Short): 3
-     RowsPerStrip (1 Short): 64
-     StripByteCounts (1 Short): 12288
-

-

BigTIFFLong.tif

-

- All previous TIFFs specify DataType Short for StripOffsets and StripByteCounts tags. This BigTIFF instead specifies DataType Long, for these tags. -

-

- Format: BigTIFF
- Byte Order: Intel
- Ifd Offset: 12304
-     ImageWidth (1 Short): 64
-     ImageLength (1 Short): 64
-     BitsPerSample (3 Short): 8, 8, 8
-     PhotometricInterpretation (1 Short): RGB
-     StripOffsets (1 Long): 16
-     SamplesPerPixel (1 Short): 3
-     RowsPerStrip (1 Short): 64
-     StripByteCounts (1 Long): 12288
-

-

BigTIFFLong8.tif

-

- This next one specifies DataType Long8, for StripOffsets and StripByteCounts tags. -

-

- Format: BigTIFF
- Byte Order: Intel
- Ifd Offset: 12304
-     ImageWidth (1 Short): 64
-     ImageLength (1 Short): 64
-     BitsPerSample (3 Short): 8, 8, 8
-     PhotometricInterpretation (1 Short): RGB
-     StripOffsets (1 Long8): 16
-     SamplesPerPixel (1 Short): 3
-     RowsPerStrip (1 Short): 64
-     StripByteCounts (1 Long8): 12288
-

-

BigTIFFMotorolaLongStrips.tif

-

- This BigTIFF has Motorola byte order, plus, it's divided over two strips. StripOffsets and StripByteCounts tags have DataType Long, so their actual value fits inside the IFD. -

-

- Format: BigTIFF
- Byte Order: Motorola
- Ifd Offset: 12304
-     ImageWidth (1 Short): 64
-     ImageLength (1 Short): 64
-     BitsPerSample (3 Short): 8, 8, 8
-     PhotometricInterpretation (1 Short): RGB
-     StripOffsets (2 Long): 16, 6160
-     SamplesPerPixel (1 Short): 3
-     RowsPerStrip (1 Short): 32
-     StripByteCounts (2 Long): 6144, 6144
-

-

BigTIFFLong8Tiles.tif

-

- BigTIFFLong8Tiles.tif is a tiled BigTIFF. TileOffsets and TileByteCounts tags specify DataType Long8. -

-

- Format: BigTIFF
- Byte Order: Intel
- Ifd Offset: 12368
-     ImageWidth (1 Short): 64
-     ImageLength (1 Short): 64
-     BitsPerSample (3 Short): 8, 8, 8
-     PhotometricInterpretation (1 Short): RGB
-     SamplesPerPixel (1 Short): 3
-     TileWidth (1 Short): 32
-     TileLength (1 Short): 32
-     TileOffsets (4 Long8): 16, 3088, 6160, 9232
-     TileByteCounts (4 Long8): 3072, 3072, 3072, 3072
-

-

BigTIFFSubIFD4.tif

-

- This BigTIFF contains two pages, the second page showing almost the same image content as the first, except that the black square is white, and text color is black. - Both pages point to a downsample SubIFD, using SubIFDs DataType TIFF_IFD. -

-

- Format: BigTIFF
- Byte Order: Intel
- Ifd Offset: 15572
-     ImageWidth (1 Short): 64
-     ImageLength (1 Short): 64
-     BitsPerSample (3 Short): 8, 8, 8
-     PhotometricInterpretation (1 Short): RGB
-     StripOffsets (1 Short): 3284
-     SamplesPerPixel (1 Short): 3
-     RowsPerStrip (1 Short): 64
-     StripByteCounts (1 Short): 12288
-     SubIFDs (1 IFD): 3088
- SubIfd Offset: 3088
-     NewSubFileType (1 Long): 1
-     ImageWidth (1 Short): 32
-     ImageLength (1 Short): 32
-     BitsPerSample (3 Short): 8, 8, 8
-     PhotometricInterpretation (1 Short): RGB
-     StripOffsets (1 Short): 16
-     SamplesPerPixel (1 Short): 3
-     RowsPerStrip (1 Short): 32
-     StripByteCounts (1 Short): 3072
- Ifd Offset: 31324
-     ImageWidth (1 Short): 64
-     ImageLength (1 Short): 64
-     BitsPerSample (3 Short): 8, 8, 8
-     PhotometricInterpretation (1 Short): RGB
-     StripOffsets (1 Short): 19036
-     SamplesPerPixel (1 Short): 3
-     RowsPerStrip (1 Short): 64
-     StripByteCounts (1 Short): 12288
-     SubIFDs (1 IFD): 18840
- SubIfd Offset: 18840
-     NewSubFileType (1 Long): 1
-     ImageWidth (1 Short): 32
-     ImageLength (1 Short): 32
-     BitsPerSample (3 Short): 8, 8, 8
-     PhotometricInterpretation (1 Short): RGB
-     StripOffsets (1 Short): 15768
-     SamplesPerPixel (1 Short): 3
-     RowsPerStrip (1 Short): 32
-     StripByteCounts (1 Short): 3072
-

-

BigTIFFSubIFD8.tif

-

- BigTIFFSubIFD4.tif is very much the same as BigTIFFSubIFD4.tif, except that the new DataType TIFF_IFD8 is used for the SubIFDs tag. -

-

- Format: BigTIFF
- Byte Order: Intel
- Ifd Offset: 15572
-     ImageWidth (1 Short): 64
-     ImageLength (1 Short): 64
-     BitsPerSample (3 Short): 8, 8, 8
-     PhotometricInterpretation (1 Short): RGB
-     StripOffsets (1 Short): 3284
-     SamplesPerPixel (1 Short): 3
-     RowsPerStrip (1 Short): 64
-     StripByteCounts (1 Short): 12288
-     SubIFDs (1 IFD8): 3088
- SubIfd Offset: 3088
-     NewSubFileType (1 Long): 1
-     ImageWidth (1 Short): 32
-     ImageLength (1 Short): 32
-     BitsPerSample (3 Short): 8, 8, 8
-     PhotometricInterpretation (1 Short): RGB
-     StripOffsets (1 Short): 16
-     SamplesPerPixel (1 Short): 3
-     RowsPerStrip (1 Short): 32
-     StripByteCounts (1 Short): 3072
- Ifd Offset: 31324
-     ImageWidth (1 Short): 64
-     ImageLength (1 Short): 64
-     BitsPerSample (3 Short): 8, 8, 8
-     PhotometricInterpretation (1 Short): RGB
-     StripOffsets (1 Short): 19036
-     SamplesPerPixel (1 Short): 3
-     RowsPerStrip (1 Short): 64
-     StripByteCounts (1 Short): 12288
-     SubIFDs (1 IFD8): 18840
- SubIfd Offset: 18840
-     NewSubFileType (1 Long): 1
-     ImageWidth (1 Short): 32
-     ImageLength (1 Short): 32
-     BitsPerSample (3 Short): 8, 8, 8
-     PhotometricInterpretation (1 Short): RGB
-     StripOffsets (1 Short): 15768
-     SamplesPerPixel (1 Short): 3
-     RowsPerStrip (1 Short): 32
-     StripByteCounts (1 Short): 3072
-

- - diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFFSamples.md b/tests/Images/Input/Tiff/BigTiff/BigTIFFSamples.md new file mode 100644 index 000000000..c3047009e --- /dev/null +++ b/tests/Images/Input/Tiff/BigTiff/BigTIFFSamples.md @@ -0,0 +1,220 @@ +These images were created by [AWare Systems](http://www.awaresystems.be/). + +# Index + +[Classic.tif](#classic) +[BigTIFF.tif](#bigtiff) +[BigTIFFMotorola.tif](#bigtiffmotorola) +[BigTIFFLong.tif](#bigtifflong) +[BigTIFFLong8.tif](#bigtifflong8) +[BigTIFFMotorolaLongStrips.tif](#bigtiffmotorolalongstrips) +[BigTIFFLong8Tiles.tif](#bigtifflong8tiles) +[BigTIFFSubIFD4.tif](#bigtiffsubifd4) +[BigTIFFSubIFD8.tif](#bigtiffsubifd8) + +# Classic.tif + +Classic.tif is a basic Classic TIFF file. All files in this package have the same actual image content, so this TIFF file serves as a reference. + +Format: Classic TIFF +Byte Order: Intel +Ifd Offset: 12302 +    ImageWidth (1 Short): 64 +    ImageLength (1 Short): 64 +    BitsPerSample (3 Short): 8, 8, 8 +    PhotometricInterpretation (1 Short): RGB +    StripOffsets (1 Short): 8 +    SamplesPerPixel (1 Short): 3 +    RowsPerStrip (1 Short): 64 +    StripByteCounts (1 Short): 12288 + +# BigTIFF.tif + +BigTIFF.tif ressembles Classic.tif as close as possible. Except that it's a BigTIFF, that is... + +Format: BigTIFF +Byte Order: Intel +Ifd Offset: 12304 +    ImageWidth (1 Short): 64 +    ImageLength (1 Short): 64 +    BitsPerSample (3 Short): 8, 8, 8 +    PhotometricInterpretation (1 Short): RGB +    StripOffsets (1 Short): 16 +    SamplesPerPixel (1 Short): 3 +    RowsPerStrip (1 Short): 64 +    StripByteCounts (1 Short): 12288 + +# BigTIFFMotorola.tif + +BigTIFFMotorola.tif reverses the byte order. + +Format: BigTIFF +Byte Order: Motorola +Ifd Offset: 12304 +    ImageWidth (1 Short): 64 +    ImageLength (1 Short): 64 +    BitsPerSample (3 Short): 8, 8, 8 +    PhotometricInterpretation (1 Short): RGB +    StripOffsets (1 Short): 16 +    SamplesPerPixel (1 Short): 3 +    RowsPerStrip (1 Short): 64 +    StripByteCounts (1 Short): 12288 + +# BigTIFFLong.tif + +All previous TIFFs specify DataType Short for StripOffsets and StripByteCounts tags. This BigTIFF instead specifies DataType Long, for these tags. + +Format: BigTIFF +Byte Order: Intel +Ifd Offset: 12304 +    ImageWidth (1 Short): 64 +    ImageLength (1 Short): 64 +    BitsPerSample (3 Short): 8, 8, 8 +    PhotometricInterpretation (1 Short): RGB +    StripOffsets (1 Long): 16 +    SamplesPerPixel (1 Short): 3 +    RowsPerStrip (1 Short): 64 +    StripByteCounts (1 Long): 12288 + +# BigTIFFLong8.tif + +This next one specifies DataType Long8, for StripOffsets and StripByteCounts tags. + +Format: BigTIFF +Byte Order: Intel +Ifd Offset: 12304 +    ImageWidth (1 Short): 64 +    ImageLength (1 Short): 64 +    BitsPerSample (3 Short): 8, 8, 8 +    PhotometricInterpretation (1 Short): RGB +    StripOffsets (1 Long8): 16 +    SamplesPerPixel (1 Short): 3 +    RowsPerStrip (1 Short): 64 +    StripByteCounts (1 Long8): 12288 + +# BigTIFFMotorolaLongStrips.tif + +This BigTIFF has Motorola byte order, plus, it's divided over two strips. StripOffsets and StripByteCounts tags have DataType Long, so their actual value fits inside the IFD. + +Format: BigTIFF +Byte Order: Motorola +Ifd Offset: 12304 +    ImageWidth (1 Short): 64 +    ImageLength (1 Short): 64 +    BitsPerSample (3 Short): 8, 8, 8 +    PhotometricInterpretation (1 Short): RGB +    StripOffsets (2 Long): 16, 6160 +    SamplesPerPixel (1 Short): 3 +    RowsPerStrip (1 Short): 32 +    StripByteCounts (2 Long): 6144, 6144 + +# BigTIFFLong8Tiles.tif + +BigTIFFLong8Tiles.tif is a tiled BigTIFF. TileOffsets and TileByteCounts tags specify DataType Long8. + +Format: BigTIFF +Byte Order: Intel +Ifd Offset: 12368 +    ImageWidth (1 Short): 64 +    ImageLength (1 Short): 64 +    BitsPerSample (3 Short): 8, 8, 8 +    PhotometricInterpretation (1 Short): RGB +    SamplesPerPixel (1 Short): 3 +    TileWidth (1 Short): 32 +    TileLength (1 Short): 32 +    TileOffsets (4 Long8): 16, 3088, 6160, 9232 +    TileByteCounts (4 Long8): 3072, 3072, 3072, 3072 + +# BigTIFFSubIFD4.tif + +This BigTIFF contains two pages, the second page showing almost the same image content as the first, except that the black square is white, and text color is black. Both pages point to a downsample SubIFD, using SubIFDs DataType TIFF_IFD. + +Format: BigTIFF +Byte Order: Intel +Ifd Offset: 15572 +    ImageWidth (1 Short): 64 +    ImageLength (1 Short): 64 +    BitsPerSample (3 Short): 8, 8, 8 +    PhotometricInterpretation (1 Short): RGB +    StripOffsets (1 Short): 3284 +    SamplesPerPixel (1 Short): 3 +    RowsPerStrip (1 Short): 64 +    StripByteCounts (1 Short): 12288 +    SubIFDs (1 IFD): 3088 +SubIfd Offset: 3088 +    NewSubFileType (1 Long): 1 +    ImageWidth (1 Short): 32 +    ImageLength (1 Short): 32 +    BitsPerSample (3 Short): 8, 8, 8 +    PhotometricInterpretation (1 Short): RGB +    StripOffsets (1 Short): 16 +    SamplesPerPixel (1 Short): 3 +    RowsPerStrip (1 Short): 32 +    StripByteCounts (1 Short): 3072 +Ifd Offset: 31324 +    ImageWidth (1 Short): 64 +    ImageLength (1 Short): 64 +    BitsPerSample (3 Short): 8, 8, 8 +    PhotometricInterpretation (1 Short): RGB +    StripOffsets (1 Short): 19036 +    SamplesPerPixel (1 Short): 3 +    RowsPerStrip (1 Short): 64 +    StripByteCounts (1 Short): 12288 +    SubIFDs (1 IFD): 18840 +SubIfd Offset: 18840 +    NewSubFileType (1 Long): 1 +    ImageWidth (1 Short): 32 +    ImageLength (1 Short): 32 +    BitsPerSample (3 Short): 8, 8, 8 +    PhotometricInterpretation (1 Short): RGB +    StripOffsets (1 Short): 15768 +    SamplesPerPixel (1 Short): 3 +    RowsPerStrip (1 Short): 32 +    StripByteCounts (1 Short): 3072 + +# BigTIFFSubIFD8.tif + +BigTIFFSubIFD4.tif is very much the same as BigTIFFSubIFD4.tif, except that the new DataType TIFF_IFD8 is used for the SubIFDs tag. + +Format: BigTIFF +Byte Order: Intel +Ifd Offset: 15572 +    ImageWidth (1 Short): 64 +    ImageLength (1 Short): 64 +    BitsPerSample (3 Short): 8, 8, 8 +    PhotometricInterpretation (1 Short): RGB +    StripOffsets (1 Short): 3284 +    SamplesPerPixel (1 Short): 3 +    RowsPerStrip (1 Short): 64 +    StripByteCounts (1 Short): 12288 +    SubIFDs (1 IFD8): 3088 +SubIfd Offset: 3088 +    NewSubFileType (1 Long): 1 +    ImageWidth (1 Short): 32 +    ImageLength (1 Short): 32 +    BitsPerSample (3 Short): 8, 8, 8 +    PhotometricInterpretation (1 Short): RGB +    StripOffsets (1 Short): 16 +    SamplesPerPixel (1 Short): 3 +    RowsPerStrip (1 Short): 32 +    StripByteCounts (1 Short): 3072 +Ifd Offset: 31324 +    ImageWidth (1 Short): 64 +    ImageLength (1 Short): 64 +    BitsPerSample (3 Short): 8, 8, 8 +    PhotometricInterpretation (1 Short): RGB +    StripOffsets (1 Short): 19036 +    SamplesPerPixel (1 Short): 3 +    RowsPerStrip (1 Short): 64 +    StripByteCounts (1 Short): 12288 +    SubIFDs (1 IFD8): 18840 +SubIfd Offset: 18840 +    NewSubFileType (1 Long): 1 +    ImageWidth (1 Short): 32 +    ImageLength (1 Short): 32 +    BitsPerSample (3 Short): 8, 8, 8 +    PhotometricInterpretation (1 Short): RGB +    StripOffsets (1 Short): 15768 +    SamplesPerPixel (1 Short): 3 +    RowsPerStrip (1 Short): 32 +    StripByteCounts (1 Short): 3072 \ No newline at end of file diff --git a/tests/Images/Input/Tiff/BigTiff/readme.md b/tests/Images/Input/Tiff/BigTiff/readme.md index be5d28906..80f6b595b 100644 --- a/tests/Images/Input/Tiff/BigTiff/readme.md +++ b/tests/Images/Input/Tiff/BigTiff/readme.md @@ -1,3 +1,5 @@ -BigTIFF samples. +#### BigTIFF samples. -Downloaded from AWARE SYSTEMS: https://www.awaresystems.be/imaging/tiff/bigtiff.html \ No newline at end of file +For details: BigTIFFSamples.md + +Downloaded from https://www.awaresystems.be/imaging/tiff/bigtiff.html \ No newline at end of file From feb49798bb316635c87a116da2974faf9e98cc14 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Tue, 4 Jan 2022 15:35:06 +0300 Subject: [PATCH 62/62] format --- .../Input/Tiff/BigTiff/BigTIFFSamples.md | 18 +++++++++--------- tests/Images/Input/Tiff/BigTiff/readme.md | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFFSamples.md b/tests/Images/Input/Tiff/BigTiff/BigTIFFSamples.md index c3047009e..0401109fd 100644 --- a/tests/Images/Input/Tiff/BigTiff/BigTIFFSamples.md +++ b/tests/Images/Input/Tiff/BigTiff/BigTIFFSamples.md @@ -2,15 +2,15 @@ These images were created by [AWare Systems](http://www.awaresystems.be/). # Index -[Classic.tif](#classic) -[BigTIFF.tif](#bigtiff) -[BigTIFFMotorola.tif](#bigtiffmotorola) -[BigTIFFLong.tif](#bigtifflong) -[BigTIFFLong8.tif](#bigtifflong8) -[BigTIFFMotorolaLongStrips.tif](#bigtiffmotorolalongstrips) -[BigTIFFLong8Tiles.tif](#bigtifflong8tiles) -[BigTIFFSubIFD4.tif](#bigtiffsubifd4) -[BigTIFFSubIFD8.tif](#bigtiffsubifd8) +[Classic.tif](#classictif) +[BigTIFF.tif](#bigtifftif) +[BigTIFFMotorola.tif](#bigtiffmotorolatif) +[BigTIFFLong.tif](#bigtifflongtif) +[BigTIFFLong8.tif](#bigtifflong8tif) +[BigTIFFMotorolaLongStrips.tif](#bigtiffmotorolalongstripstif) +[BigTIFFLong8Tiles.tif](#bigtifflong8tilestif) +[BigTIFFSubIFD4.tif](#bigtiffsubifd4tif) +[BigTIFFSubIFD8.tif](#bigtiffsubifd8tif) # Classic.tif diff --git a/tests/Images/Input/Tiff/BigTiff/readme.md b/tests/Images/Input/Tiff/BigTiff/readme.md index 80f6b595b..29f9c8ea9 100644 --- a/tests/Images/Input/Tiff/BigTiff/readme.md +++ b/tests/Images/Input/Tiff/BigTiff/readme.md @@ -1,5 +1,5 @@ #### BigTIFF samples. -For details: BigTIFFSamples.md +For details: [BigTIFFSamples.md](BigTIFFSamples.md) Downloaded from https://www.awaresystems.be/imaging/tiff/bigtiff.html \ No newline at end of file