diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml
index 6d31e8c532..9942a0ca28 100644
--- a/.github/workflows/build-and-test.yml
+++ b/.github/workflows/build-and-test.yml
@@ -109,12 +109,14 @@ jobs:
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets') }}
restore-keys: ${{ runner.os }}-nuget-
- - name: DotNet Setup Preview
- if: ${{ matrix.options.sdk-preview == true }}
+ - name: DotNet Setup
uses: actions/setup-dotnet@v1
with:
- dotnet-version: ${{ matrix.options.sdk }}
- include-prerelease: true
+ dotnet-version: |
+ 6.0.x
+ 5.0.x
+ 3.1.x
+ 2.1.x
- name: DotNet Build
if: ${{ matrix.options.sdk-preview != true }}
diff --git a/src/ImageSharp/Diagnostics/MemoryDiagnostics.cs b/src/ImageSharp/Diagnostics/MemoryDiagnostics.cs
new file mode 100644
index 0000000000..d83e737f12
--- /dev/null
+++ b/src/ImageSharp/Diagnostics/MemoryDiagnostics.cs
@@ -0,0 +1,83 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System.Threading;
+
+namespace SixLabors.ImageSharp.Diagnostics
+{
+ ///
+ /// Represents the method to handle .
+ ///
+ public delegate void UndisposedAllocationDelegate(string allocationStackTrace);
+
+ ///
+ /// Utilities to track memory usage and detect memory leaks from not disposing ImageSharp objects.
+ ///
+ public static class MemoryDiagnostics
+ {
+ private static int totalUndisposedAllocationCount;
+
+ private static UndisposedAllocationDelegate undisposedAllocation;
+ private static int undisposedAllocationSubscriptionCounter;
+ private static readonly object SyncRoot = new();
+
+ ///
+ /// Fires when an ImageSharp object's undisposed memory resource leaks to the finalizer.
+ /// The event brings significant overhead, and is intended to be used for troubleshooting only.
+ /// For production diagnostics, use .
+ ///
+ public static event UndisposedAllocationDelegate UndisposedAllocation
+ {
+ add
+ {
+ lock (SyncRoot)
+ {
+ undisposedAllocationSubscriptionCounter++;
+ undisposedAllocation += value;
+ }
+ }
+
+ remove
+ {
+ lock (SyncRoot)
+ {
+ undisposedAllocation -= value;
+ undisposedAllocationSubscriptionCounter--;
+ }
+ }
+ }
+
+ ///
+ /// Gets a value indicating the total number of memory resource objects leaked to the finalizer.
+ ///
+ public static int TotalUndisposedAllocationCount => totalUndisposedAllocationCount;
+
+ internal static bool UndisposedAllocationSubscribed => Volatile.Read(ref undisposedAllocationSubscriptionCounter) > 0;
+
+ internal static void IncrementTotalUndisposedAllocationCount() =>
+ Interlocked.Increment(ref totalUndisposedAllocationCount);
+
+ internal static void DecrementTotalUndisposedAllocationCount() =>
+ Interlocked.Decrement(ref totalUndisposedAllocationCount);
+
+ internal static void RaiseUndisposedMemoryResource(string allocationStackTrace)
+ {
+ if (undisposedAllocation is null)
+ {
+ return;
+ }
+
+ // Schedule on the ThreadPool, to avoid user callback messing up the finalizer thread.
+#if NETSTANDARD2_1 || NETCOREAPP2_1_OR_GREATER
+ ThreadPool.QueueUserWorkItem(
+ stackTrace => undisposedAllocation?.Invoke(stackTrace),
+ allocationStackTrace,
+ preferLocal: false);
+#else
+ ThreadPool.QueueUserWorkItem(
+ stackTrace => undisposedAllocation?.Invoke((string)stackTrace),
+ allocationStackTrace);
+#endif
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
index 3c0bf9edf3..b17ae7f17a 100644
--- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
@@ -376,7 +376,17 @@ namespace SixLabors.ImageSharp.Formats.Gif
indices = this.Configuration.MemoryAllocator.Allocate2D(this.imageDescriptor.Width, this.imageDescriptor.Height, AllocationOptions.Clean);
this.ReadFrameIndices(indices);
- ReadOnlySpan colorTable = MemoryMarshal.Cast((localColorTable ?? this.globalColorTable).GetSpan());
+ Span rawColorTable = default;
+ if (localColorTable != null)
+ {
+ rawColorTable = localColorTable.GetSpan();
+ }
+ else if (this.globalColorTable != null)
+ {
+ rawColorTable = this.globalColorTable.GetSpan();
+ }
+
+ ReadOnlySpan colorTable = MemoryMarshal.Cast(rawColorTable);
this.ReadFrameColors(ref image, ref previousFrame, indices, colorTable, this.imageDescriptor);
// Skip any remaining blocks
@@ -415,6 +425,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
{
int imageWidth = this.logicalScreenDescriptor.Width;
int imageHeight = this.logicalScreenDescriptor.Height;
+ bool transFlag = this.graphicsControlExtension.TransparencyFlag;
ImageFrame prevFrame = null;
ImageFrame currentFrame = null;
@@ -422,8 +433,15 @@ namespace SixLabors.ImageSharp.Formats.Gif
if (previousFrame is null)
{
- // This initializes the image to become fully transparent because the alpha channel is zero.
- image = new Image(this.Configuration, imageWidth, imageHeight, this.metadata);
+ if (!transFlag)
+ {
+ image = new Image(this.Configuration, imageWidth, imageHeight, Color.Black.ToPixel(), this.metadata);
+ }
+ else
+ {
+ // This initializes the image to become fully transparent because the alpha channel is zero.
+ image = new Image(this.Configuration, imageWidth, imageHeight, this.metadata);
+ }
this.SetFrameMetadata(image.Frames.RootFrame.Metadata);
@@ -445,6 +463,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.RestoreToBackground(imageFrame);
}
+ if (colorTable.Length == 0)
+ {
+ return;
+ }
+
int interlacePass = 0; // The interlace pass
int interlaceIncrement = 8; // The interlacing line increment
int interlaceY = 0; // The current interlaced line
@@ -452,7 +475,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
int descriptorBottom = descriptorTop + descriptor.Height;
int descriptorLeft = descriptor.Left;
int descriptorRight = descriptorLeft + descriptor.Width;
- bool transFlag = this.graphicsControlExtension.TransparencyFlag;
byte transIndex = this.graphicsControlExtension.TransparencyIndex;
int colorTableMaxIdx = colorTable.Length - 1;
@@ -635,10 +657,13 @@ namespace SixLabors.ImageSharp.Formats.Gif
int globalColorTableLength = this.logicalScreenDescriptor.GlobalColorTableSize * 3;
this.gifMetadata.GlobalColorTableLength = globalColorTableLength;
- this.globalColorTable = this.MemoryAllocator.Allocate(globalColorTableLength, AllocationOptions.Clean);
+ if (globalColorTableLength > 0)
+ {
+ this.globalColorTable = this.MemoryAllocator.Allocate(globalColorTableLength, AllocationOptions.Clean);
- // Read the global color table data from the stream
- stream.Read(this.globalColorTable.GetSpan());
+ // Read the global color table data from the stream
+ stream.Read(this.globalColorTable.GetSpan());
+ }
}
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs
index 2ae3ae86bc..cf2fd02908 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs
@@ -148,11 +148,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
private void ParseBaselineData()
{
- if (this.componentsCount == this.frame.ComponentCount)
+ if (this.componentsCount != 1)
{
this.ParseBaselineDataInterleaved();
this.spectralConverter.CommitConversion();
}
+ else if (this.frame.ComponentCount == 1)
+ {
+ this.ParseBaselineDataSingleComponent();
+ this.spectralConverter.CommitConversion();
+ }
else
{
this.ParseBaselineDataNonInterleaved();
@@ -161,7 +166,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
private void ParseBaselineDataInterleaved()
{
- // Interleaved
int mcu = 0;
int mcusPerColumn = this.frame.McusPerColumn;
int mcusPerLine = this.frame.McusPerLine;
@@ -198,7 +202,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
if (buffer.NoData)
{
- // It is very likely that some spectral data was decoded before we encountered EOI marker
+ // It is very likely that some spectral data was decoded before we've encountered 'end of scan'
// so we need to decode what's left and return (or maybe throw?)
this.spectralConverter.ConvertStrideBaseline();
return;
@@ -221,9 +225,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
this.HandleRestart();
}
- // convert from spectral to actual pixels via given converter
+ // Convert from spectral to actual pixels via given converter
this.spectralConverter.ConvertStrideBaseline();
}
+
+ // Stride conversion must be sealed for stride conversion approach
+ this.spectralConverter.CommitConversion();
}
private void ParseBaselineDataNonInterleaved()
@@ -261,6 +268,52 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
}
}
+ private void ParseBaselineDataSingleComponent()
+ {
+ JpegComponent component = this.frame.Components[0];
+ int mcuLines = this.frame.McusPerColumn;
+ int w = component.WidthInBlocks;
+ int h = component.SamplingFactors.Height;
+ ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId];
+ ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId];
+
+ ref HuffmanScanBuffer buffer = ref this.scanBuffer;
+
+ for (int i = 0; i < mcuLines; i++)
+ {
+ this.cancellationToken.ThrowIfCancellationRequested();
+
+ // decode from binary to spectral
+ for (int j = 0; j < h; j++)
+ {
+ Span blockSpan = component.SpectralBlocks.DangerousGetRowSpan(j);
+ ref Block8x8 blockRef = ref MemoryMarshal.GetReference(blockSpan);
+
+ for (int k = 0; k < w; k++)
+ {
+ if (buffer.NoData)
+ {
+ // It is very likely that some spectral data was decoded before we've encountered 'end of scan'
+ // so we need to decode what's left and return (or maybe throw?)
+ this.spectralConverter.ConvertStrideBaseline();
+ return;
+ }
+
+ this.DecodeBlockBaseline(
+ component,
+ ref Unsafe.Add(ref blockRef, k),
+ ref dcHuffmanTable,
+ ref acHuffmanTable);
+
+ this.HandleRestart();
+ }
+ }
+
+ // Convert from spectral to actual pixels via given converter
+ this.spectralConverter.ConvertStrideBaseline();
+ }
+ }
+
private void CheckProgressiveData()
{
// Validate successive scan parameters.
diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj
index 1b3a8e27f6..39c85c4f22 100644
--- a/src/ImageSharp/ImageSharp.csproj
+++ b/src/ImageSharp/ImageSharp.csproj
@@ -10,7 +10,7 @@
Apache-2.0
https://github.com/SixLabors/ImageSharp/
$(RepositoryUrl)
- Image Resize Crop Gif Jpg Jpeg Bitmap Pbm Png Tga NetCore
+ Image Resize Crop Gif Jpg Jpeg Bitmap Pbm Png Tga Tiff WebP NetCore
A new, fully featured, fully managed, cross-platform, 2D graphics API for .NET
Debug;Release;Debug-InnerLoop;Release-InnerLoop
@@ -49,6 +49,7 @@
+
@@ -57,13 +58,6 @@
-
-
-
-
-
-
-
True
diff --git a/src/ImageSharp/Memory/Allocators/Internals/RefCountedLifetimeGuard.cs b/src/ImageSharp/Memory/Allocators/Internals/RefCountedLifetimeGuard.cs
deleted file mode 100644
index 61682aa567..0000000000
--- a/src/ImageSharp/Memory/Allocators/Internals/RefCountedLifetimeGuard.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
-
-using System;
-using System.Runtime.InteropServices;
-using System.Threading;
-
-namespace SixLabors.ImageSharp.Memory.Internals
-{
- ///
- /// Implements reference counting lifetime guard mechanism similar to the one provided by ,
- /// but without the restriction of the guarded object being a handle.
- ///
- internal abstract class RefCountedLifetimeGuard : IDisposable
- {
- private int refCount = 1;
- private int disposed;
- private int released;
-
- ~RefCountedLifetimeGuard()
- {
- Interlocked.Exchange(ref this.disposed, 1);
- this.ReleaseRef();
- }
-
- public bool IsDisposed => this.disposed == 1;
-
- public void AddRef() => Interlocked.Increment(ref this.refCount);
-
- public void ReleaseRef()
- {
- Interlocked.Decrement(ref this.refCount);
- if (this.refCount == 0)
- {
- int wasReleased = Interlocked.Exchange(ref this.released, 1);
-
- if (wasReleased == 0)
- {
- this.Release();
- }
- }
- }
-
- public void Dispose()
- {
- int wasDisposed = Interlocked.Exchange(ref this.disposed, 1);
- if (wasDisposed == 0)
- {
- this.ReleaseRef();
- GC.SuppressFinalize(this);
- }
- }
-
- protected abstract void Release();
- }
-}
diff --git a/src/ImageSharp/Memory/Allocators/Internals/RefCountedMemoryLifetimeGuard.cs b/src/ImageSharp/Memory/Allocators/Internals/RefCountedMemoryLifetimeGuard.cs
new file mode 100644
index 0000000000..1c7d6cdfa9
--- /dev/null
+++ b/src/ImageSharp/Memory/Allocators/Internals/RefCountedMemoryLifetimeGuard.cs
@@ -0,0 +1,79 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Runtime.InteropServices;
+using System.Threading;
+using SixLabors.ImageSharp.Diagnostics;
+
+namespace SixLabors.ImageSharp.Memory.Internals
+{
+ ///
+ /// Implements reference counting lifetime guard mechanism for memory resources
+ /// and maintains the value of .
+ ///
+ internal abstract class RefCountedMemoryLifetimeGuard : IDisposable
+ {
+ private int refCount = 1;
+ private int disposed;
+ private int released;
+ private string allocationStackTrace;
+
+ protected RefCountedMemoryLifetimeGuard()
+ {
+ if (MemoryDiagnostics.UndisposedAllocationSubscribed)
+ {
+ this.allocationStackTrace = Environment.StackTrace;
+ }
+
+ MemoryDiagnostics.IncrementTotalUndisposedAllocationCount();
+ }
+
+ ~RefCountedMemoryLifetimeGuard()
+ {
+ Interlocked.Exchange(ref this.disposed, 1);
+ this.ReleaseRef(true);
+ }
+
+ public bool IsDisposed => this.disposed == 1;
+
+ public void AddRef() => Interlocked.Increment(ref this.refCount);
+
+ public void ReleaseRef() => this.ReleaseRef(false);
+
+ public void Dispose()
+ {
+ int wasDisposed = Interlocked.Exchange(ref this.disposed, 1);
+ if (wasDisposed == 0)
+ {
+ this.ReleaseRef();
+ GC.SuppressFinalize(this);
+ }
+ }
+
+ protected abstract void Release();
+
+ private void ReleaseRef(bool finalizing)
+ {
+ Interlocked.Decrement(ref this.refCount);
+ if (this.refCount == 0)
+ {
+ int wasReleased = Interlocked.Exchange(ref this.released, 1);
+
+ if (wasReleased == 0)
+ {
+ if (!finalizing)
+ {
+ MemoryDiagnostics.DecrementTotalUndisposedAllocationCount();
+ }
+ else if (this.allocationStackTrace != null)
+ {
+ MemoryDiagnostics.RaiseUndisposedMemoryResource(this.allocationStackTrace);
+ }
+
+ this.Release();
+ }
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Memory/Allocators/Internals/SharedArrayPoolBuffer{T}.cs b/src/ImageSharp/Memory/Allocators/Internals/SharedArrayPoolBuffer{T}.cs
index 9302c67e7d..2ea76da957 100644
--- a/src/ImageSharp/Memory/Allocators/Internals/SharedArrayPoolBuffer{T}.cs
+++ b/src/ImageSharp/Memory/Allocators/Internals/SharedArrayPoolBuffer{T}.cs
@@ -60,7 +60,7 @@ namespace SixLabors.ImageSharp.Memory.Internals
}
}
- private sealed class LifetimeGuard : RefCountedLifetimeGuard
+ private sealed class LifetimeGuard : RefCountedMemoryLifetimeGuard
{
private byte[] array;
diff --git a/src/ImageSharp/Memory/Allocators/Internals/UniformUnmanagedMemoryPool.LifetimeGuards.cs b/src/ImageSharp/Memory/Allocators/Internals/UniformUnmanagedMemoryPool.LifetimeGuards.cs
index 666b248552..151fef69c4 100644
--- a/src/ImageSharp/Memory/Allocators/Internals/UniformUnmanagedMemoryPool.LifetimeGuards.cs
+++ b/src/ImageSharp/Memory/Allocators/Internals/UniformUnmanagedMemoryPool.LifetimeGuards.cs
@@ -20,9 +20,9 @@ namespace SixLabors.ImageSharp.Memory.Internals
return buffer;
}
- public RefCountedLifetimeGuard CreateGroupLifetimeGuard(UnmanagedMemoryHandle[] handles) => new GroupLifetimeGuard(this, handles);
+ public RefCountedMemoryLifetimeGuard CreateGroupLifetimeGuard(UnmanagedMemoryHandle[] handles) => new GroupLifetimeGuard(this, handles);
- private sealed class GroupLifetimeGuard : RefCountedLifetimeGuard
+ private sealed class GroupLifetimeGuard : RefCountedMemoryLifetimeGuard
{
private readonly UniformUnmanagedMemoryPool pool;
private readonly UnmanagedMemoryHandle[] handles;
diff --git a/src/ImageSharp/Memory/Allocators/Internals/UnmanagedBufferLifetimeGuard.cs b/src/ImageSharp/Memory/Allocators/Internals/UnmanagedBufferLifetimeGuard.cs
index 5f0759f203..8221120240 100644
--- a/src/ImageSharp/Memory/Allocators/Internals/UnmanagedBufferLifetimeGuard.cs
+++ b/src/ImageSharp/Memory/Allocators/Internals/UnmanagedBufferLifetimeGuard.cs
@@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Memory.Internals
///
/// Defines a strategy for managing unmanaged memory ownership.
///
- internal abstract class UnmanagedBufferLifetimeGuard : RefCountedLifetimeGuard
+ internal abstract class UnmanagedBufferLifetimeGuard : RefCountedMemoryLifetimeGuard
{
private UnmanagedMemoryHandle handle;
diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs
index 21faf8e562..01aac3148e 100644
--- a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs
+++ b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs
@@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Memory
public sealed class Owned : MemoryGroup, IEnumerable>
{
private IMemoryOwner[] memoryOwners;
- private RefCountedLifetimeGuard groupLifetimeGuard;
+ private RefCountedMemoryLifetimeGuard groupLifetimeGuard;
public Owned(IMemoryOwner[] memoryOwners, int bufferLength, long totalLength, bool swappable)
: base(bufferLength, totalLength)
diff --git a/src/ImageSharp/Metadata/ImageMetadata.cs b/src/ImageSharp/Metadata/ImageMetadata.cs
index 39e1cef8a0..89592f776c 100644
--- a/src/ImageSharp/Metadata/ImageMetadata.cs
+++ b/src/ImageSharp/Metadata/ImageMetadata.cs
@@ -72,7 +72,7 @@ namespace SixLabors.ImageSharp.Metadata
///
/// Gets or sets the resolution of the image in x- direction.
- /// It is defined as the number of dots per inch and should be an positive value.
+ /// It is defined as the number of dots per and should be an positive value.
///
/// The density of the image in x- direction.
public double HorizontalResolution
@@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp.Metadata
///
/// Gets or sets the resolution of the image in y- direction.
- /// It is defined as the number of dots per inch and should be an positive value.
+ /// It is defined as the number of dots per and should be an positive value.
///
/// The density of the image in y- direction.
public double VerticalResolution
@@ -108,10 +108,28 @@ namespace SixLabors.ImageSharp.Metadata
///
/// Gets or sets unit of measure used when reporting resolution.
- /// 00 : No units; width:height pixel aspect ratio = Ydensity:Xdensity
- /// 01 : Pixels per inch (2.54 cm)
- /// 02 : Pixels per centimeter
- /// 03 : Pixels per meter
+ ///
+ ///
+ /// Value
+ /// Unit
+ ///
+ /// -
+ /// AspectRatio (00)
+ /// No units; width:height pixel aspect ratio = Ydensity:Xdensity
+ ///
+ /// -
+ /// PixelsPerInch (01)
+ /// Pixels per inch (2.54 cm)
+ ///
+ /// -
+ /// PixelsPerCentimeter (02)
+ /// Pixels per centimeter
+ ///
+ /// -
+ /// PixelsPerMeter (03)
+ /// Pixels per meter (100 cm)
+ ///
+ ///
///
public PixelResolutionUnit ResolutionUnits { get; set; }
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/DC-X008-Translation-2019-E.pdf b/src/ImageSharp/Metadata/Profiles/Exif/DC-X008-Translation-2019-E.pdf
index cd7141fc8d..22a1058168 100644
Binary files a/src/ImageSharp/Metadata/Profiles/Exif/DC-X008-Translation-2019-E.pdf and b/src/ImageSharp/Metadata/Profiles/Exif/DC-X008-Translation-2019-E.pdf differ
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifConstants.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifConstants.cs
index 0c81f14dd4..543e3d5c4d 100644
--- a/src/ImageSharp/Metadata/Profiles/Exif/ExifConstants.cs
+++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifConstants.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
+using System.Text;
namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
{
@@ -22,5 +23,8 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
0x00,
0x2A
};
+
+ // UTF-8 is better than ASCII, UTF-8 encodes the ASCII codes the same way
+ public static Encoding DefaultEncoding => Encoding.UTF8;
}
}
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifEncodedStringHelpers.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifEncodedStringHelpers.cs
new file mode 100644
index 0000000000..5fd613b1f0
--- /dev/null
+++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifEncodedStringHelpers.cs
@@ -0,0 +1,121 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Buffers.Binary;
+using System.Text;
+using static SixLabors.ImageSharp.Metadata.Profiles.Exif.EncodedString;
+
+namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
+{
+ internal static class ExifEncodedStringHelpers
+ {
+ public const int CharacterCodeBytesLength = 8;
+
+ private const ulong AsciiCode = 0x_00_00_00_49_49_43_53_41;
+ private const ulong JISCode = 0x_00_00_00_00_00_53_49_4A;
+ private const ulong UnicodeCode = 0x_45_44_4F_43_49_4E_55;
+ private const ulong UndefinedCode = 0x_00_00_00_00_00_00_00_00;
+
+ private static ReadOnlySpan AsciiCodeBytes => new byte[] { 0x41, 0x53, 0x43, 0x49, 0x49, 0, 0, 0 };
+
+ private static ReadOnlySpan JISCodeBytes => new byte[] { 0x4A, 0x49, 0x53, 0, 0, 0, 0, 0 };
+
+ private static ReadOnlySpan UnicodeCodeBytes => new byte[] { 0x55, 0x4E, 0x49, 0x43, 0x4F, 0x44, 0x45, 0 };
+
+ private static ReadOnlySpan UndefinedCodeBytes => new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ // 20932 EUC-JP Japanese (JIS 0208-1990 and 0212-1990)
+ // https://docs.microsoft.com/en-us/dotnet/api/system.text.encoding?view=net-6.0
+ private static Encoding JIS0208Encoding => CodePagesEncodingProvider.Instance.GetEncoding(20932);
+
+ public static bool IsEncodedString(ExifTagValue tag) => tag switch
+ {
+ ExifTagValue.UserComment or ExifTagValue.GPSProcessingMethod or ExifTagValue.GPSAreaInformation => true,
+ _ => false
+ };
+
+ public static ReadOnlySpan GetCodeBytes(CharacterCode code) => code switch
+ {
+ CharacterCode.ASCII => AsciiCodeBytes,
+ CharacterCode.JIS => JISCodeBytes,
+ CharacterCode.Unicode => UnicodeCodeBytes,
+ CharacterCode.Undefined => UndefinedCodeBytes,
+ _ => UndefinedCodeBytes
+ };
+
+ public static Encoding GetEncoding(CharacterCode code) => code switch
+ {
+ CharacterCode.ASCII => Encoding.ASCII,
+ CharacterCode.JIS => JIS0208Encoding,
+ CharacterCode.Unicode => Encoding.Unicode,
+ CharacterCode.Undefined => Encoding.UTF8,
+ _ => Encoding.UTF8
+ };
+
+ public static bool TryParse(ReadOnlySpan buffer, out EncodedString encodedString)
+ {
+ if (TryDetect(buffer, out CharacterCode code))
+ {
+ string text = GetEncoding(code).GetString(buffer.Slice(CharacterCodeBytesLength));
+ encodedString = new EncodedString(code, text);
+ return true;
+ }
+
+ encodedString = default;
+ return false;
+ }
+
+ public static uint GetDataLength(EncodedString encodedString) =>
+ (uint)GetEncoding(encodedString.Code).GetByteCount(encodedString.Text) + CharacterCodeBytesLength;
+
+ public static int Write(EncodedString encodedString, Span destination)
+ {
+ GetCodeBytes(encodedString.Code).CopyTo(destination);
+
+ string text = encodedString.Text;
+ int count = Write(GetEncoding(encodedString.Code), text, destination.Slice(CharacterCodeBytesLength));
+
+ return CharacterCodeBytesLength + count;
+ }
+
+ public static unsafe int Write(Encoding encoding, string value, Span destination)
+ {
+ fixed (char* c = value)
+ {
+ fixed (byte* b = destination)
+ {
+ return encoding.GetBytes(c, value.Length, b, destination.Length);
+ }
+ }
+ }
+
+ private static bool TryDetect(ReadOnlySpan buffer, out CharacterCode code)
+ {
+ if (buffer.Length >= CharacterCodeBytesLength)
+ {
+ ulong test = BinaryPrimitives.ReadUInt64LittleEndian(buffer);
+ switch (test)
+ {
+ case AsciiCode:
+ code = CharacterCode.ASCII;
+ return true;
+ case JISCode:
+ code = CharacterCode.JIS;
+ return true;
+ case UnicodeCode:
+ code = CharacterCode.Unicode;
+ return true;
+ case UndefinedCode:
+ code = CharacterCode.Undefined;
+ return true;
+ default:
+ break;
+ }
+ }
+
+ code = default;
+ return false;
+ }
+ }
+}
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs
index 2fcd1cc07d..9b5e098c83 100644
--- a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs
+++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs
@@ -241,9 +241,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
return result;
}
- private byte ConvertToByte(ReadOnlySpan buffer) => buffer[0];
-
- private string ConvertToString(ReadOnlySpan buffer)
+ private static string ConvertToString(Encoding encoding, ReadOnlySpan buffer)
{
int nullCharIndex = buffer.IndexOf((byte)0);
@@ -252,9 +250,11 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
buffer = buffer.Slice(0, nullCharIndex);
}
- return Encoding.UTF8.GetString(buffer);
+ return encoding.GetString(buffer);
}
+ private byte ConvertToByte(ReadOnlySpan buffer) => buffer[0];
+
private object ConvertValue(ExifDataType dataType, ReadOnlySpan buffer, bool isArray)
{
if (buffer.Length == 0)
@@ -267,8 +267,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
case ExifDataType.Unknown:
return null;
case ExifDataType.Ascii:
- return this.ConvertToString(buffer);
+ return ConvertToString(ExifConstants.DefaultEncoding, buffer);
case ExifDataType.Byte:
+ case ExifDataType.Undefined:
if (!isArray)
{
return this.ConvertToByte(buffer);
@@ -354,13 +355,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
}
return ToArray(dataType, buffer, this.ConvertToUInt64);
- case ExifDataType.Undefined:
- if (!isArray)
- {
- return this.ConvertToByte(buffer);
- }
- return buffer.ToArray();
default:
throw new NotSupportedException($"Data type {dataType} is not supported.");
}
@@ -453,7 +448,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
// Likewise, tags that point to other IFDs, like e.g. the SubIFDs tag, are now allowed to have the datatype TIFF_IFD8 in BigTIFF.
// Again, the old datatypes TIFF_IFD, and the hardly recommendable TIFF_LONG, are still valid, too.
// https://www.awaresystems.be/imaging/tiff/bigtiff.html
- ExifValue exifValue = null;
+ ExifValue exifValue;
switch (tag)
{
case ExifTagValue.StripOffsets:
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifUcs2StringHelpers.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifUcs2StringHelpers.cs
new file mode 100644
index 0000000000..ccc1c80ade
--- /dev/null
+++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifUcs2StringHelpers.cs
@@ -0,0 +1,21 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Text;
+
+namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
+{
+ internal static class ExifUcs2StringHelpers
+ {
+ public static Encoding Ucs2Encoding => Encoding.GetEncoding("UCS-2");
+
+ public static bool IsUcs2Tag(ExifTagValue tag) => tag switch
+ {
+ ExifTagValue.XPAuthor or ExifTagValue.XPComment or ExifTagValue.XPKeywords or ExifTagValue.XPSubject or ExifTagValue.XPTitle => true,
+ _ => false,
+ };
+
+ public static int Write(string value, Span destination) => ExifEncodedStringHelpers.Write(Ucs2Encoding, value, destination);
+ }
+}
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs
index e2ed569548..a14539bca2 100644
--- a/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs
+++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs
@@ -4,7 +4,6 @@
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
-using System.Text;
namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
{
@@ -274,9 +273,19 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
{
object value = exifValue.GetValue();
+ if (ExifUcs2StringHelpers.IsUcs2Tag((ExifTagValue)(ushort)exifValue.Tag))
+ {
+ return (uint)ExifUcs2StringHelpers.Ucs2Encoding.GetByteCount((string)value);
+ }
+
+ if (value is EncodedString encodedString)
+ {
+ return ExifEncodedStringHelpers.GetDataLength(encodedString);
+ }
+
if (exifValue.DataType == ExifDataType.Ascii)
{
- return (uint)Encoding.UTF8.GetBytes((string)value).Length + 1;
+ return (uint)ExifConstants.DefaultEncoding.GetByteCount((string)value) + 1;
}
if (value is Array arrayValue)
@@ -289,11 +298,6 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
private static int WriteArray(IExifValue value, Span destination, int offset)
{
- if (value.DataType == ExifDataType.Ascii)
- {
- return WriteValue(ExifDataType.Ascii, value.GetValue(), destination, offset);
- }
-
int newOffset = offset;
foreach (object obj in (Array)value.GetValue())
{
@@ -378,7 +382,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
switch (dataType)
{
case ExifDataType.Ascii:
- offset = Write(Encoding.UTF8.GetBytes((string)value), destination, offset);
+ offset = Write(ExifConstants.DefaultEncoding.GetBytes((string)value), destination, offset);
destination[offset] = 0;
return offset + 1;
case ExifDataType.Byte:
@@ -425,14 +429,25 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
}
}
- internal static int WriteValue(IExifValue value, Span destination, int offset)
+ internal static int WriteValue(IExifValue exifValue, Span destination, int offset)
{
- if (value.IsArray && value.DataType != ExifDataType.Ascii)
+ object value = exifValue.GetValue();
+
+ if (ExifUcs2StringHelpers.IsUcs2Tag((ExifTagValue)(ushort)exifValue.Tag))
+ {
+ return offset + ExifUcs2StringHelpers.Write((string)value, destination.Slice(offset));
+ }
+ else if (value is EncodedString encodedString)
+ {
+ return offset + ExifEncodedStringHelpers.Write(encodedString, destination.Slice(offset));
+ }
+
+ if (exifValue.IsArray)
{
- return WriteArray(value, destination, offset);
+ return WriteArray(exifValue, destination, offset);
}
- return WriteValue(value.DataType, value.GetValue(), destination, offset);
+ return WriteValue(exifValue.DataType, value, destination, offset);
}
}
}
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.ByteArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.ByteArray.cs
index fdde66c513..964fb6e948 100644
--- a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.ByteArray.cs
+++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.ByteArray.cs
@@ -41,31 +41,6 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
///
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.
///
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.EncodedString.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.EncodedString.cs
new file mode 100644
index 0000000000..335098a435
--- /dev/null
+++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.EncodedString.cs
@@ -0,0 +1,24 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
+{
+ ///
+ public abstract partial class ExifTag
+ {
+ ///
+ /// Gets the UserComment exif tag.
+ ///
+ public static ExifTag UserComment { get; } = new ExifTag(ExifTagValue.UserComment);
+
+ ///
+ /// 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);
+ }
+}
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Ucs2String.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Ucs2String.cs
new file mode 100644
index 0000000000..a6911d76d7
--- /dev/null
+++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Ucs2String.cs
@@ -0,0 +1,34 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
+{
+ ///
+ public abstract partial class ExifTag
+ {
+ ///
+ /// Gets the title tag used by Windows (encoded in UCS2).
+ ///
+ public static ExifTag XPTitle => new ExifTag(ExifTagValue.XPTitle);
+
+ ///
+ /// Gets the comment tag used by Windows (encoded in UCS2).
+ ///
+ public static ExifTag XPComment => new ExifTag(ExifTagValue.XPComment);
+
+ ///
+ /// Gets the author tag used by Windows (encoded in UCS2).
+ ///
+ public static ExifTag XPAuthor => new ExifTag(ExifTagValue.XPAuthor);
+
+ ///
+ /// Gets the keywords tag used by Windows (encoded in UCS2).
+ ///
+ public static ExifTag XPKeywords => new ExifTag(ExifTagValue.XPKeywords);
+
+ ///
+ /// Gets the subject tag used by Windows (encoded in UCS2).
+ ///
+ public static ExifTag XPSubject => new ExifTag(ExifTagValue.XPSubject);
+ }
+}
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Undefined.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Undefined.cs
index 1d9af6adce..58886f4036 100644
--- a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Undefined.cs
+++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Undefined.cs
@@ -31,11 +31,6 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
///
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.
///
@@ -71,16 +66,6 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
///
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.
///
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/EncodedString.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/EncodedString.cs
new file mode 100644
index 0000000000..e9cd27427c
--- /dev/null
+++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/EncodedString.cs
@@ -0,0 +1,94 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+
+namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
+{
+ ///
+ /// The EXIF encoded string structure.
+ ///
+ public readonly struct EncodedString : IEquatable
+ {
+ ///
+ /// Initializes a new instance of the struct.
+ /// Default use Unicode character code.
+ ///
+ /// The text value.
+ public EncodedString(string text)
+ : this(CharacterCode.Unicode, text)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The character code.
+ /// The text value.
+ public EncodedString(CharacterCode code, string text)
+ {
+ this.Text = text;
+ this.Code = code;
+ }
+
+ ///
+ /// The 8-byte character code enum.
+ ///
+ public enum CharacterCode
+ {
+ ///
+ /// The ASCII (ITU-T T.50 IA5) character code.
+ ///
+ ASCII,
+
+ ///
+ /// The JIS (X208-1990) character code.
+ ///
+ JIS,
+
+ ///
+ /// The Unicode character code.
+ ///
+ Unicode,
+
+ ///
+ /// The undefined character code.
+ ///
+ Undefined
+ }
+
+ ///
+ /// Gets the character ode.
+ ///
+ public CharacterCode Code { get; }
+
+ ///
+ /// Gets the text.
+ ///
+ public string Text { get; }
+
+ ///
+ /// Converts the specified to an instance of this type.
+ ///
+ /// The text value.
+ public static implicit operator EncodedString(string text) => new(text);
+
+ ///
+ /// Converts the specified to a .
+ ///
+ /// The to convert.
+ public static explicit operator string(EncodedString encodedString) => encodedString.Text;
+
+ ///
+ public override bool Equals(object obj) => obj is EncodedString other && this.Equals(other);
+
+ ///
+ public bool Equals(EncodedString other) => this.Text == other.Text && this.Code == other.Code;
+
+ ///
+ public override int GetHashCode() => HashCode.Combine(this.Text, this.Code);
+
+ ///
+ public override string ToString() => this.Text;
+ }
+}
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifEncodedString.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifEncodedString.cs
new file mode 100644
index 0000000000..ba9fca5c8f
--- /dev/null
+++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifEncodedString.cs
@@ -0,0 +1,55 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System.Globalization;
+
+namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
+{
+ internal sealed class ExifEncodedString : ExifValue
+ {
+ public ExifEncodedString(ExifTag tag)
+ : base(tag)
+ {
+ }
+
+ public ExifEncodedString(ExifTagValue tag)
+ : base(tag)
+ {
+ }
+
+ private ExifEncodedString(ExifEncodedString value)
+ : base(value)
+ {
+ }
+
+ public override ExifDataType DataType => ExifDataType.Undefined;
+
+ protected override string StringValue => this.Value.Text;
+
+ public override bool TrySetValue(object value)
+ {
+ if (base.TrySetValue(value))
+ {
+ return true;
+ }
+
+ if (value is string stringValue)
+ {
+ this.Value = new EncodedString(stringValue);
+ return true;
+ }
+ else if (value is byte[] buffer)
+ {
+ if (ExifEncodedStringHelpers.TryParse(buffer, out EncodedString encodedString))
+ {
+ this.Value = encodedString;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public override IExifValue DeepClone() => new ExifEncodedString(this);
+ }
+}
diff --git a/src/ImageSharp/Processing/OrientationMode.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifOrientationMode.cs
similarity index 69%
rename from src/ImageSharp/Processing/OrientationMode.cs
rename to src/ImageSharp/Metadata/Profiles/Exif/Values/ExifOrientationMode.cs
index a8ba5a55c0..b68390ae01 100644
--- a/src/ImageSharp/Processing/OrientationMode.cs
+++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifOrientationMode.cs
@@ -1,56 +1,56 @@
-// Copyright (c) Six Labors.
+// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
-namespace SixLabors.ImageSharp.Processing
+namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
{
///
/// Enumerates the available orientation values supplied by EXIF metadata.
///
- internal enum OrientationMode : ushort
+ public static class ExifOrientationMode
{
///
/// Unknown rotation.
///
- Unknown = 0,
+ public const ushort Unknown = 0;
///
/// The 0th row at the top, the 0th column on the left.
///
- TopLeft = 1,
+ public const ushort TopLeft = 1;
///
/// The 0th row at the top, the 0th column on the right.
///
- TopRight = 2,
+ public const ushort TopRight = 2;
///
/// The 0th row at the bottom, the 0th column on the right.
///
- BottomRight = 3,
+ public const ushort BottomRight = 3;
///
/// The 0th row at the bottom, the 0th column on the left.
///
- BottomLeft = 4,
+ public const ushort BottomLeft = 4;
///
/// The 0th row on the left, the 0th column at the top.
///
- LeftTop = 5,
+ public const ushort LeftTop = 5;
///
/// The 0th row at the right, the 0th column at the top.
///
- RightTop = 6,
+ public const ushort RightTop = 6;
///
/// The 0th row on the right, the 0th column at the bottom.
///
- RightBottom = 7,
+ public const ushort RightBottom = 7;
///
/// The 0th row on the left, the 0th column at the bottom.
///
- LeftBottom = 8
+ public const ushort LeftBottom = 8;
}
}
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifUcs2String.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifUcs2String.cs
new file mode 100644
index 0000000000..42637925c7
--- /dev/null
+++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifUcs2String.cs
@@ -0,0 +1,47 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
+{
+ internal sealed class ExifUcs2String : ExifValue
+ {
+ public ExifUcs2String(ExifTag tag)
+ : base(tag)
+ {
+ }
+
+ public ExifUcs2String(ExifTagValue tag)
+ : base(tag)
+ {
+ }
+
+ private ExifUcs2String(ExifUcs2String value)
+ : base(value)
+ {
+ }
+
+ public override ExifDataType DataType => ExifDataType.Byte;
+
+ protected override string StringValue => this.Value;
+
+ public override object GetValue() => this.Value;
+
+ public override bool TrySetValue(object value)
+ {
+ if (base.TrySetValue(value))
+ {
+ return true;
+ }
+
+ if (value is byte[] buffer)
+ {
+ this.Value = ExifUcs2StringHelpers.Ucs2Encoding.GetString(buffer);
+ return true;
+ }
+
+ return false;
+ }
+
+ public override IExifValue DeepClone() => new ExifUcs2String(this);
+ }
+}
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs
index 33fb90cc04..fa5cf9b2fa 100644
--- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs
+++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs
@@ -15,21 +15,36 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
{
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.Long8: return isArray ? (ExifValue)new ExifLong8Array(tag) : new ExifLong8(tag);
- case ExifDataType.Rational: return isArray ? (ExifValue)new ExifRationalArray(tag) : new ExifRational(tag);
- case ExifDataType.Short: return isArray ? (ExifValue)new ExifShortArray(tag) : new ExifShort(tag);
- case ExifDataType.SignedByte: return isArray ? (ExifValue)new ExifSignedByteArray(tag) : new ExifSignedByte(tag);
- case ExifDataType.SignedLong: return isArray ? (ExifValue)new ExifSignedLongArray(tag) : new ExifSignedLong(tag);
- case ExifDataType.SignedLong8: return isArray ? (ExifValue)new ExifSignedLong8Array(tag) : new ExifSignedLong8(tag);
- case ExifDataType.SignedRational: return isArray ? (ExifValue)new ExifSignedRationalArray(tag) : new ExifSignedRational(tag);
- case ExifDataType.SignedShort: return isArray ? (ExifValue)new ExifSignedShortArray(tag) : new ExifSignedShort(tag);
- case ExifDataType.Ascii: return new ExifString(tag);
- case ExifDataType.Undefined: return isArray ? (ExifValue)new ExifByteArray(tag, dataType) : new ExifByte(tag, dataType);
- default: return null;
+ case ExifDataType.Byte:
+ return isArray ? new ExifByteArray(tag, dataType) : new ExifByte(tag, dataType);
+ case ExifDataType.DoubleFloat:
+ return isArray ? new ExifDoubleArray(tag) : new ExifDouble(tag);
+ case ExifDataType.SingleFloat:
+ return isArray ? new ExifFloatArray(tag) : new ExifFloat(tag);
+ case ExifDataType.Long:
+ return isArray ? new ExifLongArray(tag) : new ExifLong(tag);
+ case ExifDataType.Long8:
+ return isArray ? new ExifLong8Array(tag) : new ExifLong8(tag);
+ case ExifDataType.Rational:
+ return isArray ? new ExifRationalArray(tag) : new ExifRational(tag);
+ case ExifDataType.Short:
+ return isArray ? new ExifShortArray(tag) : new ExifShort(tag);
+ case ExifDataType.SignedByte:
+ return isArray ? new ExifSignedByteArray(tag) : new ExifSignedByte(tag);
+ case ExifDataType.SignedLong:
+ return isArray ? new ExifSignedLongArray(tag) : new ExifSignedLong(tag);
+ case ExifDataType.SignedLong8:
+ return isArray ? new ExifSignedLong8Array(tag) : new ExifSignedLong8(tag);
+ case ExifDataType.SignedRational:
+ return isArray ? new ExifSignedRationalArray(tag) : new ExifSignedRational(tag);
+ case ExifDataType.SignedShort:
+ return isArray ? new ExifSignedShortArray(tag) : new ExifSignedShort(tag);
+ case ExifDataType.Ascii:
+ return new ExifString(tag);
+ case ExifDataType.Undefined:
+ return isArray ? new ExifByteArray(tag, dataType) : new ExifByte(tag, dataType);
+ default:
+ return null;
}
}
@@ -37,275 +52,530 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
{
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.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.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.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.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.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.ColorResponseUnit);
- 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.SubIFDs: return new ExifLongArray(ExifTag.SubIFDs);
+ case ExifTagValue.FreeOffsets:
+ return new ExifLongArray(ExifTag.FreeOffsets);
+ case ExifTagValue.FreeByteCounts:
+ return new ExifLongArray(ExifTag.FreeByteCounts);
+ case ExifTagValue.ColorResponseUnit:
+ return new ExifLongArray(ExifTag.ColorResponseUnit);
+ 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.SubIFDs:
+ return new ExifLongArray(ExifTag.SubIFDs);
- case ExifTagValue.ImageWidth: return new ExifNumber(ExifTag.ImageWidth);
- case ExifTagValue.ImageLength: return new ExifNumber(ExifTag.ImageLength);
- case ExifTagValue.RowsPerStrip: return new ExifNumber(ExifTag.RowsPerStrip);
- 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.ImageWidth:
+ return new ExifNumber(ExifTag.ImageWidth);
+ case ExifTagValue.ImageLength:
+ return new ExifNumber(ExifTag.ImageLength);
+ case ExifTagValue.RowsPerStrip:
+ return new ExifNumber(ExifTag.RowsPerStrip);
+ 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.StripByteCounts: return new ExifNumberArray(ExifTag.StripByteCounts);
- 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.StripByteCounts:
+ return new ExifNumberArray(ExifTag.StripByteCounts);
+ 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.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.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.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.LensSpecification: return new ExifRationalArray(ExifTag.LensSpecification);
+ 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.LensSpecification:
+ return new ExifRationalArray(ExifTag.LensSpecification);
- 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.Predictor: return new ExifShort(ExifTag.Predictor);
- 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.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.Predictor:
+ return new ExifShort(ExifTag.Predictor);
+ 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.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.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.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.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.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.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.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);
+ 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.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);
- default: return null;
+ case ExifTagValue.XPTitle:
+ return new ExifUcs2String(ExifTag.XPTitle);
+ case ExifTagValue.XPComment:
+ return new ExifUcs2String(ExifTag.XPComment);
+ case ExifTagValue.XPAuthor:
+ return new ExifUcs2String(ExifTag.XPAuthor);
+ case ExifTagValue.XPKeywords:
+ return new ExifUcs2String(ExifTag.XPKeywords);
+ case ExifTagValue.XPSubject:
+ return new ExifUcs2String(ExifTag.XPSubject);
+
+ case ExifTagValue.UserComment:
+ return new ExifEncodedString(ExifTag.UserComment);
+ case ExifTagValue.GPSProcessingMethod:
+ return new ExifEncodedString(ExifTag.GPSProcessingMethod);
+ case ExifTagValue.GPSAreaInformation:
+ return new ExifEncodedString(ExifTag.GPSAreaInformation);
+
+ default:
+ return null;
}
}
}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/AutoOrientProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/AutoOrientProcessor{TPixel}.cs
index 6c5219b3a5..c9f9d4327a 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Linear/AutoOrientProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/AutoOrientProcessor{TPixel}.cs
@@ -28,42 +28,42 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
///
protected override void BeforeImageApply()
{
- OrientationMode orientation = GetExifOrientation(this.Source);
+ ushort orientation = GetExifOrientation(this.Source);
Size size = this.SourceRectangle.Size;
switch (orientation)
{
- case OrientationMode.TopRight:
+ case ExifOrientationMode.TopRight:
new FlipProcessor(FlipMode.Horizontal).Execute(this.Configuration, this.Source, this.SourceRectangle);
break;
- case OrientationMode.BottomRight:
+ case ExifOrientationMode.BottomRight:
new RotateProcessor((int)RotateMode.Rotate180, size).Execute(this.Configuration, this.Source, this.SourceRectangle);
break;
- case OrientationMode.BottomLeft:
+ case ExifOrientationMode.BottomLeft:
new FlipProcessor(FlipMode.Vertical).Execute(this.Configuration, this.Source, this.SourceRectangle);
break;
- case OrientationMode.LeftTop:
+ case ExifOrientationMode.LeftTop:
new RotateProcessor((int)RotateMode.Rotate90, size).Execute(this.Configuration, this.Source, this.SourceRectangle);
new FlipProcessor(FlipMode.Horizontal).Execute(this.Configuration, this.Source, this.SourceRectangle);
break;
- case OrientationMode.RightTop:
+ case ExifOrientationMode.RightTop:
new RotateProcessor((int)RotateMode.Rotate90, size).Execute(this.Configuration, this.Source, this.SourceRectangle);
break;
- case OrientationMode.RightBottom:
+ case ExifOrientationMode.RightBottom:
new FlipProcessor(FlipMode.Vertical).Execute(this.Configuration, this.Source, this.SourceRectangle);
new RotateProcessor((int)RotateMode.Rotate270, size).Execute(this.Configuration, this.Source, this.SourceRectangle);
break;
- case OrientationMode.LeftBottom:
+ case ExifOrientationMode.LeftBottom:
new RotateProcessor((int)RotateMode.Rotate270, size).Execute(this.Configuration, this.Source, this.SourceRectangle);
break;
- case OrientationMode.Unknown:
- case OrientationMode.TopLeft:
+ case ExifOrientationMode.Unknown:
+ case ExifOrientationMode.TopLeft:
default:
break;
}
@@ -81,32 +81,32 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// Returns the current EXIF orientation
///
/// The image to auto rotate.
- /// The
- private static OrientationMode GetExifOrientation(Image source)
+ /// The
+ private static ushort GetExifOrientation(Image source)
{
if (source.Metadata.ExifProfile is null)
{
- return OrientationMode.Unknown;
+ return ExifOrientationMode.Unknown;
}
IExifValue value = source.Metadata.ExifProfile.GetValue(ExifTag.Orientation);
if (value is null)
{
- return OrientationMode.Unknown;
+ return ExifOrientationMode.Unknown;
}
- OrientationMode orientation;
+ ushort orientation;
if (value.DataType == ExifDataType.Short)
{
- orientation = (OrientationMode)value.Value;
+ orientation = value.Value;
}
else
{
- orientation = (OrientationMode)Convert.ToUInt16(value.Value);
+ orientation = Convert.ToUInt16(value.Value);
source.Metadata.ExifProfile.RemoveValue(ExifTag.Orientation);
}
- source.Metadata.ExifProfile.SetValue(ExifTag.Orientation, (ushort)OrientationMode.TopLeft);
+ source.Metadata.ExifProfile.SetValue(ExifTag.Orientation, ExifOrientationMode.TopLeft);
return orientation;
}
diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs
index 9db666c374..988c056608 100644
--- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs
+++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs
@@ -39,10 +39,9 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
using var memoryStream = new MemoryStream(this.jpegBytes);
using var bufferedStream = new BufferedReadStream(Configuration.Default, memoryStream);
- var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder { IgnoreMetadata = true });
+ using var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder { IgnoreMetadata = true });
var scanDecoder = new HuffmanScanDecoder(bufferedStream, new NoopSpectralConverter(), cancellationToken: default);
decoder.ParseStream(bufferedStream, scanDecoder, cancellationToken: default);
- decoder.Dispose();
}
// We want to test only stream parsing and scan decoding, we don't need to convert spectral data to actual pixels
diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs
index 6bf606ac90..c8ecdb717b 100644
--- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs
@@ -247,5 +247,17 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif
"Disco")
.Dispose();
}
+
+ // https://github.com/SixLabors/ImageSharp/issues/1962
+ [Theory]
+ [WithFile(TestImages.Gif.Issues.Issue1962NoColorTable, PixelTypes.Rgba32)]
+ public void Issue1962(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel
+ {
+ using Image image = provider.GetImage();
+ image.DebugSave(provider);
+
+ image.CompareFirstFrameToReferenceOutput(ImageComparer.Exact, provider);
+ }
}
}
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs
index db5169a045..70cbc3af72 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs
@@ -42,6 +42,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
// High depth images
TestImages.Jpeg.Baseline.Testorig12bit,
+
+ // Grayscale jpeg with 2x2 sampling factors (not a usual thing to encounter in the wild)
+ TestImages.Jpeg.Baseline.GrayscaleSampling2x2,
};
public static string[] ProgressiveTestJpegs =
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs
index 53c81631de..840cc9f685 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs
@@ -4,6 +4,7 @@
using System;
using System.IO;
using System.Runtime.CompilerServices;
+using System.Text;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Metadata;
@@ -300,5 +301,72 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
});
Assert.Null(ex);
}
+
+ [Fact]
+ public void EncodedStringTags_WriteAndRead()
+ {
+ using var memoryStream = new MemoryStream();
+ using (var image = Image.Load(TestFile.GetInputFileFullPath(TestImages.Jpeg.Baseline.Calliphora)))
+ {
+ var exif = new ExifProfile();
+
+ exif.SetValue(ExifTag.GPSDateStamp, "2022-01-06");
+
+ exif.SetValue(ExifTag.XPTitle, "A bit of test metadata for image title");
+ exif.SetValue(ExifTag.XPComment, "A bit of test metadata for image comment");
+ exif.SetValue(ExifTag.XPAuthor, "Dan Petitt");
+ exif.SetValue(ExifTag.XPKeywords, "Keyword1;Keyword2");
+ exif.SetValue(ExifTag.XPSubject, "This is a subject");
+
+ // exif.SetValue(ExifTag.UserComment, new EncodedString(EncodedString.CharacterCode.JIS, "ビッ"));
+ exif.SetValue(ExifTag.UserComment, new EncodedString(EncodedString.CharacterCode.JIS, "eng comment text (JIS)"));
+
+ exif.SetValue(ExifTag.GPSProcessingMethod, new EncodedString(EncodedString.CharacterCode.ASCII, "GPS processing method (ASCII)"));
+ exif.SetValue(ExifTag.GPSAreaInformation, new EncodedString(EncodedString.CharacterCode.Unicode, "GPS area info (Unicode)"));
+
+ image.Metadata.ExifProfile = exif;
+
+ image.Save(memoryStream, new JpegEncoder());
+ }
+
+ memoryStream.Seek(0, SeekOrigin.Begin);
+ using (var image = Image.Load(memoryStream))
+ {
+ ExifProfile exif = image.Metadata.ExifProfile;
+ VerifyEncodedStrings(exif);
+ }
+ }
+
+ [Fact]
+ public void EncodedStringTags_Read()
+ {
+ using (var image = Image.Load(TestFile.GetInputFileFullPath(TestImages.Jpeg.Baseline.Calliphora_EncodedStrings)))
+ {
+ ExifProfile exif = image.Metadata.ExifProfile;
+ VerifyEncodedStrings(exif);
+ }
+ }
+
+ private static void VerifyEncodedStrings(ExifProfile exif)
+ {
+ Assert.NotNull(exif);
+
+ Assert.Equal("2022-01-06", exif.GetValue(ExifTag.GPSDateStamp).Value);
+
+ Assert.Equal("A bit of test metadata for image title", exif.GetValue(ExifTag.XPTitle).Value);
+ Assert.Equal("A bit of test metadata for image comment", exif.GetValue(ExifTag.XPComment).Value);
+ Assert.Equal("Dan Petitt", exif.GetValue(ExifTag.XPAuthor).Value);
+ Assert.Equal("Keyword1;Keyword2", exif.GetValue(ExifTag.XPKeywords).Value);
+ Assert.Equal("This is a subject", exif.GetValue(ExifTag.XPSubject).Value);
+
+ Assert.Equal("eng comment text (JIS)", exif.GetValue(ExifTag.UserComment).Value.Text);
+ Assert.Equal(EncodedString.CharacterCode.JIS, exif.GetValue(ExifTag.UserComment).Value.Code);
+
+ Assert.Equal("GPS processing method (ASCII)", exif.GetValue(ExifTag.GPSProcessingMethod).Value.Text);
+ Assert.Equal(EncodedString.CharacterCode.ASCII, exif.GetValue(ExifTag.GPSProcessingMethod).Value.Code);
+
+ Assert.Equal("GPS area info (Unicode)", (string)exif.GetValue(ExifTag.GPSAreaInformation).Value);
+ Assert.Equal(EncodedString.CharacterCode.Unicode, exif.GetValue(ExifTag.GPSAreaInformation).Value.Code);
+ }
}
}
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs
index 35113f14ff..3833b419c4 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs
@@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
// Calculating data from ImageSharp
byte[] sourceBytes = TestFile.Create(provider.SourceFileOrDescription).Bytes;
- var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder());
+ using var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder());
using var ms = new MemoryStream(sourceBytes);
using var bufferedStream = new BufferedReadStream(Configuration.Default, ms);
@@ -79,7 +79,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
// Calculating data from ImageSharp
byte[] sourceBytes = TestFile.Create(provider.SourceFileOrDescription).Bytes;
- var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder());
+ using var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder());
using var ms = new MemoryStream(sourceBytes);
using var bufferedStream = new BufferedReadStream(Configuration.Default, ms);
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/SpectralToPixelConversionTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/SpectralToPixelConversionTests.cs
index 0071c623c6..27240831c3 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/SpectralToPixelConversionTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/SpectralToPixelConversionTests.cs
@@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
// Decoding
using var converter = new SpectralConverter(Configuration.Default);
- var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder());
+ using var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder());
var scanDecoder = new HuffmanScanDecoder(bufferedStream, converter, cancellationToken: default);
decoder.ParseStream(bufferedStream, scanDecoder, cancellationToken: default);
diff --git a/tests/ImageSharp.Tests/Memory/Allocators/MemoryDiagnosticsTests.cs b/tests/ImageSharp.Tests/Memory/Allocators/MemoryDiagnosticsTests.cs
new file mode 100644
index 0000000000..5fab655cb3
--- /dev/null
+++ b/tests/ImageSharp.Tests/Memory/Allocators/MemoryDiagnosticsTests.cs
@@ -0,0 +1,122 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using System.Threading;
+using Microsoft.DotNet.RemoteExecutor;
+using SixLabors.ImageSharp.Diagnostics;
+using SixLabors.ImageSharp.Memory;
+using Xunit;
+
+namespace SixLabors.ImageSharp.Tests.Memory.Allocators
+{
+ public class MemoryDiagnosticsTests
+ {
+ private const int OneMb = 1 << 20;
+
+ private static MemoryAllocator Allocator => Configuration.Default.MemoryAllocator;
+
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public void PerfectCleanup_NoLeaksReported(bool isGroupOuter)
+ {
+ RemoteExecutor.Invoke(RunTest, isGroupOuter.ToString()).Dispose();
+
+ static void RunTest(string isGroupInner)
+ {
+ bool isGroup = bool.Parse(isGroupInner);
+ int leakCounter = 0;
+ MemoryDiagnostics.UndisposedAllocation += _ => Interlocked.Increment(ref leakCounter);
+
+ List buffers = new();
+
+ Assert.Equal(0, MemoryDiagnostics.TotalUndisposedAllocationCount);
+ for (int length = 1024; length <= 64 * OneMb; length *= 2)
+ {
+ long cntBefore = MemoryDiagnostics.TotalUndisposedAllocationCount;
+ IDisposable buffer = isGroup ?
+ Allocator.AllocateGroup(length, 1024) :
+ Allocator.Allocate(length);
+ buffers.Add(buffer);
+ long cntAfter = MemoryDiagnostics.TotalUndisposedAllocationCount;
+ Assert.True(cntAfter > cntBefore);
+ }
+
+ foreach (IDisposable buffer in buffers)
+ {
+ long cntBefore = MemoryDiagnostics.TotalUndisposedAllocationCount;
+ buffer.Dispose();
+ long cntAfter = MemoryDiagnostics.TotalUndisposedAllocationCount;
+ Assert.True(cntAfter < cntBefore);
+ }
+
+ Assert.Equal(0, MemoryDiagnostics.TotalUndisposedAllocationCount);
+ Assert.Equal(0, leakCounter);
+ }
+ }
+
+ [Theory]
+ [InlineData(false, false)]
+ [InlineData(false, true)]
+ [InlineData(true, false)]
+ [InlineData(true, true)]
+ public void MissingCleanup_LeaksAreReported(bool isGroupOuter, bool subscribeLeakHandleOuter)
+ {
+ RemoteExecutor.Invoke(RunTest, isGroupOuter.ToString(), subscribeLeakHandleOuter.ToString()).Dispose();
+
+ static void RunTest(string isGroupInner, string subscribeLeakHandleInner)
+ {
+ bool isGroup = bool.Parse(isGroupInner);
+ bool subscribeLeakHandle = bool.Parse(subscribeLeakHandleInner);
+ int leakCounter = 0;
+ bool stackTraceOk = true;
+ if (subscribeLeakHandle)
+ {
+ MemoryDiagnostics.UndisposedAllocation += stackTrace =>
+ {
+ Interlocked.Increment(ref leakCounter);
+ stackTraceOk &= stackTrace.Contains(nameof(RunTest)) && stackTrace.Contains(nameof(AllocateAndForget));
+ Assert.Contains(nameof(AllocateAndForget), stackTrace);
+ };
+ }
+
+ Assert.Equal(0, MemoryDiagnostics.TotalUndisposedAllocationCount);
+ for (int length = 1024; length <= 64 * OneMb; length *= 2)
+ {
+ long cntBefore = MemoryDiagnostics.TotalUndisposedAllocationCount;
+ AllocateAndForget(length, isGroup);
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+ GC.Collect();
+ long cntAfter = MemoryDiagnostics.TotalUndisposedAllocationCount;
+ Assert.True(cntAfter > cntBefore);
+ }
+
+ if (subscribeLeakHandle)
+ {
+ // Make sure at least some of the leak callbacks have time to complete on the ThreadPool
+ Thread.Sleep(200);
+ Assert.True(leakCounter > 3, $"leakCounter did not count enough leaks ({leakCounter} only)");
+ }
+
+ Assert.True(stackTraceOk);
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ static void AllocateAndForget(int length, bool isGroup)
+ {
+ if (isGroup)
+ {
+ _ = Allocator.AllocateGroup(length, 1024);
+ }
+ else
+ {
+ _ = Allocator.Allocate(length);
+ }
+ }
+ }
+ }
+}
diff --git a/tests/ImageSharp.Tests/Memory/Allocators/RefCountedLifetimeGuardTests.cs b/tests/ImageSharp.Tests/Memory/Allocators/RefCountedLifetimeGuardTests.cs
index 7fb3b7b7bb..4b808e8630 100644
--- a/tests/ImageSharp.Tests/Memory/Allocators/RefCountedLifetimeGuardTests.cs
+++ b/tests/ImageSharp.Tests/Memory/Allocators/RefCountedLifetimeGuardTests.cs
@@ -110,7 +110,7 @@ namespace SixLabors.ImageSharp.Tests.Memory.Allocators
}
}
- private class MockLifetimeGuard : RefCountedLifetimeGuard
+ private class MockLifetimeGuard : RefCountedMemoryLifetimeGuard
{
public int ReleaseInvocationCount { get; private set; }
diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs
index 7fc3ff6f19..a859852279 100644
--- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs
+++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs
@@ -51,6 +51,9 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif
{ ExifTag.ImageDescription, "ImageDescription" },
{ ExifTag.ExposureTime, new Rational(1.0 / 1600.0) },
{ ExifTag.Model, "Model" },
+ { ExifTag.XPAuthor, "The XPAuthor text" },
+ { ExifTag.UserComment, new EncodedString(EncodedString.CharacterCode.Unicode, "The Unicode text") },
+ { ExifTag.GPSAreaInformation, new EncodedString("Default constructor text (GPSAreaInformation)") },
};
[Theory]
@@ -504,7 +507,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif
public void IfdStructure()
{
var exif = new ExifProfile();
- exif.SetValue(ExifTag.XPAuthor, Encoding.GetEncoding("UCS-2").GetBytes("Dan Petitt"));
+ exif.SetValue(ExifTag.XPAuthor, "Dan Petitt");
Span actualBytes = exif.ToByteArray();
diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/Values/ExifValuesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/Values/ExifValuesTests.cs
index a5ca115d49..aaad4e2306 100644
--- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/Values/ExifValuesTests.cs
+++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/Values/ExifValuesTests.cs
@@ -1,6 +1,8 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
+using System;
+using System.Text;
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using Xunit;
@@ -23,11 +25,6 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif.Values
{ ExifTag.XMP },
{ ExifTag.CFAPattern2 },
{ ExifTag.TIFFEPStandardID },
- { ExifTag.XPTitle },
- { ExifTag.XPComment },
- { ExifTag.XPAuthor },
- { ExifTag.XPKeywords },
- { ExifTag.XPSubject },
{ ExifTag.GPSVersionID },
};
@@ -295,7 +292,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif.Values
{ ExifTag.GPSDestLongitudeRef },
{ ExifTag.GPSDestBearingRef },
{ ExifTag.GPSDestDistanceRef },
- { ExifTag.GPSDateStamp }
+ { ExifTag.GPSDateStamp },
};
public static TheoryData UndefinedTags => new TheoryData
@@ -311,7 +308,6 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif.Values
{ ExifTag.ExifVersion },
{ ExifTag.ComponentsConfiguration },
{ ExifTag.MakerNote },
- { ExifTag.UserComment },
{ ExifTag.FlashpixVersion },
{ ExifTag.SpatialFrequencyResponse },
{ ExifTag.SpatialFrequencyResponse2 },
@@ -319,10 +315,24 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif.Values
{ ExifTag.CFAPattern },
{ ExifTag.DeviceSettingDescription },
{ ExifTag.ImageSourceData },
+ };
+
+ public static TheoryData EncodedStringTags => new TheoryData
+ {
+ { ExifTag.UserComment },
{ ExifTag.GPSProcessingMethod },
{ ExifTag.GPSAreaInformation }
};
+ public static TheoryData Ucs2StringTags => new TheoryData
+ {
+ { ExifTag.XPTitle },
+ { ExifTag.XPComment },
+ { ExifTag.XPAuthor },
+ { ExifTag.XPKeywords },
+ { ExifTag.XPSubject },
+ };
+
[Theory]
[MemberData(nameof(ByteTags))]
public void ExifByteTests(ExifTag tag)
@@ -593,5 +603,46 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif.Values
var typed = (ExifByteArray)value;
Assert.Equal(expected, typed.Value);
}
+
+ [Theory]
+ [MemberData(nameof(EncodedStringTags))]
+ public void ExifEncodedStringTests(ExifTag tag)
+ {
+ foreach (object code in Enum.GetValues(typeof(EncodedString.CharacterCode)))
+ {
+ var charCode = (EncodedString.CharacterCode)code;
+
+ Assert.Equal(ExifEncodedStringHelpers.CharacterCodeBytesLength, ExifEncodedStringHelpers.GetCodeBytes(charCode).Length);
+
+ const string expectedText = "test string";
+ var expected = new EncodedString(charCode, expectedText);
+ ExifValue value = ExifValues.Create(tag);
+
+ Assert.False(value.TrySetValue(123));
+ Assert.True(value.TrySetValue(expected));
+
+ var typed = (ExifEncodedString)value;
+ Assert.Equal(expected, typed.Value);
+ Assert.Equal(expectedText, (string)typed.Value);
+ Assert.Equal(charCode, typed.Value.Code);
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(Ucs2StringTags))]
+ public void ExifUcs2StringTests(ExifTag tag)
+ {
+ const string expected = "Dan Petitt";
+ ExifValue value = ExifValues.Create(tag);
+
+ Assert.False(value.TrySetValue(123));
+ Assert.True(value.TrySetValue(expected));
+
+ var typed = (ExifUcs2String)value;
+ Assert.Equal(expected, typed.Value);
+
+ Assert.True(value.TrySetValue(Encoding.GetEncoding("UCS-2").GetBytes(expected)));
+ Assert.Equal(expected, typed.Value);
+ }
}
}
diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs
index 38fde5060a..597c02dfc2 100644
--- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs
+++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs
@@ -18,42 +18,41 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
public const string FlipTestFile = TestImages.Bmp.F;
public static readonly TheoryData InvalidOrientationValues
- = new TheoryData
- {
- { ExifDataType.Byte, new byte[] { 1 } },
- { ExifDataType.SignedByte, new byte[] { 2 } },
- { ExifDataType.SignedShort, BitConverter.GetBytes((short)3) },
- { ExifDataType.Long, BitConverter.GetBytes(4U) },
- { ExifDataType.SignedLong, BitConverter.GetBytes(5) }
- };
+ = new()
+ {
+ { ExifDataType.Byte, new byte[] { 1 } },
+ { ExifDataType.SignedByte, new byte[] { 2 } },
+ { ExifDataType.SignedShort, BitConverter.GetBytes((short)3) },
+ { ExifDataType.Long, BitConverter.GetBytes(4U) },
+ { ExifDataType.SignedLong, BitConverter.GetBytes(5) }
+ };
- public static readonly TheoryData ExifOrientationValues = new TheoryData
- {
- 0,
- 1,
- 2,
- 3,
- 4,
- 5,
- 6,
- 7,
- 8
- };
+ public static readonly TheoryData ExifOrientationValues
+ = new()
+ {
+ ExifOrientationMode.Unknown,
+ ExifOrientationMode.TopLeft,
+ ExifOrientationMode.TopRight,
+ ExifOrientationMode.BottomRight,
+ ExifOrientationMode.BottomLeft,
+ ExifOrientationMode.LeftTop,
+ ExifOrientationMode.RightTop,
+ ExifOrientationMode.RightBottom,
+ ExifOrientationMode.LeftBottom
+ };
[Theory]
[WithFile(FlipTestFile, nameof(ExifOrientationValues), PixelTypes.Rgba32)]
public void AutoOrient_WorksForAllExifOrientations(TestImageProvider provider, ushort orientation)
where TPixel : unmanaged, IPixel
{
- using (Image image = provider.GetImage())
- {
- image.Metadata.ExifProfile = new ExifProfile();
- image.Metadata.ExifProfile.SetValue(ExifTag.Orientation, orientation);
+ using Image image = provider.GetImage();
+ image.Metadata.ExifProfile = new ExifProfile();
+ image.Metadata.ExifProfile.SetValue(ExifTag.Orientation, orientation);
- image.Mutate(x => x.AutoOrient());
- image.DebugSave(provider, orientation, appendPixelTypeToFileName: false);
- image.CompareToReferenceOutput(provider, orientation, appendPixelTypeToFileName: false);
- }
+ image.Mutate(x => x.AutoOrient());
+ image.DebugSave(provider, orientation, appendPixelTypeToFileName: false);
+ image.CompareToReferenceOutput(provider, orientation, appendPixelTypeToFileName: false);
}
[Theory]
@@ -76,19 +75,17 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
// Change the number of components
bytes[20] = 1;
- var orientationCodeData = new byte[8];
+ byte[] orientationCodeData = new byte[8];
Array.Copy(orientation, orientationCodeData, orientation.Length);
ulong orientationCode = BitConverter.ToUInt64(orientationCodeData, 0);
- using (Image image = provider.GetImage())
- using (Image reference = image.Clone())
- {
- image.Metadata.ExifProfile = new ExifProfile(bytes);
- image.Mutate(x => x.AutoOrient());
- image.DebugSave(provider, $"{dataType}-{orientationCode}", appendPixelTypeToFileName: false);
- ImageComparer.Exact.VerifySimilarity(image, reference);
- }
+ using Image image = provider.GetImage();
+ using Image reference = image.Clone();
+ image.Metadata.ExifProfile = new ExifProfile(bytes);
+ image.Mutate(x => x.AutoOrient());
+ image.DebugSave(provider, $"{dataType}-{orientationCode}", appendPixelTypeToFileName: false);
+ ImageComparer.Exact.VerifySimilarity(image, reference);
}
}
}
diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs
index a73d262433..8b943194a5 100644
--- a/tests/ImageSharp.Tests/TestImages.cs
+++ b/tests/ImageSharp.Tests/TestImages.cs
@@ -192,6 +192,7 @@ namespace SixLabors.ImageSharp.Tests
public const string Exif = "Jpg/baseline/exif.jpg";
public const string Floorplan = "Jpg/baseline/Floorplan.jpg";
public const string Calliphora = "Jpg/baseline/Calliphora.jpg";
+ public const string Calliphora_EncodedStrings = "Jpg/baseline/Calliphora_encoded_strings.jpg";
public const string Ycck = "Jpg/baseline/ycck.jpg";
public const string Turtle420 = "Jpg/baseline/turtle.jpg";
public const string GammaDalaiLamaGray = "Jpg/baseline/gamma_dalai_lama_gray.jpg";
@@ -222,6 +223,7 @@ namespace SixLabors.ImageSharp.Tests
public const string Winter444_Interleaved = "Jpg/baseline/winter444_interleaved.jpg";
public const string Metadata = "Jpg/baseline/Metadata-test-file.jpg";
public const string ExtendedXmp = "Jpg/baseline/extended-xmp.jpg";
+ public const string GrayscaleSampling2x2 = "Jpg/baseline/grayscale_sampling22.jpg";
public static readonly string[] All =
{
@@ -449,6 +451,7 @@ namespace SixLabors.ImageSharp.Tests
public const string Issue1505 = "Gif/issues/issue1505_argumentoutofrange.png";
public const string Issue1530 = "Gif/issues/issue1530.gif";
public const string InvalidColorIndex = "Gif/issues/issue1668_invalidcolorindex.gif";
+ public const string Issue1962NoColorTable = "Gif/issues/issue1962_tiniest_gif_1st.gif";
}
public static readonly string[] All = { Rings, Giphy, Cheers, Trans, Kumin, Leo, Ratio4x1, Ratio1x4 };
diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue1962_Rgba32_issue1962_tiniest_gif_1st.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue1962_Rgba32_issue1962_tiniest_gif_1st.png
new file mode 100644
index 0000000000..24f5e9c0cd
--- /dev/null
+++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue1962_Rgba32_issue1962_tiniest_gif_1st.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4f8c6d416f09671777934e57bc67fb52ccc97145dc6f1869e628d9ffd7d8f6e7
+size 119
diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_grayscale_sampling22.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_grayscale_sampling22.png
new file mode 100644
index 0000000000..b2c3effdda
--- /dev/null
+++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_grayscale_sampling22.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ad22f28d20ea0ceda983a138b3bf9503ed836d779ed75a313f668329c910665e
+size 168405
diff --git a/tests/Images/Input/Gif/issues/issue1962_tiniest_gif_1st.gif b/tests/Images/Input/Gif/issues/issue1962_tiniest_gif_1st.gif
new file mode 100644
index 0000000000..a5bc432d8b
--- /dev/null
+++ b/tests/Images/Input/Gif/issues/issue1962_tiniest_gif_1st.gif
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3b7b8a4b411ddf8db9bacc2f3aabf406f8e4c0c087829b336ca331c40adfdff1
+size 26
diff --git a/tests/Images/Input/Jpg/baseline/Calliphora_encoded_strings.jpg b/tests/Images/Input/Jpg/baseline/Calliphora_encoded_strings.jpg
new file mode 100644
index 0000000000..b652ed2e58
--- /dev/null
+++ b/tests/Images/Input/Jpg/baseline/Calliphora_encoded_strings.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:59f76d2935a619d128a63d6bfcd5ce9feec492a7f5175327e47554c90b4ec242
+size 258081
diff --git a/tests/Images/Input/Jpg/baseline/grayscale_sampling22.jpg b/tests/Images/Input/Jpg/baseline/grayscale_sampling22.jpg
new file mode 100644
index 0000000000..b861c68ab5
--- /dev/null
+++ b/tests/Images/Input/Jpg/baseline/grayscale_sampling22.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a5cc8572082a54944d48b3e4f49e6c441871f6eb2b616fbbbfb025f20e0aeff5
+size 45066