diff --git a/README.md b/README.md index 28c377037..a1c9bca99 100644 --- a/README.md +++ b/README.md @@ -155,6 +155,7 @@ Core Team - [Dirk Lemstra](https://github.com/dlemstra) - [Anton Firsov](https://github.com/antonfirsov) - [Scott Williams](https://github.com/tocsoft) +- [Brian Popow](https://github.com/brianpopow) ### Backers diff --git a/src/ImageSharp/Common/Helpers/EnumUtils.cs b/src/ImageSharp/Common/Helpers/EnumUtils.cs new file mode 100644 index 000000000..a98b7f84c --- /dev/null +++ b/src/ImageSharp/Common/Helpers/EnumUtils.cs @@ -0,0 +1,50 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp +{ + /// + /// Common utility methods for working with enums. + /// + internal static class EnumUtils + { + /// + /// Converts the numeric representation of the enumerated constants to an equivalent enumerated object. + /// + /// The type of enum + /// The value to parse + /// The default value to return. + /// The . + public static TEnum Parse(int value, TEnum defaultValue) + where TEnum : Enum + { + foreach (TEnum enumValue in Enum.GetValues(typeof(TEnum))) + { + TEnum current = enumValue; + if (value == Unsafe.As(ref current)) + { + return enumValue; + } + } + + return defaultValue; + } + + /// + /// Returns a value indicating whether the given enum has a flag of the given value. + /// + /// The type of enum. + /// The value. + /// The flag. + /// The . + public static bool HasFlag(TEnum value, TEnum flag) + where TEnum : Enum + { + uint flagValue = Unsafe.As(ref flag); + return (Unsafe.As(ref value) & flagValue) == flagValue; + } + } +} diff --git a/src/ImageSharp/Common/Helpers/TolerantMath.cs b/src/ImageSharp/Common/Helpers/TolerantMath.cs index bef7eb161..62b564472 100644 --- a/src/ImageSharp/Common/Helpers/TolerantMath.cs +++ b/src/ImageSharp/Common/Helpers/TolerantMath.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -103,4 +103,4 @@ namespace SixLabors.ImageSharp return Math.Floor(a); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Common/Helpers/UnitConverter.cs b/src/ImageSharp/Common/Helpers/UnitConverter.cs index 75dbb032c..9e4306170 100644 --- a/src/ImageSharp/Common/Helpers/UnitConverter.cs +++ b/src/ImageSharp/Common/Helpers/UnitConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System.Runtime.CompilerServices; @@ -86,9 +86,10 @@ namespace SixLabors.ImageSharp.Common.Helpers [MethodImpl(InliningOptions.ShortMethod)] public static PixelResolutionUnit ExifProfileToResolutionUnit(ExifProfile profile) { - return profile.TryGetValue(ExifTag.ResolutionUnit, out ExifValue resolution) - ? (PixelResolutionUnit)(byte)(((ushort)resolution.Value) - 1) // EXIF is 1, 2, 3 - : default; + IExifValue resolution = profile.GetValue(ExifTag.ResolutionUnit); + + // EXIF is 1, 2, 3 so we minus "1" off the result. + return resolution is null ? default : (PixelResolutionUnit)(byte)(resolution.Value - 1); } } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 60709ed00..ec9cca8c8 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -465,24 +465,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg } } - private double GetExifResolutionValue(ExifTag tag) + private double GetExifResolutionValue(ExifTag tag) { - if (!this.Metadata.ExifProfile.TryGetValue(tag, out ExifValue exifValue)) - { - return 0; - } + IExifValue resolution = this.Metadata.ExifProfile.GetValue(tag); - switch (exifValue.DataType) - { - case ExifDataType.Rational: - return ((Rational)exifValue.Value).ToDouble(); - case ExifDataType.Long: - return (uint)exifValue.Value; - case ExifDataType.DoubleFloat: - return (double)exifValue.Value; - default: - return 0; - } + return resolution is null ? 0 : resolution.Value.ToDouble(); } /// diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs index bc9bb9543..cd3c19aa3 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -647,7 +647,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// private void WriteExifProfile(ExifProfile exifProfile) { - if (exifProfile is null) + if (exifProfile is null || exifProfile.Values.Count == 0) { return; } @@ -655,9 +655,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg const int MaxBytesApp1 = 65533; // 64k - 2 padding bytes const int MaxBytesWithExifId = 65527; // Max - 6 bytes for EXIF header. - byte[] data = exifProfile?.ToByteArray(); + byte[] data = exifProfile.ToByteArray(); - if (data is null || data.Length == 0) + if (data.Length == 0) { return; } @@ -998,4 +998,4 @@ namespace SixLabors.ImageSharp.Formats.Jpeg this.outputStream.Write(this.buffer, 0, 4); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 82796c570..22e3f252d 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -622,11 +622,13 @@ namespace SixLabors.ImageSharp.Formats.Png /// The image metadata. private void WriteExifChunk(Stream stream, ImageMetadata meta) { - if (meta.ExifProfile?.Values.Count > 0) + if (meta.ExifProfile is null || meta.ExifProfile.Values.Count == 0) { - meta.SyncProfiles(); - this.WriteChunk(stream, PngChunkType.Exif, meta.ExifProfile.ToByteArray()); + return; } + + meta.SyncProfiles(); + this.WriteChunk(stream, PngChunkType.Exif, meta.ExifProfile.ToByteArray()); } /// diff --git a/src/ImageSharp/Metadata/Profiles/Exif/DC-008-Translation-2019-E.pdf b/src/ImageSharp/Metadata/Profiles/Exif/DC-008-Translation-2019-E.pdf new file mode 100644 index 000000000..9be0c8402 Binary files /dev/null and b/src/ImageSharp/Metadata/Profiles/Exif/DC-008-Translation-2019-E.pdf differ diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifDataType.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifDataType.cs index 83e7f7fe8..74c86f721 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifDataType.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifDataType.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Metadata.Profiles.Exif @@ -20,6 +20,10 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// /// An 8-bit byte containing one 7-bit ASCII code. The final byte is terminated with NULL. + /// + /// Although the standard defines ASCII this has commonly been ignored as + /// ASCII cannot properly encode text in many languages. + /// /// Ascii = 2, @@ -64,13 +68,13 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif SignedRational = 10, /// - /// A 32-bit floating point value. + /// A 32-bit single precision floating point value. /// SingleFloat = 11, /// - /// A 64-bit floating point value. + /// A 64-bit double precision floating point value. /// DoubleFloat = 12 } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifDataTypes.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifDataTypes.cs new file mode 100644 index 000000000..d94dc5640 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifDataTypes.cs @@ -0,0 +1,45 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal static class ExifDataTypes + { + /// + /// Gets the size in bytes of the given data type. + /// + /// The data type. + /// + /// The . + /// + /// + /// Thrown if the type is unsupported. + /// + public static uint GetSize(ExifDataType dataType) + { + switch (dataType) + { + case ExifDataType.Ascii: + case ExifDataType.Byte: + case ExifDataType.SignedByte: + case ExifDataType.Undefined: + return 1; + case ExifDataType.Short: + case ExifDataType.SignedShort: + return 2; + case ExifDataType.Long: + case ExifDataType.SignedLong: + case ExifDataType.SingleFloat: + return 4; + case ExifDataType.DoubleFloat: + case ExifDataType.Rational: + case ExifDataType.SignedRational: + return 8; + default: + throw new NotSupportedException(dataType.ToString()); + } + } + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifParts.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifParts.cs index d22dc730f..6405a7ff2 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifParts.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifParts.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -29,11 +29,11 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// /// GPSTags /// - GPSTags = 8, + GpsTags = 8, /// /// All /// - All = IfdTags | ExifTags | GPSTags + All = IfdTags | ExifTags | GpsTags } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs index 3d90cb3a9..f98a1f3c7 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// /// The collection of EXIF values /// - private List values; + private List values; /// /// The thumbnail offset position in the byte stream @@ -70,9 +70,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif if (other.values != null) { - this.values = new List(other.Values.Count); + this.values = new List(other.Values.Count); - foreach (ExifValue value in other.Values) + foreach (IExifValue value in other.Values) { this.values.Add(value.DeepClone()); } @@ -98,7 +98,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// /// Gets the values of this EXIF profile. /// - public IReadOnlyList Values + public IReadOnlyList Values { get { @@ -138,46 +138,13 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// /// Returns the value with the specified tag. /// - /// The tag of the EXIF value. - /// - /// The . - /// - public ExifValue GetValue(ExifTag tag) - { - foreach (ExifValue exifValue in this.Values) - { - if (exifValue.Tag == tag) - { - return exifValue; - } - } - - return null; - } - - /// - /// Conditionally returns the value of the tag if it exists. - /// - /// The tag of the EXIF value. - /// The value of the tag, if found. - /// - /// The . - /// - public bool TryGetValue(ExifTag tag, out ExifValue value) + /// The tag of the exif value. + /// The value with the specified tag. + /// The data type of the tag. + public IExifValue GetValue(ExifTag tag) { - foreach (ExifValue exifValue in this.Values) - { - if (exifValue.Tag == tag) - { - value = exifValue; - - return true; - } - } - - value = default; - - return false; + IExifValue value = this.GetValueInternal(tag); + return value is null ? null : (IExifValue)value; } /// @@ -206,24 +173,11 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// /// Sets the value of the specified tag. /// - /// The tag of the EXIF value. + /// The tag of the exif value. /// The value. - public void SetValue(ExifTag tag, object value) - { - for (int i = 0; i < this.Values.Count; i++) - { - if (this.values[i].Tag == tag) - { - this.values[i] = this.values[i].WithValue(value); - - return; - } - } - - var newExifValue = ExifValue.Create(tag, value); - - this.values.Add(newExifValue); - } + /// The data type of the tag. + public void SetValue(ExifTag tag, TValueType value) + => this.SetValueInternal(tag, value); /// /// Converts this instance to a byte array. @@ -238,7 +192,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif if (this.values.Count == 0) { - return null; + return Array.Empty(); } var writer = new ExifWriter(this.values, this.Parts); @@ -248,6 +202,50 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// public ExifProfile DeepClone() => new ExifProfile(this); + /// + /// Returns the value with the specified tag. + /// + /// The tag of the exif value. + /// The value with the specified tag. + internal IExifValue GetValueInternal(ExifTag tag) + { + foreach (IExifValue exifValue in this.Values) + { + if (exifValue.Tag == tag) + { + return exifValue; + } + } + + return null; + } + + /// + /// Sets the value of the specified tag. + /// + /// The tag of the exif value. + /// The value. + internal void SetValueInternal(ExifTag tag, object value) + { + foreach (IExifValue exifValue in this.Values) + { + if (exifValue.Tag == tag) + { + exifValue.TrySetValue(value); + return; + } + } + + ExifValue newExifValue = ExifValues.Create(tag); + if (newExifValue is null) + { + throw new NotSupportedException(); + } + + newExifValue.TrySetValue(value); + this.values.Add(newExifValue); + } + /// /// Synchronizes the profiles with the specified metadata. /// @@ -258,9 +256,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif this.SyncResolution(ExifTag.YResolution, metadata.VerticalResolution); } - private void SyncResolution(ExifTag tag, double resolution) + private void SyncResolution(ExifTag tag, double resolution) { - ExifValue value = this.GetValue(tag); + IExifValue value = this.GetValue(tag); if (value is null) { @@ -285,7 +283,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif if (this.data is null) { - this.values = new List(); + this.values = new List(); return; } @@ -301,4 +299,4 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif this.thumbnailLength = (int)reader.ThumbnailLength; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs index 77c1cf2ea..00410fb59 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs @@ -5,7 +5,6 @@ using System; using System.Buffers.Binary; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Linq; using System.Runtime.CompilerServices; using System.Text; using SixLabors.ImageSharp.Primitives; @@ -68,12 +67,12 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// /// The . /// - public List ReadValues() + public List ReadValues() { - var values = new List(); + var values = new List(); // II == 0x4949 - this.isBigEndian = !(this.ReadUInt16() == 0x4949); + this.isBigEndian = this.ReadUInt16() != 0x4949; if (this.ReadUInt16() != 0x002A) { @@ -101,7 +100,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif private static TDataType[] ToArray(ExifDataType dataType, ReadOnlySpan data, ConverterMethod converter) { - int dataTypeSize = (int)ExifValue.GetSize(dataType); + int dataTypeSize = (int)ExifDataTypes.GetSize(dataType); int length = data.Length / dataTypeSize; var result = new TDataType[length]; @@ -135,7 +134,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// /// The values. /// The index. - private void AddValues(List values, uint index) + private void AddValues(List values, uint index) { if (index > (uint)this.exifData.Length) { @@ -153,9 +152,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif } bool duplicate = false; - foreach (ExifValue val in values) + foreach (IExifValue val in values) { - if (val.Tag == value.Tag) + if (val == value) { duplicate = true; break; @@ -167,19 +166,13 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif continue; } - if (value.Tag == ExifTag.SubIFDOffset) + if (value == ExifTag.SubIFDOffset) { - if (value.DataType == ExifDataType.Long) - { - this.exifOffset = (uint)value.Value; - } + this.exifOffset = ((ExifLong)value).Value; } - else if (value.Tag == ExifTag.GPSIFDOffset) + else if (value == ExifTag.GPSIFDOffset) { - if (value.DataType == ExifDataType.Long) - { - this.gpsOffset = (uint)value.Value; - } + this.gpsOffset = ((ExifLong)value).Value; } else { @@ -285,30 +278,24 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif private bool TryReadValue(out ExifValue exifValue) { + exifValue = default; + // 2 | 2 | 4 | 4 // tag | type | count | value offset if (this.RemainingLength < 12) { - exifValue = default; - return false; } - ExifTag tag = this.ToEnum(this.ReadUInt16(), ExifTag.Unknown); - uint type = this.ReadUInt16(); + var tag = (ExifTagValue)this.ReadUInt16(); + ExifDataType dataType = EnumUtils.Parse(this.ReadUInt16(), ExifDataType.Unknown); // Ensure that the data type is valid - if (type == 0 || type > 12) + if (dataType == ExifDataType.Unknown) { - exifValue = new ExifValue(tag, ExifDataType.Unknown, null, false); - - return true; + return false; } - var dataType = (ExifDataType)type; - - object value; - uint numberOfComponents = this.ReadUInt32(); // Issue #132: ExifDataType == Undefined is treated like a byte array. @@ -318,23 +305,20 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif numberOfComponents = 4; } - uint size = numberOfComponents * ExifValue.GetSize(dataType); + uint size = numberOfComponents * ExifDataTypes.GetSize(dataType); this.TryReadSpan(4, out ReadOnlySpan offsetBuffer); + object value; if (size > 4) { int oldIndex = this.position; - uint newIndex = this.ConvertToUInt32(offsetBuffer); // Ensure that the new index does not overrun the data if (newIndex > int.MaxValue) { - this.AddInvalidTag(tag); - - exifValue = default; - + this.AddInvalidTag(new UnkownExifTag(tag)); return false; } @@ -342,12 +326,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif if (this.RemainingLength < size) { - this.AddInvalidTag(tag); + this.AddInvalidTag(new UnkownExifTag(tag)); this.position = oldIndex; - - exifValue = default; - return false; } @@ -361,33 +342,25 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif value = this.ConvertValue(dataType, offsetBuffer, numberOfComponents); } - exifValue = new ExifValue(tag, dataType, value, isArray: value != null && numberOfComponents != 1); - - return true; - } + exifValue = ExifValues.Create(tag) ?? ExifValues.Create(tag, dataType, numberOfComponents); - private void AddInvalidTag(ExifTag tag) - { - if (this.invalidTags is null) + if (exifValue is null) { - this.invalidTags = new List(); + this.AddInvalidTag(new UnkownExifTag(tag)); + return false; } - this.invalidTags.Add(tag); - } - - [MethodImpl(InliningOptions.ShortMethod)] - private TEnum ToEnum(int value, TEnum defaultValue) - where TEnum : struct, Enum - { - if (EnumHelper.IsDefined(value)) + if (!exifValue.TrySetValue(value)) { - return Unsafe.As(ref value); + return false; } - return defaultValue; + return true; } + private void AddInvalidTag(ExifTag tag) + => (this.invalidTags ?? (this.invalidTags = new List())).Add(tag); + private bool TryReadSpan(int length, out ReadOnlySpan span) { if (this.RemainingLength < length) @@ -421,18 +394,18 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif private void GetThumbnail(uint offset) { - var values = new List(); + var values = new List(); this.AddValues(values, offset); foreach (ExifValue value in values) { - if (value.Tag == ExifTag.JPEGInterchangeFormat && (value.DataType == ExifDataType.Long)) + if (value == ExifTag.JPEGInterchangeFormat) { - this.ThumbnailOffset = (uint)value.Value; + this.ThumbnailOffset = ((ExifLong)value).Value; } - else if (value.Tag == ExifTag.JPEGInterchangeFormatLength && value.DataType == ExifDataType.Long) + else if (value == ExifTag.JPEGInterchangeFormatLength) { - this.ThumbnailLength = (uint)value.Value; + this.ThumbnailLength = ((ExifLong)value).Value; } } } @@ -541,18 +514,5 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif ? BinaryPrimitives.ReadInt16BigEndian(buffer) : BinaryPrimitives.ReadInt16LittleEndian(buffer); } - - private sealed class EnumHelper - where TEnum : struct, Enum - { - private static readonly int[] Values = Enum.GetValues(typeof(TEnum)).Cast() - .Select(e => Convert.ToInt32(e)).OrderBy(e => e).ToArray(); - - [MethodImpl(InliningOptions.ShortMethod)] - public static bool IsDefined(int value) - { - return Array.BinarySearch(Values, value) >= 0; - } - } } } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifTagDescriptionAttribute.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifTagDescriptionAttribute.cs index 845e4ee73..b9bb2ee05 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifTagDescriptionAttribute.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifTagDescriptionAttribute.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -31,7 +31,8 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// public static string GetDescription(ExifTag tag, object value) { - FieldInfo field = tag.GetType().GetTypeInfo().GetDeclaredField(tag.ToString()); + var tagValue = (ExifTagValue)(ushort)tag; + FieldInfo field = tagValue.GetType().GetTypeInfo().GetDeclaredField(tagValue.ToString()); if (field is null) { @@ -42,7 +43,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif { object attributeValue = customAttribute.ConstructorArguments[0].Value; - if (object.Equals(attributeValue, value)) + if (Equals(attributeValue, value)) { return (string)customAttribute.ConstructorArguments[1].Value; } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifTags.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifTags.cs index 0ed3a43b7..13fff5b6a 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifTags.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifTags.cs @@ -1,281 +1,275 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using static SixLabors.ImageSharp.Metadata.Profiles.Exif.ExifTag; - namespace SixLabors.ImageSharp.Metadata.Profiles.Exif { internal static class ExifTags { - /// - /// The collection if Image File Directory tags - /// - public static readonly ExifTag[] Ifd = + public static ExifParts GetPart(ExifTag tag) { - SubfileType, - OldSubfileType, - ImageWidth, - ImageLength, - BitsPerSample, - Compression, - PhotometricInterpretation, - Thresholding, - CellWidth, - CellLength, - FillOrder, - DocumentName, - ImageDescription, - Make, - Model, - StripOffsets, - Orientation, - SamplesPerPixel, - RowsPerStrip, - StripByteCounts, - MinSampleValue, - MaxSampleValue, - XResolution, - YResolution, - PlanarConfiguration, - PageName, - XPosition, - YPosition, - FreeOffsets, - FreeByteCounts, - GrayResponseUnit, - GrayResponseCurve, - T4Options, - T6Options, - ResolutionUnit, - PageNumber, - ColorResponseUnit, - TransferFunction, - Software, - DateTime, - Artist, - HostComputer, - Predictor, - WhitePoint, - PrimaryChromaticities, - ColorMap, - HalftoneHints, - TileWidth, - TileLength, - TileOffsets, - TileByteCounts, - BadFaxLines, - CleanFaxData, - ConsecutiveBadFaxLines, - InkSet, - InkNames, - NumberOfInks, - DotRange, - TargetPrinter, - ExtraSamples, - SampleFormat, - SMinSampleValue, - SMaxSampleValue, - TransferRange, - ClipPath, - XClipPathUnits, - YClipPathUnits, - Indexed, - JPEGTables, - OPIProxy, - ProfileType, - FaxProfile, - CodingMethods, - VersionYear, - ModeNumber, - Decode, - DefaultImageColor, - T82ptions, - JPEGProc, - JPEGInterchangeFormat, - JPEGInterchangeFormatLength, - JPEGRestartInterval, - JPEGLosslessPredictors, - JPEGPointTransforms, - JPEGQTables, - JPEGDCTables, - JPEGACTables, - YCbCrCoefficients, - YCbCrSubsampling, - YCbCrSubsampling, - YCbCrPositioning, - ReferenceBlackWhite, - StripRowCounts, - XMP, - Rating, - RatingPercent, - ImageID, - CFARepeatPatternDim, - CFAPattern2, - BatteryLevel, - Copyright, - MDFileTag, - MDScalePixel, - MDLabName, - MDSampleInfo, - MDPrepDate, - MDPrepTime, - MDFileUnits, - PixelScale, - IntergraphPacketData, - IntergraphRegisters, - IntergraphMatrix, - ModelTiePoint, - SEMInfo, - ModelTransform, - ImageLayer, - FaxRecvParams, - FaxSubaddress, - FaxRecvTime, - ImageSourceData, - XPTitle, - XPComment, - XPAuthor, - XPKeywords, - XPSubject, - GDALMetadata, - GDALNoData - }; + switch ((ExifTagValue)(ushort)tag) + { + case ExifTagValue.SubfileType: + case ExifTagValue.OldSubfileType: + case ExifTagValue.ImageWidth: + case ExifTagValue.ImageLength: + case ExifTagValue.BitsPerSample: + case ExifTagValue.Compression: + case ExifTagValue.PhotometricInterpretation: + case ExifTagValue.Thresholding: + case ExifTagValue.CellWidth: + case ExifTagValue.CellLength: + case ExifTagValue.FillOrder: + case ExifTagValue.DocumentName: + case ExifTagValue.ImageDescription: + case ExifTagValue.Make: + case ExifTagValue.Model: + case ExifTagValue.StripOffsets: + case ExifTagValue.Orientation: + case ExifTagValue.SamplesPerPixel: + case ExifTagValue.RowsPerStrip: + case ExifTagValue.StripByteCounts: + case ExifTagValue.MinSampleValue: + case ExifTagValue.MaxSampleValue: + case ExifTagValue.XResolution: + case ExifTagValue.YResolution: + case ExifTagValue.PlanarConfiguration: + case ExifTagValue.PageName: + case ExifTagValue.XPosition: + case ExifTagValue.YPosition: + case ExifTagValue.FreeOffsets: + case ExifTagValue.FreeByteCounts: + case ExifTagValue.GrayResponseUnit: + case ExifTagValue.GrayResponseCurve: + case ExifTagValue.T4Options: + case ExifTagValue.T6Options: + case ExifTagValue.ResolutionUnit: + case ExifTagValue.PageNumber: + case ExifTagValue.ColorResponseUnit: + case ExifTagValue.TransferFunction: + case ExifTagValue.Software: + case ExifTagValue.DateTime: + case ExifTagValue.Artist: + case ExifTagValue.HostComputer: + case ExifTagValue.Predictor: + case ExifTagValue.WhitePoint: + case ExifTagValue.PrimaryChromaticities: + case ExifTagValue.ColorMap: + case ExifTagValue.HalftoneHints: + case ExifTagValue.TileWidth: + case ExifTagValue.TileLength: + case ExifTagValue.TileOffsets: + case ExifTagValue.TileByteCounts: + case ExifTagValue.BadFaxLines: + case ExifTagValue.CleanFaxData: + case ExifTagValue.ConsecutiveBadFaxLines: + case ExifTagValue.InkSet: + case ExifTagValue.InkNames: + case ExifTagValue.NumberOfInks: + case ExifTagValue.DotRange: + case ExifTagValue.TargetPrinter: + case ExifTagValue.ExtraSamples: + case ExifTagValue.SampleFormat: + case ExifTagValue.SMinSampleValue: + case ExifTagValue.SMaxSampleValue: + case ExifTagValue.TransferRange: + case ExifTagValue.ClipPath: + case ExifTagValue.XClipPathUnits: + case ExifTagValue.YClipPathUnits: + case ExifTagValue.Indexed: + case ExifTagValue.JPEGTables: + case ExifTagValue.OPIProxy: + case ExifTagValue.ProfileType: + case ExifTagValue.FaxProfile: + case ExifTagValue.CodingMethods: + case ExifTagValue.VersionYear: + case ExifTagValue.ModeNumber: + case ExifTagValue.Decode: + case ExifTagValue.DefaultImageColor: + case ExifTagValue.T82ptions: + case ExifTagValue.JPEGProc: + case ExifTagValue.JPEGInterchangeFormat: + case ExifTagValue.JPEGInterchangeFormatLength: + case ExifTagValue.JPEGRestartInterval: + case ExifTagValue.JPEGLosslessPredictors: + case ExifTagValue.JPEGPointTransforms: + case ExifTagValue.JPEGQTables: + case ExifTagValue.JPEGDCTables: + case ExifTagValue.JPEGACTables: + case ExifTagValue.YCbCrCoefficients: + case ExifTagValue.YCbCrPositioning: + case ExifTagValue.YCbCrSubsampling: + case ExifTagValue.ReferenceBlackWhite: + case ExifTagValue.StripRowCounts: + case ExifTagValue.XMP: + case ExifTagValue.Rating: + case ExifTagValue.RatingPercent: + case ExifTagValue.ImageID: + case ExifTagValue.CFARepeatPatternDim: + case ExifTagValue.CFAPattern2: + case ExifTagValue.BatteryLevel: + case ExifTagValue.Copyright: + case ExifTagValue.MDFileTag: + case ExifTagValue.MDScalePixel: + case ExifTagValue.MDLabName: + case ExifTagValue.MDSampleInfo: + case ExifTagValue.MDPrepDate: + case ExifTagValue.MDPrepTime: + case ExifTagValue.MDFileUnits: + case ExifTagValue.PixelScale: + case ExifTagValue.IntergraphPacketData: + case ExifTagValue.IntergraphRegisters: + case ExifTagValue.IntergraphMatrix: + case ExifTagValue.ModelTiePoint: + case ExifTagValue.SEMInfo: + case ExifTagValue.ModelTransform: + case ExifTagValue.ImageLayer: + case ExifTagValue.FaxRecvParams: + case ExifTagValue.FaxSubaddress: + case ExifTagValue.FaxRecvTime: + case ExifTagValue.ImageSourceData: + case ExifTagValue.XPTitle: + case ExifTagValue.XPComment: + case ExifTagValue.XPAuthor: + case ExifTagValue.XPKeywords: + case ExifTagValue.XPSubject: + case ExifTagValue.GDALMetadata: + case ExifTagValue.GDALNoData: + return ExifParts.IfdTags; - /// - /// The collection of Exif tags - /// - public static readonly ExifTag[] Exif = - { - ExposureTime, - FNumber, - ExposureProgram, - SpectralSensitivity, - ISOSpeedRatings, - OECF, - Interlace, - TimeZoneOffset, - SelfTimerMode, - SensitivityType, - StandardOutputSensitivity, - RecommendedExposureIndex, - ISOSpeed, - ISOSpeedLatitudeyyy, - ISOSpeedLatitudezzz, - ExifVersion, - DateTimeOriginal, - DateTimeDigitized, - OffsetTime, - OffsetTimeOriginal, - OffsetTimeDigitized, - ComponentsConfiguration, - CompressedBitsPerPixel, - ShutterSpeedValue, - ApertureValue, - BrightnessValue, - ExposureBiasValue, - MaxApertureValue, - SubjectDistance, - MeteringMode, - LightSource, - Flash, - FocalLength, - FlashEnergy2, - SpatialFrequencyResponse2, - Noise, - FocalPlaneXResolution2, - FocalPlaneYResolution2, - FocalPlaneResolutionUnit2, - ImageNumber, - SecurityClassification, - ImageHistory, - SubjectArea, - ExposureIndex2, - TIFFEPStandardID, - SensingMethod2, - MakerNote, - UserComment, - SubsecTime, - SubsecTimeOriginal, - SubsecTimeDigitized, - AmbientTemperature, - Humidity, - Pressure, - WaterDepth, - Acceleration, - CameraElevationAngle, - FlashpixVersion, - ColorSpace, - PixelXDimension, - PixelYDimension, - RelatedSoundFile, - FlashEnergy, - SpatialFrequencyResponse, - FocalPlaneXResolution, - FocalPlaneYResolution, - FocalPlaneResolutionUnit, - SubjectLocation, - ExposureIndex, - SensingMethod, - FileSource, - SceneType, - CFAPattern, - CustomRendered, - ExposureMode, - WhiteBalance, - DigitalZoomRatio, - FocalLengthIn35mmFilm, - SceneCaptureType, - GainControl, - Contrast, - Saturation, - Sharpness, - DeviceSettingDescription, - SubjectDistanceRange, - ImageUniqueID, - OwnerName, - SerialNumber, - LensInfo, - LensMake, - LensModel, - LensSerialNumber - }; + case ExifTagValue.ExposureTime: + case ExifTagValue.FNumber: + case ExifTagValue.ExposureProgram: + case ExifTagValue.SpectralSensitivity: + case ExifTagValue.ISOSpeedRatings: + case ExifTagValue.OECF: + case ExifTagValue.Interlace: + case ExifTagValue.TimeZoneOffset: + case ExifTagValue.SelfTimerMode: + case ExifTagValue.SensitivityType: + case ExifTagValue.StandardOutputSensitivity: + case ExifTagValue.RecommendedExposureIndex: + case ExifTagValue.ISOSpeed: + case ExifTagValue.ISOSpeedLatitudeyyy: + case ExifTagValue.ISOSpeedLatitudezzz: + case ExifTagValue.ExifVersion: + case ExifTagValue.DateTimeOriginal: + case ExifTagValue.DateTimeDigitized: + case ExifTagValue.OffsetTime: + case ExifTagValue.OffsetTimeOriginal: + case ExifTagValue.OffsetTimeDigitized: + case ExifTagValue.ComponentsConfiguration: + case ExifTagValue.CompressedBitsPerPixel: + case ExifTagValue.ShutterSpeedValue: + case ExifTagValue.ApertureValue: + case ExifTagValue.BrightnessValue: + case ExifTagValue.ExposureBiasValue: + case ExifTagValue.MaxApertureValue: + case ExifTagValue.SubjectDistance: + case ExifTagValue.MeteringMode: + case ExifTagValue.LightSource: + case ExifTagValue.Flash: + case ExifTagValue.FocalLength: + case ExifTagValue.FlashEnergy2: + case ExifTagValue.SpatialFrequencyResponse2: + case ExifTagValue.Noise: + case ExifTagValue.FocalPlaneXResolution2: + case ExifTagValue.FocalPlaneYResolution2: + case ExifTagValue.FocalPlaneResolutionUnit2: + case ExifTagValue.ImageNumber: + case ExifTagValue.SecurityClassification: + case ExifTagValue.ImageHistory: + case ExifTagValue.SubjectArea: + case ExifTagValue.ExposureIndex2: + case ExifTagValue.TIFFEPStandardID: + case ExifTagValue.SensingMethod2: + case ExifTagValue.MakerNote: + case ExifTagValue.UserComment: + case ExifTagValue.SubsecTime: + case ExifTagValue.SubsecTimeOriginal: + case ExifTagValue.SubsecTimeDigitized: + case ExifTagValue.AmbientTemperature: + case ExifTagValue.Humidity: + case ExifTagValue.Pressure: + case ExifTagValue.WaterDepth: + case ExifTagValue.Acceleration: + case ExifTagValue.CameraElevationAngle: + case ExifTagValue.FlashpixVersion: + case ExifTagValue.ColorSpace: + case ExifTagValue.PixelXDimension: + case ExifTagValue.PixelYDimension: + case ExifTagValue.RelatedSoundFile: + case ExifTagValue.FlashEnergy: + case ExifTagValue.SpatialFrequencyResponse: + case ExifTagValue.FocalPlaneXResolution: + case ExifTagValue.FocalPlaneYResolution: + case ExifTagValue.FocalPlaneResolutionUnit: + case ExifTagValue.SubjectLocation: + case ExifTagValue.ExposureIndex: + case ExifTagValue.SensingMethod: + case ExifTagValue.FileSource: + case ExifTagValue.SceneType: + case ExifTagValue.CFAPattern: + case ExifTagValue.CustomRendered: + case ExifTagValue.ExposureMode: + case ExifTagValue.WhiteBalance: + case ExifTagValue.DigitalZoomRatio: + case ExifTagValue.FocalLengthIn35mmFilm: + case ExifTagValue.SceneCaptureType: + case ExifTagValue.GainControl: + case ExifTagValue.Contrast: + case ExifTagValue.Saturation: + case ExifTagValue.Sharpness: + case ExifTagValue.DeviceSettingDescription: + case ExifTagValue.SubjectDistanceRange: + case ExifTagValue.ImageUniqueID: + case ExifTagValue.OwnerName: + case ExifTagValue.SerialNumber: + case ExifTagValue.LensInfo: + case ExifTagValue.LensMake: + case ExifTagValue.LensModel: + case ExifTagValue.LensSerialNumber: + return ExifParts.ExifTags; - /// - /// The collection of GPS tags - /// - public static readonly ExifTag[] Gps = - { - GPSVersionID, - GPSLatitudeRef, - GPSLatitude, - GPSLongitudeRef, - GPSLongitude, - GPSAltitudeRef, - GPSAltitude, - GPSTimestamp, - GPSSatellites, - GPSStatus, - GPSMeasureMode, - GPSDOP, - GPSSpeedRef, - GPSSpeed, - GPSTrackRef, - GPSTrack, - GPSImgDirectionRef, - GPSImgDirection, - GPSMapDatum, - GPSDestLatitudeRef, - GPSDestLatitude, - GPSDestLongitudeRef, - GPSDestLongitude, - GPSDestBearingRef, - GPSDestBearing, - GPSDestDistanceRef, - GPSDestDistance, - GPSProcessingMethod, - GPSAreaInformation, - GPSDateStamp, - GPSDifferential - }; + case ExifTagValue.GPSVersionID: + case ExifTagValue.GPSLatitudeRef: + case ExifTagValue.GPSLatitude: + case ExifTagValue.GPSLongitudeRef: + case ExifTagValue.GPSLongitude: + case ExifTagValue.GPSAltitudeRef: + case ExifTagValue.GPSAltitude: + case ExifTagValue.GPSTimestamp: + case ExifTagValue.GPSSatellites: + case ExifTagValue.GPSStatus: + case ExifTagValue.GPSMeasureMode: + case ExifTagValue.GPSDOP: + case ExifTagValue.GPSSpeedRef: + case ExifTagValue.GPSSpeed: + case ExifTagValue.GPSTrackRef: + case ExifTagValue.GPSTrack: + case ExifTagValue.GPSImgDirectionRef: + case ExifTagValue.GPSImgDirection: + case ExifTagValue.GPSMapDatum: + case ExifTagValue.GPSDestLatitudeRef: + case ExifTagValue.GPSDestLatitude: + case ExifTagValue.GPSDestLongitudeRef: + case ExifTagValue.GPSDestLongitude: + case ExifTagValue.GPSDestBearingRef: + case ExifTagValue.GPSDestBearing: + case ExifTagValue.GPSDestDistanceRef: + case ExifTagValue.GPSDestDistance: + case ExifTagValue.GPSProcessingMethod: + case ExifTagValue.GPSAreaInformation: + case ExifTagValue.GPSDateStamp: + case ExifTagValue.GPSDifferential: + return ExifParts.GpsTags; + + case ExifTagValue.Unknown: + case ExifTagValue.SubIFDOffset: + case ExifTagValue.GPSIFDOffset: + default: + return ExifParts.None; + } + } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifValue.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifValue.cs deleted file mode 100644 index 05a9f35c9..000000000 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifValue.cs +++ /dev/null @@ -1,721 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Globalization; -using System.Text; -using SixLabors.ImageSharp.Primitives; - -namespace SixLabors.ImageSharp.Metadata.Profiles.Exif -{ - /// - /// Represent the value of the EXIF profile. - /// - public sealed class ExifValue : IEquatable, IDeepCloneable - { - /// - /// Initializes a new instance of the class. - /// - /// The tag. - /// The data type. - /// The value. - /// Whether the value is an array. - internal ExifValue(ExifTag tag, ExifDataType dataType, object value, bool isArray) - { - this.Tag = tag; - this.DataType = dataType; - this.IsArray = isArray && dataType != ExifDataType.Ascii; - this.Value = value; - } - - /// - /// Initializes a new instance of the class - /// by making a copy from another exif value. - /// - /// The other exif value, where the clone should be made from. - /// is null. - private ExifValue(ExifValue other) - { - Guard.NotNull(other, nameof(other)); - - this.DataType = other.DataType; - this.IsArray = other.IsArray; - this.Tag = other.Tag; - - if (!other.IsArray) - { - // All types are value types except for string which is immutable so safe to simply assign. - this.Value = other.Value; - } - else - { - // All array types are value types so Clone() is sufficient here. - var array = (Array)other.Value; - this.Value = array.Clone(); - } - } - - /// - /// Gets the data type of the exif value. - /// - public ExifDataType DataType { get; } - - /// - /// Gets a value indicating whether the value is an array. - /// - public bool IsArray { get; } - - /// - /// Gets the tag of the exif value. - /// - public ExifTag Tag { get; } - - /// - /// Gets the value. - /// - public object Value { get; } - - /// - /// Gets a value indicating whether the EXIF value has a value. - /// - internal bool HasValue - { - get - { - if (this.Value is null) - { - return false; - } - - if (this.DataType == ExifDataType.Ascii) - { - return ((string)this.Value).Length > 0; - } - - return true; - } - } - - /// - /// Gets the length of the EXIF value - /// - internal int Length - { - get - { - if (this.Value is null) - { - return 4; - } - - int size = (int)(GetSize(this.DataType) * this.NumberOfComponents); - - return size < 4 ? 4 : size; - } - } - - /// - /// Gets the number of components. - /// - internal int NumberOfComponents - { - get - { - if (this.DataType == ExifDataType.Ascii) - { - return Encoding.UTF8.GetBytes((string)this.Value).Length; - } - - if (this.IsArray) - { - return ((Array)this.Value).Length; - } - - return 1; - } - } - - /// - /// Compares two objects for equality. - /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - public static bool operator ==(ExifValue left, ExifValue right) => ReferenceEquals(left, right) || left.Equals(right); - - /// - /// Compares two objects for equality. - /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - public static bool operator !=(ExifValue left, ExifValue right) => !(left == right); - - /// - public override bool Equals(object obj) => obj is ExifValue other && this.Equals(other); - - /// - public bool Equals(ExifValue other) - { - if (other is null) - { - return false; - } - - if (ReferenceEquals(this, other)) - { - return true; - } - - return - this.Tag == other.Tag - && this.DataType == other.DataType - && object.Equals(this.Value, other.Value); - } - - /// - /// Clones the current value, overwriting the value. - /// - /// The value to overwrite. - /// - public ExifValue WithValue(object value) - { - this.CheckValue(value); - - return new ExifValue(this.Tag, this.DataType, value, this.IsArray); - } - - /// - public override int GetHashCode() - { - return HashCode.Combine(this.Tag, this.DataType, this.Value); - } - - /// - public override string ToString() - { - if (this.Value is null) - { - return null; - } - - if (this.DataType == ExifDataType.Ascii) - { - return (string)this.Value; - } - - if (!this.IsArray) - { - return this.ToString(this.Value); - } - - var sb = new StringBuilder(); - foreach (object value in (Array)this.Value) - { - sb.Append(this.ToString(value)); - sb.Append(' '); - } - - return sb.ToString(); - } - - /// - public ExifValue DeepClone() => new ExifValue(this); - - /// - /// Creates a new - /// - /// The tag. - /// The value. - /// - /// The . - /// - /// - /// Thrown if the tag is not supported. - /// - internal static ExifValue Create(ExifTag tag, object value) - { - Guard.IsFalse(tag == ExifTag.Unknown, nameof(tag), "Invalid Tag"); - - switch (tag) - { - case ExifTag.ImageDescription: - case ExifTag.Make: - case ExifTag.Model: - case ExifTag.Software: - case ExifTag.DateTime: - case ExifTag.Artist: - case ExifTag.HostComputer: - case ExifTag.Copyright: - case ExifTag.DocumentName: - case ExifTag.PageName: - case ExifTag.InkNames: - case ExifTag.TargetPrinter: - case ExifTag.ImageID: - case ExifTag.MDLabName: - case ExifTag.MDSampleInfo: - case ExifTag.MDPrepDate: - case ExifTag.MDPrepTime: - case ExifTag.MDFileUnits: - case ExifTag.SEMInfo: - case ExifTag.SpectralSensitivity: - case ExifTag.DateTimeOriginal: - case ExifTag.DateTimeDigitized: - case ExifTag.SubsecTime: - case ExifTag.SubsecTimeOriginal: - case ExifTag.SubsecTimeDigitized: - case ExifTag.FaxSubaddress: - case ExifTag.OffsetTime: - case ExifTag.OffsetTimeOriginal: - case ExifTag.OffsetTimeDigitized: - case ExifTag.SecurityClassification: - case ExifTag.ImageHistory: - case ExifTag.ImageUniqueID: - case ExifTag.OwnerName: - case ExifTag.SerialNumber: - case ExifTag.LensMake: - case ExifTag.LensModel: - case ExifTag.LensSerialNumber: - case ExifTag.GDALMetadata: - case ExifTag.GDALNoData: - case ExifTag.GPSLatitudeRef: - case ExifTag.GPSLongitudeRef: - case ExifTag.GPSSatellites: - case ExifTag.GPSStatus: - case ExifTag.GPSMeasureMode: - case ExifTag.GPSSpeedRef: - case ExifTag.GPSTrackRef: - case ExifTag.GPSImgDirectionRef: - case ExifTag.GPSMapDatum: - case ExifTag.GPSDestLatitudeRef: - case ExifTag.GPSDestLongitudeRef: - case ExifTag.GPSDestBearingRef: - case ExifTag.GPSDestDistanceRef: - case ExifTag.GPSDateStamp: - return new ExifValue(tag, ExifDataType.Ascii, value, true); - - case ExifTag.ClipPath: - case ExifTag.VersionYear: - case ExifTag.XMP: - case ExifTag.CFAPattern2: - case ExifTag.TIFFEPStandardID: - case ExifTag.XPTitle: - case ExifTag.XPComment: - case ExifTag.XPAuthor: - case ExifTag.XPKeywords: - case ExifTag.XPSubject: - case ExifTag.GPSVersionID: - return new ExifValue(tag, ExifDataType.Byte, value, true); - - case ExifTag.FaxProfile: - case ExifTag.ModeNumber: - case ExifTag.GPSAltitudeRef: - return new ExifValue(tag, ExifDataType.Byte, value, false); - - case ExifTag.FreeOffsets: - case ExifTag.FreeByteCounts: - case ExifTag.ColorResponseUnit: - case ExifTag.TileOffsets: - case ExifTag.SMinSampleValue: - case ExifTag.SMaxSampleValue: - case ExifTag.JPEGQTables: - case ExifTag.JPEGDCTables: - case ExifTag.JPEGACTables: - case ExifTag.StripRowCounts: - case ExifTag.IntergraphRegisters: - case ExifTag.TimeZoneOffset: - return new ExifValue(tag, ExifDataType.Long, value, true); - case ExifTag.SubfileType: - case ExifTag.SubIFDOffset: - case ExifTag.GPSIFDOffset: - case ExifTag.T4Options: - case ExifTag.T6Options: - case ExifTag.XClipPathUnits: - case ExifTag.YClipPathUnits: - case ExifTag.ProfileType: - case ExifTag.CodingMethods: - case ExifTag.T82ptions: - case ExifTag.JPEGInterchangeFormat: - case ExifTag.JPEGInterchangeFormatLength: - case ExifTag.MDFileTag: - case ExifTag.StandardOutputSensitivity: - case ExifTag.RecommendedExposureIndex: - case ExifTag.ISOSpeed: - case ExifTag.ISOSpeedLatitudeyyy: - case ExifTag.ISOSpeedLatitudezzz: - case ExifTag.FaxRecvParams: - case ExifTag.FaxRecvTime: - case ExifTag.ImageNumber: - return new ExifValue(tag, ExifDataType.Long, value, false); - - case ExifTag.WhitePoint: - case ExifTag.PrimaryChromaticities: - case ExifTag.YCbCrCoefficients: - case ExifTag.ReferenceBlackWhite: - case ExifTag.PixelScale: - case ExifTag.IntergraphMatrix: - case ExifTag.ModelTiePoint: - case ExifTag.ModelTransform: - case ExifTag.GPSLatitude: - case ExifTag.GPSLongitude: - case ExifTag.GPSTimestamp: - case ExifTag.GPSDestLatitude: - case ExifTag.GPSDestLongitude: - return new ExifValue(tag, ExifDataType.Rational, value, true); - - case ExifTag.XPosition: - case ExifTag.YPosition: - case ExifTag.XResolution: - case ExifTag.YResolution: - case ExifTag.BatteryLevel: - case ExifTag.ExposureTime: - case ExifTag.FNumber: - case ExifTag.MDScalePixel: - case ExifTag.CompressedBitsPerPixel: - case ExifTag.ApertureValue: - case ExifTag.MaxApertureValue: - case ExifTag.SubjectDistance: - case ExifTag.FocalLength: - case ExifTag.FlashEnergy2: - case ExifTag.FocalPlaneXResolution2: - case ExifTag.FocalPlaneYResolution2: - case ExifTag.ExposureIndex2: - case ExifTag.Humidity: - case ExifTag.Pressure: - case ExifTag.Acceleration: - case ExifTag.FlashEnergy: - case ExifTag.FocalPlaneXResolution: - case ExifTag.FocalPlaneYResolution: - case ExifTag.ExposureIndex: - case ExifTag.DigitalZoomRatio: - case ExifTag.LensInfo: - case ExifTag.GPSAltitude: - case ExifTag.GPSDOP: - case ExifTag.GPSSpeed: - case ExifTag.GPSTrack: - case ExifTag.GPSImgDirection: - case ExifTag.GPSDestBearing: - case ExifTag.GPSDestDistance: - return new ExifValue(tag, ExifDataType.Rational, value, false); - - case ExifTag.BitsPerSample: - case ExifTag.MinSampleValue: - case ExifTag.MaxSampleValue: - case ExifTag.GrayResponseCurve: - case ExifTag.ColorMap: - case ExifTag.ExtraSamples: - case ExifTag.PageNumber: - case ExifTag.TransferFunction: - case ExifTag.Predictor: - case ExifTag.HalftoneHints: - case ExifTag.SampleFormat: - case ExifTag.TransferRange: - case ExifTag.DefaultImageColor: - case ExifTag.JPEGLosslessPredictors: - case ExifTag.JPEGPointTransforms: - case ExifTag.YCbCrSubsampling: - case ExifTag.CFARepeatPatternDim: - case ExifTag.IntergraphPacketData: - case ExifTag.ISOSpeedRatings: - case ExifTag.SubjectArea: - case ExifTag.SubjectLocation: - return new ExifValue(tag, ExifDataType.Short, value, true); - - case ExifTag.OldSubfileType: - case ExifTag.Compression: - case ExifTag.PhotometricInterpretation: - case ExifTag.Thresholding: - case ExifTag.CellWidth: - case ExifTag.CellLength: - case ExifTag.FillOrder: - case ExifTag.Orientation: - case ExifTag.SamplesPerPixel: - case ExifTag.PlanarConfiguration: - case ExifTag.GrayResponseUnit: - case ExifTag.ResolutionUnit: - case ExifTag.CleanFaxData: - case ExifTag.InkSet: - case ExifTag.NumberOfInks: - case ExifTag.DotRange: - case ExifTag.Indexed: - case ExifTag.OPIProxy: - case ExifTag.JPEGProc: - case ExifTag.JPEGRestartInterval: - case ExifTag.YCbCrPositioning: - case ExifTag.Rating: - case ExifTag.RatingPercent: - case ExifTag.ExposureProgram: - case ExifTag.Interlace: - case ExifTag.SelfTimerMode: - case ExifTag.SensitivityType: - case ExifTag.MeteringMode: - case ExifTag.LightSource: - case ExifTag.FocalPlaneResolutionUnit2: - case ExifTag.SensingMethod2: - case ExifTag.Flash: - case ExifTag.ColorSpace: - case ExifTag.FocalPlaneResolutionUnit: - case ExifTag.SensingMethod: - case ExifTag.CustomRendered: - case ExifTag.ExposureMode: - case ExifTag.WhiteBalance: - case ExifTag.FocalLengthIn35mmFilm: - case ExifTag.SceneCaptureType: - case ExifTag.GainControl: - case ExifTag.Contrast: - case ExifTag.Saturation: - case ExifTag.Sharpness: - case ExifTag.SubjectDistanceRange: - case ExifTag.GPSDifferential: - return new ExifValue(tag, ExifDataType.Short, value, false); - - case ExifTag.Decode: - return new ExifValue(tag, ExifDataType.SignedRational, value, true); - - case ExifTag.ShutterSpeedValue: - case ExifTag.BrightnessValue: - case ExifTag.ExposureBiasValue: - case ExifTag.AmbientTemperature: - case ExifTag.WaterDepth: - case ExifTag.CameraElevationAngle: - return new ExifValue(tag, ExifDataType.SignedRational, value, false); - - case ExifTag.JPEGTables: - case ExifTag.OECF: - case ExifTag.ExifVersion: - case ExifTag.ComponentsConfiguration: - case ExifTag.MakerNote: - case ExifTag.UserComment: - case ExifTag.FlashpixVersion: - case ExifTag.SpatialFrequencyResponse: - case ExifTag.SpatialFrequencyResponse2: - case ExifTag.Noise: - case ExifTag.CFAPattern: - case ExifTag.DeviceSettingDescription: - case ExifTag.ImageSourceData: - case ExifTag.GPSProcessingMethod: - case ExifTag.GPSAreaInformation: - return new ExifValue(tag, ExifDataType.Undefined, value, true); - - case ExifTag.FileSource: - case ExifTag.SceneType: - return new ExifValue(tag, ExifDataType.Undefined, value, false); - - case ExifTag.StripOffsets: - case ExifTag.TileByteCounts: - case ExifTag.ImageLayer: - return CreateNumber(tag, value, true); - - case ExifTag.ImageWidth: - case ExifTag.ImageLength: - case ExifTag.TileWidth: - case ExifTag.TileLength: - case ExifTag.BadFaxLines: - case ExifTag.ConsecutiveBadFaxLines: - case ExifTag.PixelXDimension: - case ExifTag.PixelYDimension: - return CreateNumber(tag, value, false); - - default: - throw new NotSupportedException(); - } - } - - /// - /// Gets the size in bytes of the given data type. - /// - /// The data type. - /// - /// The . - /// - /// - /// Thrown if the type is unsupported. - /// - internal static uint GetSize(ExifDataType dataType) - { - switch (dataType) - { - case ExifDataType.Ascii: - case ExifDataType.Byte: - case ExifDataType.SignedByte: - case ExifDataType.Undefined: - return 1; - case ExifDataType.Short: - case ExifDataType.SignedShort: - return 2; - case ExifDataType.Long: - case ExifDataType.SignedLong: - case ExifDataType.SingleFloat: - return 4; - case ExifDataType.DoubleFloat: - case ExifDataType.Rational: - case ExifDataType.SignedRational: - return 8; - default: - throw new NotSupportedException(dataType.ToString()); - } - } - - /// - /// Returns an EXIF value with a numeric type for the given tag. - /// - /// The tag. - /// The value. - /// Whether the value is an array. - /// - /// The . - /// - private static ExifValue CreateNumber(ExifTag tag, object value, bool isArray) - { - Type type = value?.GetType(); - if (type?.IsArray == true) - { - type = type.GetElementType(); - } - - if (type is null || type == typeof(ushort)) - { - return new ExifValue(tag, ExifDataType.Short, value, isArray); - } - - if (type == typeof(short)) - { - return new ExifValue(tag, ExifDataType.SignedShort, value, isArray); - } - - if (type == typeof(uint)) - { - return new ExifValue(tag, ExifDataType.Long, value, isArray); - } - - return new ExifValue(tag, ExifDataType.SignedLong, value, isArray); - } - - /// - /// Checks the value type of the given object. - /// - /// The value to check. - /// - /// Thrown if the object type is not supported. - /// - private void CheckValue(object value) - { - if (value is null) - { - return; - } - - Type type = value.GetType(); - - if (this.DataType == ExifDataType.Ascii) - { - Guard.IsTrue(type == typeof(string), nameof(value), "Value should be a string."); - return; - } - - if (type.IsArray) - { - Guard.IsTrue(this.IsArray, nameof(value), "Value should not be an array."); - type = type.GetElementType(); - } - else - { - Guard.IsFalse(this.IsArray, nameof(value), "Value should not be an array."); - } - - switch (this.DataType) - { - case ExifDataType.Byte: - Guard.IsTrue(type == typeof(byte), nameof(value), $"Value should be a byte{(this.IsArray ? " array." : ".")}"); - break; - case ExifDataType.DoubleFloat: - Guard.IsTrue(type == typeof(double), nameof(value), $"Value should be a double{(this.IsArray ? " array." : ".")}"); - break; - case ExifDataType.Long: - Guard.IsTrue(type == typeof(uint), nameof(value), $"Value should be an unsigned int{(this.IsArray ? " array." : ".")}"); - break; - case ExifDataType.Rational: - Guard.IsTrue(type == typeof(Rational), nameof(value), $"Value should be a Rational{(this.IsArray ? " array." : ".")}"); - break; - case ExifDataType.Short: - Guard.IsTrue(type == typeof(ushort), nameof(value), $"Value should be an unsigned short{(this.IsArray ? " array." : ".")}"); - break; - case ExifDataType.SignedByte: - Guard.IsTrue(type == typeof(sbyte), nameof(value), $"Value should be a signed byte{(this.IsArray ? " array." : ".")}"); - break; - case ExifDataType.SignedLong: - Guard.IsTrue(type == typeof(int), nameof(value), $"Value should be an int{(this.IsArray ? " array." : ".")}"); - break; - case ExifDataType.SignedRational: - Guard.IsTrue(type == typeof(SignedRational), nameof(value), $"Value should be a SignedRational{(this.IsArray ? " array." : ".")}"); - break; - case ExifDataType.SignedShort: - Guard.IsTrue(type == typeof(short), nameof(value), $"Value should be a short{(this.IsArray ? " array." : ".")}"); - break; - case ExifDataType.SingleFloat: - Guard.IsTrue(type == typeof(float), nameof(value), $"Value should be a float{(this.IsArray ? " array." : ".")}"); - break; - case ExifDataType.Undefined: - Guard.IsTrue(type == typeof(byte), nameof(value), "Value should be a byte array."); - break; - default: - throw new NotSupportedException(); - } - } - - /// - /// Converts the object value of this instance to its equivalent string representation - /// - /// The value - /// The - private string ToString(object value) - { - if (ExifTagDescriptionAttribute.GetDescription(this.Tag, value) is string description) - { - return description; - } - - switch (this.DataType) - { - case ExifDataType.Ascii: - return (string)value; - case ExifDataType.Byte: - return ((byte)value).ToString("X2", CultureInfo.InvariantCulture); - case ExifDataType.DoubleFloat: - return ((double)value).ToString(CultureInfo.InvariantCulture); - case ExifDataType.Long: - return ((uint)value).ToString(CultureInfo.InvariantCulture); - case ExifDataType.Rational: - return ((Rational)value).ToString(CultureInfo.InvariantCulture); - case ExifDataType.Short: - return ((ushort)value).ToString(CultureInfo.InvariantCulture); - case ExifDataType.SignedByte: - return ((sbyte)value).ToString("X2", CultureInfo.InvariantCulture); - case ExifDataType.SignedLong: - return ((int)value).ToString(CultureInfo.InvariantCulture); - case ExifDataType.SignedRational: - return ((Rational)value).ToString(CultureInfo.InvariantCulture); - case ExifDataType.SignedShort: - return ((short)value).ToString(CultureInfo.InvariantCulture); - case ExifDataType.SingleFloat: - return ((float)value).ToString(CultureInfo.InvariantCulture); - case ExifDataType.Undefined: - return ((byte)value).ToString("X2", CultureInfo.InvariantCulture); - default: - throw new NotSupportedException(); - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs index 67c1b2b65..48b0fddca 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs @@ -18,24 +18,24 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// Which parts will be written. /// private readonly ExifParts allowedParts; - private readonly IList values; + private readonly IList values; private List dataOffsets; - private readonly List ifdIndexes; - private readonly List exifIndexes; - private readonly List gpsIndexes; + private readonly List ifdValues; + private readonly List exifValues; + private readonly List gpsValues; /// /// Initializes a new instance of the class. /// /// The values. /// The allowed parts. - public ExifWriter(IList values, ExifParts allowedParts) + public ExifWriter(IList values, ExifParts allowedParts) { this.values = values; this.allowedParts = allowedParts; - this.ifdIndexes = this.GetIndexes(ExifParts.IfdTags, ExifTags.Ifd); - this.exifIndexes = this.GetIndexes(ExifParts.ExifTags, ExifTags.Exif); - this.gpsIndexes = this.GetIndexes(ExifParts.GPSTags, ExifTags.Gps); + this.ifdValues = this.GetPartValues(ExifParts.IfdTags); + this.exifValues = this.GetPartValues(ExifParts.ExifTags); + this.gpsValues = this.GetPartValues(ExifParts.GpsTags); } /// @@ -46,43 +46,29 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// public byte[] GetData() { - uint startIndex = 0; + const uint startIndex = 0; uint length; - int exifIndex = -1; - int gpsIndex = -1; - if (this.exifIndexes.Count > 0) - { - exifIndex = this.GetIndex(this.ifdIndexes, ExifTag.SubIFDOffset); - } - - if (this.gpsIndexes.Count > 0) - { - gpsIndex = this.GetIndex(this.ifdIndexes, ExifTag.GPSIFDOffset); - } - - uint ifdLength = 2 + this.GetLength(this.ifdIndexes) + 4; - uint exifLength = this.GetLength(this.exifIndexes); - uint gpsLength = this.GetLength(this.gpsIndexes); + IExifValue exifOffset = GetOffsetValue(this.ifdValues, this.exifValues, ExifTag.SubIFDOffset); + IExifValue gpsOffset = GetOffsetValue(this.ifdValues, this.gpsValues, ExifTag.GPSIFDOffset); - if (exifLength > 0) + if (this.ifdValues.Count == 0 && this.exifValues.Count == 0 && this.gpsValues.Count == 0) { - exifLength += 2; + return Array.Empty(); } - if (gpsLength > 0) - { - gpsLength += 2; - } + uint ifdLength = this.GetLength(this.ifdValues) + 4U; + uint exifLength = this.GetLength(this.exifValues); + uint gpsLength = this.GetLength(this.gpsValues); length = ifdLength + exifLength + gpsLength; - if (length == 6) + if (length == 4U) { - return null; + return Array.Empty(); } - // two bytes for the byte Order marker 'II', followed by the number 42 (0x2A) and a 0, making 4 bytes total + // two bytes for the byte Order marker 'II' or 'MM', followed by the number 42 (0x2A) and a 0, making 4 bytes total length += (uint)ExifConstants.LittleEndianByteOrderMarker.Length; length += 4 + 2; @@ -91,38 +77,31 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif int i = 0; - // the byte order marker for little-endian, followed by the number 42 and a 0 + // The byte order marker for little-endian, followed by the number 42 and a 0 ExifConstants.LittleEndianByteOrderMarker.AsSpan().CopyTo(result.AsSpan(start: i)); i += ExifConstants.LittleEndianByteOrderMarker.Length; - uint ifdOffset = ((uint)i - startIndex) + 4; + uint ifdOffset = ((uint)i - startIndex) + 4U; uint thumbnailOffset = ifdOffset + ifdLength + exifLength + gpsLength; - if (exifLength > 0) - { - this.values[exifIndex] = this.values[exifIndex].WithValue(ifdOffset + ifdLength); - } - - if (gpsLength > 0) - { - this.values[gpsIndex] = this.values[gpsIndex].WithValue(ifdOffset + ifdLength + exifLength); - } + exifOffset?.TrySetValue(ifdOffset + ifdLength); + gpsOffset?.TrySetValue(ifdOffset + ifdLength + exifLength); i = WriteUInt32(ifdOffset, result, i); - i = this.WriteHeaders(this.ifdIndexes, result, i); + i = this.WriteHeaders(this.ifdValues, result, i); i = WriteUInt32(thumbnailOffset, result, i); - i = this.WriteData(startIndex, this.ifdIndexes, result, i); + i = this.WriteData(startIndex, this.ifdValues, result, i); if (exifLength > 0) { - i = this.WriteHeaders(this.exifIndexes, result, i); - i = this.WriteData(startIndex, this.exifIndexes, result, i); + i = this.WriteHeaders(this.exifValues, result, i); + i = this.WriteData(startIndex, this.exifValues, result, i); } if (gpsLength > 0) { - i = this.WriteHeaders(this.gpsIndexes, result, i); - i = this.WriteData(startIndex, this.gpsIndexes, result, i); + i = this.WriteHeaders(this.gpsValues, result, i); + i = this.WriteData(startIndex, this.gpsValues, result, i); } WriteUInt16(0, result, i); @@ -179,79 +158,137 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return offset + 4; } - private int GetIndex(IList indexes, ExifTag tag) + private static IExifValue GetOffsetValue(List ifdValues, List values, ExifTag offset) { - foreach (int index in indexes) + int index = -1; + + for (int i = 0; i < ifdValues.Count; i++) { - if (this.values[index].Tag == tag) + if (ifdValues[i].Tag == offset) { - return index; + index = i; } } - int newIndex = this.values.Count; - indexes.Add(newIndex); - this.values.Add(ExifValue.Create(tag, null)); - return newIndex; + if (values.Count > 0) + { + if (index != -1) + { + return ifdValues[index]; + } + + ExifValue result = ExifValues.Create(offset); + ifdValues.Add(result); + + return result; + } + else if (index != -1) + { + ifdValues.RemoveAt(index); + } + + return null; } - private List GetIndexes(ExifParts part, ExifTag[] tags) + private List GetPartValues(ExifParts part) { - if (((int)this.allowedParts & (int)part) == 0) + var result = new List(); + + if (!EnumUtils.HasFlag(this.allowedParts, part)) { - return new List(); + return result; } - var result = new List(); - for (int i = 0; i < this.values.Count; i++) + foreach (IExifValue value in this.values) { - ExifValue value = this.values[i]; - - if (!value.HasValue) + if (!HasValue(value)) { continue; } - int index = Array.IndexOf(tags, value.Tag); - if (index > -1) + if (ExifTags.GetPart(value.Tag) == part) { - result.Add(i); + result.Add(value); } } return result; } - private uint GetLength(IList indexes) + private static bool HasValue(IExifValue exifValue) + { + object value = exifValue.GetValue(); + if (value is null) + { + return false; + } + + if (exifValue.DataType == ExifDataType.Ascii) + { + string stringValue = (string)value; + return stringValue.Length > 0; + } + + if (value is Array arrayValue) + { + return arrayValue.Length > 0; + } + + return true; + } + + private uint GetLength(IList values) { - uint length = 0; + if (values.Count == 0) + { + return 0; + } - foreach (int index in indexes) + uint length = 2; + + foreach (IExifValue value in values) { - uint valueLength = (uint)this.values[index].Length; + uint valueLength = GetLength(value); + + length += 2 + 2 + 4 + 4; if (valueLength > 4) { - length += 12 + valueLength; - } - else - { - length += 12; + length += valueLength; } } return length; } - private int WriteArray(ExifValue value, Span destination, int offset) + private static uint GetLength(IExifValue value) => GetNumberOfComponents(value) * ExifDataTypes.GetSize(value.DataType); + + private static uint GetNumberOfComponents(IExifValue exifValue) + { + object value = exifValue.GetValue(); + + if (exifValue.DataType == ExifDataType.Ascii) + { + return (uint)Encoding.UTF8.GetBytes((string)value).Length + 1; + } + + if (value is Array arrayValue) + { + return (uint)arrayValue.Length; + } + + return 1; + } + + private int WriteArray(IExifValue value, Span destination, int offset) { if (value.DataType == ExifDataType.Ascii) { - return this.WriteValue(ExifDataType.Ascii, value.Value, destination, offset); + return this.WriteValue(ExifDataType.Ascii, value.GetValue(), destination, offset); } int newOffset = offset; - foreach (object obj in (Array)value.Value) + foreach (object obj in (Array)value.GetValue()) { newOffset = this.WriteValue(value.DataType, obj, destination, newOffset); } @@ -259,7 +296,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return newOffset; } - private int WriteData(uint startIndex, List indexes, Span destination, int offset) + private int WriteData(uint startIndex, List values, Span destination, int offset) { if (this.dataOffsets.Count == 0) { @@ -269,10 +306,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif int newOffset = offset; int i = 0; - foreach (int index in indexes) + foreach (IExifValue value in values) { - ExifValue value = this.values[index]; - if (value.Length > 4) + if (GetLength(value) > 4) { WriteUInt32((uint)(newOffset - startIndex), destination, this.dataOffsets[i++]); newOffset = this.WriteValue(value, destination, newOffset); @@ -282,25 +318,25 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return newOffset; } - private int WriteHeaders(List indexes, Span destination, int offset) + private int WriteHeaders(List values, Span destination, int offset) { this.dataOffsets = new List(); - int newOffset = WriteUInt16((ushort)indexes.Count, destination, offset); + int newOffset = WriteUInt16((ushort)values.Count, destination, offset); - if (indexes.Count == 0) + if (values.Count == 0) { return newOffset; } - foreach (int index in indexes) + foreach (IExifValue value in values) { - ExifValue value = this.values[index]; newOffset = WriteUInt16((ushort)value.Tag, destination, newOffset); newOffset = WriteUInt16((ushort)value.DataType, destination, newOffset); - newOffset = WriteUInt32((uint)value.NumberOfComponents, destination, newOffset); + newOffset = WriteUInt32(GetNumberOfComponents(value), destination, newOffset); - if (value.Length > 4) + uint length = GetLength(value); + if (length > 4) { this.dataOffsets.Add(newOffset); } @@ -332,7 +368,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif switch (dataType) { case ExifDataType.Ascii: - return Write(Encoding.UTF8.GetBytes((string)value), destination, offset); + offset = Write(Encoding.UTF8.GetBytes((string)value), destination, offset); + destination[offset] = 0; + return offset + 1; case ExifDataType.Byte: case ExifDataType.Undefined: destination[offset] = (byte)value; @@ -340,8 +378,18 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif case ExifDataType.DoubleFloat: return WriteDouble((double)value, destination, offset); case ExifDataType.Short: + if (value is Number shortNumber) + { + return WriteUInt16((ushort)shortNumber, destination, offset); + } + return WriteUInt16((ushort)value, destination, offset); case ExifDataType.Long: + if (value is Number longNumber) + { + return WriteUInt32((uint)longNumber, destination, offset); + } + return WriteUInt32((uint)value, destination, offset); case ExifDataType.Rational: WriteRational(destination.Slice(offset, 8), (Rational)value); @@ -363,14 +411,14 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif } } - private int WriteValue(ExifValue value, Span destination, int offset) + private int WriteValue(IExifValue value, Span destination, int offset) { if (value.IsArray && value.DataType != ExifDataType.Ascii) { return this.WriteArray(value, destination, offset); } - return this.WriteValue(value.DataType, value.Value, destination, offset); + return this.WriteValue(value.DataType, value.GetValue(), destination, offset); } } } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/README.md b/src/ImageSharp/Metadata/Profiles/Exif/README.md index b6e27b70c..7901527e1 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/README.md +++ b/src/ImageSharp/Metadata/Profiles/Exif/README.md @@ -1,3 +1,3 @@ -Adapted from Magick.NET: +Adapted from Magick.NET: -https://github.com/dlemstra/Magick.NET/tree/784e23b1f5c824fc03d4b95d3387b3efe1ed510b/Magick.NET/Core/Profiles/Exif \ No newline at end of file +https://github.com/dlemstra/Magick.NET diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Byte.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Byte.cs new file mode 100644 index 000000000..dc33fd8b0 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Byte.cs @@ -0,0 +1,24 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + public abstract partial class ExifTag + { + /// + /// Gets the FaxProfile exif tag. + /// + public static ExifTag FaxProfile { get; } = new ExifTag(ExifTagValue.FaxProfile); + + /// + /// Gets the ModeNumber exif tag. + /// + public static ExifTag ModeNumber { get; } = new ExifTag(ExifTagValue.ModeNumber); + + /// + /// Gets the GPSAltitudeRef exif tag. + /// + public static ExifTag GPSAltitudeRef { get; } = new ExifTag(ExifTagValue.GPSAltitudeRef); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.ByteArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.ByteArray.cs new file mode 100644 index 000000000..2bfa8ff21 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.ByteArray.cs @@ -0,0 +1,64 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + public abstract partial class ExifTag + { + /// + /// Gets the ClipPath exif tag. + /// + public static ExifTag ClipPath => new ExifTag(ExifTagValue.ClipPath); + + /// + /// Gets the VersionYear exif tag. + /// + public static ExifTag VersionYear => new ExifTag(ExifTagValue.VersionYear); + + /// + /// Gets the XMP exif tag. + /// + public static ExifTag XMP => new ExifTag(ExifTagValue.XMP); + + /// + /// Gets the CFAPattern2 exif tag. + /// + public static ExifTag CFAPattern2 => new ExifTag(ExifTagValue.CFAPattern2); + + /// + /// Gets the TIFFEPStandardID exif tag. + /// + public static ExifTag TIFFEPStandardID => new ExifTag(ExifTagValue.TIFFEPStandardID); + + /// + /// Gets the XPTitle exif tag. + /// + public static ExifTag XPTitle => new ExifTag(ExifTagValue.XPTitle); + + /// + /// Gets the XPComment exif tag. + /// + public static ExifTag XPComment => new ExifTag(ExifTagValue.XPComment); + + /// + /// Gets the XPAuthor exif tag. + /// + public static ExifTag XPAuthor => new ExifTag(ExifTagValue.XPAuthor); + + /// + /// Gets the XPKeywords exif tag. + /// + public static ExifTag XPKeywords => new ExifTag(ExifTagValue.XPKeywords); + + /// + /// Gets the XPSubject exif tag. + /// + public static ExifTag XPSubject => new ExifTag(ExifTagValue.XPSubject); + + /// + /// Gets the GPSVersionID exif tag. + /// + public static ExifTag GPSVersionID => new ExifTag(ExifTagValue.GPSVersionID); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.DoubleArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.DoubleArray.cs new file mode 100644 index 000000000..6cbae4c55 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.DoubleArray.cs @@ -0,0 +1,29 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + public abstract partial class ExifTag + { + /// + /// Gets the PixelScale exif tag. + /// + public static ExifTag PixelScale { get; } = new ExifTag(ExifTagValue.PixelScale); + + /// + /// Gets the IntergraphMatrix exif tag. + /// + public static ExifTag IntergraphMatrix { get; } = new ExifTag(ExifTagValue.IntergraphMatrix); + + /// + /// Gets the ModelTiePoint exif tag. + /// + public static ExifTag ModelTiePoint { get; } = new ExifTag(ExifTagValue.ModelTiePoint); + + /// + /// Gets the ModelTransform exif tag. + /// + public static ExifTag ModelTransform { get; } = new ExifTag(ExifTagValue.ModelTransform); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Long.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Long.cs new file mode 100644 index 000000000..571b50efb --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Long.cs @@ -0,0 +1,114 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + public abstract partial class ExifTag + { + /// + /// Gets the SubfileType exif tag. + /// + public static ExifTag SubfileType { get; } = new ExifTag(ExifTagValue.SubfileType); + + /// + /// Gets the SubIFDOffset exif tag. + /// + public static ExifTag SubIFDOffset { get; } = new ExifTag(ExifTagValue.SubIFDOffset); + + /// + /// Gets the GPSIFDOffset exif tag. + /// + public static ExifTag GPSIFDOffset { get; } = new ExifTag(ExifTagValue.GPSIFDOffset); + + /// + /// Gets the T4Options exif tag. + /// + public static ExifTag T4Options { get; } = new ExifTag(ExifTagValue.T4Options); + + /// + /// Gets the T6Options exif tag. + /// + public static ExifTag T6Options { get; } = new ExifTag(ExifTagValue.T6Options); + + /// + /// Gets the XClipPathUnits exif tag. + /// + public static ExifTag XClipPathUnits { get; } = new ExifTag(ExifTagValue.XClipPathUnits); + + /// + /// Gets the YClipPathUnits exif tag. + /// + public static ExifTag YClipPathUnits { get; } = new ExifTag(ExifTagValue.YClipPathUnits); + + /// + /// Gets the ProfileType exif tag. + /// + public static ExifTag ProfileType { get; } = new ExifTag(ExifTagValue.ProfileType); + + /// + /// Gets the CodingMethods exif tag. + /// + public static ExifTag CodingMethods { get; } = new ExifTag(ExifTagValue.CodingMethods); + + /// + /// Gets the T82ptions exif tag. + /// + public static ExifTag T82ptions { get; } = new ExifTag(ExifTagValue.T82ptions); + + /// + /// Gets the JPEGInterchangeFormat exif tag. + /// + public static ExifTag JPEGInterchangeFormat { get; } = new ExifTag(ExifTagValue.JPEGInterchangeFormat); + + /// + /// Gets the JPEGInterchangeFormatLength exif tag. + /// + public static ExifTag JPEGInterchangeFormatLength { get; } = new ExifTag(ExifTagValue.JPEGInterchangeFormatLength); + + /// + /// Gets the MDFileTag exif tag. + /// + public static ExifTag MDFileTag { get; } = new ExifTag(ExifTagValue.MDFileTag); + + /// + /// Gets the StandardOutputSensitivity exif tag. + /// + public static ExifTag StandardOutputSensitivity { get; } = new ExifTag(ExifTagValue.StandardOutputSensitivity); + + /// + /// Gets the RecommendedExposureIndex exif tag. + /// + public static ExifTag RecommendedExposureIndex { get; } = new ExifTag(ExifTagValue.RecommendedExposureIndex); + + /// + /// Gets the ISOSpeed exif tag. + /// + public static ExifTag ISOSpeed { get; } = new ExifTag(ExifTagValue.ISOSpeed); + + /// + /// Gets the ISOSpeedLatitudeyyy exif tag. + /// + public static ExifTag ISOSpeedLatitudeyyy { get; } = new ExifTag(ExifTagValue.ISOSpeedLatitudeyyy); + + /// + /// Gets the ISOSpeedLatitudezzz exif tag. + /// + public static ExifTag ISOSpeedLatitudezzz { get; } = new ExifTag(ExifTagValue.ISOSpeedLatitudezzz); + + /// + /// Gets the FaxRecvParams exif tag. + /// + public static ExifTag FaxRecvParams { get; } = new ExifTag(ExifTagValue.FaxRecvParams); + + /// + /// Gets the FaxRecvTime exif tag. + /// + public static ExifTag FaxRecvTime { get; } = new ExifTag(ExifTagValue.FaxRecvTime); + + /// + /// Gets the ImageNumber exif tag. + /// + public static ExifTag ImageNumber { get; } = new ExifTag(ExifTagValue.ImageNumber); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.LongArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.LongArray.cs new file mode 100644 index 000000000..120f2dab0 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.LongArray.cs @@ -0,0 +1,69 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + public abstract partial class ExifTag + { + /// + /// Gets the FreeOffsets exif tag. + /// + public static ExifTag FreeOffsets { get; } = new ExifTag(ExifTagValue.FreeOffsets); + + /// + /// Gets the FreeByteCounts exif tag. + /// + public static ExifTag FreeByteCounts { get; } = new ExifTag(ExifTagValue.FreeByteCounts); + + /// + /// Gets the ColorResponseUnit exif tag. + /// + public static ExifTag ColorResponseUnit { get; } = new ExifTag(ExifTagValue.ColorResponseUnit); + + /// + /// Gets the TileOffsets exif tag. + /// + public static ExifTag TileOffsets { get; } = new ExifTag(ExifTagValue.TileOffsets); + + /// + /// Gets the SMinSampleValue exif tag. + /// + public static ExifTag SMinSampleValue { get; } = new ExifTag(ExifTagValue.SMinSampleValue); + + /// + /// Gets the SMaxSampleValue exif tag. + /// + public static ExifTag SMaxSampleValue { get; } = new ExifTag(ExifTagValue.SMaxSampleValue); + + /// + /// Gets the JPEGQTables exif tag. + /// + public static ExifTag JPEGQTables { get; } = new ExifTag(ExifTagValue.JPEGQTables); + + /// + /// Gets the JPEGDCTables exif tag. + /// + public static ExifTag JPEGDCTables { get; } = new ExifTag(ExifTagValue.JPEGDCTables); + + /// + /// Gets the JPEGACTables exif tag. + /// + public static ExifTag JPEGACTables { get; } = new ExifTag(ExifTagValue.JPEGACTables); + + /// + /// Gets the StripRowCounts exif tag. + /// + public static ExifTag StripRowCounts { get; } = new ExifTag(ExifTagValue.StripRowCounts); + + /// + /// Gets the IntergraphRegisters exif tag. + /// + public static ExifTag IntergraphRegisters { get; } = new ExifTag(ExifTagValue.IntergraphRegisters); + + /// + /// Gets the TimeZoneOffset exif tag. + /// + public static ExifTag TimeZoneOffset { get; } = new ExifTag(ExifTagValue.TimeZoneOffset); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Number.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Number.cs new file mode 100644 index 000000000..7f6be3c4d --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Number.cs @@ -0,0 +1,51 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Primitives; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + public abstract partial class ExifTag + { + /// + /// Gets the ImageWidth exif tag. + /// + public static ExifTag ImageWidth { get; } = new ExifTag(ExifTagValue.ImageWidth); + + /// + /// Gets the ImageLength exif tag. + /// + public static ExifTag ImageLength { get; } = new ExifTag(ExifTagValue.ImageLength); + + /// + /// Gets the TileWidth exif tag. + /// + public static ExifTag TileWidth { get; } = new ExifTag(ExifTagValue.TileWidth); + + /// + /// Gets the TileLength exif tag. + /// + public static ExifTag TileLength { get; } = new ExifTag(ExifTagValue.TileLength); + + /// + /// Gets the BadFaxLines exif tag. + /// + public static ExifTag BadFaxLines { get; } = new ExifTag(ExifTagValue.BadFaxLines); + + /// + /// Gets the ConsecutiveBadFaxLines exif tag. + /// + public static ExifTag ConsecutiveBadFaxLines { get; } = new ExifTag(ExifTagValue.ConsecutiveBadFaxLines); + + /// + /// Gets the PixelXDimension exif tag. + /// + public static ExifTag PixelXDimension { get; } = new ExifTag(ExifTagValue.PixelXDimension); + + /// + /// Gets the PixelYDimension exif tag. + /// + public static ExifTag PixelYDimension { get; } = new ExifTag(ExifTagValue.PixelYDimension); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.NumberArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.NumberArray.cs new file mode 100644 index 000000000..b4feba056 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.NumberArray.cs @@ -0,0 +1,26 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Primitives; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + public abstract partial class ExifTag + { + /// + /// Gets the StripOffsets exif tag. + /// + public static ExifTag StripOffsets { get; } = new ExifTag(ExifTagValue.StripOffsets); + + /// + /// Gets the TileByteCounts exif tag. + /// + public static ExifTag TileByteCounts { get; } = new ExifTag(ExifTagValue.TileByteCounts); + + /// + /// Gets the ImageLayer exif tag. + /// + public static ExifTag ImageLayer { get; } = new ExifTag(ExifTagValue.ImageLayer); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Rational.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Rational.cs new file mode 100644 index 000000000..db52bbb22 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Rational.cs @@ -0,0 +1,176 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Primitives; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + public abstract partial class ExifTag + { + /// + /// Gets the XPosition exif tag. + /// + public static ExifTag XPosition { get; } = new ExifTag(ExifTagValue.XPosition); + + /// + /// Gets the YPosition exif tag. + /// + public static ExifTag YPosition { get; } = new ExifTag(ExifTagValue.YPosition); + + /// + /// Gets the XResolution exif tag. + /// + public static ExifTag XResolution { get; } = new ExifTag(ExifTagValue.XResolution); + + /// + /// Gets the YResolution exif tag. + /// + public static ExifTag YResolution { get; } = new ExifTag(ExifTagValue.YResolution); + + /// + /// Gets the BatteryLevel exif tag. + /// + public static ExifTag BatteryLevel { get; } = new ExifTag(ExifTagValue.BatteryLevel); + + /// + /// Gets the ExposureTime exif tag. + /// + public static ExifTag ExposureTime { get; } = new ExifTag(ExifTagValue.ExposureTime); + + /// + /// Gets the FNumber exif tag. + /// + public static ExifTag FNumber { get; } = new ExifTag(ExifTagValue.FNumber); + + /// + /// Gets the MDScalePixel exif tag. + /// + public static ExifTag MDScalePixel { get; } = new ExifTag(ExifTagValue.MDScalePixel); + + /// + /// Gets the CompressedBitsPerPixel exif tag. + /// + public static ExifTag CompressedBitsPerPixel { get; } = new ExifTag(ExifTagValue.CompressedBitsPerPixel); + + /// + /// Gets the ApertureValue exif tag. + /// + public static ExifTag ApertureValue { get; } = new ExifTag(ExifTagValue.ApertureValue); + + /// + /// Gets the MaxApertureValue exif tag. + /// + public static ExifTag MaxApertureValue { get; } = new ExifTag(ExifTagValue.MaxApertureValue); + + /// + /// Gets the SubjectDistance exif tag. + /// + public static ExifTag SubjectDistance { get; } = new ExifTag(ExifTagValue.SubjectDistance); + + /// + /// Gets the FocalLength exif tag. + /// + public static ExifTag FocalLength { get; } = new ExifTag(ExifTagValue.FocalLength); + + /// + /// Gets the FlashEnergy2 exif tag. + /// + public static ExifTag FlashEnergy2 { get; } = new ExifTag(ExifTagValue.FlashEnergy2); + + /// + /// Gets the FocalPlaneXResolution2 exif tag. + /// + public static ExifTag FocalPlaneXResolution2 { get; } = new ExifTag(ExifTagValue.FocalPlaneXResolution2); + + /// + /// Gets the FocalPlaneYResolution2 exif tag. + /// + public static ExifTag FocalPlaneYResolution2 { get; } = new ExifTag(ExifTagValue.FocalPlaneYResolution2); + + /// + /// Gets the ExposureIndex2 exif tag. + /// + public static ExifTag ExposureIndex2 { get; } = new ExifTag(ExifTagValue.ExposureIndex2); + + /// + /// Gets the Humidity exif tag. + /// + public static ExifTag Humidity { get; } = new ExifTag(ExifTagValue.Humidity); + + /// + /// Gets the Pressure exif tag. + /// + public static ExifTag Pressure { get; } = new ExifTag(ExifTagValue.Pressure); + + /// + /// Gets the Acceleration exif tag. + /// + public static ExifTag Acceleration { get; } = new ExifTag(ExifTagValue.Acceleration); + + /// + /// Gets the FlashEnergy exif tag. + /// + public static ExifTag FlashEnergy { get; } = new ExifTag(ExifTagValue.FlashEnergy); + + /// + /// Gets the FocalPlaneXResolution exif tag. + /// + public static ExifTag FocalPlaneXResolution { get; } = new ExifTag(ExifTagValue.FocalPlaneXResolution); + + /// + /// Gets the FocalPlaneYResolution exif tag. + /// + public static ExifTag FocalPlaneYResolution { get; } = new ExifTag(ExifTagValue.FocalPlaneYResolution); + + /// + /// Gets the ExposureIndex exif tag. + /// + public static ExifTag ExposureIndex { get; } = new ExifTag(ExifTagValue.ExposureIndex); + + /// + /// Gets the DigitalZoomRatio exif tag. + /// + public static ExifTag DigitalZoomRatio { get; } = new ExifTag(ExifTagValue.DigitalZoomRatio); + + /// + /// Gets the LensInfo exif tag. + /// + public static ExifTag LensInfo { get; } = new ExifTag(ExifTagValue.LensInfo); + + /// + /// Gets the GPSAltitude exif tag. + /// + public static ExifTag GPSAltitude { get; } = new ExifTag(ExifTagValue.GPSAltitude); + + /// + /// Gets the GPSDOP exif tag. + /// + public static ExifTag GPSDOP { get; } = new ExifTag(ExifTagValue.GPSDOP); + + /// + /// Gets the GPSSpeed exif tag. + /// + public static ExifTag GPSSpeed { get; } = new ExifTag(ExifTagValue.GPSSpeed); + + /// + /// Gets the GPSTrack exif tag. + /// + public static ExifTag GPSTrack { get; } = new ExifTag(ExifTagValue.GPSTrack); + + /// + /// Gets the GPSImgDirection exif tag. + /// + public static ExifTag GPSImgDirection { get; } = new ExifTag(ExifTagValue.GPSImgDirection); + + /// + /// Gets the GPSDestBearing exif tag. + /// + public static ExifTag GPSDestBearing { get; } = new ExifTag(ExifTagValue.GPSDestBearing); + + /// + /// Gets the GPSDestDistance exif tag. + /// + public static ExifTag GPSDestDistance { get; } = new ExifTag(ExifTagValue.GPSDestDistance); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.RationalArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.RationalArray.cs new file mode 100644 index 000000000..ece06c247 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.RationalArray.cs @@ -0,0 +1,56 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Primitives; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + public abstract partial class ExifTag + { + /// + /// Gets the WhitePoint exif tag. + /// + public static ExifTag WhitePoint { get; } = new ExifTag(ExifTagValue.WhitePoint); + + /// + /// Gets the PrimaryChromaticities exif tag. + /// + public static ExifTag PrimaryChromaticities { get; } = new ExifTag(ExifTagValue.PrimaryChromaticities); + + /// + /// Gets the YCbCrCoefficients exif tag. + /// + public static ExifTag YCbCrCoefficients { get; } = new ExifTag(ExifTagValue.YCbCrCoefficients); + + /// + /// Gets the ReferenceBlackWhite exif tag. + /// + public static ExifTag ReferenceBlackWhite { get; } = new ExifTag(ExifTagValue.ReferenceBlackWhite); + + /// + /// Gets the GPSLatitude exif tag. + /// + public static ExifTag GPSLatitude { get; } = new ExifTag(ExifTagValue.GPSLatitude); + + /// + /// Gets the GPSLongitude exif tag. + /// + public static ExifTag GPSLongitude { get; } = new ExifTag(ExifTagValue.GPSLongitude); + + /// + /// Gets the GPSTimestamp exif tag. + /// + public static ExifTag GPSTimestamp { get; } = new ExifTag(ExifTagValue.GPSTimestamp); + + /// + /// Gets the GPSDestLatitude exif tag. + /// + public static ExifTag GPSDestLatitude { get; } = new ExifTag(ExifTagValue.GPSDestLatitude); + + /// + /// Gets the GPSDestLongitude exif tag. + /// + public static ExifTag GPSDestLongitude { get; } = new ExifTag(ExifTagValue.GPSDestLongitude); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Short.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Short.cs new file mode 100644 index 000000000..7fe9a58bc --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Short.cs @@ -0,0 +1,239 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + public abstract partial class ExifTag + { + /// + /// Gets the OldSubfileType exif tag. + /// + public static ExifTag OldSubfileType { get; } = new ExifTag(ExifTagValue.OldSubfileType); + + /// + /// Gets the Compression exif tag. + /// + public static ExifTag Compression { get; } = new ExifTag(ExifTagValue.Compression); + + /// + /// Gets the PhotometricInterpretation exif tag. + /// + public static ExifTag PhotometricInterpretation { get; } = new ExifTag(ExifTagValue.PhotometricInterpretation); + + /// + /// Gets the Thresholding exif tag. + /// + public static ExifTag Thresholding { get; } = new ExifTag(ExifTagValue.Thresholding); + + /// + /// Gets the CellWidth exif tag. + /// + public static ExifTag CellWidth { get; } = new ExifTag(ExifTagValue.CellWidth); + + /// + /// Gets the CellLength exif tag. + /// + public static ExifTag CellLength { get; } = new ExifTag(ExifTagValue.CellLength); + + /// + /// Gets the FillOrder exif tag. + /// + public static ExifTag FillOrder { get; } = new ExifTag(ExifTagValue.FillOrder); + + /// + /// Gets the Orientation exif tag. + /// + public static ExifTag Orientation { get; } = new ExifTag(ExifTagValue.Orientation); + + /// + /// Gets the SamplesPerPixel exif tag. + /// + public static ExifTag SamplesPerPixel { get; } = new ExifTag(ExifTagValue.SamplesPerPixel); + + /// + /// Gets the PlanarConfiguration exif tag. + /// + public static ExifTag PlanarConfiguration { get; } = new ExifTag(ExifTagValue.PlanarConfiguration); + + /// + /// Gets the GrayResponseUnit exif tag. + /// + public static ExifTag GrayResponseUnit { get; } = new ExifTag(ExifTagValue.GrayResponseUnit); + + /// + /// Gets the ResolutionUnit exif tag. + /// + public static ExifTag ResolutionUnit { get; } = new ExifTag(ExifTagValue.ResolutionUnit); + + /// + /// Gets the CleanFaxData exif tag. + /// + public static ExifTag CleanFaxData { get; } = new ExifTag(ExifTagValue.CleanFaxData); + + /// + /// Gets the InkSet exif tag. + /// + public static ExifTag InkSet { get; } = new ExifTag(ExifTagValue.InkSet); + + /// + /// Gets the NumberOfInks exif tag. + /// + public static ExifTag NumberOfInks { get; } = new ExifTag(ExifTagValue.NumberOfInks); + + /// + /// Gets the DotRange exif tag. + /// + public static ExifTag DotRange { get; } = new ExifTag(ExifTagValue.DotRange); + + /// + /// Gets the Indexed exif tag. + /// + public static ExifTag Indexed { get; } = new ExifTag(ExifTagValue.Indexed); + + /// + /// Gets the OPIProxy exif tag. + /// + public static ExifTag OPIProxy { get; } = new ExifTag(ExifTagValue.OPIProxy); + + /// + /// Gets the JPEGProc exif tag. + /// + public static ExifTag JPEGProc { get; } = new ExifTag(ExifTagValue.JPEGProc); + + /// + /// Gets the JPEGRestartInterval exif tag. + /// + public static ExifTag JPEGRestartInterval { get; } = new ExifTag(ExifTagValue.JPEGRestartInterval); + + /// + /// Gets the YCbCrPositioning exif tag. + /// + public static ExifTag YCbCrPositioning { get; } = new ExifTag(ExifTagValue.YCbCrPositioning); + + /// + /// Gets the Rating exif tag. + /// + public static ExifTag Rating { get; } = new ExifTag(ExifTagValue.Rating); + + /// + /// Gets the RatingPercent exif tag. + /// + public static ExifTag RatingPercent { get; } = new ExifTag(ExifTagValue.RatingPercent); + + /// + /// Gets the ExposureProgram exif tag. + /// + public static ExifTag ExposureProgram { get; } = new ExifTag(ExifTagValue.ExposureProgram); + + /// + /// Gets the Interlace exif tag. + /// + public static ExifTag Interlace { get; } = new ExifTag(ExifTagValue.Interlace); + + /// + /// Gets the SelfTimerMode exif tag. + /// + public static ExifTag SelfTimerMode { get; } = new ExifTag(ExifTagValue.SelfTimerMode); + + /// + /// Gets the SensitivityType exif tag. + /// + public static ExifTag SensitivityType { get; } = new ExifTag(ExifTagValue.SensitivityType); + + /// + /// Gets the MeteringMode exif tag. + /// + public static ExifTag MeteringMode { get; } = new ExifTag(ExifTagValue.MeteringMode); + + /// + /// Gets the LightSource exif tag. + /// + public static ExifTag LightSource { get; } = new ExifTag(ExifTagValue.LightSource); + + /// + /// Gets the FocalPlaneResolutionUnit2 exif tag. + /// + public static ExifTag FocalPlaneResolutionUnit2 { get; } = new ExifTag(ExifTagValue.FocalPlaneResolutionUnit2); + + /// + /// Gets the SensingMethod2 exif tag. + /// + public static ExifTag SensingMethod2 { get; } = new ExifTag(ExifTagValue.SensingMethod2); + + /// + /// Gets the Flash exif tag. + /// + public static ExifTag Flash { get; } = new ExifTag(ExifTagValue.Flash); + + /// + /// Gets the ColorSpace exif tag. + /// + public static ExifTag ColorSpace { get; } = new ExifTag(ExifTagValue.ColorSpace); + + /// + /// Gets the FocalPlaneResolutionUnit exif tag. + /// + public static ExifTag FocalPlaneResolutionUnit { get; } = new ExifTag(ExifTagValue.FocalPlaneResolutionUnit); + + /// + /// Gets the SensingMethod exif tag. + /// + public static ExifTag SensingMethod { get; } = new ExifTag(ExifTagValue.SensingMethod); + + /// + /// Gets the CustomRendered exif tag. + /// + public static ExifTag CustomRendered { get; } = new ExifTag(ExifTagValue.CustomRendered); + + /// + /// Gets the ExposureMode exif tag. + /// + public static ExifTag ExposureMode { get; } = new ExifTag(ExifTagValue.ExposureMode); + + /// + /// Gets the WhiteBalance exif tag. + /// + public static ExifTag WhiteBalance { get; } = new ExifTag(ExifTagValue.WhiteBalance); + + /// + /// Gets the FocalLengthIn35mmFilm exif tag. + /// + public static ExifTag FocalLengthIn35mmFilm { get; } = new ExifTag(ExifTagValue.FocalLengthIn35mmFilm); + + /// + /// Gets the SceneCaptureType exif tag. + /// + public static ExifTag SceneCaptureType { get; } = new ExifTag(ExifTagValue.SceneCaptureType); + + /// + /// Gets the GainControl exif tag. + /// + public static ExifTag GainControl { get; } = new ExifTag(ExifTagValue.GainControl); + + /// + /// Gets the Contrast exif tag. + /// + public static ExifTag Contrast { get; } = new ExifTag(ExifTagValue.Contrast); + + /// + /// Gets the Saturation exif tag. + /// + public static ExifTag Saturation { get; } = new ExifTag(ExifTagValue.Saturation); + + /// + /// Gets the Sharpness exif tag. + /// + public static ExifTag Sharpness { get; } = new ExifTag(ExifTagValue.Sharpness); + + /// + /// Gets the SubjectDistanceRange exif tag. + /// + public static ExifTag SubjectDistanceRange { get; } = new ExifTag(ExifTagValue.SubjectDistanceRange); + + /// + /// Gets the GPSDifferential exif tag. + /// + public static ExifTag GPSDifferential { get; } = new ExifTag(ExifTagValue.GPSDifferential); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.ShortArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.ShortArray.cs new file mode 100644 index 000000000..90485f75a --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.ShortArray.cs @@ -0,0 +1,114 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + public abstract partial class ExifTag + { + /// + /// Gets the BitsPerSample exif tag. + /// + public static ExifTag BitsPerSample { get; } = new ExifTag(ExifTagValue.BitsPerSample); + + /// + /// Gets the MinSampleValue exif tag. + /// + public static ExifTag MinSampleValue { get; } = new ExifTag(ExifTagValue.MinSampleValue); + + /// + /// Gets the MaxSampleValue exif tag. + /// + public static ExifTag MaxSampleValue { get; } = new ExifTag(ExifTagValue.MaxSampleValue); + + /// + /// Gets the GrayResponseCurve exif tag. + /// + public static ExifTag GrayResponseCurve { get; } = new ExifTag(ExifTagValue.GrayResponseCurve); + + /// + /// Gets the ColorMap exif tag. + /// + public static ExifTag ColorMap { get; } = new ExifTag(ExifTagValue.ColorMap); + + /// + /// Gets the ExtraSamples exif tag. + /// + public static ExifTag ExtraSamples { get; } = new ExifTag(ExifTagValue.ExtraSamples); + + /// + /// Gets the PageNumber exif tag. + /// + public static ExifTag PageNumber { get; } = new ExifTag(ExifTagValue.PageNumber); + + /// + /// Gets the TransferFunction exif tag. + /// + public static ExifTag TransferFunction { get; } = new ExifTag(ExifTagValue.TransferFunction); + + /// + /// Gets the Predictor exif tag. + /// + public static ExifTag Predictor { get; } = new ExifTag(ExifTagValue.Predictor); + + /// + /// Gets the HalftoneHints exif tag. + /// + public static ExifTag HalftoneHints { get; } = new ExifTag(ExifTagValue.HalftoneHints); + + /// + /// Gets the SampleFormat exif tag. + /// + public static ExifTag SampleFormat { get; } = new ExifTag(ExifTagValue.SampleFormat); + + /// + /// Gets the TransferRange exif tag. + /// + public static ExifTag TransferRange { get; } = new ExifTag(ExifTagValue.TransferRange); + + /// + /// Gets the DefaultImageColor exif tag. + /// + public static ExifTag DefaultImageColor { get; } = new ExifTag(ExifTagValue.DefaultImageColor); + + /// + /// Gets the JPEGLosslessPredictors exif tag. + /// + public static ExifTag JPEGLosslessPredictors { get; } = new ExifTag(ExifTagValue.JPEGLosslessPredictors); + + /// + /// Gets the JPEGPointTransforms exif tag. + /// + public static ExifTag JPEGPointTransforms { get; } = new ExifTag(ExifTagValue.JPEGPointTransforms); + + /// + /// Gets the YCbCrSubsampling exif tag. + /// + public static ExifTag YCbCrSubsampling { get; } = new ExifTag(ExifTagValue.YCbCrSubsampling); + + /// + /// Gets the CFARepeatPatternDim exif tag. + /// + public static ExifTag CFARepeatPatternDim { get; } = new ExifTag(ExifTagValue.CFARepeatPatternDim); + + /// + /// Gets the IntergraphPacketData exif tag. + /// + public static ExifTag IntergraphPacketData { get; } = new ExifTag(ExifTagValue.IntergraphPacketData); + + /// + /// Gets the ISOSpeedRatings exif tag. + /// + public static ExifTag ISOSpeedRatings { get; } = new ExifTag(ExifTagValue.ISOSpeedRatings); + + /// + /// Gets the SubjectArea exif tag. + /// + public static ExifTag SubjectArea { get; } = new ExifTag(ExifTagValue.SubjectArea); + + /// + /// Gets the SubjectLocation exif tag. + /// + public static ExifTag SubjectLocation { get; } = new ExifTag(ExifTagValue.SubjectLocation); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.SignedRational.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.SignedRational.cs new file mode 100644 index 000000000..293ed7288 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.SignedRational.cs @@ -0,0 +1,41 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Primitives; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + public abstract partial class ExifTag + { + /// + /// Gets the ClipPath exif tag. + /// + public static ExifTag ShutterSpeedValue { get; } = new ExifTag(ExifTagValue.ShutterSpeedValue); + + /// + /// Gets the ClipPath exif tag. + /// + public static ExifTag BrightnessValue { get; } = new ExifTag(ExifTagValue.BrightnessValue); + + /// + /// Gets the ClipPath exif tag. + /// + public static ExifTag ExposureBiasValue { get; } = new ExifTag(ExifTagValue.ExposureBiasValue); + + /// + /// Gets the ClipPath exif tag. + /// + public static ExifTag AmbientTemperature { get; } = new ExifTag(ExifTagValue.AmbientTemperature); + + /// + /// Gets the ClipPath exif tag. + /// + public static ExifTag WaterDepth { get; } = new ExifTag(ExifTagValue.WaterDepth); + + /// + /// Gets the ClipPath exif tag. + /// + public static ExifTag CameraElevationAngle { get; } = new ExifTag(ExifTagValue.CameraElevationAngle); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.SignedRationalArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.SignedRationalArray.cs new file mode 100644 index 000000000..4022f5c1a --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.SignedRationalArray.cs @@ -0,0 +1,16 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Primitives; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + public abstract partial class ExifTag + { + /// + /// Gets the Decode exif tag. + /// + public static ExifTag Decode { get; } = new ExifTag(ExifTagValue.Decode); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.String.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.String.cs new file mode 100644 index 000000000..506f87454 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.String.cs @@ -0,0 +1,279 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + public abstract partial class ExifTag + { + /// + /// Gets the ImageDescription exif tag. + /// + public static ExifTag ImageDescription { get; } = new ExifTag(ExifTagValue.ImageDescription); + + /// + /// Gets the Make exif tag. + /// + public static ExifTag Make { get; } = new ExifTag(ExifTagValue.Make); + + /// + /// Gets the Model exif tag. + /// + public static ExifTag Model { get; } = new ExifTag(ExifTagValue.Model); + + /// + /// Gets the Software exif tag. + /// + public static ExifTag Software { get; } = new ExifTag(ExifTagValue.Software); + + /// + /// Gets the DateTime exif tag. + /// + public static ExifTag DateTime { get; } = new ExifTag(ExifTagValue.DateTime); + + /// + /// Gets the Artist exif tag. + /// + public static ExifTag Artist { get; } = new ExifTag(ExifTagValue.Artist); + + /// + /// Gets the HostComputer exif tag. + /// + public static ExifTag HostComputer { get; } = new ExifTag(ExifTagValue.HostComputer); + + /// + /// Gets the Copyright exif tag. + /// + public static ExifTag Copyright { get; } = new ExifTag(ExifTagValue.Copyright); + + /// + /// Gets the DocumentName exif tag. + /// + public static ExifTag DocumentName { get; } = new ExifTag(ExifTagValue.DocumentName); + + /// + /// Gets the PageName exif tag. + /// + public static ExifTag PageName { get; } = new ExifTag(ExifTagValue.PageName); + + /// + /// Gets the InkNames exif tag. + /// + public static ExifTag InkNames { get; } = new ExifTag(ExifTagValue.InkNames); + + /// + /// Gets the TargetPrinter exif tag. + /// + public static ExifTag TargetPrinter { get; } = new ExifTag(ExifTagValue.TargetPrinter); + + /// + /// Gets the ImageID exif tag. + /// + public static ExifTag ImageID { get; } = new ExifTag(ExifTagValue.ImageID); + + /// + /// Gets the MDLabName exif tag. + /// + public static ExifTag MDLabName { get; } = new ExifTag(ExifTagValue.MDLabName); + + /// + /// Gets the MDSampleInfo exif tag. + /// + public static ExifTag MDSampleInfo { get; } = new ExifTag(ExifTagValue.MDSampleInfo); + + /// + /// Gets the MDPrepDate exif tag. + /// + public static ExifTag MDPrepDate { get; } = new ExifTag(ExifTagValue.MDPrepDate); + + /// + /// Gets the MDPrepTime exif tag. + /// + public static ExifTag MDPrepTime { get; } = new ExifTag(ExifTagValue.MDPrepTime); + + /// + /// Gets the MDFileUnits exif tag. + /// + public static ExifTag MDFileUnits => new ExifTag(ExifTagValue.MDFileUnits); + + /// + /// Gets the SEMInfo exif tag. + /// + public static ExifTag SEMInfo { get; } = new ExifTag(ExifTagValue.SEMInfo); + + /// + /// Gets the SpectralSensitivity exif tag. + /// + public static ExifTag SpectralSensitivity { get; } = new ExifTag(ExifTagValue.SpectralSensitivity); + + /// + /// Gets the DateTimeOriginal exif tag. + /// + public static ExifTag DateTimeOriginal { get; } = new ExifTag(ExifTagValue.DateTimeOriginal); + + /// + /// Gets the DateTimeDigitized exif tag. + /// + public static ExifTag DateTimeDigitized { get; } = new ExifTag(ExifTagValue.DateTimeDigitized); + + /// + /// Gets the SubsecTime exif tag. + /// + public static ExifTag SubsecTime { get; } = new ExifTag(ExifTagValue.SubsecTime); + + /// + /// Gets the SubsecTimeOriginal exif tag. + /// + public static ExifTag SubsecTimeOriginal { get; } = new ExifTag(ExifTagValue.SubsecTimeOriginal); + + /// + /// Gets the SubsecTimeDigitized exif tag. + /// + public static ExifTag SubsecTimeDigitized { get; } = new ExifTag(ExifTagValue.SubsecTimeDigitized); + + /// + /// Gets the RelatedSoundFile exif tag. + /// + public static ExifTag RelatedSoundFile { get; } = new ExifTag(ExifTagValue.RelatedSoundFile); + + /// + /// Gets the FaxSubaddress exif tag. + /// + public static ExifTag FaxSubaddress { get; } = new ExifTag(ExifTagValue.FaxSubaddress); + + /// + /// Gets the OffsetTime exif tag. + /// + public static ExifTag OffsetTime { get; } = new ExifTag(ExifTagValue.OffsetTime); + + /// + /// Gets the OffsetTimeOriginal exif tag. + /// + public static ExifTag OffsetTimeOriginal { get; } = new ExifTag(ExifTagValue.OffsetTimeOriginal); + + /// + /// Gets the OffsetTimeDigitized exif tag. + /// + public static ExifTag OffsetTimeDigitized { get; } = new ExifTag(ExifTagValue.OffsetTimeDigitized); + + /// + /// Gets the SecurityClassification exif tag. + /// + public static ExifTag SecurityClassification { get; } = new ExifTag(ExifTagValue.SecurityClassification); + + /// + /// Gets the ImageHistory exif tag. + /// + public static ExifTag ImageHistory { get; } = new ExifTag(ExifTagValue.ImageHistory); + + /// + /// Gets the ImageUniqueID exif tag. + /// + public static ExifTag ImageUniqueID { get; } = new ExifTag(ExifTagValue.ImageUniqueID); + + /// + /// Gets the OwnerName exif tag. + /// + public static ExifTag OwnerName { get; } = new ExifTag(ExifTagValue.OwnerName); + + /// + /// Gets the SerialNumber exif tag. + /// + public static ExifTag SerialNumber { get; } = new ExifTag(ExifTagValue.SerialNumber); + + /// + /// Gets the LensMake exif tag. + /// + public static ExifTag LensMake { get; } = new ExifTag(ExifTagValue.LensMake); + + /// + /// Gets the LensModel exif tag. + /// + public static ExifTag LensModel { get; } = new ExifTag(ExifTagValue.LensModel); + + /// + /// Gets the LensSerialNumber exif tag. + /// + public static ExifTag LensSerialNumber { get; } = new ExifTag(ExifTagValue.LensSerialNumber); + + /// + /// Gets the GDALMetadata exif tag. + /// + public static ExifTag GDALMetadata { get; } = new ExifTag(ExifTagValue.GDALMetadata); + + /// + /// Gets the GDALNoData exif tag. + /// + public static ExifTag GDALNoData { get; } = new ExifTag(ExifTagValue.GDALNoData); + + /// + /// Gets the GPSLatitudeRef exif tag. + /// + public static ExifTag GPSLatitudeRef { get; } = new ExifTag(ExifTagValue.GPSLatitudeRef); + + /// + /// Gets the GPSLongitudeRef exif tag. + /// + public static ExifTag GPSLongitudeRef { get; } = new ExifTag(ExifTagValue.GPSLongitudeRef); + + /// + /// Gets the GPSSatellites exif tag. + /// + public static ExifTag GPSSatellites { get; } = new ExifTag(ExifTagValue.GPSSatellites); + + /// + /// Gets the GPSStatus exif tag. + /// + public static ExifTag GPSStatus { get; } = new ExifTag(ExifTagValue.GPSStatus); + + /// + /// Gets the GPSMeasureMode exif tag. + /// + public static ExifTag GPSMeasureMode { get; } = new ExifTag(ExifTagValue.GPSMeasureMode); + + /// + /// Gets the GPSSpeedRef exif tag. + /// + public static ExifTag GPSSpeedRef { get; } = new ExifTag(ExifTagValue.GPSSpeedRef); + + /// + /// Gets the GPSTrackRef exif tag. + /// + public static ExifTag GPSTrackRef { get; } = new ExifTag(ExifTagValue.GPSTrackRef); + + /// + /// Gets the GPSImgDirectionRef exif tag. + /// + public static ExifTag GPSImgDirectionRef { get; } = new ExifTag(ExifTagValue.GPSImgDirectionRef); + + /// + /// Gets the GPSMapDatum exif tag. + /// + public static ExifTag GPSMapDatum { get; } = new ExifTag(ExifTagValue.GPSMapDatum); + + /// + /// Gets the GPSDestLatitudeRef exif tag. + /// + public static ExifTag GPSDestLatitudeRef { get; } = new ExifTag(ExifTagValue.GPSDestLatitudeRef); + + /// + /// Gets the GPSDestLongitudeRef exif tag. + /// + public static ExifTag GPSDestLongitudeRef { get; } = new ExifTag(ExifTagValue.GPSDestLongitudeRef); + + /// + /// Gets the GPSDestBearingRef exif tag. + /// + public static ExifTag GPSDestBearingRef { get; } = new ExifTag(ExifTagValue.GPSDestBearingRef); + + /// + /// Gets the GPSDestDistanceRef exif tag. + /// + public static ExifTag GPSDestDistanceRef { get; } = new ExifTag(ExifTagValue.GPSDestDistanceRef); + + /// + /// Gets the GPSDateStamp exif tag. + /// + public static ExifTag GPSDateStamp { get; } = new ExifTag(ExifTagValue.GPSDateStamp); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Undefined.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Undefined.cs new file mode 100644 index 000000000..5f4841226 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Undefined.cs @@ -0,0 +1,94 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + public abstract partial class ExifTag + { + /// + /// Gets the JPEGTables exif tag. + /// + public static ExifTag JPEGTables { get; } = new ExifTag(ExifTagValue.JPEGTables); + + /// + /// Gets the OECF exif tag. + /// + public static ExifTag OECF { get; } = new ExifTag(ExifTagValue.OECF); + + /// + /// Gets the ExifVersion exif tag. + /// + public static ExifTag ExifVersion { get; } = new ExifTag(ExifTagValue.ExifVersion); + + /// + /// Gets the ComponentsConfiguration exif tag. + /// + public static ExifTag ComponentsConfiguration { get; } = new ExifTag(ExifTagValue.ComponentsConfiguration); + + /// + /// Gets the MakerNote exif tag. + /// + public static ExifTag MakerNote { get; } = new ExifTag(ExifTagValue.MakerNote); + + /// + /// Gets the UserComment exif tag. + /// + public static ExifTag UserComment { get; } = new ExifTag(ExifTagValue.UserComment); + + /// + /// Gets the FlashpixVersion exif tag. + /// + public static ExifTag FlashpixVersion { get; } = new ExifTag(ExifTagValue.FlashpixVersion); + + /// + /// Gets the SpatialFrequencyResponse exif tag. + /// + public static ExifTag SpatialFrequencyResponse { get; } = new ExifTag(ExifTagValue.SpatialFrequencyResponse); + + /// + /// Gets the SpatialFrequencyResponse2 exif tag. + /// + public static ExifTag SpatialFrequencyResponse2 { get; } = new ExifTag(ExifTagValue.SpatialFrequencyResponse2); + + /// + /// Gets the Noise exif tag. + /// + public static ExifTag Noise { get; } = new ExifTag(ExifTagValue.Noise); + + /// + /// Gets the CFAPattern exif tag. + /// + public static ExifTag CFAPattern { get; } = new ExifTag(ExifTagValue.CFAPattern); + + /// + /// Gets the DeviceSettingDescription exif tag. + /// + public static ExifTag DeviceSettingDescription { get; } = new ExifTag(ExifTagValue.DeviceSettingDescription); + + /// + /// Gets the ImageSourceData exif tag. + /// + public static ExifTag ImageSourceData { get; } = new ExifTag(ExifTagValue.ImageSourceData); + + /// + /// Gets the GPSProcessingMethod exif tag. + /// + public static ExifTag GPSProcessingMethod { get; } = new ExifTag(ExifTagValue.GPSProcessingMethod); + + /// + /// Gets the GPSAreaInformation exif tag. + /// + public static ExifTag GPSAreaInformation { get; } = new ExifTag(ExifTagValue.GPSAreaInformation); + + /// + /// Gets the FileSource exif tag. + /// + public static ExifTag FileSource { get; } = new ExifTag(ExifTagValue.FileSource); + + /// + /// Gets the ImageDescription exif tag. + /// + public static ExifTag SceneType { get; } = new ExifTag(ExifTagValue.SceneType); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.cs new file mode 100644 index 000000000..65e031462 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + /// Class that represents an exif tag from the Exif standard 2.31. + /// + public abstract partial class ExifTag : IEquatable + { + private readonly ushort value; + + internal ExifTag(ushort value) => this.value = value; + + /// + /// Converts the specified to a . + /// + /// The to convert. + public static explicit operator ushort(ExifTag tag) => tag?.value ?? (ushort)ExifTagValue.Unknown; + + /// + /// Determines whether the specified instances are considered equal. + /// + /// The first to compare. + /// The second to compare. + public static bool operator ==(ExifTag left, ExifTag right) => Equals(left, right); + + /// + /// Determines whether the specified instances are not considered equal. + /// + /// The first to compare. + /// The second to compare. + public static bool operator !=(ExifTag left, ExifTag right) => !Equals(left, right); + + /// + public override bool Equals(object obj) + { + if (obj is ExifTag value) + { + return this.Equals(value); + } + + return false; + } + + /// + public bool Equals(ExifTag other) + { + if (other is null) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + + return this.value == other.value; + } + + /// + public override int GetHashCode() => this.value.GetHashCode(); + + /// + public override string ToString() => ((ExifTagValue)this.value).ToString(); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifTag.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTagValue.cs similarity index 99% rename from src/ImageSharp/Metadata/Profiles/Exif/ExifTag.cs rename to src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTagValue.cs index ddd4591fa..7268762c6 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifTag.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTagValue.cs @@ -1,13 +1,12 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Metadata.Profiles.Exif { /// - /// All exif tags from the Exif standard 2.2 - /// Descriptions from: + /// All exif tags from the Exif standard 2.31. /// - public enum ExifTag + internal enum ExifTagValue { /// /// Unknown @@ -1539,6 +1538,6 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// /// GPSDifferential /// - GPSDifferential = 0x001E + GPSDifferential = 0x001E, } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag{TValueType}.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag{TValueType}.cs new file mode 100644 index 000000000..5a674277a --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag{TValueType}.cs @@ -0,0 +1,17 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + /// Class that represents an exif tag from the Exif standard 2.31 with as the data type of the tag. + /// + /// The data type of the tag. + public sealed class ExifTag : ExifTag + { + internal ExifTag(ExifTagValue value) + : base((ushort)value) + { + } + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/UnkownExifTag.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/UnkownExifTag.cs new file mode 100644 index 000000000..8f0b36638 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/UnkownExifTag.cs @@ -0,0 +1,13 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class UnkownExifTag : ExifTag + { + internal UnkownExifTag(ExifTagValue value) + : base((ushort)value) + { + } + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifArrayValue{TValueType}.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifArrayValue{TValueType}.cs new file mode 100644 index 000000000..263bf0934 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifArrayValue{TValueType}.cs @@ -0,0 +1,55 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal abstract class ExifArrayValue : ExifValue, IExifValue + { + protected ExifArrayValue(ExifTag tag) + : base(tag) + { + } + + protected ExifArrayValue(ExifTagValue tag) + : base(tag) + { + } + + internal ExifArrayValue(ExifArrayValue value) + : base(value) + { + } + + public override bool IsArray => true; + + public TValueType[] Value { get; set; } + + public override object GetValue() => this.Value; + + public override bool TrySetValue(object value) + { + if (value is null) + { + this.Value = null; + return true; + } + + Type type = value.GetType(); + if (value.GetType() == typeof(TValueType[])) + { + this.Value = (TValueType[])value; + return true; + } + + if (type == typeof(TValueType)) + { + this.Value = new TValueType[] { (TValueType)value }; + return true; + } + + return false; + } + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifByte.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifByte.cs new file mode 100644 index 000000000..184f4a07c --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifByte.cs @@ -0,0 +1,47 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Globalization; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifByte : ExifValue + { + public ExifByte(ExifTag tag, ExifDataType dataType) + : base(tag) => this.DataType = dataType; + + public ExifByte(ExifTagValue tag, ExifDataType dataType) + : base(tag) => this.DataType = dataType; + + private ExifByte(ExifByte value) + : base(value) => this.DataType = value.DataType; + + public override ExifDataType DataType { get; } + + protected override string StringValue => this.Value.ToString("X2", CultureInfo.InvariantCulture); + + public override bool TrySetValue(object value) + { + if (base.TrySetValue(value)) + { + return true; + } + + switch (value) + { + case int intValue: + if (intValue >= byte.MinValue && intValue <= byte.MaxValue) + { + this.Value = (byte)intValue; + return true; + } + + return false; + default: + return base.TrySetValue(value); + } + } + + public override IExifValue DeepClone() => new ExifByte(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifByteArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifByteArray.cs new file mode 100644 index 000000000..854eafc76 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifByteArray.cs @@ -0,0 +1,66 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifByteArray : ExifArrayValue + { + public ExifByteArray(ExifTag tag, ExifDataType dataType) + : base(tag) => this.DataType = dataType; + + public ExifByteArray(ExifTagValue tag, ExifDataType dataType) + : base(tag) => this.DataType = dataType; + + private ExifByteArray(ExifByteArray value) + : base(value) => this.DataType = value.DataType; + + public override ExifDataType DataType { get; } + + public override bool TrySetValue(object value) + { + if (base.TrySetValue(value)) + { + return true; + } + + if (value is int[] intArrayValue) + { + return this.TrySetSignedIntArray(intArrayValue); + } + + if (value is int intValue) + { + if (intValue >= byte.MinValue && intValue <= byte.MaxValue) + { + this.Value = new byte[] { (byte)intValue }; + } + + return true; + } + + return false; + } + + public override IExifValue DeepClone() => new ExifByteArray(this); + + private bool TrySetSignedIntArray(int[] intArrayValue) + { + if (Array.FindIndex(intArrayValue, x => x < byte.MinValue || x > byte.MaxValue) > -1) + { + return false; + } + + var value = new byte[intArrayValue.Length]; + for (int i = 0; i < intArrayValue.Length; i++) + { + int s = intArrayValue[i]; + value[i] = (byte)s; + } + + this.Value = value; + return true; + } + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifDouble.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifDouble.cs new file mode 100644 index 000000000..0af5f47ce --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifDouble.cs @@ -0,0 +1,48 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Globalization; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifDouble : ExifValue + { + public ExifDouble(ExifTag tag) + : base(tag) + { + } + + public ExifDouble(ExifTagValue tag) + : base(tag) + { + } + + private ExifDouble(ExifDouble value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.DoubleFloat; + + protected override string StringValue => this.Value.ToString(CultureInfo.InvariantCulture); + + public override bool TrySetValue(object value) + { + if (base.TrySetValue(value)) + { + return true; + } + + switch (value) + { + case int intValue: + this.Value = intValue; + return true; + default: + return false; + } + } + + public override IExifValue DeepClone() => new ExifDouble(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifDoubleArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifDoubleArray.cs new file mode 100644 index 000000000..259d5c98a --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifDoubleArray.cs @@ -0,0 +1,27 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifDoubleArray : ExifArrayValue + { + public ExifDoubleArray(ExifTag tag) + : base(tag) + { + } + + public ExifDoubleArray(ExifTagValue tag) + : base(tag) + { + } + + private ExifDoubleArray(ExifDoubleArray value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.DoubleFloat; + + public override IExifValue DeepClone() => new ExifDoubleArray(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifFloat.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifFloat.cs new file mode 100644 index 000000000..8d6c41f58 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifFloat.cs @@ -0,0 +1,43 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Globalization; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifFloat : ExifValue + { + public ExifFloat(ExifTagValue tag) + : base(tag) + { + } + + private ExifFloat(ExifFloat value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.SingleFloat; + + protected override string StringValue => this.Value.ToString(CultureInfo.InvariantCulture); + + public override bool TrySetValue(object value) + { + if (base.TrySetValue(value)) + { + return true; + } + + switch (value) + { + case int intValue: + this.Value = intValue; + return true; + default: + return false; + } + } + + public override IExifValue DeepClone() => new ExifFloat(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifFloatArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifFloatArray.cs new file mode 100644 index 000000000..7789bc3b5 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifFloatArray.cs @@ -0,0 +1,22 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifFloatArray : ExifArrayValue + { + public ExifFloatArray(ExifTagValue tag) + : base(tag) + { + } + + private ExifFloatArray(ExifFloatArray value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.SingleFloat; + + public override IExifValue DeepClone() => new ExifFloatArray(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong.cs new file mode 100644 index 000000000..7f2f631a9 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong.cs @@ -0,0 +1,53 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Globalization; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifLong : ExifValue + { + public ExifLong(ExifTag tag) + : base(tag) + { + } + + public ExifLong(ExifTagValue tag) + : base(tag) + { + } + + private ExifLong(ExifLong value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.Long; + + protected override string StringValue => this.Value.ToString(CultureInfo.InvariantCulture); + + public override bool TrySetValue(object value) + { + if (base.TrySetValue(value)) + { + return true; + } + + switch (value) + { + case int intValue: + if (intValue >= uint.MinValue) + { + this.Value = (uint)intValue; + return true; + } + + return false; + default: + return false; + } + } + + public override IExifValue DeepClone() => new ExifLong(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLongArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLongArray.cs new file mode 100644 index 000000000..e05f50bee --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLongArray.cs @@ -0,0 +1,27 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifLongArray : ExifArrayValue + { + public ExifLongArray(ExifTag tag) + : base(tag) + { + } + + public ExifLongArray(ExifTagValue tag) + : base(tag) + { + } + + private ExifLongArray(ExifLongArray value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.Long; + + public override IExifValue DeepClone() => new ExifLongArray(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumber.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumber.cs new file mode 100644 index 000000000..8d886d21c --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumber.cs @@ -0,0 +1,74 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Globalization; +using SixLabors.ImageSharp.Primitives; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifNumber : ExifValue + { + public ExifNumber(ExifTag tag) + : base(tag) + { + } + + private ExifNumber(ExifNumber value) + : base(value) + { + } + + public override ExifDataType DataType + { + get + { + if (this.Value > ushort.MaxValue) + { + return ExifDataType.Long; + } + + return ExifDataType.Short; + } + } + + protected override string StringValue => this.Value.ToString(CultureInfo.InvariantCulture); + + public override bool TrySetValue(object value) + { + if (base.TrySetValue(value)) + { + return true; + } + + 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 short shortValue: + if (shortValue >= uint.MinValue) + { + this.Value = (uint)shortValue; + return true; + } + + return false; + case ushort ushortValue: + this.Value = ushortValue; + return true; + default: + return false; + } + } + + public override IExifValue DeepClone() => new ExifNumber(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs new file mode 100644 index 000000000..a7ecf7bc8 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs @@ -0,0 +1,43 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Primitives; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifNumberArray : ExifArrayValue + { + public ExifNumberArray(ExifTag tag) + : base(tag) + { + } + + private ExifNumberArray(ExifNumberArray value) + : base(value) + { + } + + public override ExifDataType DataType + { + get + { + if (this.Value is null) + { + return ExifDataType.Short; + } + + for (int i = 0; i < this.Value.Length; i++) + { + if (this.Value[i] > ushort.MaxValue) + { + return ExifDataType.Long; + } + } + + return ExifDataType.Short; + } + } + + public override IExifValue DeepClone() => new ExifNumberArray(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifRational.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifRational.cs new file mode 100644 index 000000000..2806386b5 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifRational.cs @@ -0,0 +1,54 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Globalization; +using SixLabors.ImageSharp.Primitives; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifRational : ExifValue + { + public ExifRational(ExifTag tag) + : base(tag) + { + } + + public ExifRational(ExifTagValue tag) + : base(tag) + { + } + + private ExifRational(ExifRational value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.Rational; + + protected override string StringValue => this.Value.ToString(CultureInfo.InvariantCulture); + + public override bool TrySetValue(object value) + { + if (base.TrySetValue(value)) + { + return true; + } + + switch (value) + { + case SignedRational signed: + + if (signed.Numerator >= uint.MinValue && signed.Denominator >= uint.MinValue) + { + this.Value = new Rational((uint)signed.Numerator, (uint)signed.Denominator); + } + + return true; + default: + return false; + } + } + + public override IExifValue DeepClone() => new ExifRational(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifRationalArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifRationalArray.cs new file mode 100644 index 000000000..ae4fb0c57 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifRationalArray.cs @@ -0,0 +1,73 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.Primitives; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifRationalArray : ExifArrayValue + { + public ExifRationalArray(ExifTag tag) + : base(tag) + { + } + + public ExifRationalArray(ExifTagValue tag) + : base(tag) + { + } + + private ExifRationalArray(ExifRationalArray value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.Rational; + + public override bool TrySetValue(object value) + { + if (base.TrySetValue(value)) + { + return true; + } + + if (value is SignedRational[] signedArray) + { + return this.TrySetSignedArray(signedArray); + } + + if (value is SignedRational signed) + { + if (signed.Numerator >= 0 && signed.Denominator >= 0) + { + this.Value = new[] { new Rational((uint)signed.Numerator, (uint)signed.Denominator) }; + } + + return true; + } + + return false; + } + + public override IExifValue DeepClone() => new ExifRationalArray(this); + + private bool TrySetSignedArray(SignedRational[] signed) + { + if (Array.FindIndex(signed, x => x.Numerator < 0 || x.Denominator < 0) > -1) + { + return false; + } + + var unsigned = new Rational[signed.Length]; + for (int i = 0; i < signed.Length; i++) + { + SignedRational s = signed[i]; + unsigned[i] = new Rational((uint)s.Numerator, (uint)s.Denominator); + } + + this.Value = unsigned; + return true; + } + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifShort.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifShort.cs new file mode 100644 index 000000000..b11f3fc9f --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifShort.cs @@ -0,0 +1,61 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Globalization; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifShort : ExifValue + { + public ExifShort(ExifTag tag) + : base(tag) + { + } + + public ExifShort(ExifTagValue tag) + : base(tag) + { + } + + private ExifShort(ExifShort value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.Short; + + protected override string StringValue => this.Value.ToString(CultureInfo.InvariantCulture); + + public override bool TrySetValue(object value) + { + if (base.TrySetValue(value)) + { + return true; + } + + switch (value) + { + case int intValue: + if (intValue >= ushort.MinValue && intValue <= ushort.MaxValue) + { + this.Value = (ushort)intValue; + return true; + } + + return false; + case short shortValue: + if (shortValue >= ushort.MinValue) + { + this.Value = (ushort)shortValue; + return true; + } + + return false; + default: + return false; + } + } + + public override IExifValue DeepClone() => new ExifShort(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifShortArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifShortArray.cs new file mode 100644 index 000000000..379338c10 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifShortArray.cs @@ -0,0 +1,105 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifShortArray : ExifArrayValue + { + public ExifShortArray(ExifTag tag) + : base(tag) + { + } + + public ExifShortArray(ExifTagValue tag) + : base(tag) + { + } + + private ExifShortArray(ExifShortArray value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.Short; + + public override bool TrySetValue(object value) + { + if (base.TrySetValue(value)) + { + return true; + } + + if (value is int[] signedIntArray) + { + return this.TrySetSignedIntArray(signedIntArray); + } + + if (value is short[] signedShortArray) + { + return this.TrySetSignedShortArray(signedShortArray); + } + + if (value is int signedInt) + { + if (signedInt >= ushort.MinValue && signedInt <= ushort.MaxValue) + { + this.Value = new ushort[] { (ushort)signedInt }; + } + + return true; + } + + if (value is short signedShort) + { + if (signedShort >= ushort.MinValue) + { + this.Value = new ushort[] { (ushort)signedShort }; + } + + return true; + } + + return false; + } + + public override IExifValue DeepClone() => new ExifShortArray(this); + + private bool TrySetSignedIntArray(int[] signed) + { + if (Array.FindIndex(signed, x => x < ushort.MinValue || x > ushort.MaxValue) > -1) + { + return false; + } + + var unsigned = new ushort[signed.Length]; + for (int i = 0; i < signed.Length; i++) + { + int s = signed[i]; + unsigned[i] = (ushort)s; + } + + this.Value = unsigned; + return true; + } + + private bool TrySetSignedShortArray(short[] signed) + { + if (Array.FindIndex(signed, x => x < ushort.MinValue) > -1) + { + return false; + } + + var unsigned = new ushort[signed.Length]; + for (int i = 0; i < signed.Length; i++) + { + short s = signed[i]; + unsigned[i] = (ushort)s; + } + + this.Value = unsigned; + return true; + } + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedByte.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedByte.cs new file mode 100644 index 000000000..a9cb013ca --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedByte.cs @@ -0,0 +1,48 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Globalization; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifSignedByte : ExifValue + { + public ExifSignedByte(ExifTagValue tag) + : base(tag) + { + } + + private ExifSignedByte(ExifSignedByte value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.SignedByte; + + protected override string StringValue => this.Value.ToString("X2", CultureInfo.InvariantCulture); + + public override bool TrySetValue(object value) + { + if (base.TrySetValue(value)) + { + return true; + } + + switch (value) + { + case int intValue: + if (intValue >= sbyte.MinValue && intValue <= sbyte.MaxValue) + { + this.Value = (sbyte)intValue; + return true; + } + + return false; + default: + return false; + } + } + + public override IExifValue DeepClone() => new ExifSignedByte(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedByteArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedByteArray.cs new file mode 100644 index 000000000..b0d35cc8a --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedByteArray.cs @@ -0,0 +1,22 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifSignedByteArray : ExifArrayValue + { + public ExifSignedByteArray(ExifTagValue tag) + : base(tag) + { + } + + private ExifSignedByteArray(ExifSignedByteArray value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.SignedByte; + + public override IExifValue DeepClone() => new ExifSignedByteArray(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedLong.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedLong.cs new file mode 100644 index 000000000..c1e6808bf --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedLong.cs @@ -0,0 +1,26 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Globalization; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifSignedLong : ExifValue + { + public ExifSignedLong(ExifTagValue tag) + : base(tag) + { + } + + private ExifSignedLong(ExifSignedLong value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.SignedLong; + + protected override string StringValue => this.Value.ToString(CultureInfo.InvariantCulture); + + public override IExifValue DeepClone() => new ExifSignedLong(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedLongArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedLongArray.cs new file mode 100644 index 000000000..36d4c0007 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedLongArray.cs @@ -0,0 +1,22 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifSignedLongArray : ExifArrayValue + { + public ExifSignedLongArray(ExifTagValue tag) + : base(tag) + { + } + + private ExifSignedLongArray(ExifSignedLongArray value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.SignedLong; + + public override IExifValue DeepClone() => new ExifSignedLongArray(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedRational.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedRational.cs new file mode 100644 index 000000000..2a4051601 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedRational.cs @@ -0,0 +1,32 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Globalization; +using SixLabors.ImageSharp.Primitives; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifSignedRational : ExifValue + { + internal ExifSignedRational(ExifTag tag) + : base(tag) + { + } + + internal ExifSignedRational(ExifTagValue tag) + : base(tag) + { + } + + private ExifSignedRational(ExifSignedRational value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.SignedRational; + + protected override string StringValue => this.Value.ToString(CultureInfo.InvariantCulture); + + public override IExifValue DeepClone() => new ExifSignedRational(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedRationalArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedRationalArray.cs new file mode 100644 index 000000000..6c6bb0a4a --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedRationalArray.cs @@ -0,0 +1,29 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Primitives; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifSignedRationalArray : ExifArrayValue + { + public ExifSignedRationalArray(ExifTag tag) + : base(tag) + { + } + + public ExifSignedRationalArray(ExifTagValue tag) + : base(tag) + { + } + + private ExifSignedRationalArray(ExifSignedRationalArray value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.SignedRational; + + public override IExifValue DeepClone() => new ExifSignedRationalArray(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedShort.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedShort.cs new file mode 100644 index 000000000..e00f5c085 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedShort.cs @@ -0,0 +1,48 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Globalization; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifSignedShort : ExifValue + { + public ExifSignedShort(ExifTagValue tag) + : base(tag) + { + } + + private ExifSignedShort(ExifSignedShort value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.SignedShort; + + protected override string StringValue => this.Value.ToString(CultureInfo.InvariantCulture); + + public override bool TrySetValue(object value) + { + if (base.TrySetValue(value)) + { + return true; + } + + switch (value) + { + case int intValue: + if (intValue >= short.MinValue && intValue <= short.MaxValue) + { + this.Value = (short)intValue; + return true; + } + + return false; + default: + return false; + } + } + + public override IExifValue DeepClone() => new ExifSignedShort(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedShortArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedShortArray.cs new file mode 100644 index 000000000..403a50186 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedShortArray.cs @@ -0,0 +1,67 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifSignedShortArray : ExifArrayValue + { + public ExifSignedShortArray(ExifTagValue tag) + : base(tag) + { + } + + private ExifSignedShortArray(ExifSignedShortArray value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.SignedShort; + + public override bool TrySetValue(object value) + { + if (base.TrySetValue(value)) + { + return true; + } + + if (value is int[] intArray) + { + return this.TrySetSignedArray(intArray); + } + + if (value is int intValue) + { + if (intValue >= short.MinValue && intValue <= short.MaxValue) + { + this.Value = new short[] { (short)intValue }; + } + + return true; + } + + return false; + } + + public override IExifValue DeepClone() => new ExifSignedShortArray(this); + + private bool TrySetSignedArray(int[] intArray) + { + if (Array.FindIndex(intArray, x => x < short.MinValue || x > short.MaxValue) > -1) + { + return false; + } + + var value = new short[intArray.Length]; + for (int i = 0; i < intArray.Length; i++) + { + int s = intArray[i]; + value[i] = (short)s; + } + + this.Value = value; + return true; + } + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifString.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifString.cs new file mode 100644 index 000000000..0678bc3e4 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifString.cs @@ -0,0 +1,48 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Globalization; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifString : ExifValue + { + public ExifString(ExifTag tag) + : base(tag) + { + } + + public ExifString(ExifTagValue tag) + : base(tag) + { + } + + private ExifString(ExifString value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.Ascii; + + protected override string StringValue => this.Value; + + public override bool TrySetValue(object value) + { + if (base.TrySetValue(value)) + { + return true; + } + + switch (value) + { + case int intValue: + this.Value = intValue.ToString(CultureInfo.InvariantCulture); + return true; + default: + return false; + } + } + + public override IExifValue DeepClone() => new ExifString(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValue.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValue.cs new file mode 100644 index 000000000..547e099c9 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValue.cs @@ -0,0 +1,83 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal abstract class ExifValue : IExifValue, IEquatable + { + protected ExifValue(ExifTag tag) => this.Tag = tag; + + protected ExifValue(ExifTagValue tag) => this.Tag = new UnkownExifTag(tag); + + internal ExifValue(ExifValue other) + { + Guard.NotNull(other, nameof(other)); + + this.DataType = other.DataType; + this.IsArray = other.IsArray; + this.Tag = other.Tag; + + if (!other.IsArray) + { + // All types are value types except for string which is immutable so safe to simply assign. + this.TrySetValue(other.GetValue()); + } + else + { + // All array types are value types so Clone() is sufficient here. + var array = (Array)other.GetValue(); + this.TrySetValue(array.Clone()); + } + } + + public virtual ExifDataType DataType { get; } + + public virtual bool IsArray { get; } + + public ExifTag Tag { get; } + + public static bool operator ==(ExifValue left, ExifTag right) => Equals(left, right); + + public static bool operator !=(ExifValue left, ExifTag right) => !Equals(left, right); + + public override bool Equals(object obj) + { + if (obj is null) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj is ExifTag tag) + { + return this.Equals(tag); + } + + if (obj is ExifValue value) + { + return this.Tag.Equals(value.Tag) && Equals(this.GetValue(), value.GetValue()); + } + + return false; + } + + [MethodImpl(InliningOptions.ShortMethod)] + public bool Equals(ExifTag other) => this.Tag.Equals(other); + + [MethodImpl(InliningOptions.ShortMethod)] + public override int GetHashCode() => HashCode.Combine(this.Tag, this.GetValue()); + + public abstract object GetValue(); + + public abstract bool TrySetValue(object value); + + public abstract IExifValue DeepClone(); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs new file mode 100644 index 000000000..7b14ebc31 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs @@ -0,0 +1,307 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal static partial class ExifValues + { + public static ExifValue Create(ExifTagValue tag) => (ExifValue)CreateValue(tag); + + public static ExifValue Create(ExifTag tag) => (ExifValue)CreateValue((ExifTagValue)(ushort)tag); + + public static ExifValue Create(ExifTagValue tag, ExifDataType dataType, uint numberOfComponents) + { + bool isArray = numberOfComponents != 1; + + switch (dataType) + { + case ExifDataType.Byte: return isArray ? (ExifValue)new ExifByteArray(tag, dataType) : new ExifByte(tag, dataType); + 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.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.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); + case ExifDataType.Undefined: return isArray ? (ExifValue)new ExifByteArray(tag, dataType) : new ExifByte(tag, dataType); + default: return null; + } + } + + private static object CreateValue(ExifTagValue tag) + { + switch (tag) + { + case ExifTagValue.FaxProfile: return new ExifByte(ExifTag.FaxProfile, ExifDataType.Byte); + case ExifTagValue.ModeNumber: return new ExifByte(ExifTag.ModeNumber, ExifDataType.Byte); + case ExifTagValue.GPSAltitudeRef: return new ExifByte(ExifTag.GPSAltitudeRef, ExifDataType.Byte); + + case ExifTagValue.ClipPath: return new ExifByteArray(ExifTag.ClipPath, ExifDataType.Byte); + case ExifTagValue.VersionYear: return new ExifByteArray(ExifTag.VersionYear, ExifDataType.Byte); + case ExifTagValue.XMP: return new ExifByteArray(ExifTag.XMP, ExifDataType.Byte); + case ExifTagValue.CFAPattern2: return new ExifByteArray(ExifTag.CFAPattern2, ExifDataType.Byte); + case ExifTagValue.TIFFEPStandardID: return new ExifByteArray(ExifTag.TIFFEPStandardID, ExifDataType.Byte); + case ExifTagValue.XPTitle: return new ExifByteArray(ExifTag.XPTitle, ExifDataType.Byte); + case ExifTagValue.XPComment: return new ExifByteArray(ExifTag.XPComment, ExifDataType.Byte); + case ExifTagValue.XPAuthor: return new ExifByteArray(ExifTag.XPAuthor, ExifDataType.Byte); + case ExifTagValue.XPKeywords: return new ExifByteArray(ExifTag.XPKeywords, ExifDataType.Byte); + case ExifTagValue.XPSubject: return new ExifByteArray(ExifTag.XPSubject, ExifDataType.Byte); + case ExifTagValue.GPSVersionID: return new ExifByteArray(ExifTag.GPSVersionID, ExifDataType.Byte); + + case ExifTagValue.PixelScale: return new ExifDoubleArray(ExifTag.PixelScale); + case ExifTagValue.IntergraphMatrix: return new ExifDoubleArray(ExifTag.IntergraphMatrix); + case ExifTagValue.ModelTiePoint: return new ExifDoubleArray(ExifTag.ModelTiePoint); + case ExifTagValue.ModelTransform: return new ExifDoubleArray(ExifTag.ModelTransform); + + case ExifTagValue.SubfileType: return new ExifLong(ExifTag.SubfileType); + case ExifTagValue.SubIFDOffset: return new ExifLong(ExifTag.SubIFDOffset); + case ExifTagValue.GPSIFDOffset: return new ExifLong(ExifTag.GPSIFDOffset); + case ExifTagValue.T4Options: return new ExifLong(ExifTag.T4Options); + case ExifTagValue.T6Options: return new ExifLong(ExifTag.T6Options); + case ExifTagValue.XClipPathUnits: return new ExifLong(ExifTag.XClipPathUnits); + case ExifTagValue.YClipPathUnits: return new ExifLong(ExifTag.YClipPathUnits); + case ExifTagValue.ProfileType: return new ExifLong(ExifTag.ProfileType); + case ExifTagValue.CodingMethods: return new ExifLong(ExifTag.CodingMethods); + case ExifTagValue.T82ptions: return new ExifLong(ExifTag.T82ptions); + case ExifTagValue.JPEGInterchangeFormat: return new ExifLong(ExifTag.JPEGInterchangeFormat); + case ExifTagValue.JPEGInterchangeFormatLength: return new ExifLong(ExifTag.JPEGInterchangeFormatLength); + case ExifTagValue.MDFileTag: return new ExifLong(ExifTag.MDFileTag); + case ExifTagValue.StandardOutputSensitivity: return new ExifLong(ExifTag.StandardOutputSensitivity); + case ExifTagValue.RecommendedExposureIndex: return new ExifLong(ExifTag.RecommendedExposureIndex); + case ExifTagValue.ISOSpeed: return new ExifLong(ExifTag.ISOSpeed); + case ExifTagValue.ISOSpeedLatitudeyyy: return new ExifLong(ExifTag.ISOSpeedLatitudeyyy); + case ExifTagValue.ISOSpeedLatitudezzz: return new ExifLong(ExifTag.ISOSpeedLatitudezzz); + case ExifTagValue.FaxRecvParams: return new ExifLong(ExifTag.FaxRecvParams); + case ExifTagValue.FaxRecvTime: return new ExifLong(ExifTag.FaxRecvTime); + case ExifTagValue.ImageNumber: return new ExifLong(ExifTag.ImageNumber); + + case ExifTagValue.FreeOffsets: return new ExifLongArray(ExifTag.FreeOffsets); + case ExifTagValue.FreeByteCounts: return new ExifLongArray(ExifTag.FreeByteCounts); + case ExifTagValue.ColorResponseUnit: return new ExifLongArray(ExifTag.TileOffsets); + case ExifTagValue.TileOffsets: return new ExifLongArray(ExifTag.TileOffsets); + case ExifTagValue.SMinSampleValue: return new ExifLongArray(ExifTag.SMinSampleValue); + case ExifTagValue.SMaxSampleValue: return new ExifLongArray(ExifTag.SMaxSampleValue); + case ExifTagValue.JPEGQTables: return new ExifLongArray(ExifTag.JPEGQTables); + case ExifTagValue.JPEGDCTables: return new ExifLongArray(ExifTag.JPEGDCTables); + case ExifTagValue.JPEGACTables: return new ExifLongArray(ExifTag.JPEGACTables); + 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.ImageWidth: return new ExifNumber(ExifTag.ImageWidth); + case ExifTagValue.ImageLength: return new ExifNumber(ExifTag.ImageLength); + case ExifTagValue.TileWidth: return new ExifNumber(ExifTag.TileWidth); + case ExifTagValue.TileLength: return new ExifNumber(ExifTag.TileLength); + case ExifTagValue.BadFaxLines: return new ExifNumber(ExifTag.BadFaxLines); + case ExifTagValue.ConsecutiveBadFaxLines: return new ExifNumber(ExifTag.ConsecutiveBadFaxLines); + case ExifTagValue.PixelXDimension: return new ExifNumber(ExifTag.PixelXDimension); + case ExifTagValue.PixelYDimension: return new ExifNumber(ExifTag.PixelYDimension); + + case ExifTagValue.StripOffsets: return new ExifNumberArray(ExifTag.StripOffsets); + case ExifTagValue.TileByteCounts: return new ExifNumberArray(ExifTag.TileByteCounts); + case ExifTagValue.ImageLayer: return new ExifNumberArray(ExifTag.ImageLayer); + + case ExifTagValue.XPosition: return new ExifRational(ExifTag.XPosition); + case ExifTagValue.YPosition: return new ExifRational(ExifTag.YPosition); + case ExifTagValue.XResolution: return new ExifRational(ExifTag.XResolution); + case ExifTagValue.YResolution: return new ExifRational(ExifTag.YResolution); + case ExifTagValue.BatteryLevel: return new ExifRational(ExifTag.BatteryLevel); + case ExifTagValue.ExposureTime: return new ExifRational(ExifTag.ExposureTime); + case ExifTagValue.FNumber: return new ExifRational(ExifTag.FNumber); + case ExifTagValue.MDScalePixel: return new ExifRational(ExifTag.MDScalePixel); + case ExifTagValue.CompressedBitsPerPixel: return new ExifRational(ExifTag.CompressedBitsPerPixel); + case ExifTagValue.ApertureValue: return new ExifRational(ExifTag.ApertureValue); + case ExifTagValue.MaxApertureValue: return new ExifRational(ExifTag.MaxApertureValue); + case ExifTagValue.SubjectDistance: return new ExifRational(ExifTag.SubjectDistance); + case ExifTagValue.FocalLength: return new ExifRational(ExifTag.FocalLength); + case ExifTagValue.FlashEnergy2: return new ExifRational(ExifTag.FlashEnergy2); + case ExifTagValue.FocalPlaneXResolution2: return new ExifRational(ExifTag.FocalPlaneXResolution2); + case ExifTagValue.FocalPlaneYResolution2: return new ExifRational(ExifTag.FocalPlaneYResolution2); + case ExifTagValue.ExposureIndex2: return new ExifRational(ExifTag.ExposureIndex2); + case ExifTagValue.Humidity: return new ExifRational(ExifTag.Humidity); + case ExifTagValue.Pressure: return new ExifRational(ExifTag.Pressure); + case ExifTagValue.Acceleration: return new ExifRational(ExifTag.Acceleration); + case ExifTagValue.FlashEnergy: return new ExifRational(ExifTag.FlashEnergy); + case ExifTagValue.FocalPlaneXResolution: return new ExifRational(ExifTag.FocalPlaneXResolution); + case ExifTagValue.FocalPlaneYResolution: return new ExifRational(ExifTag.FocalPlaneYResolution); + case ExifTagValue.ExposureIndex: return new ExifRational(ExifTag.ExposureIndex); + case ExifTagValue.DigitalZoomRatio: return new ExifRational(ExifTag.DigitalZoomRatio); + case ExifTagValue.LensInfo: return new ExifRational(ExifTag.LensInfo); + case ExifTagValue.GPSAltitude: return new ExifRational(ExifTag.GPSAltitude); + case ExifTagValue.GPSDOP: return new ExifRational(ExifTag.GPSDOP); + case ExifTagValue.GPSSpeed: return new ExifRational(ExifTag.GPSSpeed); + case ExifTagValue.GPSTrack: return new ExifRational(ExifTag.GPSTrack); + case ExifTagValue.GPSImgDirection: return new ExifRational(ExifTag.GPSImgDirection); + case ExifTagValue.GPSDestBearing: return new ExifRational(ExifTag.GPSDestBearing); + case ExifTagValue.GPSDestDistance: return new ExifRational(ExifTag.GPSDestDistance); + + case ExifTagValue.WhitePoint: return new ExifRationalArray(ExifTag.WhitePoint); + case ExifTagValue.PrimaryChromaticities: return new ExifRationalArray(ExifTag.PrimaryChromaticities); + case ExifTagValue.YCbCrCoefficients: return new ExifRationalArray(ExifTag.YCbCrCoefficients); + case ExifTagValue.ReferenceBlackWhite: return new ExifRationalArray(ExifTag.ReferenceBlackWhite); + case ExifTagValue.GPSLatitude: return new ExifRationalArray(ExifTag.GPSLatitude); + case ExifTagValue.GPSLongitude: return new ExifRationalArray(ExifTag.GPSLongitude); + case ExifTagValue.GPSTimestamp: return new ExifRationalArray(ExifTag.GPSTimestamp); + case ExifTagValue.GPSDestLatitude: return new ExifRationalArray(ExifTag.GPSDestLatitude); + case ExifTagValue.GPSDestLongitude: return new ExifRationalArray(ExifTag.GPSDestLongitude); + + case ExifTagValue.OldSubfileType: return new ExifShort(ExifTag.OldSubfileType); + case ExifTagValue.Compression: return new ExifShort(ExifTag.Compression); + case ExifTagValue.PhotometricInterpretation: return new ExifShort(ExifTag.PhotometricInterpretation); + case ExifTagValue.Thresholding: return new ExifShort(ExifTag.Thresholding); + case ExifTagValue.CellWidth: return new ExifShort(ExifTag.CellWidth); + case ExifTagValue.CellLength: return new ExifShort(ExifTag.CellLength); + case ExifTagValue.FillOrder: return new ExifShort(ExifTag.FillOrder); + case ExifTagValue.Orientation: return new ExifShort(ExifTag.Orientation); + case ExifTagValue.SamplesPerPixel: return new ExifShort(ExifTag.SamplesPerPixel); + case ExifTagValue.PlanarConfiguration: return new ExifShort(ExifTag.PlanarConfiguration); + case ExifTagValue.GrayResponseUnit: return new ExifShort(ExifTag.GrayResponseUnit); + case ExifTagValue.ResolutionUnit: return new ExifShort(ExifTag.ResolutionUnit); + case ExifTagValue.CleanFaxData: return new ExifShort(ExifTag.CleanFaxData); + case ExifTagValue.InkSet: return new ExifShort(ExifTag.InkSet); + case ExifTagValue.NumberOfInks: return new ExifShort(ExifTag.NumberOfInks); + case ExifTagValue.DotRange: return new ExifShort(ExifTag.DotRange); + case ExifTagValue.Indexed: return new ExifShort(ExifTag.Indexed); + case ExifTagValue.OPIProxy: return new ExifShort(ExifTag.OPIProxy); + case ExifTagValue.JPEGProc: return new ExifShort(ExifTag.JPEGProc); + case ExifTagValue.JPEGRestartInterval: return new ExifShort(ExifTag.JPEGRestartInterval); + case ExifTagValue.YCbCrPositioning: return new ExifShort(ExifTag.YCbCrPositioning); + case ExifTagValue.Rating: return new ExifShort(ExifTag.Rating); + case ExifTagValue.RatingPercent: return new ExifShort(ExifTag.RatingPercent); + case ExifTagValue.ExposureProgram: return new ExifShort(ExifTag.ExposureProgram); + case ExifTagValue.Interlace: return new ExifShort(ExifTag.Interlace); + case ExifTagValue.SelfTimerMode: return new ExifShort(ExifTag.SelfTimerMode); + case ExifTagValue.SensitivityType: return new ExifShort(ExifTag.SensitivityType); + case ExifTagValue.MeteringMode: return new ExifShort(ExifTag.MeteringMode); + case ExifTagValue.LightSource: return new ExifShort(ExifTag.LightSource); + case ExifTagValue.FocalPlaneResolutionUnit2: return new ExifShort(ExifTag.FocalPlaneResolutionUnit2); + case ExifTagValue.SensingMethod2: return new ExifShort(ExifTag.SensingMethod2); + case ExifTagValue.Flash: return new ExifShort(ExifTag.Flash); + case ExifTagValue.ColorSpace: return new ExifShort(ExifTag.ColorSpace); + case ExifTagValue.FocalPlaneResolutionUnit: return new ExifShort(ExifTag.FocalPlaneResolutionUnit); + case ExifTagValue.SensingMethod: return new ExifShort(ExifTag.SensingMethod); + case ExifTagValue.CustomRendered: return new ExifShort(ExifTag.CustomRendered); + case ExifTagValue.ExposureMode: return new ExifShort(ExifTag.ExposureMode); + case ExifTagValue.WhiteBalance: return new ExifShort(ExifTag.WhiteBalance); + case ExifTagValue.FocalLengthIn35mmFilm: return new ExifShort(ExifTag.FocalLengthIn35mmFilm); + case ExifTagValue.SceneCaptureType: return new ExifShort(ExifTag.SceneCaptureType); + case ExifTagValue.GainControl: return new ExifShort(ExifTag.GainControl); + case ExifTagValue.Contrast: return new ExifShort(ExifTag.Contrast); + case ExifTagValue.Saturation: return new ExifShort(ExifTag.Saturation); + case ExifTagValue.Sharpness: return new ExifShort(ExifTag.Sharpness); + case ExifTagValue.SubjectDistanceRange: return new ExifShort(ExifTag.SubjectDistanceRange); + case ExifTagValue.GPSDifferential: return new ExifShort(ExifTag.GPSDifferential); + + case ExifTagValue.BitsPerSample: return new ExifShortArray(ExifTag.BitsPerSample); + case ExifTagValue.MinSampleValue: return new ExifShortArray(ExifTag.MinSampleValue); + case ExifTagValue.MaxSampleValue: return new ExifShortArray(ExifTag.MaxSampleValue); + case ExifTagValue.GrayResponseCurve: return new ExifShortArray(ExifTag.GrayResponseCurve); + case ExifTagValue.ColorMap: return new ExifShortArray(ExifTag.ColorMap); + case ExifTagValue.ExtraSamples: return new ExifShortArray(ExifTag.ExtraSamples); + case ExifTagValue.PageNumber: return new ExifShortArray(ExifTag.PageNumber); + case ExifTagValue.TransferFunction: return new ExifShortArray(ExifTag.TransferFunction); + case ExifTagValue.Predictor: return new ExifShortArray(ExifTag.Predictor); + case ExifTagValue.HalftoneHints: return new ExifShortArray(ExifTag.HalftoneHints); + case ExifTagValue.SampleFormat: return new ExifShortArray(ExifTag.SampleFormat); + case ExifTagValue.TransferRange: return new ExifShortArray(ExifTag.TransferRange); + case ExifTagValue.DefaultImageColor: return new ExifShortArray(ExifTag.DefaultImageColor); + case ExifTagValue.JPEGLosslessPredictors: return new ExifShortArray(ExifTag.JPEGLosslessPredictors); + case ExifTagValue.JPEGPointTransforms: return new ExifShortArray(ExifTag.JPEGPointTransforms); + case ExifTagValue.YCbCrSubsampling: return new ExifShortArray(ExifTag.YCbCrSubsampling); + case ExifTagValue.CFARepeatPatternDim: return new ExifShortArray(ExifTag.CFARepeatPatternDim); + case ExifTagValue.IntergraphPacketData: return new ExifShortArray(ExifTag.IntergraphPacketData); + case ExifTagValue.ISOSpeedRatings: return new ExifShortArray(ExifTag.ISOSpeedRatings); + case ExifTagValue.SubjectArea: return new ExifShortArray(ExifTag.SubjectArea); + case ExifTagValue.SubjectLocation: return new ExifShortArray(ExifTag.SubjectLocation); + + case ExifTagValue.ShutterSpeedValue: return new ExifSignedRational(ExifTag.ShutterSpeedValue); + case ExifTagValue.BrightnessValue: return new ExifSignedRational(ExifTag.BrightnessValue); + case ExifTagValue.ExposureBiasValue: return new ExifSignedRational(ExifTag.ExposureBiasValue); + case ExifTagValue.AmbientTemperature: return new ExifSignedRational(ExifTag.AmbientTemperature); + case ExifTagValue.WaterDepth: return new ExifSignedRational(ExifTag.WaterDepth); + case ExifTagValue.CameraElevationAngle: return new ExifSignedRational(ExifTag.CameraElevationAngle); + + case ExifTagValue.Decode: return new ExifSignedRationalArray(ExifTag.Decode); + + case ExifTagValue.ImageDescription: return new ExifString(ExifTag.ImageDescription); + case ExifTagValue.Make: return new ExifString(ExifTag.Make); + case ExifTagValue.Model: return new ExifString(ExifTag.Model); + case ExifTagValue.Software: return new ExifString(ExifTag.Software); + case ExifTagValue.DateTime: return new ExifString(ExifTag.DateTime); + case ExifTagValue.Artist: return new ExifString(ExifTag.Artist); + case ExifTagValue.HostComputer: return new ExifString(ExifTag.HostComputer); + case ExifTagValue.Copyright: return new ExifString(ExifTag.Copyright); + case ExifTagValue.DocumentName: return new ExifString(ExifTag.DocumentName); + case ExifTagValue.PageName: return new ExifString(ExifTag.PageName); + case ExifTagValue.InkNames: return new ExifString(ExifTag.InkNames); + case ExifTagValue.TargetPrinter: return new ExifString(ExifTag.TargetPrinter); + case ExifTagValue.ImageID: return new ExifString(ExifTag.ImageID); + case ExifTagValue.MDLabName: return new ExifString(ExifTag.MDLabName); + case ExifTagValue.MDSampleInfo: return new ExifString(ExifTag.MDSampleInfo); + case ExifTagValue.MDPrepDate: return new ExifString(ExifTag.MDPrepDate); + case ExifTagValue.MDPrepTime: return new ExifString(ExifTag.MDPrepTime); + case ExifTagValue.MDFileUnits: return new ExifString(ExifTag.MDFileUnits); + case ExifTagValue.SEMInfo: return new ExifString(ExifTag.SEMInfo); + case ExifTagValue.SpectralSensitivity: return new ExifString(ExifTag.SpectralSensitivity); + case ExifTagValue.DateTimeOriginal: return new ExifString(ExifTag.DateTimeOriginal); + case ExifTagValue.DateTimeDigitized: return new ExifString(ExifTag.DateTimeDigitized); + case ExifTagValue.SubsecTime: return new ExifString(ExifTag.SubsecTime); + case ExifTagValue.SubsecTimeOriginal: return new ExifString(ExifTag.SubsecTimeOriginal); + case ExifTagValue.SubsecTimeDigitized: return new ExifString(ExifTag.SubsecTimeDigitized); + case ExifTagValue.RelatedSoundFile: return new ExifString(ExifTag.RelatedSoundFile); + case ExifTagValue.FaxSubaddress: return new ExifString(ExifTag.FaxSubaddress); + case ExifTagValue.OffsetTime: return new ExifString(ExifTag.OffsetTime); + case ExifTagValue.OffsetTimeOriginal: return new ExifString(ExifTag.OffsetTimeOriginal); + case ExifTagValue.OffsetTimeDigitized: return new ExifString(ExifTag.OffsetTimeDigitized); + case ExifTagValue.SecurityClassification: return new ExifString(ExifTag.SecurityClassification); + case ExifTagValue.ImageHistory: return new ExifString(ExifTag.ImageHistory); + case ExifTagValue.ImageUniqueID: return new ExifString(ExifTag.ImageUniqueID); + case ExifTagValue.OwnerName: return new ExifString(ExifTag.OwnerName); + case ExifTagValue.SerialNumber: return new ExifString(ExifTag.SerialNumber); + case ExifTagValue.LensMake: return new ExifString(ExifTag.LensMake); + case ExifTagValue.LensModel: return new ExifString(ExifTag.LensModel); + case ExifTagValue.LensSerialNumber: return new ExifString(ExifTag.LensSerialNumber); + case ExifTagValue.GDALMetadata: return new ExifString(ExifTag.GDALMetadata); + case ExifTagValue.GDALNoData: return new ExifString(ExifTag.GDALNoData); + case ExifTagValue.GPSLatitudeRef: return new ExifString(ExifTag.GPSLatitudeRef); + case ExifTagValue.GPSLongitudeRef: return new ExifString(ExifTag.GPSLongitudeRef); + case ExifTagValue.GPSSatellites: return new ExifString(ExifTag.GPSSatellites); + case ExifTagValue.GPSStatus: return new ExifString(ExifTag.GPSStatus); + case ExifTagValue.GPSMeasureMode: return new ExifString(ExifTag.GPSMeasureMode); + case ExifTagValue.GPSSpeedRef: return new ExifString(ExifTag.GPSSpeedRef); + case ExifTagValue.GPSTrackRef: return new ExifString(ExifTag.GPSTrackRef); + case ExifTagValue.GPSImgDirectionRef: return new ExifString(ExifTag.GPSImgDirectionRef); + case ExifTagValue.GPSMapDatum: return new ExifString(ExifTag.GPSMapDatum); + case ExifTagValue.GPSDestLatitudeRef: return new ExifString(ExifTag.GPSDestLatitudeRef); + case ExifTagValue.GPSDestLongitudeRef: return new ExifString(ExifTag.GPSDestLongitudeRef); + case ExifTagValue.GPSDestBearingRef: return new ExifString(ExifTag.GPSDestBearingRef); + case ExifTagValue.GPSDestDistanceRef: return new ExifString(ExifTag.GPSDestDistanceRef); + case ExifTagValue.GPSDateStamp: return new ExifString(ExifTag.GPSDateStamp); + + case ExifTagValue.FileSource: return new ExifByte(ExifTag.FileSource, ExifDataType.Undefined); + case ExifTagValue.SceneType: return new ExifByte(ExifTag.SceneType, ExifDataType.Undefined); + + case ExifTagValue.JPEGTables: return new ExifByteArray(ExifTag.JPEGTables, ExifDataType.Undefined); + case ExifTagValue.OECF: return new ExifByteArray(ExifTag.OECF, ExifDataType.Undefined); + case ExifTagValue.ExifVersion: return new ExifByteArray(ExifTag.ExifVersion, ExifDataType.Undefined); + case ExifTagValue.ComponentsConfiguration: return new ExifByteArray(ExifTag.ComponentsConfiguration, ExifDataType.Undefined); + case ExifTagValue.MakerNote: return new ExifByteArray(ExifTag.MakerNote, ExifDataType.Undefined); + case ExifTagValue.UserComment: return new ExifByteArray(ExifTag.UserComment, ExifDataType.Undefined); + case ExifTagValue.FlashpixVersion: return new ExifByteArray(ExifTag.FlashpixVersion, ExifDataType.Undefined); + case ExifTagValue.SpatialFrequencyResponse: return new ExifByteArray(ExifTag.SpatialFrequencyResponse, ExifDataType.Undefined); + case ExifTagValue.SpatialFrequencyResponse2: return new ExifByteArray(ExifTag.SpatialFrequencyResponse2, ExifDataType.Undefined); + case ExifTagValue.Noise: return new ExifByteArray(ExifTag.Noise, ExifDataType.Undefined); + case ExifTagValue.CFAPattern: return new ExifByteArray(ExifTag.CFAPattern, ExifDataType.Undefined); + case ExifTagValue.DeviceSettingDescription: return new ExifByteArray(ExifTag.DeviceSettingDescription, ExifDataType.Undefined); + case ExifTagValue.ImageSourceData: return new ExifByteArray(ExifTag.ImageSourceData, ExifDataType.Undefined); + case ExifTagValue.GPSProcessingMethod: return new ExifByteArray(ExifTag.GPSProcessingMethod, ExifDataType.Undefined); + case ExifTagValue.GPSAreaInformation: return new ExifByteArray(ExifTag.GPSAreaInformation, ExifDataType.Undefined); + + default: return null; + } + } + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValue{TValueType}.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValue{TValueType}.cs new file mode 100644 index 000000000..601630af6 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValue{TValueType}.cs @@ -0,0 +1,62 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal abstract class ExifValue : ExifValue, IExifValue + { + protected ExifValue(ExifTag tag) + : base(tag) + { + } + + protected ExifValue(ExifTagValue tag) + : base(tag) + { + } + + internal ExifValue(ExifValue value) + : base(value) + { + } + + public TValueType Value { get; set; } + + /// + /// Gets the value of the current instance as a string. + /// + protected abstract string StringValue { get; } + + public override object GetValue() => this.Value; + + public override bool TrySetValue(object value) + { + if (value is null) + { + this.Value = default; + return true; + } + + // We use type comparison here over "is" to avoid compiler optimizations + // that equate short with ushort, and sbyte with byte. + if (value.GetType() == typeof(TValueType)) + { + this.Value = (TValueType)value; + return true; + } + + return false; + } + + public override string ToString() + { + if (this.Value == null) + { + return null; + } + + string description = ExifTagDescriptionAttribute.GetDescription(this.Tag, this.Value); + return description ?? this.StringValue; + } + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/IExifValue.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/IExifValue.cs new file mode 100644 index 000000000..50c421832 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/IExifValue.cs @@ -0,0 +1,39 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + /// A value of the exif profile. + /// + public interface IExifValue : IDeepCloneable + { + /// + /// Gets the data type of the exif value. + /// + ExifDataType DataType { get; } + + /// + /// Gets a value indicating whether the value is an array. + /// + bool IsArray { get; } + + /// + /// Gets the tag of the exif value. + /// + ExifTag Tag { get; } + + /// + /// Gets the value of this exif value. + /// + /// The value of this exif value. + object GetValue(); + + /// + /// Sets the value of this exif value. + /// + /// The value of this exif value. + /// A value indicating whether the value could be set. + bool TrySetValue(object value); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/IExifValue{TValueType}.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/IExifValue{TValueType}.cs new file mode 100644 index 000000000..72b93ddf9 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/IExifValue{TValueType}.cs @@ -0,0 +1,17 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + /// A value of the exif profile. + /// + /// The type of the value. + public interface IExifValue : IExifValue + { + /// + /// Gets or sets the value. + /// + TValueType Value { get; set; } + } +} diff --git a/src/ImageSharp/Primitives/Number.cs b/src/ImageSharp/Primitives/Number.cs new file mode 100644 index 000000000..88974e72b --- /dev/null +++ b/src/ImageSharp/Primitives/Number.cs @@ -0,0 +1,187 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Globalization; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.Primitives +{ + /// + /// Represents an integral number. + /// + [StructLayout(LayoutKind.Explicit)] + public struct Number : IEquatable, IComparable + { + [FieldOffset(0)] + private readonly int signedValue; + + [FieldOffset(0)] + private readonly uint unsignedValue; + + [FieldOffset(4)] + private readonly bool isSigned; + + /// + /// Initializes a new instance of the struct. + /// + /// The value of the number. + public Number(int value) + : this() + { + this.signedValue = value; + this.isSigned = true; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The value of the number. + 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(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(value); + + /// + /// Converts the specified to an instance of this type. + /// + /// The value. + public static implicit operator Number(ushort value) => new Number((uint)value); + + /// + /// Converts the specified to a . + /// + /// The to convert. + public static explicit operator int(Number number) + { + return number.isSigned + ? number.signedValue + : (int)number.unsignedValue.Clamp(0, int.MaxValue); + } + + /// + /// Converts the specified to a . + /// + /// The to convert. + public static explicit operator uint(Number number) + { + return number.isSigned + ? (uint)number.signedValue.Clamp(0, int.MaxValue) + : number.unsignedValue; + } + + /// + /// Converts the specified to a . + /// + /// The to convert. + public static explicit operator ushort(Number number) + { + return number.isSigned + ? (ushort)number.signedValue.Clamp(ushort.MinValue, ushort.MaxValue) + : (ushort)number.unsignedValue.Clamp(ushort.MinValue, ushort.MaxValue); + } + + /// + /// Determines whether the specified instances are considered equal. + /// + /// The first to compare. + /// The second to compare. + public static bool operator ==(Number left, Number right) => Equals(left, right); + + /// + /// Determines whether the specified instances are not considered equal. + /// + /// The first to compare. + /// The second to compare. + public static bool operator !=(Number left, Number right) => !Equals(left, right); + + /// + /// Determines whether the first is more than the second . + /// + /// The first to compare. + /// The second to compare. + public static bool operator >(Number left, Number right) => left.CompareTo(right) == 1; + + /// + /// Determines whether the first is less than the second . + /// + /// The first to compare. + /// The second to compare. + public static bool operator <(Number left, Number right) => left.CompareTo(right) == -1; + + /// + /// Determines whether the first is more than or equal to the second . + /// + /// The first to compare. + /// The second to compare. + public static bool operator >=(Number left, Number right) => left.CompareTo(right) >= 0; + + /// + /// Determines whether the first is less than or equal to the second . + /// + /// The first to compare. + /// The second to compare. + public static bool operator <=(Number left, Number right) => left.CompareTo(right) <= 0; + + /// + public int CompareTo(Number other) + { + return this.isSigned + ? this.signedValue.CompareTo(other.signedValue) + : this.unsignedValue.CompareTo(other.unsignedValue); + } + + /// + public override bool Equals(object obj) => obj is Number other && this.Equals(other); + + /// + public bool Equals(Number other) + { + if (this.isSigned != other.isSigned) + { + return false; + } + + return this.isSigned + ? this.signedValue.Equals(other.signedValue) + : this.unsignedValue.Equals(other.unsignedValue); + } + + /// + public override int GetHashCode() + { + return this.isSigned + ? this.signedValue.GetHashCode() + : this.unsignedValue.GetHashCode(); + } + + /// + public override string ToString() => this.ToString(CultureInfo.InvariantCulture); + + /// + /// Converts the numeric value of this instance to its equivalent string representation using the specified culture-specific format information. + /// + /// An object that supplies culture-specific formatting information. + /// The string representation of the value of this instance, which consists of a sequence of digits ranging from 0 to 9, without a sign or leading zeros. + public string ToString(IFormatProvider provider) + { + return this.isSigned + ? this.signedValue.ToString(provider) + : this.unsignedValue.ToString(provider); + } + } +} diff --git a/src/ImageSharp/Processing/Extensions/BinaryDiffuseExtensions.cs b/src/ImageSharp/Processing/Extensions/Binarization/BinaryDiffuseExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/BinaryDiffuseExtensions.cs rename to src/ImageSharp/Processing/Extensions/Binarization/BinaryDiffuseExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/BinaryDitherExtensions.cs b/src/ImageSharp/Processing/Extensions/Binarization/BinaryDitherExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/BinaryDitherExtensions.cs rename to src/ImageSharp/Processing/Extensions/Binarization/BinaryDitherExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/BinaryThresholdExtensions.cs b/src/ImageSharp/Processing/Extensions/Binarization/BinaryThresholdExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/BinaryThresholdExtensions.cs rename to src/ImageSharp/Processing/Extensions/Binarization/BinaryThresholdExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/BokehBlurExtensions.cs b/src/ImageSharp/Processing/Extensions/Convolution/BokehBlurExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/BokehBlurExtensions.cs rename to src/ImageSharp/Processing/Extensions/Convolution/BokehBlurExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/BoxBlurExtensions.cs b/src/ImageSharp/Processing/Extensions/Convolution/BoxBlurExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/BoxBlurExtensions.cs rename to src/ImageSharp/Processing/Extensions/Convolution/BoxBlurExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/DetectEdgesExtensions.cs b/src/ImageSharp/Processing/Extensions/Convolution/DetectEdgesExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/DetectEdgesExtensions.cs rename to src/ImageSharp/Processing/Extensions/Convolution/DetectEdgesExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/GaussianBlurExtensions.cs b/src/ImageSharp/Processing/Extensions/Convolution/GaussianBlurExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/GaussianBlurExtensions.cs rename to src/ImageSharp/Processing/Extensions/Convolution/GaussianBlurExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/GaussianSharpenExtensions.cs b/src/ImageSharp/Processing/Extensions/Convolution/GaussianSharpenExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/GaussianSharpenExtensions.cs rename to src/ImageSharp/Processing/Extensions/Convolution/GaussianSharpenExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/DiffuseExtensions.cs b/src/ImageSharp/Processing/Extensions/Dithering/DiffuseExtensions.cs similarity index 99% rename from src/ImageSharp/Processing/Extensions/DiffuseExtensions.cs rename to src/ImageSharp/Processing/Extensions/Dithering/DiffuseExtensions.cs index b72111099..45eb932fe 100644 --- a/src/ImageSharp/Processing/Extensions/DiffuseExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Dithering/DiffuseExtensions.cs @@ -6,7 +6,7 @@ using System; using SixLabors.ImageSharp.Processing.Processors.Dithering; using SixLabors.Primitives; -namespace SixLabors.ImageSharp.Processing.Dithering +namespace SixLabors.ImageSharp.Processing { /// /// Defines extension methods to apply diffusion to an diff --git a/src/ImageSharp/Processing/Extensions/DitherExtensions.cs b/src/ImageSharp/Processing/Extensions/Dithering/DitherExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/DitherExtensions.cs rename to src/ImageSharp/Processing/Extensions/Dithering/DitherExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/OilPaintExtensions.cs b/src/ImageSharp/Processing/Extensions/Effects/OilPaintExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/OilPaintExtensions.cs rename to src/ImageSharp/Processing/Extensions/Effects/OilPaintExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/PixelateExtensions.cs b/src/ImageSharp/Processing/Extensions/Effects/PixelateExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/PixelateExtensions.cs rename to src/ImageSharp/Processing/Extensions/Effects/PixelateExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/BlackWhiteExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/BlackWhiteExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/BlackWhiteExtensions.cs rename to src/ImageSharp/Processing/Extensions/Filters/BlackWhiteExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/BrightnessExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/BrightnessExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/BrightnessExtensions.cs rename to src/ImageSharp/Processing/Extensions/Filters/BrightnessExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/ColorBlindnessExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/ColorBlindnessExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/ColorBlindnessExtensions.cs rename to src/ImageSharp/Processing/Extensions/Filters/ColorBlindnessExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/ContrastExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/ContrastExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/ContrastExtensions.cs rename to src/ImageSharp/Processing/Extensions/Filters/ContrastExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/FilterExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/FilterExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/FilterExtensions.cs rename to src/ImageSharp/Processing/Extensions/Filters/FilterExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/GrayscaleExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/GrayscaleExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/GrayscaleExtensions.cs rename to src/ImageSharp/Processing/Extensions/Filters/GrayscaleExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/HueExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/HueExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/HueExtensions.cs rename to src/ImageSharp/Processing/Extensions/Filters/HueExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/InvertExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/InvertExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/InvertExtensions.cs rename to src/ImageSharp/Processing/Extensions/Filters/InvertExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/KodachromeExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/KodachromeExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/KodachromeExtensions.cs rename to src/ImageSharp/Processing/Extensions/Filters/KodachromeExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/LightnessExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/LightnessExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/LightnessExtensions.cs rename to src/ImageSharp/Processing/Extensions/Filters/LightnessExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/LomographExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/LomographExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/LomographExtensions.cs rename to src/ImageSharp/Processing/Extensions/Filters/LomographExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/OpacityExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/OpacityExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/OpacityExtensions.cs rename to src/ImageSharp/Processing/Extensions/Filters/OpacityExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/PolaroidExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/PolaroidExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/PolaroidExtensions.cs rename to src/ImageSharp/Processing/Extensions/Filters/PolaroidExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/SaturateExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/SaturateExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/SaturateExtensions.cs rename to src/ImageSharp/Processing/Extensions/Filters/SaturateExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/SepiaExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/SepiaExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/SepiaExtensions.cs rename to src/ImageSharp/Processing/Extensions/Filters/SepiaExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/HistogramEqualizationExtensions.cs b/src/ImageSharp/Processing/Extensions/Normalization/HistogramEqualizationExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/HistogramEqualizationExtensions.cs rename to src/ImageSharp/Processing/Extensions/Normalization/HistogramEqualizationExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/BackgroundColorExtensions.cs b/src/ImageSharp/Processing/Extensions/Overlays/BackgroundColorExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/BackgroundColorExtensions.cs rename to src/ImageSharp/Processing/Extensions/Overlays/BackgroundColorExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/GlowExtensions.cs b/src/ImageSharp/Processing/Extensions/Overlays/GlowExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/GlowExtensions.cs rename to src/ImageSharp/Processing/Extensions/Overlays/GlowExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/VignetteExtensions.cs b/src/ImageSharp/Processing/Extensions/Overlays/VignetteExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/VignetteExtensions.cs rename to src/ImageSharp/Processing/Extensions/Overlays/VignetteExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/QuantizeExtensions.cs b/src/ImageSharp/Processing/Extensions/Quantization/QuantizeExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/QuantizeExtensions.cs rename to src/ImageSharp/Processing/Extensions/Quantization/QuantizeExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/AutoOrientExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/AutoOrientExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/AutoOrientExtensions.cs rename to src/ImageSharp/Processing/Extensions/Transforms/AutoOrientExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/CropExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/CropExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/CropExtensions.cs rename to src/ImageSharp/Processing/Extensions/Transforms/CropExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/EntropyCropExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/EntropyCropExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/EntropyCropExtensions.cs rename to src/ImageSharp/Processing/Extensions/Transforms/EntropyCropExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/FlipExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/FlipExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/FlipExtensions.cs rename to src/ImageSharp/Processing/Extensions/Transforms/FlipExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/PadExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/PadExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/PadExtensions.cs rename to src/ImageSharp/Processing/Extensions/Transforms/PadExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/ResizeExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/ResizeExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/ResizeExtensions.cs rename to src/ImageSharp/Processing/Extensions/Transforms/ResizeExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/RotateExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/RotateExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/RotateExtensions.cs rename to src/ImageSharp/Processing/Extensions/Transforms/RotateExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/RotateFlipExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/RotateFlipExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/RotateFlipExtensions.cs rename to src/ImageSharp/Processing/Extensions/Transforms/RotateFlipExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/SkewExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/SkewExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/SkewExtensions.cs rename to src/ImageSharp/Processing/Extensions/Transforms/SkewExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/TransformExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/TransformExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/TransformExtensions.cs rename to src/ImageSharp/Processing/Extensions/Transforms/TransformExtensions.cs diff --git a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs index b9952ac8f..6e3f1e3e5 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs @@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms return OrientationMode.Unknown; } - ExifValue value = source.Metadata.ExifProfile.GetValue(ExifTag.Orientation); + IExifValue value = source.Metadata.ExifProfile.GetValue(ExifTag.Orientation); if (value is null) { return OrientationMode.Unknown; diff --git a/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorHelpers.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorHelpers.cs index 00c1227a6..abc8c9d43 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorHelpers.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorHelpers.cs @@ -1,8 +1,9 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { @@ -25,34 +26,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms return; } - // Removing the previously stored value allows us to set a value with our own data tag if required. + // Only set the value if it already exists. if (profile.GetValue(ExifTag.PixelXDimension) != null) { - profile.RemoveValue(ExifTag.PixelXDimension); - - if (image.Width <= ushort.MaxValue) - { - profile.SetValue(ExifTag.PixelXDimension, (ushort)image.Width); - } - else - { - profile.SetValue(ExifTag.PixelXDimension, (uint)image.Width); - } + profile.SetValue(ExifTag.PixelXDimension, image.Width); } if (profile.GetValue(ExifTag.PixelYDimension) != null) { - profile.RemoveValue(ExifTag.PixelYDimension); - - if (image.Height <= ushort.MaxValue) - { - profile.SetValue(ExifTag.PixelYDimension, (ushort)image.Height); - } - else - { - profile.SetValue(ExifTag.PixelYDimension, (uint)image.Height); - } + profile.SetValue(ExifTag.PixelYDimension, image.Height); } } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Benchmarks/Samplers/Diffuse.cs b/tests/ImageSharp.Benchmarks/Samplers/Diffuse.cs index 6e67d11ef..c2b9cdc19 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/Diffuse.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/Diffuse.cs @@ -1,7 +1,6 @@ using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Dithering; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Benchmarks.Samplers diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs index 4f928e070..9c86d060a 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs @@ -6,6 +6,7 @@ using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Reflection; using System.Text; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; @@ -44,16 +45,19 @@ namespace SixLabors.ImageSharp.Tests Assert.Null(image.Metadata.ExifProfile); + const string expected = "Dirk Lemstra"; image.Metadata.ExifProfile = new ExifProfile(); - image.Metadata.ExifProfile.SetValue(ExifTag.Copyright, "Dirk Lemstra"); + image.Metadata.ExifProfile.SetValue(ExifTag.Copyright, expected); image = WriteAndRead(image, imageFormat); Assert.NotNull(image.Metadata.ExifProfile); - Assert.Equal(1, image.Metadata.ExifProfile.Values.Count()); + Assert.Equal(1, image.Metadata.ExifProfile.Values.Count); - ExifValue value = image.Metadata.ExifProfile.Values.FirstOrDefault(val => val.Tag == ExifTag.Copyright); - TestValue(value, "Dirk Lemstra"); + IExifValue value = image.Metadata.ExifProfile.GetValue(ExifTag.Copyright); + + Assert.NotNull(value); + Assert.Equal(expected, value.Value); } [Fact] @@ -100,9 +104,9 @@ namespace SixLabors.ImageSharp.Tests profile = image.Metadata.ExifProfile; Assert.NotNull(profile); - ExifValue value = profile.GetValue(ExifTag.ExposureTime); + IExifValue value = profile.GetValue(ExifTag.ExposureTime); Assert.NotNull(value); - Assert.NotEqual(exposureTime, ((Rational)value.Value).ToDouble()); + Assert.NotEqual(exposureTime, value.Value.ToDouble()); memStream.Position = 0; profile = GetExifProfile(); @@ -116,7 +120,9 @@ namespace SixLabors.ImageSharp.Tests Assert.NotNull(profile); value = profile.GetValue(ExifTag.ExposureTime); - Assert.Equal(exposureTime, ((Rational)value.Value).ToDouble()); + Assert.Equal(exposureTime, value.Value.ToDouble()); + + image.Dispose(); } } @@ -129,7 +135,7 @@ namespace SixLabors.ImageSharp.Tests image.Metadata.ExifProfile.SetValue(ExifTag.ExposureBiasValue, new SignedRational(double.PositiveInfinity)); image = WriteAndReadJpeg(image); - ExifValue value = image.Metadata.ExifProfile.GetValue(ExifTag.ExposureBiasValue); + IExifValue value = image.Metadata.ExifProfile.GetValue(ExifTag.ExposureBiasValue); Assert.NotNull(value); Assert.Equal(new SignedRational(double.PositiveInfinity), value.Value); @@ -143,9 +149,9 @@ namespace SixLabors.ImageSharp.Tests image.Metadata.ExifProfile.SetValue(ExifTag.FlashEnergy, new Rational(double.NegativeInfinity)); image = WriteAndRead(image, imageFormat); - value = image.Metadata.ExifProfile.GetValue(ExifTag.FlashEnergy); - Assert.NotNull(value); - Assert.Equal(new Rational(double.PositiveInfinity), value.Value); + IExifValue value2 = image.Metadata.ExifProfile.GetValue(ExifTag.FlashEnergy); + Assert.NotNull(value2); + Assert.Equal(new Rational(double.PositiveInfinity), value2.Value); } [Theory] @@ -153,77 +159,84 @@ namespace SixLabors.ImageSharp.Tests [InlineData(TestImageWriteFormat.Png)] public void SetValue(TestImageWriteFormat imageFormat) { - var latitude = new Rational[] { new Rational(12.3), new Rational(4.56), new Rational(789.0) }; - Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateRgba32Image(); image.Metadata.ExifProfile.SetValue(ExifTag.Software, "ImageSharp"); - ExifValue value = image.Metadata.ExifProfile.GetValue(ExifTag.Software); - TestValue(value, "ImageSharp"); + IExifValue software = image.Metadata.ExifProfile.GetValue(ExifTag.Software); + Assert.Equal("ImageSharp", software.Value); - Assert.Throws(() => { value.WithValue(15); }); + // ExifString can set integer values. + Assert.True(software.TrySetValue(15)); + Assert.False(software.TrySetValue(15F)); image.Metadata.ExifProfile.SetValue(ExifTag.ShutterSpeedValue, new SignedRational(75.55)); - value = image.Metadata.ExifProfile.GetValue(ExifTag.ShutterSpeedValue); - - TestValue(value, new SignedRational(7555, 100)); + IExifValue shutterSpeed = image.Metadata.ExifProfile.GetValue(ExifTag.ShutterSpeedValue); - Assert.Throws(() => { value.WithValue(75); }); + Assert.Equal(new SignedRational(7555, 100), shutterSpeed.Value); + Assert.False(shutterSpeed.TrySetValue(75)); image.Metadata.ExifProfile.SetValue(ExifTag.XResolution, new Rational(150.0)); // We also need to change this value because this overrides XResolution when the image is written. image.Metadata.HorizontalResolution = 150.0; - value = image.Metadata.ExifProfile.GetValue(ExifTag.XResolution); - TestValue(value, new Rational(150, 1)); + IExifValue xResolution = image.Metadata.ExifProfile.GetValue(ExifTag.XResolution); + Assert.Equal(new Rational(150, 1), xResolution.Value); - Assert.Throws(() => { value.WithValue("ImageSharp"); }); + Assert.False(xResolution.TrySetValue("ImageSharp")); image.Metadata.ExifProfile.SetValue(ExifTag.ReferenceBlackWhite, null); - value = image.Metadata.ExifProfile.GetValue(ExifTag.ReferenceBlackWhite); - TestValue(value, (string)null); + IExifValue referenceBlackWhite = image.Metadata.ExifProfile.GetValue(ExifTag.ReferenceBlackWhite); + Assert.Null(referenceBlackWhite.Value); - image.Metadata.ExifProfile.SetValue(ExifTag.GPSLatitude, latitude); + var expectedLatitude = new Rational[] { new Rational(12.3), new Rational(4.56), new Rational(789.0) }; + image.Metadata.ExifProfile.SetValue(ExifTag.GPSLatitude, expectedLatitude); - value = image.Metadata.ExifProfile.GetValue(ExifTag.GPSLatitude); - TestValue(value, latitude); + IExifValue latitude = image.Metadata.ExifProfile.GetValue(ExifTag.GPSLatitude); + Assert.Equal(expectedLatitude, latitude.Value); + int profileCount = image.Metadata.ExifProfile.Values.Count; image = WriteAndRead(image, imageFormat); Assert.NotNull(image.Metadata.ExifProfile); - Assert.Equal(17, image.Metadata.ExifProfile.Values.Count()); - value = image.Metadata.ExifProfile.GetValue(ExifTag.Software); - TestValue(value, "ImageSharp"); + // Should be 3 less. + // 1 x due to setting of null "ReferenceBlackWhite" value. + // 2 x due to use of non-standard padding tag 0xEA1C listed in EXIF Tool. We can read those values but adhere + // strictly to the 2.3.1 specification when writing. (TODO: Support 2.3.2) + // https://exiftool.org/TagNames/EXIF.html + Assert.Equal(profileCount - 3, image.Metadata.ExifProfile.Values.Count); - value = image.Metadata.ExifProfile.GetValue(ExifTag.ShutterSpeedValue); - TestValue(value, new SignedRational(75.55)); + software = image.Metadata.ExifProfile.GetValue(ExifTag.Software); + Assert.Equal("15", software.Value); - value = image.Metadata.ExifProfile.GetValue(ExifTag.XResolution); - TestValue(value, new Rational(150.0)); + shutterSpeed = image.Metadata.ExifProfile.GetValue(ExifTag.ShutterSpeedValue); + Assert.Equal(new SignedRational(75.55), shutterSpeed.Value); - value = image.Metadata.ExifProfile.GetValue(ExifTag.ReferenceBlackWhite); - Assert.Null(value); + xResolution = image.Metadata.ExifProfile.GetValue(ExifTag.XResolution); + Assert.Equal(new Rational(150.0), xResolution.Value); - value = image.Metadata.ExifProfile.GetValue(ExifTag.GPSLatitude); - TestValue(value, latitude); + referenceBlackWhite = image.Metadata.ExifProfile.GetValue(ExifTag.ReferenceBlackWhite); + Assert.Null(referenceBlackWhite); + + latitude = image.Metadata.ExifProfile.GetValue(ExifTag.GPSLatitude); + Assert.Equal(expectedLatitude, latitude.Value); image.Metadata.ExifProfile.Parts = ExifParts.ExifTags; image = WriteAndRead(image, imageFormat); Assert.NotNull(image.Metadata.ExifProfile); - Assert.Equal(8, image.Metadata.ExifProfile.Values.Count()); + Assert.Equal(8, image.Metadata.ExifProfile.Values.Count); Assert.NotNull(image.Metadata.ExifProfile.GetValue(ExifTag.ColorSpace)); Assert.True(image.Metadata.ExifProfile.RemoveValue(ExifTag.ColorSpace)); Assert.False(image.Metadata.ExifProfile.RemoveValue(ExifTag.ColorSpace)); Assert.Null(image.Metadata.ExifProfile.GetValue(ExifTag.ColorSpace)); - Assert.Equal(7, image.Metadata.ExifProfile.Values.Count()); + Assert.Equal(7, image.Metadata.ExifProfile.Values.Count); } [Fact] @@ -242,23 +255,23 @@ namespace SixLabors.ImageSharp.Tests metaData.HorizontalResolution = 100; - Assert.Equal(200, ((Rational)metaData.ExifProfile.GetValue(ExifTag.XResolution).Value).ToDouble()); - Assert.Equal(300, ((Rational)metaData.ExifProfile.GetValue(ExifTag.YResolution).Value).ToDouble()); + Assert.Equal(200, metaData.ExifProfile.GetValue(ExifTag.XResolution).Value.ToDouble()); + Assert.Equal(300, metaData.ExifProfile.GetValue(ExifTag.YResolution).Value.ToDouble()); exifProfile.Sync(metaData); - Assert.Equal(100, ((Rational)metaData.ExifProfile.GetValue(ExifTag.XResolution).Value).ToDouble()); - Assert.Equal(300, ((Rational)metaData.ExifProfile.GetValue(ExifTag.YResolution).Value).ToDouble()); + Assert.Equal(100, (metaData.ExifProfile.GetValue(ExifTag.XResolution).Value).ToDouble()); + Assert.Equal(300, (metaData.ExifProfile.GetValue(ExifTag.YResolution).Value).ToDouble()); metaData.VerticalResolution = 150; - Assert.Equal(100, ((Rational)metaData.ExifProfile.GetValue(ExifTag.XResolution).Value).ToDouble()); - Assert.Equal(300, ((Rational)metaData.ExifProfile.GetValue(ExifTag.YResolution).Value).ToDouble()); + Assert.Equal(100, (metaData.ExifProfile.GetValue(ExifTag.XResolution).Value).ToDouble()); + Assert.Equal(300, (metaData.ExifProfile.GetValue(ExifTag.YResolution).Value).ToDouble()); exifProfile.Sync(metaData); - Assert.Equal(100, ((Rational)metaData.ExifProfile.GetValue(ExifTag.XResolution).Value).ToDouble()); - Assert.Equal(150, ((Rational)metaData.ExifProfile.GetValue(ExifTag.YResolution).Value).ToDouble()); + Assert.Equal(100, (metaData.ExifProfile.GetValue(ExifTag.XResolution).Value).ToDouble()); + Assert.Equal(150, (metaData.ExifProfile.GetValue(ExifTag.YResolution).Value).ToDouble()); } [Fact] @@ -274,36 +287,42 @@ namespace SixLabors.ImageSharp.Tests Assert.Equal(170, thumbnail.Height); } - [Theory] - [InlineData(ExifTag.Software)] - [InlineData(ExifTag.Copyright)] - [InlineData(ExifTag.Model)] - [InlineData(ExifTag.ImageDescription)] - public void ReadWriteLargeProfileJpg(ExifTag exifValueToChange) + [Fact] + public void ReadWriteLargeProfileJpg() { - // arrange - var junk = new StringBuilder(); - for (int i = 0; i < 65600; i++) + ExifTag[] tags = new[] { ExifTag.Software, ExifTag.Copyright, ExifTag.Model, ExifTag.ImageDescription }; + foreach (ExifTag tag in tags) { - junk.Append("a"); - } - var image = new Image(100, 100); - ExifProfile expectedProfile = CreateExifProfile(); - var expectedProfileTags = expectedProfile.Values.Select(x => x.Tag).ToList(); - expectedProfile.SetValue(exifValueToChange, junk.ToString()); - image.Metadata.ExifProfile = expectedProfile; + // Arrange + var junk = new StringBuilder(); + for (int i = 0; i < 65600; i++) + { + junk.Append("a"); + } - // act - Image reloadedImage = WriteAndRead(image, TestImageWriteFormat.Jpeg); + var image = new Image(100, 100); + ExifProfile expectedProfile = CreateExifProfile(); + var expectedProfileTags = expectedProfile.Values.Select(x => x.Tag).ToList(); + expectedProfile.SetValue(tag, junk.ToString()); + image.Metadata.ExifProfile = expectedProfile; - // assert - ExifProfile actualProfile = reloadedImage.Metadata.ExifProfile; - Assert.NotNull(actualProfile); - foreach (ExifTag expectedProfileTag in expectedProfileTags) - { - ExifValue actualProfileValue = actualProfile.GetValue(expectedProfileTag); - ExifValue expectedProfileValue = expectedProfile.GetValue(expectedProfileTag); - Assert.Equal(expectedProfileValue.Value, actualProfileValue.Value); + // Act + Image reloadedImage = WriteAndRead(image, TestImageWriteFormat.Jpeg); + + // Assert + ExifProfile actualProfile = reloadedImage.Metadata.ExifProfile; + Assert.NotNull(actualProfile); + + foreach (ExifTag expectedProfileTag in expectedProfileTags) + { + IExifValue actualProfileValue = actualProfile.GetValueInternal(expectedProfileTag); + IExifValue expectedProfileValue = expectedProfile.GetValueInternal(expectedProfileTag); + Assert.Equal(expectedProfileValue.GetValue(), actualProfileValue.GetValue()); + } + + IExifValue expected = expectedProfile.GetValue(tag); + IExifValue actual = actualProfile.GetValue(tag); + Assert.Equal(expected, actual); } } @@ -312,7 +331,6 @@ namespace SixLabors.ImageSharp.Tests { // This image contains an 802 byte EXIF profile // It has a tag with an index offset of 18,481,152 bytes (overrunning the data) - Image image = TestFile.Create(TestImages.Jpeg.Progressive.Bad.ExifUndefType).CreateRgba32Image(); Assert.NotNull(image); @@ -323,7 +341,8 @@ namespace SixLabors.ImageSharp.Tests { if (value.DataType == ExifDataType.Undefined) { - Assert.Equal(4, value.NumberOfComponents); + Assert.True(value.IsArray); + Assert.Equal(4U, 4 * ExifDataTypes.GetSize(value.DataType)); } } } @@ -338,10 +357,10 @@ namespace SixLabors.ImageSharp.Tests Assert.NotNull(profile); // Force parsing of the profile. - Assert.Equal(24, profile.Values.Count); + Assert.Equal(25, profile.Values.Count); byte[] bytes = profile.ToByteArray(); - Assert.Equal(489, bytes.Length); + Assert.Equal(525, bytes.Length); } [Theory] @@ -349,46 +368,45 @@ namespace SixLabors.ImageSharp.Tests [InlineData(TestImageWriteFormat.Png)] public void WritingImagePreservesExifProfile(TestImageWriteFormat imageFormat) { - // arrange + // Arrange var image = new Image(1, 1); - ExifProfile expected = CreateExifProfile(); - image.Metadata.ExifProfile = expected; + image.Metadata.ExifProfile = CreateExifProfile(); - // act + // Act Image reloadedImage = WriteAndRead(image, imageFormat); - // assert + // Assert ExifProfile actual = reloadedImage.Metadata.ExifProfile; Assert.NotNull(actual); foreach (KeyValuePair expectedProfileValue in TestProfileValues) { - ExifValue actualProfileValue = actual.GetValue(expectedProfileValue.Key); + IExifValue actualProfileValue = actual.GetValueInternal(expectedProfileValue.Key); Assert.NotNull(actualProfileValue); - Assert.Equal(expectedProfileValue.Value, actualProfileValue.Value); + Assert.Equal(expectedProfileValue.Value, actualProfileValue.GetValue()); } } [Fact] public void ProfileToByteArray() { - // arrange + // Arrange byte[] exifBytesWithoutExifCode = ExifConstants.LittleEndianByteOrderMarker; ExifProfile expectedProfile = CreateExifProfile(); var expectedProfileTags = expectedProfile.Values.Select(x => x.Tag).ToList(); - // act + // Act byte[] actualBytes = expectedProfile.ToByteArray(); var actualProfile = new ExifProfile(actualBytes); - // assert + // Assert Assert.NotNull(actualBytes); Assert.NotEmpty(actualBytes); Assert.Equal(exifBytesWithoutExifCode, actualBytes.Take(exifBytesWithoutExifCode.Length).ToArray()); foreach (ExifTag expectedProfileTag in expectedProfileTags) { - ExifValue actualProfileValue = actualProfile.GetValue(expectedProfileTag); - ExifValue expectedProfileValue = expectedProfile.GetValue(expectedProfileTag); - Assert.Equal(expectedProfileValue.Value, actualProfileValue.Value); + IExifValue actualProfileValue = actualProfile.GetValueInternal(expectedProfileTag); + IExifValue expectedProfileValue = expectedProfile.GetValueInternal(expectedProfileTag); + Assert.Equal(expectedProfileValue.GetValue(), actualProfileValue.GetValue()); } } @@ -398,7 +416,7 @@ namespace SixLabors.ImageSharp.Tests foreach (KeyValuePair exifProfileValue in TestProfileValues) { - profile.SetValue(exifProfileValue.Key, exifProfileValue.Value); + profile.SetValueInternal(exifProfileValue.Key, exifProfileValue.Value); } return profile; @@ -423,7 +441,7 @@ namespace SixLabors.ImageSharp.Tests case TestImageWriteFormat.Png: return WriteAndReadPng(image); default: - throw new ArgumentException("unexpected test image format, only Jpeg and Png are allowed"); + throw new ArgumentException("Unexpected test image format, only Jpeg and Png are allowed"); } } @@ -455,65 +473,21 @@ namespace SixLabors.ImageSharp.Tests { Assert.NotNull(profile); - Assert.Equal(16, profile.Values.Count()); + Assert.Equal(16, profile.Values.Count); - foreach (ExifValue value in profile.Values) + foreach (IExifValue value in profile.Values) { - Assert.NotNull(value.Value); - - if (value.Tag == ExifTag.Software) - { - Assert.Equal("Windows Photo Editor 10.0.10011.16384", value.ToString()); - } - - if (value.Tag == ExifTag.XResolution) - { - Assert.Equal(new Rational(300.0), value.Value); - } - - if (value.Tag == ExifTag.PixelXDimension) - { - Assert.Equal(2338U, value.Value); - } + Assert.NotNull(value.GetValue()); } - } - - private static void TestValue(ExifValue value, string expected) - { - Assert.NotNull(value); - Assert.Equal(expected, value.Value); - } - - private static void TestValue(ExifValue value, Rational expected) - { - Assert.NotNull(value); - Assert.Equal(expected, value.Value); - } - - private static void TestValue(ExifValue value, SignedRational expected) - { - Assert.NotNull(value); - Assert.Equal(expected, value.Value); - } - private static void TestValue(ExifValue value, Rational[] expected) - { - Assert.NotNull(value); - - Assert.Equal(expected, (ICollection)value.Value); - } + IExifValue software = profile.GetValue(ExifTag.Software); + Assert.Equal("Windows Photo Editor 10.0.10011.16384", software.Value); - private static void TestValue(ExifValue value, double expected) - { - Assert.NotNull(value); - Assert.Equal(expected, value.Value); - } - - private static void TestValue(ExifValue value, double[] expected) - { - Assert.NotNull(value); + IExifValue xResolution = profile.GetValue(ExifTag.XResolution); + Assert.Equal(new Rational(300.0), xResolution.Value); - Assert.Equal(expected, (ICollection)value.Value); + IExifValue xDimension = profile.GetValue(ExifTag.PixelXDimension); + Assert.Equal(2338U, xDimension.Value); } } } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifReaderTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifReaderTests.cs index 19ff7d269..85c9231fa 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifReaderTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifReaderTests.cs @@ -1,6 +1,7 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; using System.Collections.Generic; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using Xunit; @@ -12,9 +13,9 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void Read_DataIsEmpty_ReturnsEmptyCollection() { - var reader = new ExifReader(new byte[] { }); + var reader = new ExifReader(Array.Empty()); - IList result = reader.ReadValues(); + IList result = reader.ReadValues(); Assert.Equal(0, result.Count); } @@ -24,7 +25,7 @@ namespace SixLabors.ImageSharp.Tests { var reader = new ExifReader(new byte[] { 69, 120, 105, 102, 0, 0 }); - IList result = reader.ReadValues(); + IList result = reader.ReadValues(); Assert.Equal(0, result.Count); } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifTagDescriptionAttributeTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifTagDescriptionAttributeTests.cs index 144a6e4a3..a6ad8df8b 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifTagDescriptionAttributeTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifTagDescriptionAttributeTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Metadata.Profiles.Exif; @@ -11,10 +11,10 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void TestExifTag() { - ExifProfile exifProfile = new ExifProfile(); + var exifProfile = new ExifProfile(); exifProfile.SetValue(ExifTag.ResolutionUnit, (ushort)1); - ExifValue value = exifProfile.GetValue(ExifTag.ResolutionUnit); + IExifValue value = exifProfile.GetValue(ExifTag.ResolutionUnit); Assert.Equal("None", value.ToString()); exifProfile.SetValue(ExifTag.ResolutionUnit, (ushort)2); @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Tests value = exifProfile.GetValue(ExifTag.ResolutionUnit); Assert.Equal("4", value.ToString()); - exifProfile.SetValue(ExifTag.ImageWidth, 123); + exifProfile.SetValue(ExifTag.ImageWidth, 123U); value = exifProfile.GetValue(ExifTag.ImageWidth); Assert.Equal("123", value.ToString()); } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifValueTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifValueTests.cs index 8d786811c..7f52fb6ca 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifValueTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifValueTests.cs @@ -1,7 +1,6 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System.Linq; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; using Xunit; @@ -10,40 +9,45 @@ namespace SixLabors.ImageSharp.Tests { public class ExifValueTests { - private static ExifValue GetExifValue() + private ExifProfile profile; + + public ExifValueTests() { - ExifProfile profile; using (Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateRgba32Image()) { - profile = image.Metadata.ExifProfile; + this.profile = image.Metadata.ExifProfile; } + } - Assert.NotNull(profile); + private IExifValue GetExifValue() + { + Assert.NotNull(this.profile); - return profile.Values.First(); + return this.profile.GetValue(ExifTag.Software); } [Fact] public void IEquatable() { - ExifValue first = GetExifValue(); - ExifValue second = GetExifValue(); + IExifValue first = this.GetExifValue(); + IExifValue second = this.GetExifValue(); Assert.True(first == second); Assert.True(first.Equals(second)); - Assert.True(first.Equals((object)second)); } [Fact] public void Properties() { - ExifValue value = GetExifValue(); + IExifValue value = this.GetExifValue(); Assert.Equal(ExifDataType.Ascii, value.DataType); - Assert.Equal(ExifTag.GPSDOP, value.Tag); + Assert.Equal(ExifTag.Software, value.Tag); Assert.False(value.IsArray); - Assert.Equal("Windows Photo Editor 10.0.10011.16384", value.ToString()); - Assert.Equal("Windows Photo Editor 10.0.10011.16384", value.Value); + + const string expected = "Windows Photo Editor 10.0.10011.16384"; + Assert.Equal(expected, value.ToString()); + Assert.Equal(expected, value.Value); } } } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/Values/ExifValuesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/Values/ExifValuesTests.cs new file mode 100644 index 000000000..d015fefc4 --- /dev/null +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/Values/ExifValuesTests.cs @@ -0,0 +1,553 @@ +using SixLabors.ImageSharp.Metadata.Profiles.Exif; +using SixLabors.ImageSharp.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif.Values +{ + public class ExifValuesTests + { + public static TheoryData ByteTags => new TheoryData + { + { ExifTag.FaxProfile }, + { ExifTag.ModeNumber }, + { ExifTag.GPSAltitudeRef } + }; + + public static TheoryData ByteArrayTags => new TheoryData + { + { ExifTag.ClipPath }, + { ExifTag.VersionYear }, + { ExifTag.XMP }, + { ExifTag.CFAPattern2 }, + { ExifTag.TIFFEPStandardID }, + { ExifTag.XPTitle }, + { ExifTag.XPComment }, + { ExifTag.XPAuthor }, + { ExifTag.XPKeywords }, + { ExifTag.XPSubject }, + { ExifTag.GPSVersionID }, + }; + + public static TheoryData DoubleArrayTags => new TheoryData + { + { ExifTag.PixelScale }, + { ExifTag.IntergraphMatrix }, + { ExifTag.ModelTiePoint }, + { ExifTag.ModelTransform } + }; + + public static TheoryData LongTags => new TheoryData + { + { ExifTag.SubfileType }, + { ExifTag.SubIFDOffset }, + { ExifTag.GPSIFDOffset }, + { ExifTag.T4Options }, + { ExifTag.T6Options }, + { ExifTag.XClipPathUnits }, + { ExifTag.YClipPathUnits }, + { ExifTag.ProfileType }, + { ExifTag.CodingMethods }, + { ExifTag.T82ptions }, + { ExifTag.JPEGInterchangeFormat }, + { ExifTag.JPEGInterchangeFormatLength }, + { ExifTag.MDFileTag }, + { ExifTag.StandardOutputSensitivity }, + { ExifTag.RecommendedExposureIndex }, + { ExifTag.ISOSpeed }, + { ExifTag.ISOSpeedLatitudeyyy }, + { ExifTag.ISOSpeedLatitudezzz }, + { ExifTag.FaxRecvParams }, + { ExifTag.FaxRecvTime }, + { ExifTag.ImageNumber }, + }; + + public static TheoryData LongArrayTags => new TheoryData + { + { ExifTag.FreeOffsets }, + { ExifTag.FreeByteCounts }, + { ExifTag.ColorResponseUnit }, + { ExifTag.TileOffsets }, + { ExifTag.SMinSampleValue }, + { ExifTag.SMaxSampleValue }, + { ExifTag.JPEGQTables }, + { ExifTag.JPEGDCTables }, + { ExifTag.JPEGACTables }, + { ExifTag.StripRowCounts }, + { ExifTag.IntergraphRegisters }, + { ExifTag.TimeZoneOffset } + }; + + public static TheoryData NumberTags => new TheoryData + { + { ExifTag.ImageWidth }, + { ExifTag.ImageLength }, + { ExifTag.TileWidth }, + { ExifTag.TileLength }, + { ExifTag.BadFaxLines }, + { ExifTag.ConsecutiveBadFaxLines }, + { ExifTag.PixelXDimension }, + { ExifTag.PixelYDimension } + }; + + public static TheoryData NumberArrayTags => new TheoryData + { + { ExifTag.StripOffsets }, + { ExifTag.TileByteCounts }, + { ExifTag.ImageLayer } + }; + + public static TheoryData RationalTags => new TheoryData + { + { ExifTag.XPosition }, + { ExifTag.YPosition }, + { ExifTag.XResolution }, + { ExifTag.YResolution }, + { ExifTag.BatteryLevel }, + { ExifTag.ExposureTime }, + { ExifTag.FNumber }, + { ExifTag.MDScalePixel }, + { ExifTag.CompressedBitsPerPixel }, + { ExifTag.ApertureValue }, + { ExifTag.MaxApertureValue }, + { ExifTag.SubjectDistance }, + { ExifTag.FocalLength }, + { ExifTag.FlashEnergy2 }, + { ExifTag.FocalPlaneXResolution2 }, + { ExifTag.FocalPlaneYResolution2 }, + { ExifTag.ExposureIndex2 }, + { ExifTag.Humidity }, + { ExifTag.Pressure }, + { ExifTag.Acceleration }, + { ExifTag.FlashEnergy }, + { ExifTag.FocalPlaneXResolution }, + { ExifTag.FocalPlaneYResolution }, + { ExifTag.ExposureIndex }, + { ExifTag.DigitalZoomRatio }, + { ExifTag.LensInfo }, + { ExifTag.GPSAltitude }, + { ExifTag.GPSDOP }, + { ExifTag.GPSSpeed }, + { ExifTag.GPSTrack }, + { ExifTag.GPSImgDirection }, + { ExifTag.GPSDestBearing }, + { ExifTag.GPSDestDistance }, + }; + + public static TheoryData RationalArrayTags => new TheoryData + { + { ExifTag.WhitePoint }, + { ExifTag.PrimaryChromaticities }, + { ExifTag.YCbCrCoefficients }, + { ExifTag.ReferenceBlackWhite }, + { ExifTag.GPSLatitude }, + { ExifTag.GPSLongitude }, + { ExifTag.GPSTimestamp }, + { ExifTag.GPSDestLatitude }, + { ExifTag.GPSDestLongitude } + }; + + public static TheoryData ShortTags => new TheoryData + { + { ExifTag.OldSubfileType }, + { ExifTag.Compression }, + { ExifTag.PhotometricInterpretation }, + { ExifTag.Thresholding }, + { ExifTag.CellWidth }, + { ExifTag.CellLength }, + { ExifTag.FillOrder }, + { ExifTag.Orientation }, + { ExifTag.SamplesPerPixel }, + { ExifTag.PlanarConfiguration }, + { ExifTag.GrayResponseUnit }, + { ExifTag.ResolutionUnit }, + { ExifTag.CleanFaxData }, + { ExifTag.InkSet }, + { ExifTag.NumberOfInks }, + { ExifTag.DotRange }, + { ExifTag.Indexed }, + { ExifTag.OPIProxy }, + { ExifTag.JPEGProc }, + { ExifTag.JPEGRestartInterval }, + { ExifTag.YCbCrPositioning }, + { ExifTag.Rating }, + { ExifTag.RatingPercent }, + { ExifTag.ExposureProgram }, + { ExifTag.Interlace }, + { ExifTag.SelfTimerMode }, + { ExifTag.SensitivityType }, + { ExifTag.MeteringMode }, + { ExifTag.LightSource }, + { ExifTag.FocalPlaneResolutionUnit2 }, + { ExifTag.SensingMethod2 }, + { ExifTag.Flash }, + { ExifTag.ColorSpace }, + { ExifTag.FocalPlaneResolutionUnit }, + { ExifTag.SensingMethod }, + { ExifTag.CustomRendered }, + { ExifTag.ExposureMode }, + { ExifTag.WhiteBalance }, + { ExifTag.FocalLengthIn35mmFilm }, + { ExifTag.SceneCaptureType }, + { ExifTag.GainControl }, + { ExifTag.Contrast }, + { ExifTag.Saturation }, + { ExifTag.Sharpness }, + { ExifTag.SubjectDistanceRange }, + { ExifTag.GPSDifferential } + }; + + public static TheoryData ShortArrayTags => new TheoryData + { + { ExifTag.BitsPerSample }, + { ExifTag.MinSampleValue }, + { ExifTag.MaxSampleValue }, + { ExifTag.GrayResponseCurve }, + { ExifTag.ColorMap }, + { ExifTag.ExtraSamples }, + { ExifTag.PageNumber }, + { ExifTag.TransferFunction }, + { ExifTag.Predictor }, + { ExifTag.HalftoneHints }, + { ExifTag.SampleFormat }, + { ExifTag.TransferRange }, + { ExifTag.DefaultImageColor }, + { ExifTag.JPEGLosslessPredictors }, + { ExifTag.JPEGPointTransforms }, + { ExifTag.YCbCrSubsampling }, + { ExifTag.CFARepeatPatternDim }, + { ExifTag.IntergraphPacketData }, + { ExifTag.ISOSpeedRatings }, + { ExifTag.SubjectArea }, + { ExifTag.SubjectLocation } + }; + + public static TheoryData SignedRationalTags => new TheoryData + { + { ExifTag.ShutterSpeedValue }, + { ExifTag.BrightnessValue }, + { ExifTag.ExposureBiasValue }, + { ExifTag.AmbientTemperature }, + { ExifTag.WaterDepth }, + { ExifTag.CameraElevationAngle } + }; + + public static TheoryData SignedRationalArrayTags => new TheoryData + { + { ExifTag.Decode } + }; + + public static TheoryData StringTags => new TheoryData + { + { ExifTag.ImageDescription }, + { ExifTag.Make }, + { ExifTag.Model }, + { ExifTag.Software }, + { ExifTag.DateTime }, + { ExifTag.Artist }, + { ExifTag.HostComputer }, + { ExifTag.Copyright }, + { ExifTag.DocumentName }, + { ExifTag.PageName }, + { ExifTag.InkNames }, + { ExifTag.TargetPrinter }, + { ExifTag.ImageID }, + { ExifTag.MDLabName }, + { ExifTag.MDSampleInfo }, + { ExifTag.MDPrepDate }, + { ExifTag.MDPrepTime }, + { ExifTag.MDFileUnits }, + { ExifTag.SEMInfo }, + { ExifTag.SpectralSensitivity }, + { ExifTag.DateTimeOriginal }, + { ExifTag.DateTimeDigitized }, + { ExifTag.SubsecTime }, + { ExifTag.SubsecTimeOriginal }, + { ExifTag.SubsecTimeDigitized }, + { ExifTag.RelatedSoundFile }, + { ExifTag.FaxSubaddress }, + { ExifTag.OffsetTime }, + { ExifTag.OffsetTimeOriginal }, + { ExifTag.OffsetTimeDigitized }, + { ExifTag.SecurityClassification }, + { ExifTag.ImageHistory }, + { ExifTag.ImageUniqueID }, + { ExifTag.OwnerName }, + { ExifTag.SerialNumber }, + { ExifTag.LensMake }, + { ExifTag.LensModel }, + { ExifTag.LensSerialNumber }, + { ExifTag.GDALMetadata }, + { ExifTag.GDALNoData }, + { ExifTag.GPSLatitudeRef }, + { ExifTag.GPSLongitudeRef }, + { ExifTag.GPSSatellites }, + { ExifTag.GPSStatus }, + { ExifTag.GPSMeasureMode }, + { ExifTag.GPSSpeedRef }, + { ExifTag.GPSTrackRef }, + { ExifTag.GPSImgDirectionRef }, + { ExifTag.GPSMapDatum }, + { ExifTag.GPSDestLatitudeRef }, + { ExifTag.GPSDestLongitudeRef }, + { ExifTag.GPSDestBearingRef }, + { ExifTag.GPSDestDistanceRef }, + { ExifTag.GPSDateStamp } + }; + + public static TheoryData UndefinedTags => new TheoryData + { + { ExifTag.FileSource }, + { ExifTag.SceneType } + }; + + public static TheoryData UndefinedArrayTags => new TheoryData + { + { ExifTag.JPEGTables }, + { ExifTag.OECF }, + { ExifTag.ExifVersion }, + { ExifTag.ComponentsConfiguration }, + { ExifTag.MakerNote }, + { ExifTag.UserComment }, + { ExifTag.FlashpixVersion }, + { ExifTag.SpatialFrequencyResponse }, + { ExifTag.SpatialFrequencyResponse2 }, + { ExifTag.Noise }, + { ExifTag.CFAPattern }, + { ExifTag.DeviceSettingDescription }, + { ExifTag.ImageSourceData }, + { ExifTag.GPSProcessingMethod }, + { ExifTag.GPSAreaInformation } + }; + + [Theory] + [MemberData(nameof(ByteTags))] + public void ExifByteTests(ExifTag tag) + { + const byte expected = byte.MaxValue; + ExifValue value = ExifValues.Create(tag); + + Assert.False(value.TrySetValue(expected.ToString())); + Assert.True(value.TrySetValue((int)expected)); + Assert.True(value.TrySetValue(expected)); + + var typed = (ExifByte)value; + Assert.Equal(expected, typed.Value); + } + + [Theory] + [MemberData(nameof(ByteArrayTags))] + public void ExifByteArrayTests(ExifTag tag) + { + byte[] expected = new[] { byte.MaxValue }; + ExifValue value = ExifValues.Create(tag); + + Assert.False(value.TrySetValue(expected.ToString())); + Assert.True(value.TrySetValue(expected)); + + var typed = (ExifByteArray)value; + Assert.Equal(expected, typed.Value); + } + + [Theory] + [MemberData(nameof(DoubleArrayTags))] + public void ExifDoubleArrayTests(ExifTag tag) + { + double[] expected = new[] { double.MaxValue }; + ExifValue value = ExifValues.Create(tag); + + Assert.False(value.TrySetValue(expected.ToString())); + Assert.True(value.TrySetValue(expected)); + + var typed = (ExifDoubleArray)value; + Assert.Equal(expected, typed.Value); + } + + [Theory] + [MemberData(nameof(LongTags))] + public void ExifLongTests(ExifTag tag) + { + const uint expected = uint.MaxValue; + ExifValue value = ExifValues.Create(tag); + + Assert.False(value.TrySetValue(expected.ToString())); + Assert.True(value.TrySetValue(expected)); + + var typed = (ExifLong)value; + Assert.Equal(expected, typed.Value); + } + + [Theory] + [MemberData(nameof(LongArrayTags))] + public void ExifLongArrayTests(ExifTag tag) + { + uint[] expected = new[] { uint.MaxValue }; + ExifValue value = ExifValues.Create(tag); + + Assert.False(value.TrySetValue(expected.ToString())); + Assert.True(value.TrySetValue(expected)); + + var typed = (ExifLongArray)value; + Assert.Equal(expected, typed.Value); + } + + [Theory] + [MemberData(nameof(NumberTags))] + public void ExifNumberTests(ExifTag tag) + { + Number expected = ushort.MaxValue; + ExifValue value = ExifValues.Create(tag); + + Assert.False(value.TrySetValue(expected.ToString())); + Assert.True(value.TrySetValue((uint)expected)); + Assert.True(value.TrySetValue((int)expected)); + Assert.True(value.TrySetValue(expected)); + + var typed = (ExifNumber)value; + Assert.Equal(expected, typed.Value); + } + + [Theory] + [MemberData(nameof(NumberArrayTags))] + public void ExifNumberArrayTests(ExifTag tag) + { + Number[] expected = new[] { new Number(uint.MaxValue) }; + ExifValue value = ExifValues.Create(tag); + + Assert.False(value.TrySetValue(expected.ToString())); + Assert.True(value.TrySetValue(expected)); + + var typed = (ExifNumberArray)value; + Assert.Equal(expected, typed.Value); + } + + [Theory] + [MemberData(nameof(RationalTags))] + public void ExifRationalTests(ExifTag tag) + { + var expected = new Rational(21, 42); + ExifValue value = ExifValues.Create(tag); + + Assert.False(value.TrySetValue(expected.ToString())); + Assert.True(value.TrySetValue(new SignedRational(expected.ToDouble()))); + Assert.True(value.TrySetValue(expected)); + + var typed = (ExifRational)value; + Assert.Equal(expected, typed.Value); + } + + [Theory] + [MemberData(nameof(RationalArrayTags))] + public void ExifRationalArrayTests(ExifTag tag) + { + Rational[] expected = new[] { new Rational(21, 42) }; + ExifValue value = ExifValues.Create(tag); + + Assert.False(value.TrySetValue(expected.ToString())); + Assert.True(value.TrySetValue(expected)); + + var typed = (ExifRationalArray)value; + Assert.Equal(expected, typed.Value); + } + + [Theory] + [MemberData(nameof(ShortTags))] + public void ExifShortTests(ExifTag tag) + { + const ushort expected = (ushort)short.MaxValue; + ExifValue value = ExifValues.Create(tag); + + Assert.False(value.TrySetValue(expected.ToString())); + Assert.True(value.TrySetValue((int)expected)); + Assert.True(value.TrySetValue((short)expected)); + Assert.True(value.TrySetValue(expected)); + + var typed = (ExifShort)value; + Assert.Equal(expected, typed.Value); + } + + [Theory] + [MemberData(nameof(ShortArrayTags))] + public void ExifShortArrayTests(ExifTag tag) + { + ushort[] expected = new[] { ushort.MaxValue }; + ExifValue value = ExifValues.Create(tag); + + Assert.False(value.TrySetValue(expected.ToString())); + Assert.True(value.TrySetValue(expected)); + + var typed = (ExifShortArray)value; + Assert.Equal(expected, typed.Value); + } + + [Theory] + [MemberData(nameof(SignedRationalTags))] + public void ExifSignedRationalTests(ExifTag tag) + { + var expected = new SignedRational(21, 42); + ExifValue value = ExifValues.Create(tag); + + Assert.False(value.TrySetValue(expected.ToString())); + Assert.True(value.TrySetValue(expected)); + + var typed = (ExifSignedRational)value; + Assert.Equal(expected, typed.Value); + } + + [Theory] + [MemberData(nameof(SignedRationalArrayTags))] + public void ExifSignedRationalArrayTests(ExifTag tag) + { + SignedRational[] expected = new[] { new SignedRational(21, 42) }; + ExifValue value = ExifValues.Create(tag); + + Assert.False(value.TrySetValue(expected.ToString())); + Assert.True(value.TrySetValue(expected)); + + var typed = (ExifSignedRationalArray)value; + Assert.Equal(expected, typed.Value); + } + + [Theory] + [MemberData(nameof(StringTags))] + public void ExifStringTests(ExifTag tag) + { + const string expected = "ImageSharp"; + ExifValue value = ExifValues.Create(tag); + + Assert.False(value.TrySetValue(0M)); + Assert.True(value.TrySetValue(expected)); + + var typed = (ExifString)value; + Assert.Equal(expected, typed.Value); + } + + [Theory] + [MemberData(nameof(UndefinedTags))] + public void ExifUndefinedTests(ExifTag tag) + { + const byte expected = byte.MaxValue; + ExifValue value = ExifValues.Create(tag); + + Assert.False(value.TrySetValue(expected.ToString())); + Assert.True(value.TrySetValue((int)expected)); + Assert.True(value.TrySetValue(expected)); + + var typed = (ExifByte)value; + Assert.Equal(expected, typed.Value); + } + + [Theory] + [MemberData(nameof(UndefinedArrayTags))] + public void ExifUndefinedArrayTests(ExifTag tag) + { + byte[] expected = new[] { byte.MaxValue }; + ExifValue value = ExifValues.Create(tag); + + Assert.False(value.TrySetValue(expected.ToString())); + Assert.True(value.TrySetValue(expected)); + + var typed = (ExifByteArray)value; + Assert.Equal(expected, typed.Value); + } + } +} diff --git a/tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs b/tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs index c5d18cbb2..53a50468b 100644 --- a/tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs +++ b/tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs @@ -4,7 +4,6 @@ using System; using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Dithering; using SixLabors.ImageSharp.Processing.Processors.Dithering; using Xunit; diff --git a/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs index 5417bc1f3..5d65a9e61 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs @@ -1,9 +1,8 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Dithering; using SixLabors.ImageSharp.Processing.Processors.Dithering; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; @@ -149,4 +148,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization appendPixelTypeToFileName: false); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs index 6171c3c69..c683a51dc 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs @@ -58,9 +58,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms } } - [Theory( - Skip = "Debug only, enable manually" - )] + [Theory(Skip = "Debug only, enable manually")] [WithTestPatternImages(4000, 4000, PixelTypes.Rgba32, 300, 1024)] [WithTestPatternImages(3032, 3032, PixelTypes.Rgba32, 400, 1024)] [WithTestPatternImages(3032, 3032, PixelTypes.Rgba32, 400, 128)] @@ -560,5 +558,25 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms image.CompareToReferenceOutput(ValidatorComparer, provider); } } + + [Theory] + [WithFile(TestImages.Jpeg.Issues.ExifDecodeOutOfRange694, DefaultPixelType)] + [WithFile(TestImages.Jpeg.Issues.ExifGetString750Transform, DefaultPixelType)] + [WithFile(TestImages.Jpeg.Issues.ExifResize1049, DefaultPixelType)] + public void CanResizeExifIssueImages(TestImageProvider provider) + where TPixel : struct, IPixel + { + // Test images are large so skip on 32bit for now. + if (!TestEnvironment.Is64BitProcess) + { + return; + } + + using (Image image = provider.GetImage()) + { + // Don't bother saving, we're testing the EXIF metadata updates. + image.Mutate(x => x.Resize(image.Width / 2, image.Height / 2)); + } + } } } diff --git a/tests/ImageSharp.Tests/Processing/Transforms/TransformsHelpersTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/TransformsHelpersTest.cs index 19226b307..9f8034fa3 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/TransformsHelpersTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/TransformsHelpersTest.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Metadata.Profiles.Exif; @@ -19,8 +19,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms { var profile = new ExifProfile(); img.Metadata.ExifProfile = profile; - profile.SetValue(ExifTag.PixelXDimension, (uint)xy); - profile.SetValue(ExifTag.PixelYDimension, (uint)xy); + profile.SetValue(ExifTag.PixelXDimension, xy + ushort.MaxValue); + profile.SetValue(ExifTag.PixelYDimension, xy + ushort.MaxValue); Assert.Equal(ExifDataType.Long, profile.GetValue(ExifTag.PixelXDimension).DataType); Assert.Equal(ExifDataType.Long, profile.GetValue(ExifTag.PixelYDimension).DataType); @@ -32,4 +32,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms } } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 8389d8535..ada077496 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -191,6 +191,7 @@ namespace SixLabors.ImageSharp.Tests public const string IncorrectQuality845 = "Jpg/issues/Issue845-Incorrect-Quality99.jpg"; public const string IncorrectColorspace855 = "Jpg/issues/issue855-incorrect-colorspace.jpg"; public const string IncorrectResize1006 = "Jpg/issues/issue1006-incorrect-resize.jpg"; + public const string ExifResize1049 = "Jpg/issues/issue1049-exif-resize.jpg"; public static class Fuzz { diff --git a/tests/Images/Input/Jpg/issues/issue1049-exif-resize.jpg b/tests/Images/Input/Jpg/issues/issue1049-exif-resize.jpg new file mode 100644 index 000000000..b3abb7d2f --- /dev/null +++ b/tests/Images/Input/Jpg/issues/issue1049-exif-resize.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1f313e48c5b9d4d1af2df44057970f03ddafc809862d14e8593f3e1fc0aef2c1 +size 718443