From c6383861d22ca81fad55af57c9d58b1aa0af411c Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sun, 12 Sep 2021 11:08:50 +0300 Subject: [PATCH] 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 922c1f1747..4ab920ff89 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 5c26fde7bd..b9628ad738 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 3cf59adacd..17fe888f7b 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; + } } }